iam: GetUserPolicy returns NoSuchEntity for a policy name the user does not have (#11944)

* iam: GetUserPolicy returns NoSuchEntity for a policy name the user does not have

When the user has stored inline policies, an unknown name fell through to the
policy rebuilt from the user's actions, so a deleted policy still read back.
The standalone IAM server already returns NoSuchEntity here.

* iam: return service failure when listing inline policies fails

A ListUserInlinePolicies error in GetUserPolicy fell through to
action-based reconstruction, masking a store failure as a policy
document. Propagate it as ServiceFailureException instead, matching
the standalone iam server and the other callers in this file.

* iam: return service failure when the inline policy store read fails

---------

Co-authored-by: chrislusf <chrislusf@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-10-23 17:08:49 -07:00
1 parent f8579bf728
commit f5e319ae8e
2 files changed
+60 -2

No files matched your search

+10 -2
View File
@@ -1291,9 +1291,9 @@ func (e *EmbeddedIamApi) GetUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, values
ctx := context.Background()
storedDoc, storeErr := e.credentialManager.GetUserInlinePolicy(ctx, userName, policyName)
if storeErr != nil {
glog.Warningf("GetUserPolicy: failed to read stored inline policy %q for user %s: %v; falling back to reconstruction", policyName, userName, storeErr)
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: fmt.Errorf("get inline policy %s for %s: %w", policyName, userName, storeErr)}
}
if storeErr == nil && storedDoc != nil {
if storedDoc != nil {
policyDocumentJSON, err := json.Marshal(storedDoc)
if err != nil {
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
@@ -1301,6 +1301,14 @@ func (e *EmbeddedIamApi) GetUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, values
resp.GetUserPolicyResult.PolicyDocument = string(policyDocumentJSON)
return resp, nil
}
// The user has stored inline policies, but none by this name
policyNames, err := e.credentialManager.ListUserInlinePolicies(ctx, userName)
if err != nil {
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: fmt.Errorf("list inline policies for %s: %w", userName, err)}
}
if len(policyNames) > 0 {
return resp, &iamError{Code: iam.ErrCodeNoSuchEntityException, Error: fmt.Errorf("the user policy with name %s cannot be found", policyName)}
}
}
// Fallback: reconstruct from ident.Actions (lossy - fine-grained actions
+50
View File
@@ -1979,6 +1979,56 @@ func TestEmbeddedIamGetUserPolicyFallback(t *testing.T) {
assert.ElementsMatch(t, []string{"arn:aws:s3:::b-le*", "arn:aws:s3:::b-le*/*"}, stmt.Resource.Strings())
}
// TestEmbeddedIamGetUserPolicyMissingName checks that a policy name the user does not have
// is NoSuchEntity, not a policy rebuilt from the user's other inline policies.
func TestEmbeddedIamGetUserPolicyMissingName(t *testing.T) {
api := NewEmbeddedIamApiForTest()
s3cfg := &iam_pb.S3ApiConfiguration{
Identities: []*iam_pb.Identity{{Name: "steward"}},
}
api.mockConfig = s3cfg
_, iamErr := api.PutUserPolicy(s3cfg, url.Values{
"UserName": []string{"steward"},
"PolicyName": []string{"read"},
"PolicyDocument": []string{`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::b/*"}]}`},
})
require.Nil(t, iamErr)
_, iamErr = api.GetUserPolicy(s3cfg, url.Values{
"UserName": []string{"steward"},
"PolicyName": []string{"write"},
})
require.NotNil(t, iamErr)
assert.Equal(t, iam.ErrCodeNoSuchEntityException, iamErr.Code)
}
// TestEmbeddedIamGetUserPolicyStoreError checks that a store failure surfaces as
// ServiceFailure instead of rebuilding a policy from the identity's actions.
func TestEmbeddedIamGetUserPolicyStoreError(t *testing.T) {
api := NewEmbeddedIamApiForTest()
s3cfg := &iam_pb.S3ApiConfiguration{
Identities: []*iam_pb.Identity{{Name: "steward"}},
}
api.mockConfig = s3cfg
_, iamErr := api.PutUserPolicy(s3cfg, url.Values{
"UserName": []string{"steward"},
"PolicyName": []string{"read"},
"PolicyDocument": []string{`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::b/*"}]}`},
})
require.Nil(t, iamErr)
api.credentialManager = &credential.CredentialManager{Store: &memory.MemoryStore{}}
_, iamErr = api.GetUserPolicy(s3cfg, url.Values{
"UserName": []string{"steward"},
"PolicyName": []string{"read"},
})
require.NotNil(t, iamErr)
assert.Equal(t, iam.ErrCodeServiceFailureException, iamErr.Code)
}
// TestEmbeddedIamGetUserPolicyUserNotFound tests GetUserPolicy with non-existent user
func TestEmbeddedIamGetUserPolicyUserNotFound(t *testing.T) {
api := NewEmbeddedIamApiForTest()