14
14
15
15
import base64
16
16
import datetime
17
+ import json
17
18
import os
18
19
import shutil
19
20
import tempfile
20
21
from types import SimpleNamespace
21
22
22
23
import yaml
23
- from asynctest import Mock , TestCase , main , patch
24
+ from asynctest import Mock , PropertyMock , TestCase , main , patch
24
25
from six import PY3
25
26
26
27
from .config_exception import ConfigException
@@ -39,6 +40,10 @@ def _base64(string):
39
40
return base64 .encodestring (string .encode ()).decode ()
40
41
41
42
43
+ def _unpadded_base64 (string ):
44
+ return base64 .b64encode (string .encode ()).decode ().rstrip ('' )
45
+
46
+
42
47
def _raise_exception (st ):
43
48
raise Exception (st )
44
49
@@ -67,6 +72,20 @@ def _raise_exception(st):
67
72
TEST_CLIENT_CERT = "client-cert"
68
73
TEST_CLIENT_CERT_BASE64 = _base64 (TEST_CLIENT_CERT )
69
74
75
+ TEST_OIDC_TOKEN = "test-oidc-token"
76
+ TEST_OIDC_INFO = "{\" name\" : \" test\" }"
77
+ TEST_OIDC_BASE = _unpadded_base64 (TEST_OIDC_TOKEN ) + "." + _unpadded_base64 (TEST_OIDC_INFO )
78
+ TEST_OIDC_LOGIN = TEST_OIDC_BASE + "." + TEST_CLIENT_CERT_BASE64
79
+ TEST_OIDC_TOKEN = "Bearer %s" % TEST_OIDC_LOGIN
80
+ TEST_OIDC_EXP = "{\" name\" : \" test\" ,\" exp\" : 536457600}"
81
+ TEST_OIDC_EXP_BASE = _unpadded_base64 (TEST_OIDC_TOKEN ) + "." + _unpadded_base64 (TEST_OIDC_EXP )
82
+ TEST_OIDC_EXPIRED_LOGIN = TEST_OIDC_EXP_BASE + "." + TEST_CLIENT_CERT_BASE64
83
+ TEST_OIDC_CA = _base64 (TEST_CERTIFICATE_AUTH )
84
+
85
+
86
+ async def _return_async_value (val ):
87
+ return val
88
+
70
89
71
90
class BaseTestCase (TestCase ):
72
91
@@ -333,6 +352,27 @@ class TestKubeConfigLoader(BaseTestCase):
333
352
"user" : "expired_gcp"
334
353
}
335
354
},
355
+ {
356
+ "name" : "oidc" ,
357
+ "context" : {
358
+ "cluster" : "default" ,
359
+ "user" : "oidc"
360
+ }
361
+ },
362
+ {
363
+ "name" : "expired_oidc" ,
364
+ "context" : {
365
+ "cluster" : "default" ,
366
+ "user" : "expired_oidc"
367
+ }
368
+ },
369
+ {
370
+ "name" : "expired_oidc_no_idp_cert_data" ,
371
+ "context" : {
372
+ "cluster" : "default" ,
373
+ "user" : "expired_oidc_no_idp_cert_data"
374
+ }
375
+ },
336
376
{
337
377
"name" : "user_pass" ,
338
378
"context" : {
@@ -450,6 +490,48 @@ class TestKubeConfigLoader(BaseTestCase):
450
490
"password" : TEST_PASSWORD , # should be ignored
451
491
}
452
492
},
493
+ {
494
+ "name" : "oidc" ,
495
+ "user" : {
496
+ "auth-provider" : {
497
+ "name" : "oidc" ,
498
+ "config" : {
499
+ "id-token" : TEST_OIDC_LOGIN
500
+ }
501
+ }
502
+ }
503
+ },
504
+ {
505
+ "name" : "expired_oidc" ,
506
+ "user" : {
507
+ "auth-provider" : {
508
+ "name" : "oidc" ,
509
+ "config" : {
510
+ "client-id" : "tectonic-kubectl" ,
511
+ "client-secret" : "FAKE_SECRET" ,
512
+ "id-token" : TEST_OIDC_EXPIRED_LOGIN ,
513
+ "idp-certificate-authority-data" : TEST_OIDC_CA ,
514
+ "idp-issuer-url" : "https://example.localhost/identity" ,
515
+ "refresh-token" : "lucWJjEhlxZW01cXI3YmVlcYnpxNGhzk"
516
+ }
517
+ }
518
+ }
519
+ },
520
+ {
521
+ "name" : "expired_oidc_no_idp_cert_data" ,
522
+ "user" : {
523
+ "auth-provider" : {
524
+ "name" : "oidc" ,
525
+ "config" : {
526
+ "client-id" : "tectonic-kubectl" ,
527
+ "client-secret" : "FAKE_SECRET" ,
528
+ "id-token" : TEST_OIDC_EXPIRED_LOGIN ,
529
+ "idp-issuer-url" : "https://example.localhost/identity" ,
530
+ "refresh-token" : "lucWJjEhlxZW01cXI3YmVlcYnpxNGhzk"
531
+ }
532
+ }
533
+ }
534
+ },
453
535
{
454
536
"name" : "user_pass" ,
455
537
"user" : {
@@ -564,6 +646,64 @@ async def cred():
564
646
self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
565
647
loader .token )
566
648
649
+ async def test_oidc_no_refresh (self ):
650
+ loader = KubeConfigLoader (
651
+ config_dict = self .TEST_KUBE_CONFIG ,
652
+ active_context = 'oidc' ,
653
+ )
654
+ self .assertTrue (await loader ._load_oid_token ())
655
+ self .assertEqual (TEST_OIDC_TOKEN , loader .token )
656
+
657
+ @patch ('kubernetes_asyncio.config.kube_config.OAuth2Session.refresh_token' )
658
+ @patch ('kubernetes_asyncio.config.kube_config.ApiClient' )
659
+ async def test_oidc_with_refresh (self , mock_api_client , mock_oauth2_session ):
660
+ mock_response = Mock ()
661
+ type(mock_response ).status = PropertyMock (
662
+ return_value = 200
663
+ )
664
+ type(mock_response ).data = PropertyMock (
665
+ return_value = json .dumps ({
666
+ 'token_endpoint' : 'https://example.localhost/identity/token'
667
+ })
668
+ )
669
+
670
+ mock_api_client ().request .return_value = _return_async_value (mock_response )
671
+ mock_oauth2_session .return_value = {
672
+ 'id_token' : 'abc123' ,
673
+ 'refresh_token' : 'newtoken123'
674
+ }
675
+ loader = KubeConfigLoader (
676
+ config_dict = self .TEST_KUBE_CONFIG ,
677
+ active_context = 'expired_oidc' ,
678
+ )
679
+ self .assertTrue (await loader ._load_oid_token ())
680
+ self .assertEqual ('Bearer abc123' , loader .token )
681
+
682
+ @patch ('kubernetes_asyncio.config.kube_config.OAuth2Session.refresh_token' )
683
+ @patch ('kubernetes_asyncio.config.kube_config.ApiClient' )
684
+ async def test_oidc_with_refresh_no_idp_cert_data (self , mock_api_client , mock_oauth2_session ):
685
+ mock_response = Mock ()
686
+ type(mock_response ).status = PropertyMock (
687
+ return_value = 200
688
+ )
689
+ type(mock_response ).data = PropertyMock (
690
+ return_value = json .dumps ({
691
+ 'token_endpoint' : 'https://example.localhost/identity/token'
692
+ })
693
+ )
694
+
695
+ mock_api_client ().request .return_value = _return_async_value (mock_response )
696
+ mock_oauth2_session .return_value = {
697
+ 'id_token' : 'abc123' ,
698
+ 'refresh_token' : 'newtoken123'
699
+ }
700
+ loader = KubeConfigLoader (
701
+ config_dict = self .TEST_KUBE_CONFIG ,
702
+ active_context = 'expired_oidc_no_idp_cert_data' ,
703
+ )
704
+ self .assertTrue (await loader ._load_oid_token ())
705
+ self .assertEqual ('Bearer abc123' , loader .token )
706
+
567
707
async def test_user_pass (self ):
568
708
expected = FakeConfig (host = TEST_HOST , token = TEST_BASIC_TOKEN )
569
709
actual = FakeConfig ()
0 commit comments