4949
5050class TestCredentials (object ):
5151 CREDENTIAL_SOURCE_EXECUTABLE_COMMAND = "/fake/external/excutable --arg1=value1 --arg2=value2"
52+ CREDENTIAL_SOURCE_EXECUTABLE_OUTPUT_FILE = "fake_output_file"
5253 CREDENTIAL_SOURCE_EXECUTABLE = {
5354 "command" : CREDENTIAL_SOURCE_EXECUTABLE_COMMAND ,
5455 "timeout_millis" : 5000 ,
55- "output_file" : "/fake/output/file"
56+ "output_file" : CREDENTIAL_SOURCE_EXECUTABLE_OUTPUT_FILE
5657 }
5758 CREDENTIAL_SOURCE = {
5859 "executable" : CREDENTIAL_SOURCE_EXECUTABLE
@@ -78,7 +79,7 @@ class TestCredentials(object):
7879 "success" : True ,
7980 "token_type" : "urn:ietf:params:oauth:token-type:saml2" ,
8081 "saml_response" : EXECUTABLE_SAML_TOKEN ,
81- "expiration_time" : 1620433341
82+ "expiration_time" : 9999999999
8283 }
8384 EXECUTABLE_FAILED_RESPONSE = {
8485 "version" : 1 ,
@@ -488,7 +489,20 @@ def test_retrieve_subject_token_failed(self, fp):
488489 subject_token = credentials .retrieve_subject_token (None )
489490
490491 assert excinfo .match (r"Executable returned unsuccessful response" )
491-
492+
493+ @mock .patch .dict (os .environ , {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES" : "0" })
494+ def test_retrieve_subject_token_not_allowd (self , fp ):
495+ fp .register (self .CREDENTIAL_SOURCE_EXECUTABLE_COMMAND .split (), stdout = json .dumps (self .EXECUTABLE_SUCCESSFUL_OIDC_RESPONSE_ID_TOKEN ))
496+
497+ credentials = self .make_pluggable (
498+ credential_source = self .CREDENTIAL_SOURCE
499+ )
500+
501+ with pytest .raises (ValueError ) as excinfo :
502+ subject_token = credentials .retrieve_subject_token (None )
503+
504+ assert excinfo .match (r"Executables need to be explicitly allowed" )
505+
492506 @mock .patch .dict (os .environ , {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES" : "1" })
493507 def test_retrieve_subject_token_invalid_version (self , fp ):
494508 EXECUTABLE_SUCCESSFUL_OIDC_RESPONSE_VERSION_2 = {
@@ -498,7 +512,7 @@ def test_retrieve_subject_token_invalid_version(self, fp):
498512 "id_token" : self .EXECUTABLE_OIDC_TOKEN ,
499513 "expiration_time" : 9999999999
500514 }
501-
515+
502516 fp .register (self .CREDENTIAL_SOURCE_EXECUTABLE_COMMAND .split (), stdout = json .dumps (EXECUTABLE_SUCCESSFUL_OIDC_RESPONSE_VERSION_2 ))
503517
504518 credentials = self .make_pluggable (
@@ -508,4 +522,41 @@ def test_retrieve_subject_token_invalid_version(self, fp):
508522 with pytest .raises (exceptions .RefreshError ) as excinfo :
509523 subject_token = credentials .retrieve_subject_token (None )
510524
511- assert excinfo .match (r"Executable returned unsupported version" )
525+ assert excinfo .match (r"Executable returned unsupported version" )
526+
527+ @mock .patch .dict (os .environ , {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES" : "1" })
528+ def test_retrieve_subject_token_expired_token (self , fp ):
529+ EXECUTABLE_SUCCESSFUL_OIDC_RESPONSE_EXPIRED = {
530+ "version" : 1 ,
531+ "success" : True ,
532+ "token_type" : "urn:ietf:params:oauth:token-type:id_token" ,
533+ "id_token" : self .EXECUTABLE_OIDC_TOKEN ,
534+ "expiration_time" : 0
535+ }
536+
537+ fp .register (self .CREDENTIAL_SOURCE_EXECUTABLE_COMMAND .split (), stdout = json .dumps (EXECUTABLE_SUCCESSFUL_OIDC_RESPONSE_EXPIRED ))
538+
539+ credentials = self .make_pluggable (
540+ credential_source = self .CREDENTIAL_SOURCE
541+ )
542+
543+ with pytest .raises (exceptions .RefreshError ) as excinfo :
544+ subject_token = credentials .retrieve_subject_token (None )
545+
546+ assert excinfo .match (r"The token returned by the executable is expired" )
547+
548+ @mock .patch .dict (os .environ , {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES" : "1" })
549+ def test_retrieve_subject_token_file_cache (self , fp ):
550+ with open (self .CREDENTIAL_SOURCE_EXECUTABLE_OUTPUT_FILE , 'w' ) as output_file :
551+ json .dump (self .EXECUTABLE_SUCCESSFUL_OIDC_RESPONSE_ID_TOKEN , output_file )
552+
553+ credentials = self .make_pluggable (
554+ credential_source = self .CREDENTIAL_SOURCE
555+ )
556+
557+ subject_token = credentials .retrieve_subject_token (None )
558+
559+ assert subject_token == self .EXECUTABLE_OIDC_TOKEN
560+
561+ if os .path .exists (self .CREDENTIAL_SOURCE_EXECUTABLE_OUTPUT_FILE ):
562+ os .remove (self .CREDENTIAL_SOURCE_EXECUTABLE_OUTPUT_FILE )
0 commit comments