22import concurrent .futures
33import grpc
44import time
5+ import http .server
6+ import urllib
7+ import threading
8+ import json
59
610import ydb .iam
711
1418PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu+fyUt6zHCycbxYKTsxe\n ftoB/mIoN0RNjE5fbsAtAWSgL+n6GK+8csEMliCvltXAYc/EeC3xZmaNozNGgr3U\n ZjQXVtBA0k5xy8QrBYaEFi1avmyOX+Y85HKIoqFLWhKQAz6sIFVC1bTf55zyYmev\n 3VN9At45kevi1IBJ7VLVrd3qt8UCJ+ZRt8wtZJQWPx8bXx+R7vMQb8EUEyZ4d1KT\n y4/F8Epq4g4Ha4Ue6OrplslbhqzCpSx5nor5X842LX8ztTDiDBvLdv88RkfsW+Fc\n JLO97B9Sq7h/9PyTF1Pe56cKXvlJvrl4aZXT8oBhKxImu2m8mQdoDKV6q1mCjKNN\n jQIDAQAB\n -----END PUBLIC KEY-----\n "
1519
1620
17- def test_credentials ():
21+ def test_metadata_credentials ():
1822 credentials = ydb .iam .MetadataUrlCredentials ()
1923 raised = False
2024 try :
@@ -48,29 +52,14 @@ def __init__(self):
4852 self .server .start ()
4953
5054 def stop (self ):
55+ self .server .stop (1 )
5156 self .server .wait_for_termination ()
5257
5358 def get_endpoint (self ):
5459 return "[::]:54321"
5560
5661
5762class TestServiceAccountCredentials (ydb .iam .ServiceAccountCredentials ):
58- def __init__ (
59- self ,
60- service_account_id ,
61- access_key_id ,
62- private_key ,
63- iam_endpoint = None ,
64- iam_channel_credentials = None ,
65- ):
66- super (TestServiceAccountCredentials , self ).__init__ (
67- service_account_id ,
68- access_key_id ,
69- private_key ,
70- iam_endpoint ,
71- iam_channel_credentials ,
72- )
73-
7463 def _channel_factory (self ):
7564 return grpc .insecure_channel (
7665 self ._iam_endpoint
@@ -80,12 +69,80 @@ def get_expire_time(self):
8069 return self ._expires_in - time .time ()
8170
8271
83- def test_service_account_credentials ():
72+ class TestNebiusServiceAccountCredentials (ydb .iam .NebiusServiceAccountCredentials ):
73+ def get_expire_time (self ):
74+ return self ._expires_in - time .time ()
75+
76+
77+ class NebiusTokenServiceHandler (http .server .BaseHTTPRequestHandler ):
78+ def do_POST (self ):
79+ assert self .headers ["Content-Type" ] == "application/x-www-form-urlencoded"
80+ assert self .path == "/token/exchange"
81+ content_length = int (self .headers ["Content-Length" ])
82+ post_data = self .rfile .read (content_length ).decode ("utf8" )
83+ print ("NebiusTokenServiceHandler.POST data: {}" .format (post_data ))
84+ parsed_request = urllib .parse .parse_qs (str (post_data ))
85+ assert len (parsed_request ["grant_type" ]) == 1
86+ assert parsed_request ["grant_type" ][0 ] == "urn:ietf:params:oauth:grant-type:token-exchange"
87+
88+ assert len (parsed_request ["requested_token_type" ]) == 1
89+ assert parsed_request ["requested_token_type" ][0 ] == "urn:ietf:params:oauth:token-type:access_token"
90+
91+ assert len (parsed_request ["subject_token_type" ]) == 1
92+ assert parsed_request ["subject_token_type" ][0 ] == "urn:ietf:params:oauth:token-type:jwt"
93+
94+ assert len (parsed_request ["subject_token" ]) == 1
95+ jwt_token = parsed_request ["subject_token" ][0 ]
96+ decoded = jwt .decode (jwt_token , key = PUBLIC_KEY , algorithms = ["RS256" ], audience = "token-service.iam.new.nebiuscloud.net" )
97+ assert decoded ["iss" ] == SERVICE_ACCOUNT_ID
98+ assert decoded ["sub" ] == SERVICE_ACCOUNT_ID
99+ assert decoded ["aud" ] == "token-service.iam.new.nebiuscloud.net"
100+ assert abs (decoded ["iat" ] - time .time ()) <= 60
101+ assert abs (decoded ["exp" ] - time .time ()) <= 3600
102+
103+ response = {
104+ "access_token" : "test_nebius_token" ,
105+ "issued_token_type" : "urn:ietf:params:oauth:token-type:access_token" ,
106+ "token_type" : "Bearer" ,
107+ "expires_in" : 42
108+ }
109+
110+ self .send_response (200 )
111+ self .send_header ("Content-type" , "application/json" )
112+ self .end_headers ()
113+ self .wfile .write (json .dumps (response ).encode ("utf8" ))
114+
115+
116+ class NebiusTokenServiceForTest (http .server .HTTPServer ):
117+ def __init__ (self ):
118+ http .server .HTTPServer .__init__ (self , ("localhost" , 54322 ), NebiusTokenServiceHandler )
119+
120+ def endpoint (self ):
121+ return "http://localhost:54322/token/exchange"
122+
123+
124+ def test_yandex_service_account_credentials ():
84125 server = IamTokenServiceTestServer ()
85- iam_endpoint = server .get_endpoint ()
86- grpc_channel_creds = grpc .local_channel_credentials ()
87- credentials = TestServiceAccountCredentials (SERVICE_ACCOUNT_ID , ACCESS_KEY_ID , PRIVATE_KEY , iam_endpoint , grpc_channel_creds )
126+ credentials = TestServiceAccountCredentials (SERVICE_ACCOUNT_ID , ACCESS_KEY_ID , PRIVATE_KEY , server .get_endpoint ())
88127 credentials .set_token_expiration_timeout (1 )
89128 t = credentials .get_auth_token ()
90129 assert t == "test_token"
91130 assert credentials .get_expire_time () <= 42
131+ server .stop ()
132+
133+
134+ def test_nebius_service_account_credentials ():
135+ server = NebiusTokenServiceForTest ()
136+
137+ def serve (s ):
138+ s .handle_request ()
139+
140+ serve_thread = threading .Thread (target = serve , args = (server ,))
141+ serve_thread .start ()
142+
143+ credentials = TestNebiusServiceAccountCredentials (SERVICE_ACCOUNT_ID , ACCESS_KEY_ID , PRIVATE_KEY , server .endpoint ())
144+ t = credentials .get_auth_token ()
145+ assert t == "test_nebius_token"
146+ assert credentials .get_expire_time () <= 42
147+
148+ serve_thread .join ()
0 commit comments