@@ -410,6 +410,92 @@ def with_scopes(self, scopes, default_scopes=None):
410410 cred ._target_scopes = scopes or default_scopes
411411 return cred
412412
413+ @classmethod
414+ def _source_credentials_from_impersonated_account_info (cls , info ):
415+ """Creates a Credentials instance from parsed authorized user info.
416+
417+ Args:
418+ info (Mapping[str, str]): The authorized user info in Google
419+ format.
420+
421+ Returns:
422+ google.oauth2.credentials.Credentials: The constructed
423+ credentials.
424+
425+ Raises:
426+ InvalidType: If the source_credentials are not a support impersonation type
427+ ValueError: If the info is not in the expected format.
428+ """
429+ _AUTHORIZED_USER_TYPE = "authorized_user"
430+ _SERVICE_ACCOUNT_TYPE = "service_account"
431+ _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = "external_account_authorized_user"
432+
433+ source_credentials_info = info .get ("source_credentials" )
434+ source_credentials_type = source_credentials_info .get ("type" )
435+ if source_credentials_type == _AUTHORIZED_USER_TYPE :
436+ from google .oauth2 import credentials
437+ source_credentials , _ = credentials .Credentials .from_authorized_user_info (
438+ info
439+ )
440+ elif source_credentials_type == _SERVICE_ACCOUNT_TYPE :
441+ from google .oauth2 import service_account
442+ source_credentials , _ = service_account .Credentials .from_service_account_info (
443+ source_credentials_info
444+ )
445+ elif source_credentials_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE :
446+ from google .auth import external_account_authorized_user
447+ source_credentials = external_account_authorized_user .Credentials .from_info (
448+ source_credentials_info
449+ )
450+ else :
451+ raise exceptions .InvalidType (
452+ "source credential of type {} is not supported." .format (
453+ source_credentials_type
454+ )
455+ )
456+
457+ return source_credentials
458+
459+ @classmethod
460+ def from_impersonated_account_info (cls , info , scopes = None ):
461+ """Creates a Credentials instance from parsed authorized user info.
462+
463+ Args:
464+ info (Mapping[str, str]): The authorized user info in Google
465+ format.
466+ scopes (Sequence[str]): Optional list of scopes to include in the
467+ credentials.
468+
469+ Returns:
470+ google.oauth2.credentials.Credentials: The constructed
471+ credentials.
472+
473+ Raises:
474+ InvalidType: If the source_credentials are not a support impersonation type
475+ ValueError: If the info is not in the expected format.
476+ """
477+ source_credentials = cls ._source_credentials_from_impersonated_account_info (info )
478+
479+ impersonation_url = info .get ("service_account_impersonation_url" )
480+ start_index = impersonation_url .rfind ("/" )
481+ end_index = impersonation_url .find (":generateAccessToken" )
482+ if start_index == - 1 or end_index == - 1 or start_index > end_index :
483+ raise exceptions .InvalidValue (
484+ "Cannot extract target principal from {}" .format (impersonation_url )
485+ )
486+ target_principal = impersonation_url [start_index + 1 : end_index ]
487+
488+ delegates = info .get ("delegates" )
489+ quota_project_id = info .get ("quota_project_id" )
490+
491+ return cls (
492+ source_credentials ,
493+ target_principal ,
494+ scopes ,
495+ delegates ,
496+ quota_project_id = quota_project_id
497+ )
498+
413499
414500class IDTokenCredentials (credentials .CredentialsWithQuotaProject ):
415501 """Open ID Connect ID Token-based service account credentials.
0 commit comments