|
47 | 47 |
|
48 | 48 | _GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
|
49 | 49 |
|
| 50 | +_SOURCE_CREDENTIAL_AUTHORIZED_USER_TYPE = "authorized_user" |
| 51 | +_SOURCE_CREDENTIAL_SERVICE_ACCOUNT_TYPE = "service_account" |
| 52 | +_SOURCE_CREDENTIAL_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = ( |
| 53 | + "external_account_authorized_user" |
| 54 | +) |
| 55 | + |
50 | 56 |
|
51 | 57 | def _make_iam_token_request(
|
52 | 58 | request,
|
@@ -410,6 +416,75 @@ def with_scopes(self, scopes, default_scopes=None):
|
410 | 416 | cred._target_scopes = scopes or default_scopes
|
411 | 417 | return cred
|
412 | 418 |
|
| 419 | + @classmethod |
| 420 | + def from_impersonated_service_account_info(cls, info, scopes=None): |
| 421 | + """Creates a Credentials instance from parsed impersonated service account credentials info. |
| 422 | +
|
| 423 | + Args: |
| 424 | + info (Mapping[str, str]): The impersonated service account credentials info in Google |
| 425 | + format. |
| 426 | + scopes (Sequence[str]): Optional list of scopes to include in the |
| 427 | + credentials. |
| 428 | +
|
| 429 | + Returns: |
| 430 | + google.oauth2.credentials.Credentials: The constructed |
| 431 | + credentials. |
| 432 | +
|
| 433 | + Raises: |
| 434 | + InvalidType: If the info["source_credentials"] are not a supported impersonation type |
| 435 | + InvalidValue: If the info["service_account_impersonation_url"] is not in the expected format. |
| 436 | + ValueError: If the info is not in the expected format. |
| 437 | + """ |
| 438 | + |
| 439 | + source_credentials_info = info.get("source_credentials") |
| 440 | + source_credentials_type = source_credentials_info.get("type") |
| 441 | + if source_credentials_type == _SOURCE_CREDENTIAL_AUTHORIZED_USER_TYPE: |
| 442 | + from google.oauth2 import credentials |
| 443 | + |
| 444 | + source_credentials = credentials.Credentials.from_authorized_user_info( |
| 445 | + source_credentials_info |
| 446 | + ) |
| 447 | + elif source_credentials_type == _SOURCE_CREDENTIAL_SERVICE_ACCOUNT_TYPE: |
| 448 | + from google.oauth2 import service_account |
| 449 | + |
| 450 | + source_credentials = service_account.Credentials.from_service_account_info( |
| 451 | + source_credentials_info |
| 452 | + ) |
| 453 | + elif ( |
| 454 | + source_credentials_type |
| 455 | + == _SOURCE_CREDENTIAL_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE |
| 456 | + ): |
| 457 | + from google.auth import external_account_authorized_user |
| 458 | + |
| 459 | + source_credentials = external_account_authorized_user.Credentials.from_info( |
| 460 | + source_credentials_info |
| 461 | + ) |
| 462 | + else: |
| 463 | + raise exceptions.InvalidType( |
| 464 | + "source credential of type {} is not supported.".format( |
| 465 | + source_credentials_type |
| 466 | + ) |
| 467 | + ) |
| 468 | + |
| 469 | + impersonation_url = info.get("service_account_impersonation_url") |
| 470 | + start_index = impersonation_url.rfind("/") |
| 471 | + end_index = impersonation_url.find(":generateAccessToken") |
| 472 | + if start_index == -1 or end_index == -1 or start_index > end_index: |
| 473 | + raise exceptions.InvalidValue( |
| 474 | + "Cannot extract target principal from {}".format(impersonation_url) |
| 475 | + ) |
| 476 | + target_principal = impersonation_url[start_index + 1 : end_index] |
| 477 | + delegates = info.get("delegates") |
| 478 | + quota_project_id = info.get("quota_project_id") |
| 479 | + |
| 480 | + return cls( |
| 481 | + source_credentials, |
| 482 | + target_principal, |
| 483 | + scopes, |
| 484 | + delegates, |
| 485 | + quota_project_id=quota_project_id, |
| 486 | + ) |
| 487 | + |
413 | 488 |
|
414 | 489 | class IDTokenCredentials(credentials.CredentialsWithQuotaProject):
|
415 | 490 | """Open ID Connect ID Token-based service account credentials.
|
|
0 commit comments