Repository navigation
Accept GitHub immutable OIDC subject claims in trust policies #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c51069d
feat: accept GitHub immutable OIDC subject claims in trust policies
ejfine f8d9a49
fix: use StringLike for every OIDC sub condition
ejfine 8f065bd
feat: reject wildcard characters in OIDC restrictions
ejfine 5b196bd
feat: require a known GitHub org ID for every OIDC role
ejfine 8d49ca1
test: vary org ID answers across copier test data
ejfine 628cd89
fix: reject wildcard repo names on ref-restricted OIDC roles
ejfine File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
176 changes: 176 additions & 0 deletions
176
template/tests/unit/iac_management/test_oidc_assume_role_policy.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import random | ||
| import re | ||
| import string | ||
| from unittest.mock import ANY | ||
| from unittest.mock import MagicMock | ||
|
|
||
| import pydantic | ||
| import pytest | ||
| from pulumi_aws.iam import GetPolicyDocumentStatementConditionArgs | ||
| from pulumi_aws.iam import get_policy_document | ||
| from pytest_mock import MockerFixture | ||
|
|
||
| import aws_central_infrastructure.iac_management.lib.github_oidc_lib as github_oidc_lib_module | ||
| from aws_central_infrastructure.iac_management.lib.constants import CENTRAL_INFRA_GITHUB_ORG_NAME | ||
| from aws_central_infrastructure.iac_management.lib.constants import GITHUB_ORG_IDS | ||
| from aws_central_infrastructure.iac_management.lib.github_oidc_lib import ANY_SUBJECT_CONTEXT | ||
| from aws_central_infrastructure.iac_management.lib.github_oidc_lib import GithubOidcConfig | ||
| from aws_central_infrastructure.iac_management.lib.github_oidc_lib import create_oidc_assume_role_policy | ||
|
|
||
| CENTRAL_ORG_IMMUTABLE_PREFIX = f"{CENTRAL_INFRA_GITHUB_ORG_NAME}@{GITHUB_ORG_IDS[CENTRAL_INFRA_GITHUB_ORG_NAME]}" | ||
|
|
||
|
|
||
| def _random_account_id() -> str: | ||
| return "".join(random.choices(string.digits, k=12)) | ||
|
|
||
|
|
||
| def _random_name() -> str: | ||
| return "".join(random.choices(string.ascii_lowercase + "-", k=8)) | ||
|
|
||
|
|
||
| def _sub_conditions_passed_to(mock_get_policy_document: MagicMock) -> list[GetPolicyDocumentStatementConditionArgs]: | ||
| (statement,) = mock_get_policy_document.call_args.kwargs["statements"] | ||
| return [ | ||
| condition | ||
| for condition in statement.conditions | ||
| if condition.variable == "token.actions.githubusercontent.com:sub" | ||
| ] | ||
|
|
||
|
|
||
| class TestCreateOidcAssumeRolePolicy: | ||
| def test_Given_no_restrictions__When_policy_created__Then_sub_matches_legacy_and_immutable_subjects( | ||
| self, mocker: MockerFixture | ||
| ) -> None: | ||
| repo_name = _random_name() | ||
| oidc_config = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name=repo_name, | ||
| ) | ||
| mock_get_policy_document = mocker.patch.object(github_oidc_lib_module, get_policy_document.__name__) | ||
|
|
||
| _ = create_oidc_assume_role_policy(oidc_config=oidc_config, provider_arn=_random_name()) | ||
|
|
||
| sub_conditions = _sub_conditions_passed_to(mock_get_policy_document) | ||
|
|
||
| mock_get_policy_document.assert_called_once_with(statements=[ANY]) | ||
| assert len(sub_conditions) == 1 | ||
| assert sub_conditions[0].test == "StringLike" | ||
| assert sub_conditions[0].values == [ | ||
| f"repo:{CENTRAL_INFRA_GITHUB_ORG_NAME}/{repo_name}:*", | ||
| f"repo:{CENTRAL_ORG_IMMUTABLE_PREFIX}/{repo_name}@*:*", | ||
| ] | ||
|
|
||
| def test_Given_ref_restriction__When_policy_created__Then_sub_matches_both_subjects_scoped_to_that_ref( | ||
| self, mocker: MockerFixture | ||
| ) -> None: | ||
| repo_name = _random_name() | ||
| restriction = f"ref:refs/heads/{_random_name()}" | ||
| oidc_config = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name=repo_name, | ||
| restrictions=restriction, | ||
| ) | ||
| mock_get_policy_document = mocker.patch.object(github_oidc_lib_module, get_policy_document.__name__) | ||
|
|
||
| _ = create_oidc_assume_role_policy(oidc_config=oidc_config, provider_arn=_random_name()) | ||
|
|
||
| sub_conditions = _sub_conditions_passed_to(mock_get_policy_document) | ||
|
|
||
| mock_get_policy_document.assert_called_once_with(statements=[ANY]) | ||
| assert len(sub_conditions) == 1 | ||
| assert sub_conditions[0].test == "StringLike" | ||
| assert sub_conditions[0].values == [ | ||
| f"repo:{CENTRAL_INFRA_GITHUB_ORG_NAME}/{repo_name}:{restriction}", | ||
| f"repo:{CENTRAL_ORG_IMMUTABLE_PREFIX}/{repo_name}@*:{restriction}", | ||
| ] | ||
|
|
||
|
|
||
| class TestWhenGithubOidcConfigCreated: | ||
| @pytest.mark.parametrize("wildcard_char", ["*", "?"]) | ||
| def test_Given_restriction_containing_wildcard__Then_validation_error_names_restriction( | ||
| self, wildcard_char: str | ||
| ) -> None: | ||
| restriction = f"ref:refs/heads/{_random_name()}{wildcard_char}" | ||
|
|
||
| with pytest.raises(pydantic.ValidationError, match=re.escape(restriction)): | ||
| _ = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name=_random_name(), | ||
| restrictions=restriction, | ||
| ) | ||
|
|
||
| def test_Given_bare_wildcard_restriction__Then_config_keeps_it(self) -> None: | ||
| oidc_config = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name=_random_name(), | ||
| restrictions=ANY_SUBJECT_CONTEXT, | ||
| ) | ||
|
|
||
| assert oidc_config.restrictions == ANY_SUBJECT_CONTEXT | ||
|
|
||
| def test_Given_restrictions_explicitly_none__Then_config_has_no_restrictions(self) -> None: | ||
| oidc_config = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name=_random_name(), | ||
| restrictions=None, | ||
| ) | ||
|
|
||
| assert oidc_config.restrictions is None | ||
|
|
||
| @pytest.mark.parametrize("wildcard_char", ["*", "?"]) | ||
| def test_Given_wildcard_repo_name_with_ref_restriction__Then_validation_error_names_repo_and_restriction( | ||
| self, wildcard_char: str | ||
| ) -> None: | ||
| repo_name = f"{_random_name()}{wildcard_char}" | ||
| restriction = f"ref:refs/heads/{_random_name()}" | ||
|
|
||
| with pytest.raises(pydantic.ValidationError, match=f"{re.escape(repo_name)}.*{re.escape(restriction)}"): | ||
| _ = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name=repo_name, | ||
| restrictions=restriction, | ||
| ) | ||
|
|
||
| def test_Given_wildcard_repo_name_without_restriction__Then_config_keeps_it(self) -> None: | ||
| oidc_config = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name="*", | ||
| ) | ||
|
|
||
| assert oidc_config.repo_name == "*" | ||
|
|
||
| def test_Given_wildcard_repo_name_with_bare_wildcard_restriction__Then_config_keeps_it(self) -> None: | ||
| oidc_config = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=CENTRAL_INFRA_GITHUB_ORG_NAME, | ||
| repo_name="*", | ||
| restrictions=ANY_SUBJECT_CONTEXT, | ||
| ) | ||
|
|
||
| assert oidc_config.repo_name == "*" | ||
|
|
||
| def test_Given_repo_org_without_known_org_id__Then_validation_error_names_org(self) -> None: | ||
| unknown_org_name = _random_name() | ||
|
|
||
| with pytest.raises(pydantic.ValidationError, match=re.escape(unknown_org_name)): | ||
| _ = GithubOidcConfig( | ||
| aws_account_id=_random_account_id(), | ||
| role_name=_random_name(), | ||
| repo_org=unknown_org_name, | ||
| repo_name=_random_name(), | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.