iam: scope CreateServiceAccount's ParentUser by policy resource (#11736)

* iam: scope CreateServiceAccount's ParentUser by policy resource

AuthorizeIamAction evaluated the iam:CreateServiceAccount grant against a
hardcoded arn:aws:iam:::* resource, so the policy's Resource never had a
say; #11218 then bound the target to the caller outright. Evaluate
self-target actions against the target identity's ARN instead: a wildcard
resource keeps the deliberate any-target grant, a scoped resource confines
issuance to the named parent identity, anything else is denied.

buildResourceARN gains the ARN pass-through buildS3ResourceArn already has,
so iam: ARNs reach the policy engine intact on the attached-policies route.

Closes #11735

* ci: retrigger after unrelated weed/storage metrics flake

* iam: fold IAM-ARN matching into awsIAMMatch, drop debug test

- remove zz_debug_test.go (local debugging leftover)
- fold awsIAMMatchResource into awsIAMMatch: the arn:aws:iam:: value
  prefix selects case-sensitive matching, so no separate name is needed
- trim comments

---------

Co-authored-by: batonogov <batonogov@users.noreply.github.com>
Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-18 11:48:01 -07:00
1 parent 4a57432488
commit eea3f232da
5 files changed
+140 -50

No files matched your search

+12
View File
@@ -1240,6 +1240,18 @@ func awsIAMMatch(pattern, value string, evalCtx *EvaluationContext) bool {
return true // Universal wildcard
}
// IAM identity ARNs match case-sensitively: identity lookup is
// case-sensitive, so a case-variant names a different identity.
if strings.HasPrefix(value, "arn:aws:iam::") {
if expandedPattern == value {
return true
}
if strings.Contains(expandedPattern, "*") || strings.Contains(expandedPattern, "?") {
return wildcard.MatchesWildcard(expandedPattern, value)
}
return false
}
// Step 3: Case-insensitive exact match
if strings.EqualFold(expandedPattern, value) {
return true