-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix: Add pre-validation for Bedrock and MLflow role permissions #6162
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rsareddy0329
wants to merge
4
commits into
aws:master
Choose a base branch
from
rsareddy0329:master-eval-role-log-fix
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+332
−5
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
bfc1047
feat(evaluate): Add pre-validation for execution role permissions
da692d2
refactor(evaluate): Use evaluation role type via iam_role_resolver
3b9de11
fix(evaluate): Address review feedback on role validation
966a36d
Merge branch 'master' into master-eval-role-log-fix
rsareddy0329 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
185 changes: 185 additions & 0 deletions
185
sagemaker-train/tests/unit/train/evaluate/test_bedrock_role_validation.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,185 @@ | ||
| """Unit tests for the 'evaluation' role type in iam_role_resolver.""" | ||
|
|
||
| import pytest | ||
| from unittest.mock import MagicMock, patch | ||
| from botocore.exceptions import ClientError | ||
|
|
||
| from sagemaker.core.helper.iam_role_resolver import ( | ||
| resolve_and_validate_role, | ||
| RoleValidationError, | ||
| _evaluate_permissions, | ||
| _role_trusts_service, | ||
| _get_smoke_test_actions, | ||
| _expected_trust_services, | ||
| ) | ||
|
|
||
|
|
||
| class TestEvaluationRoleType: | ||
| """Tests for the 'evaluation' role type configuration.""" | ||
|
|
||
| def test_evaluation_role_type_exists(self): | ||
| """The 'evaluation' role type should be recognized.""" | ||
| from sagemaker.core.helper.iam_policies import IAM_POLICY_CONFIG | ||
| assert "evaluation" in IAM_POLICY_CONFIG | ||
|
|
||
| def test_evaluation_trust_includes_sagemaker(self): | ||
| """Evaluation role type should require sagemaker.amazonaws.com trust.""" | ||
| expected = _expected_trust_services("evaluation") | ||
| assert "sagemaker.amazonaws.com" in expected | ||
|
|
||
|
|
||
| def test_evaluation_trust_does_not_require_bedrock(self): | ||
| """Evaluation role type should NOT require bedrock.amazonaws.com trust. | ||
|
|
||
| The serverless evaluation backend runs as the SageMaker execution role | ||
| and calls Bedrock APIs using the role's own credentials. Bedrock does | ||
| not need to assume the role, so trust is not required. | ||
| """ | ||
| expected = _expected_trust_services("evaluation") | ||
| assert "bedrock.amazonaws.com" not in expected | ||
|
|
||
| def test_evaluation_smoke_actions_include_bedrock(self): | ||
| """Smoke test actions should include Bedrock evaluation actions.""" | ||
| actions = _get_smoke_test_actions("evaluation") | ||
| assert "bedrock:CreateEvaluationJob" in actions | ||
| assert "bedrock:GetEvaluationJob" in actions | ||
|
|
||
| def test_evaluation_smoke_actions_include_bedrock_invoke(self): | ||
| """Smoke test actions should include Bedrock invoke actions.""" | ||
| actions = _get_smoke_test_actions("evaluation") | ||
| assert "bedrock:InvokeModel" in actions | ||
|
|
||
| def test_resolve_raises_when_bedrock_permissions_denied(self): | ||
| """Should raise RoleValidationError when Bedrock permissions are denied.""" | ||
| mock_iam = MagicMock() | ||
|
|
||
| # Simulate: bedrock:CreateEvaluationJob denied | ||
| paginator = MagicMock() | ||
| paginator.paginate.return_value = [ | ||
| { | ||
| "EvaluationResults": [ | ||
| {"EvalActionName": "bedrock:CreateEvaluationJob", "EvalDecision": "implicitDeny"}, | ||
| {"EvalActionName": "bedrock:GetEvaluationJob", "EvalDecision": "allowed"}, | ||
| {"EvalActionName": "bedrock:InvokeModel", "EvalDecision": "allowed"}, | ||
| {"EvalActionName": "bedrock:InvokeModelWithResponseStream", "EvalDecision": "allowed"}, | ||
| ] | ||
| } | ||
| ] | ||
| mock_iam.get_paginator.return_value = paginator | ||
|
|
||
| verdict, denied = _evaluate_permissions(mock_iam, "arn:aws:iam::123456789012:role/MyRole", "evaluation") | ||
| assert verdict is False | ||
| assert "bedrock:CreateEvaluationJob" in denied | ||
|
|
||
| def test_resolve_passes_when_all_allowed(self): | ||
| """Should pass when all evaluation permissions are allowed.""" | ||
| mock_iam = MagicMock() | ||
|
|
||
| actions = _get_smoke_test_actions("evaluation") | ||
| paginator = MagicMock() | ||
| paginator.paginate.return_value = [ | ||
| { | ||
| "EvaluationResults": [ | ||
| {"EvalActionName": a, "EvalDecision": "allowed"} for a in actions | ||
| ] | ||
| } | ||
| ] | ||
| mock_iam.get_paginator.return_value = paginator | ||
|
|
||
| verdict, denied = _evaluate_permissions(mock_iam, "arn:aws:iam::123456789012:role/MyRole", "evaluation") | ||
| assert verdict is True | ||
| assert denied == [] | ||
|
|
||
| def test_trust_check_passes_with_sagemaker(self): | ||
| """Should pass when trust policy includes sagemaker.amazonaws.com.""" | ||
| mock_iam = MagicMock() | ||
| mock_iam.get_role.return_value = { | ||
| "Role": { | ||
| "AssumeRolePolicyDocument": { | ||
| "Version": "2012-10-17", | ||
| "Statement": [{ | ||
| "Effect": "Allow", | ||
| "Principal": {"Service": "sagemaker.amazonaws.com"}, | ||
| "Action": "sts:AssumeRole", | ||
| }], | ||
| } | ||
| } | ||
| } | ||
|
|
||
| result = _role_trusts_service(mock_iam, "arn:aws:iam::123456789012:role/MyRole", "evaluation") | ||
| assert result is True | ||
|
|
||
| def test_resolve_and_validate_passes_with_sagemaker_trust(self): | ||
| """Full resolve_and_validate_role should pass with sagemaker trust only.""" | ||
| with patch("sagemaker.core.helper.iam_role_resolver._get_boto_session") as mock_session: | ||
| mock_boto = MagicMock() | ||
| mock_session.return_value = mock_boto | ||
|
|
||
| mock_iam = MagicMock() | ||
| mock_boto.client.return_value = mock_iam | ||
|
|
||
| # Role exists with sagemaker trust only (no bedrock needed) | ||
| mock_iam.get_role.return_value = { | ||
| "Role": { | ||
| "Arn": "arn:aws:iam::123456789012:role/MyRole", | ||
| "AssumeRolePolicyDocument": { | ||
| "Version": "2012-10-17", | ||
| "Statement": [{ | ||
| "Effect": "Allow", | ||
| "Principal": {"Service": "sagemaker.amazonaws.com"}, | ||
| "Action": "sts:AssumeRole", | ||
| }], | ||
| } | ||
| } | ||
| } | ||
|
|
||
| # All permissions allowed | ||
| actions = _get_smoke_test_actions("evaluation") | ||
| paginator = MagicMock() | ||
| paginator.paginate.return_value = [ | ||
| {"EvaluationResults": [{"EvalActionName": a, "EvalDecision": "allowed"} for a in actions]} | ||
| ] | ||
| mock_iam.get_paginator.return_value = paginator | ||
|
|
||
| result = resolve_and_validate_role( | ||
| provided_role="arn:aws:iam::123456789012:role/MyRole", | ||
| role_type="evaluation", | ||
| ) | ||
| assert result == "arn:aws:iam::123456789012:role/MyRole" | ||
|
|
||
| def test_resolve_and_validate_passes_with_correct_role(self): | ||
| """Full resolve_and_validate_role should pass with correct permissions and trust.""" | ||
| with patch("sagemaker.core.helper.iam_role_resolver._get_boto_session") as mock_session: | ||
| mock_boto = MagicMock() | ||
| mock_session.return_value = mock_boto | ||
|
|
||
| mock_iam = MagicMock() | ||
| mock_boto.client.return_value = mock_iam | ||
|
|
||
| # Role exists with correct trust | ||
| mock_iam.get_role.return_value = { | ||
| "Role": { | ||
| "Arn": "arn:aws:iam::123456789012:role/MyRole", | ||
| "AssumeRolePolicyDocument": { | ||
| "Version": "2012-10-17", | ||
| "Statement": [{ | ||
| "Effect": "Allow", | ||
| "Principal": {"Service": ["sagemaker.amazonaws.com", "bedrock.amazonaws.com"]}, | ||
| "Action": "sts:AssumeRole", | ||
| }], | ||
| } | ||
| } | ||
| } | ||
|
|
||
| # All permissions allowed | ||
| actions = _get_smoke_test_actions("evaluation") | ||
| paginator = MagicMock() | ||
| paginator.paginate.return_value = [ | ||
| {"EvaluationResults": [{"EvalActionName": a, "EvalDecision": "allowed"} for a in actions]} | ||
| ] | ||
| mock_iam.get_paginator.return_value = paginator | ||
|
|
||
| result = resolve_and_validate_role( | ||
| provided_role="arn:aws:iam::123456789012:role/MyRole", | ||
| role_type="evaluation", | ||
| ) | ||
| assert result == "arn:aws:iam::123456789012:role/MyRole" | ||
Oops, something went wrong.
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.