diff --git a/spinifex/gateway/policy/actions.go b/spinifex/gateway/policy/actions.go index df92be8b2..395d0411c 100644 --- a/spinifex/gateway/policy/actions.go +++ b/spinifex/gateway/policy/actions.go @@ -1,6 +1,27 @@ package policy -// IAMAction formats a service and action as the IAM policy string "service:ActionName". -func IAMAction(service, action string) string { - return service + ":" + action +// bedrockIAMFamily lists the Bedrock wire/signing service names that all +// authorize under the single "bedrock:" IAM namespace in AWS (runtime, agent, +// and agent-runtime actions are namespaced as bedrock: on AWS). +var bedrockIAMFamily = map[string]bool{ + "bedrock-runtime": true, + "bedrock-agent": true, + "bedrock-agent-runtime": true, +} + +// iamNamespace maps a wire service name to its IAM policy namespace, collapsing +// the Bedrock family to the shared "bedrock:" namespace. Every other service +// keeps its own name. +func iamNamespace(service string) string { + if bedrockIAMFamily[service] { + return "bedrock" + } + return service +} + +// IAMAction formats a service and action as the IAM policy string +// "namespace:ActionName", collapsing the Bedrock family to the shared bedrock: +// namespace so AWS-shaped policies authorize as they do on AWS. +func IAMAction(service, action string) string { + return iamNamespace(service) + ":" + action } diff --git a/spinifex/gateway/policy/actions_test.go b/spinifex/gateway/policy/actions_test.go new file mode 100644 index 000000000..c63c23c3c --- /dev/null +++ b/spinifex/gateway/policy/actions_test.go @@ -0,0 +1,27 @@ +package policy + +import "testing" + +// TestIAMAction_BedrockNamespace asserts the Bedrock wire-service family +// collapses to the shared "bedrock:" IAM namespace, while other services keep +// their own prefix, so AWS-shaped policies authorize as they do on AWS. +func TestIAMAction_BedrockNamespace(t *testing.T) { + cases := []struct { + service, action, want string + }{ + {"bedrock", "Converse", "bedrock:Converse"}, + {"bedrock-runtime", "ConverseStream", "bedrock:ConverseStream"}, + {"bedrock-runtime", "InvokeModel", "bedrock:InvokeModel"}, + {"bedrock-agent", "CreateKnowledgeBase", "bedrock:CreateKnowledgeBase"}, + {"bedrock-agent-runtime", "Retrieve", "bedrock:Retrieve"}, + {"bedrock-agent-runtime", "RetrieveAndGenerate", "bedrock:RetrieveAndGenerate"}, + {"ecr", "GetAuthorizationToken", "ecr:GetAuthorizationToken"}, + {"ecs", "RunTask", "ecs:RunTask"}, + {"s3", "GetObject", "s3:GetObject"}, + } + for _, c := range cases { + if got := IAMAction(c.service, c.action); got != c.want { + t.Errorf("IAMAction(%q, %q) = %q, want %q", c.service, c.action, got, c.want) + } + } +}