@@ -22,6 +22,7 @@ class AuthType(str, metaclass=ExtendedEnumMeta):
22
22
API_KEY = "api_key"
23
23
RESOURCE_PRINCIPAL = "resource_principal"
24
24
INSTANCE_PRINCIPAL = "instance_principal"
25
+ SECURITY_TOKEN = "security_token"
25
26
26
27
27
28
class SingletonMeta (type ):
@@ -140,6 +141,14 @@ def set_auth(
140
141
141
142
>>> ads.set_auth("instance_principal") # Set instance principal authentication
142
143
144
+ >>> ads.set_auth("security_token") # Set security token authentication
145
+
146
+ >>> config = dict(
147
+ ... key_file=~/.oci/sessions/DEFAULT/oci_api_key.pem
148
+ ... security_token_file=~/.oci/sessions/DEFAULT/token
149
+ ... )
150
+ >>> ads.set_auth("security_token", config=config) # Set security token authentication from provided config
151
+
143
152
>>> singer = oci.signer.Signer(
144
153
... user=ocid1.user.oc1..<unique_ID>,
145
154
... fingerprint=<fingerprint>,
@@ -274,6 +283,50 @@ def resource_principal(
274
283
return signer_generator (signer_args ).create_signer ()
275
284
276
285
286
+ def security_token (
287
+ oci_config : Union [str , Dict ] = os .path .expanduser (DEFAULT_LOCATION ),
288
+ profile : str = DEFAULT_PROFILE ,
289
+ client_kwargs : Dict = None ,
290
+ ) -> Dict :
291
+ """
292
+ Prepares authentication and extra arguments necessary for creating clients for different OCI services using Security Token.
293
+
294
+ Parameters
295
+ ----------
296
+ oci_config: Optional[Union[str, Dict]], default is ~/.oci/config
297
+ OCI authentication config file location or a dictionary with config attributes.
298
+ profile: Optional[str], is DEFAULT_PROFILE, which is 'DEFAULT'
299
+ Profile name to select from the config file.
300
+ client_kwargs: Optional[Dict], default None
301
+ kwargs that are required to instantiate the Client if we need to override the defaults.
302
+
303
+ Returns
304
+ -------
305
+ dict
306
+ Contains keys - config, signer and client_kwargs.
307
+
308
+ - The config contains the config loaded from the configuration loaded from `oci_config`.
309
+ - The signer contains the signer object created from the security token.
310
+ - client_kwargs contains the `client_kwargs` that was passed in as input parameter.
311
+
312
+ Examples
313
+ --------
314
+ >>> from ads.common import oci_client as oc
315
+ >>> auth = ads.auth.security_token(oci_config="/home/datascience/.oci/config", profile="TEST", client_kwargs={"timeout": 6000})
316
+ >>> oc.OCIClientFactory(**auth).object_storage # Creates Object storage client with timeout set to 6000 using Security Token authentication
317
+ """
318
+ signer_args = dict (
319
+ oci_config = oci_config if isinstance (oci_config , Dict ) else {},
320
+ oci_config_location = oci_config
321
+ if isinstance (oci_config , str )
322
+ else os .path .expanduser (DEFAULT_LOCATION ),
323
+ oci_key_profile = profile ,
324
+ client_kwargs = client_kwargs ,
325
+ )
326
+ signer_generator = AuthFactory ().signerGenerator (AuthType .SECURITY_TOKEN )
327
+ return signer_generator (signer_args ).create_signer ()
328
+
329
+
277
330
def create_signer (
278
331
auth_type : Optional [str ] = AuthType .API_KEY ,
279
332
oci_config_location : Optional [str ] = DEFAULT_LOCATION ,
@@ -346,6 +399,11 @@ def create_signer(
346
399
>>> signer_callable = oci.auth.signers.InstancePrincipalsSecurityTokenSigner
347
400
>>> signer_kwargs = dict(log_requests=True) # will log the request url and response data when retrieving
348
401
>>> auth = ads.auth.create_signer(signer_callable=signer_callable, signer_kwargs=signer_kwargs) # instance principals authentication dictionary created based on callable with kwargs parameters
402
+ >>> config = dict(
403
+ ... key_file=~/.oci/sessions/DEFAULT/oci_api_key.pem
404
+ ... security_token_file=~/.oci/sessions/DEFAULT/token
405
+ ... )
406
+ >>> auth = ads.auth.create_signer(auth_type="security_token", config=config) # security token authentication created based on provided config
349
407
"""
350
408
if signer or signer_callable :
351
409
configuration = ads .telemetry .update_oci_client_config ()
@@ -365,8 +423,6 @@ def create_signer(
365
423
oci_config = config ,
366
424
client_kwargs = client_kwargs ,
367
425
)
368
- if config :
369
- auth_type = AuthType .API_KEY
370
426
371
427
signer_generator = AuthFactory ().signerGenerator (auth_type )
372
428
@@ -678,6 +734,102 @@ def create_signer(self) -> Dict:
678
734
return signer_dict
679
735
680
736
737
+ class SecurityToken (AuthSignerGenerator ):
738
+ def __init__ (self , args : Optional [Dict ] = None ):
739
+ """
740
+ Signer created based on args provided. If not provided current values of according arguments
741
+ will be used from current global state from AuthState class.
742
+
743
+ Parameters
744
+ ----------
745
+ args: dict
746
+ args that are required to create Security Token signer. Contains keys: oci_config,
747
+ oci_config_location, oci_key_profile, client_kwargs.
748
+
749
+ - oci_config is a configuration dict that can be used to create clients
750
+ - oci_config_location - path to config file
751
+ - oci_key_profile - the profile to load from config file
752
+ - client_kwargs - optional parameters for OCI client creation in next steps
753
+ """
754
+ self .oci_config = args .get ("oci_config" )
755
+ self .oci_config_location = args .get ("oci_config_location" )
756
+ self .oci_key_profile = args .get ("oci_key_profile" )
757
+ self .client_kwargs = args .get ("client_kwargs" )
758
+
759
+ def create_signer (self ) -> Dict :
760
+ """
761
+ Creates security token configuration and signer with extra arguments necessary for creating clients.
762
+ Signer constructed from the `oci_config` provided. If not 'oci_config', configuration will be
763
+ constructed from 'oci_config_location' and 'oci_key_profile' in place.
764
+
765
+ Returns
766
+ -------
767
+ dict
768
+ Contains keys - config, signer and client_kwargs.
769
+
770
+ - config contains the configuration information
771
+ - signer contains the signer object created. It is instantiated from signer_callable, or
772
+ signer provided in args used, or instantiated in place
773
+ - client_kwargs contains the `client_kwargs` that was passed in as input parameter
774
+
775
+ Examples
776
+ --------
777
+ >>> signer_args = dict(
778
+ ... client_kwargs=client_kwargs
779
+ ... )
780
+ >>> signer_generator = AuthFactory().signerGenerator(AuthType.SECURITY_TOKEN)
781
+ >>> signer_generator(signer_args).create_signer()
782
+ """
783
+ if self .oci_config :
784
+ configuration = ads .telemetry .update_oci_client_config (self .oci_config )
785
+ else :
786
+ configuration = ads .telemetry .update_oci_client_config (
787
+ oci .config .from_file (self .oci_config_location , self .oci_key_profile )
788
+ )
789
+
790
+ logger .info (f"Using 'security_token' authentication." )
791
+
792
+ if "security_token_file" not in configuration and "security_token_content" not in configuration :
793
+ raise ValueError (
794
+ "Parameter `security_token_file` or `security_token_content` must be provided for using `security_token` authentication."
795
+ )
796
+
797
+ if "key_file" not in configuration and "key_content" not in configuration :
798
+ raise ValueError (
799
+ "Parameter `key_file` or `key_content` must be provided for using `security_token` authentication."
800
+ )
801
+
802
+ if "security_token_content" not in configuration and not self .oci_config :
803
+ os .system (f'oci session refresh --profile { self .oci_key_profile or DEFAULT_PROFILE } ' )
804
+
805
+ return {
806
+ "config" : configuration ,
807
+ "signer" : oci .auth .signers .SecurityTokenSigner (
808
+ token = (
809
+ configuration .get ("security_token_content" , None )
810
+ or self ._read_security_token_file (configuration .get ("security_token_file" ))
811
+ ),
812
+ private_key = (
813
+ oci .signer .load_private_key (configuration .get ("key_content" ))
814
+ if configuration .get ("key_content" )
815
+ else oci .signer .load_private_key_from_file (configuration .get ("key_file" ))
816
+ ),
817
+ generic_headers = configuration .get ("generic_headers" ),
818
+ body_headers = configuration .get ("body_headers" )
819
+ ),
820
+ "client_kwargs" : self .client_kwargs ,
821
+ }
822
+
823
+ def _read_security_token_file (self , security_token_file : str ) -> str :
824
+ try :
825
+ token = None
826
+ with open (security_token_file , 'r' ) as f :
827
+ token = f .read ()
828
+ return token
829
+ except :
830
+ raise
831
+
832
+
681
833
class AuthFactory :
682
834
"""
683
835
AuthFactory class which contains list of registered signers and alllows to register new signers.
@@ -687,12 +839,14 @@ class AuthFactory:
687
839
* APIKey
688
840
* ResourcePrincipal
689
841
* InstancePrincipal
842
+ * SecurityToken
690
843
"""
691
844
692
845
classes = {
693
846
AuthType .API_KEY : APIKey ,
694
847
AuthType .RESOURCE_PRINCIPAL : ResourcePrincipal ,
695
848
AuthType .INSTANCE_PRINCIPAL : InstancePrincipal ,
849
+ AuthType .SECURITY_TOKEN : SecurityToken ,
696
850
}
697
851
698
852
@classmethod
@@ -726,7 +880,7 @@ def signerGenerator(self, iam_type: Optional[str] = "api_key"):
726
880
727
881
Returns
728
882
-------
729
- :class:`APIKey` or :class:`ResourcePrincipal` or :class:`InstancePrincipal`
883
+ :class:`APIKey` or :class:`ResourcePrincipal` or :class:`InstancePrincipal` or :class:`SecurityToken`
730
884
returns one of classes, which implements creation of signer of specified type
731
885
732
886
Raises
0 commit comments