|
55 | 55 | SUBJECT_TOKEN_TEXT_FILE = os.path.join(DATA_DIR, "external_subject_token.txt") |
56 | 56 | TOKEN_URL = "https://sts.googleapis.com/v1/token" |
57 | 57 | AUDIENCE = "//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID" |
| 58 | +WORKFORCE_AUDIENCE = ( |
| 59 | + "//iam.googleapis.com/locations/global/workforcePools/POOL_ID/providers/PROVIDER_ID" |
| 60 | +) |
| 61 | +WORKFORCE_POOL_USER_PROJECT = "WORKFORCE_POOL_USER_PROJECT_NUMBER" |
58 | 62 | REGION_URL = "http://169.254.169.254/latest/meta-data/placement/availability-zone" |
59 | 63 | SECURITY_CREDS_URL = "http://169.254.169.254/latest/meta-data/iam/security-credentials" |
60 | 64 | CRED_VERIFICATION_URL = ( |
|
79 | 83 | "regional_cred_verification_url": CRED_VERIFICATION_URL, |
80 | 84 | }, |
81 | 85 | } |
| 86 | +SERVICE_ACCOUNT_EMAIL = "service-1234@service-name.iam.gserviceaccount.com" |
| 87 | +SERVICE_ACCOUNT_IMPERSONATION_URL = ( |
| 88 | + "https://us-east1-iamcredentials.googleapis.com/v1/projects/-" |
| 89 | + + "/serviceAccounts/{}:generateAccessToken".format(SERVICE_ACCOUNT_EMAIL) |
| 90 | +) |
| 91 | +IMPERSONATED_IDENTITY_POOL_DATA = { |
| 92 | + "type": "external_account", |
| 93 | + "audience": AUDIENCE, |
| 94 | + "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", |
| 95 | + "token_url": TOKEN_URL, |
| 96 | + "credential_source": {"file": SUBJECT_TOKEN_TEXT_FILE}, |
| 97 | + "service_account_impersonation_url": SERVICE_ACCOUNT_IMPERSONATION_URL, |
| 98 | +} |
| 99 | +IMPERSONATED_AWS_DATA = { |
| 100 | + "type": "external_account", |
| 101 | + "audience": AUDIENCE, |
| 102 | + "subject_token_type": "urn:ietf:params:aws:token-type:aws4_request", |
| 103 | + "token_url": TOKEN_URL, |
| 104 | + "credential_source": { |
| 105 | + "environment_id": "aws1", |
| 106 | + "region_url": REGION_URL, |
| 107 | + "url": SECURITY_CREDS_URL, |
| 108 | + "regional_cred_verification_url": CRED_VERIFICATION_URL, |
| 109 | + }, |
| 110 | + "service_account_impersonation_url": SERVICE_ACCOUNT_IMPERSONATION_URL, |
| 111 | +} |
| 112 | +IDENTITY_POOL_WORKFORCE_DATA = { |
| 113 | + "type": "external_account", |
| 114 | + "audience": WORKFORCE_AUDIENCE, |
| 115 | + "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", |
| 116 | + "token_url": TOKEN_URL, |
| 117 | + "credential_source": {"file": SUBJECT_TOKEN_TEXT_FILE}, |
| 118 | + "workforce_pool_user_project": WORKFORCE_POOL_USER_PROJECT, |
| 119 | +} |
| 120 | +IMPERSONATED_IDENTITY_POOL_WORKFORCE_DATA = { |
| 121 | + "type": "external_account", |
| 122 | + "audience": WORKFORCE_AUDIENCE, |
| 123 | + "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", |
| 124 | + "token_url": TOKEN_URL, |
| 125 | + "credential_source": {"file": SUBJECT_TOKEN_TEXT_FILE}, |
| 126 | + "service_account_impersonation_url": SERVICE_ACCOUNT_IMPERSONATION_URL, |
| 127 | + "workforce_pool_user_project": WORKFORCE_POOL_USER_PROJECT, |
| 128 | +} |
82 | 129 |
|
83 | 130 | MOCK_CREDENTIALS = mock.Mock(spec=credentials.CredentialsWithQuotaProject) |
84 | 131 | MOCK_CREDENTIALS.with_quota_project.return_value = MOCK_CREDENTIALS |
@@ -256,6 +303,68 @@ def test_load_credentials_from_file_external_account_aws(get_project_id, tmpdir) |
256 | 303 | assert get_project_id.called |
257 | 304 |
|
258 | 305 |
|
| 306 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 307 | +def test_load_credentials_from_file_external_account_identity_pool_impersonated( |
| 308 | + get_project_id, tmpdir |
| 309 | +): |
| 310 | + config_file = tmpdir.join("config.json") |
| 311 | + config_file.write(json.dumps(IMPERSONATED_IDENTITY_POOL_DATA)) |
| 312 | + credentials, project_id = _default.load_credentials_from_file(str(config_file)) |
| 313 | + |
| 314 | + assert isinstance(credentials, identity_pool.Credentials) |
| 315 | + assert not credentials.is_user |
| 316 | + assert not credentials.is_workforce_pool |
| 317 | + # Since no scopes are specified, the project ID cannot be determined. |
| 318 | + assert project_id is None |
| 319 | + assert get_project_id.called |
| 320 | + |
| 321 | + |
| 322 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 323 | +def test_load_credentials_from_file_external_account_aws_impersonated( |
| 324 | + get_project_id, tmpdir |
| 325 | +): |
| 326 | + config_file = tmpdir.join("config.json") |
| 327 | + config_file.write(json.dumps(IMPERSONATED_AWS_DATA)) |
| 328 | + credentials, project_id = _default.load_credentials_from_file(str(config_file)) |
| 329 | + |
| 330 | + assert isinstance(credentials, aws.Credentials) |
| 331 | + assert not credentials.is_user |
| 332 | + assert not credentials.is_workforce_pool |
| 333 | + # Since no scopes are specified, the project ID cannot be determined. |
| 334 | + assert project_id is None |
| 335 | + assert get_project_id.called |
| 336 | + |
| 337 | + |
| 338 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 339 | +def test_load_credentials_from_file_external_account_workforce(get_project_id, tmpdir): |
| 340 | + config_file = tmpdir.join("config.json") |
| 341 | + config_file.write(json.dumps(IDENTITY_POOL_WORKFORCE_DATA)) |
| 342 | + credentials, project_id = _default.load_credentials_from_file(str(config_file)) |
| 343 | + |
| 344 | + assert isinstance(credentials, identity_pool.Credentials) |
| 345 | + assert credentials.is_user |
| 346 | + assert credentials.is_workforce_pool |
| 347 | + # Since no scopes are specified, the project ID cannot be determined. |
| 348 | + assert project_id is None |
| 349 | + assert get_project_id.called |
| 350 | + |
| 351 | + |
| 352 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 353 | +def test_load_credentials_from_file_external_account_workforce_impersonated( |
| 354 | + get_project_id, tmpdir |
| 355 | +): |
| 356 | + config_file = tmpdir.join("config.json") |
| 357 | + config_file.write(json.dumps(IMPERSONATED_IDENTITY_POOL_WORKFORCE_DATA)) |
| 358 | + credentials, project_id = _default.load_credentials_from_file(str(config_file)) |
| 359 | + |
| 360 | + assert isinstance(credentials, identity_pool.Credentials) |
| 361 | + assert not credentials.is_user |
| 362 | + assert credentials.is_workforce_pool |
| 363 | + # Since no scopes are specified, the project ID cannot be determined. |
| 364 | + assert project_id is None |
| 365 | + assert get_project_id.called |
| 366 | + |
| 367 | + |
259 | 368 | @EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
260 | 369 | def test_load_credentials_from_file_external_account_with_user_and_default_scopes( |
261 | 370 | get_project_id, tmpdir |
@@ -718,18 +827,98 @@ def test_default_no_app_engine_compute_engine_module(unused_get): |
718 | 827 |
|
719 | 828 |
|
720 | 829 | @EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
721 | | -def test_default_environ_external_credentials(get_project_id, monkeypatch, tmpdir): |
| 830 | +def test_default_environ_external_credentials_identity_pool( |
| 831 | + get_project_id, monkeypatch, tmpdir |
| 832 | +): |
722 | 833 | config_file = tmpdir.join("config.json") |
723 | 834 | config_file.write(json.dumps(IDENTITY_POOL_DATA)) |
724 | 835 | monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file)) |
725 | 836 |
|
726 | 837 | credentials, project_id = _default.default() |
727 | 838 |
|
728 | 839 | assert isinstance(credentials, identity_pool.Credentials) |
| 840 | + assert not credentials.is_user |
| 841 | + assert not credentials.is_workforce_pool |
729 | 842 | # Without scopes, project ID cannot be determined. |
730 | 843 | assert project_id is None |
731 | 844 |
|
732 | 845 |
|
| 846 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 847 | +def test_default_environ_external_credentials_identity_pool_impersonated( |
| 848 | + get_project_id, monkeypatch, tmpdir |
| 849 | +): |
| 850 | + config_file = tmpdir.join("config.json") |
| 851 | + config_file.write(json.dumps(IMPERSONATED_IDENTITY_POOL_DATA)) |
| 852 | + monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file)) |
| 853 | + |
| 854 | + credentials, project_id = _default.default( |
| 855 | + scopes=["https://www.google.com/calendar/feeds"] |
| 856 | + ) |
| 857 | + |
| 858 | + assert isinstance(credentials, identity_pool.Credentials) |
| 859 | + assert not credentials.is_user |
| 860 | + assert not credentials.is_workforce_pool |
| 861 | + assert project_id is mock.sentinel.project_id |
| 862 | + assert credentials.scopes == ["https://www.google.com/calendar/feeds"] |
| 863 | + |
| 864 | + |
| 865 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 866 | +def test_default_environ_external_credentials_aws_impersonated( |
| 867 | + get_project_id, monkeypatch, tmpdir |
| 868 | +): |
| 869 | + config_file = tmpdir.join("config.json") |
| 870 | + config_file.write(json.dumps(IMPERSONATED_AWS_DATA)) |
| 871 | + monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file)) |
| 872 | + |
| 873 | + credentials, project_id = _default.default( |
| 874 | + scopes=["https://www.google.com/calendar/feeds"] |
| 875 | + ) |
| 876 | + |
| 877 | + assert isinstance(credentials, aws.Credentials) |
| 878 | + assert not credentials.is_user |
| 879 | + assert not credentials.is_workforce_pool |
| 880 | + assert project_id is mock.sentinel.project_id |
| 881 | + assert credentials.scopes == ["https://www.google.com/calendar/feeds"] |
| 882 | + |
| 883 | + |
| 884 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 885 | +def test_default_environ_external_credentials_workforce( |
| 886 | + get_project_id, monkeypatch, tmpdir |
| 887 | +): |
| 888 | + config_file = tmpdir.join("config.json") |
| 889 | + config_file.write(json.dumps(IDENTITY_POOL_WORKFORCE_DATA)) |
| 890 | + monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file)) |
| 891 | + |
| 892 | + credentials, project_id = _default.default( |
| 893 | + scopes=["https://www.google.com/calendar/feeds"] |
| 894 | + ) |
| 895 | + |
| 896 | + assert isinstance(credentials, identity_pool.Credentials) |
| 897 | + assert credentials.is_user |
| 898 | + assert credentials.is_workforce_pool |
| 899 | + assert project_id is mock.sentinel.project_id |
| 900 | + assert credentials.scopes == ["https://www.google.com/calendar/feeds"] |
| 901 | + |
| 902 | + |
| 903 | +@EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
| 904 | +def test_default_environ_external_credentials_workforce_impersonated( |
| 905 | + get_project_id, monkeypatch, tmpdir |
| 906 | +): |
| 907 | + config_file = tmpdir.join("config.json") |
| 908 | + config_file.write(json.dumps(IMPERSONATED_IDENTITY_POOL_WORKFORCE_DATA)) |
| 909 | + monkeypatch.setenv(environment_vars.CREDENTIALS, str(config_file)) |
| 910 | + |
| 911 | + credentials, project_id = _default.default( |
| 912 | + scopes=["https://www.google.com/calendar/feeds"] |
| 913 | + ) |
| 914 | + |
| 915 | + assert isinstance(credentials, identity_pool.Credentials) |
| 916 | + assert not credentials.is_user |
| 917 | + assert credentials.is_workforce_pool |
| 918 | + assert project_id is mock.sentinel.project_id |
| 919 | + assert credentials.scopes == ["https://www.google.com/calendar/feeds"] |
| 920 | + |
| 921 | + |
733 | 922 | @EXTERNAL_ACCOUNT_GET_PROJECT_ID_PATCH |
734 | 923 | def test_default_environ_external_credentials_with_user_and_default_scopes_and_quota_project_id( |
735 | 924 | get_project_id, monkeypatch, tmpdir |
|
0 commit comments