iam: correct LimitExceeded, InvalidAction and ServiceFailure mappings

LimitExceeded is a conflict (409), an unknown Action is InvalidAction
(404) rather than NotImplemented, and internal failures report the IAM
receiver fault type. Applies to both the embedded IAM endpoint and the
standalone iamapi server.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Chris LuandDevin committed 2026-10-06 09:25:32 -07:00
1 parent d742b5c9ce
commit 3dcc74e93d
5 files changed
+55 -9

No files matched your search

+43 -2
View File
@@ -1043,7 +1043,7 @@ func TestEmbeddedIamAttachPolicyLimitExceeded(t *testing.T) {
response, err := executeEmbeddedIamRequest(api, req.HTTPRequest, nil)
assert.NoError(t, err)
assert.Equal(t, http.StatusForbidden, response.Code)
assert.Equal(t, http.StatusConflict, response.Code)
code, _ := extractEmbeddedIamErrorCodeAndMessage(response)
assert.Equal(t, iam.ErrCodeLimitExceededException, code)
assert.Len(t, api.mockConfig.Identities[0].PolicyNames, MaxManagedPoliciesPerUser)
@@ -2039,7 +2039,8 @@ func TestEmbeddedIamNotImplementedAction(t *testing.T) {
apiRouter.Path("/").Methods(http.MethodPost).HandlerFunc(api.DoActions)
apiRouter.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotImplemented, rr.Code)
assert.Equal(t, http.StatusNotFound, rr.Code)
assert.Contains(t, rr.Body.String(), "InvalidAction")
assert.Contains(t, rr.Body.String(), "<RequestId>")
assert.NotContains(t, rr.Body.String(), "<ResponseMetadata>")
assert.Equal(t, rr.Header().Get(request_id.AmzRequestIDHeader), extractEmbeddedIamRequestID(rr))
@@ -2831,3 +2832,43 @@ func TestEmbeddedIamReadOnly(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, responseList.Code)
}
func TestWriteIamErrorResponseMapping(t *testing.T) {
tests := []struct {
name string
code string
wantStatus int
}{
{"NoSuchEntity", iam.ErrCodeNoSuchEntityException, http.StatusNotFound},
{"EntityAlreadyExists", iam.ErrCodeEntityAlreadyExistsException, http.StatusConflict},
{"InvalidInput", iam.ErrCodeInvalidInputException, http.StatusBadRequest},
{"LimitExceeded", iam.ErrCodeLimitExceededException, http.StatusConflict},
{"DeleteConflict", iam.ErrCodeDeleteConflictException, http.StatusConflict},
{"InvalidAction", "InvalidAction", http.StatusNotFound},
{"AccessDenied", "AccessDenied", http.StatusForbidden},
{"InvalidClientTokenId", "InvalidClientTokenId", http.StatusForbidden},
{"SignatureDoesNotMatch", "SignatureDoesNotMatch", http.StatusForbidden},
{"IncompleteSignature", "IncompleteSignature", http.StatusBadRequest},
{"ServiceFailure", iam.ErrCodeServiceFailureException, http.StatusInternalServerError},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rec := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodPost, "/", nil)
writeIamErrorResponse(rec, r, "req-1", &iamError{Code: tt.code, Error: fmt.Errorf("boom")})
assert.Equal(t, tt.wantStatus, rec.Code)
body := rec.Body.String()
assert.True(t, strings.HasPrefix(strings.TrimSpace(body), "<ErrorResponse") ||
strings.Contains(body, "<ErrorResponse"), "expected IAM ErrorResponse envelope, got %s", body)
assert.Contains(t, body, "<Code>"+tt.code+"</Code>")
})
}
}
func TestIamAuthErrorCode(t *testing.T) {
assert.Equal(t, "InvalidClientTokenId", iamAuthErrorCode(s3err.ErrInvalidAccessKeyID))
assert.Equal(t, "SignatureDoesNotMatch", iamAuthErrorCode(s3err.ErrSignatureDoesNotMatch))
assert.Equal(t, "AccessDenied", iamAuthErrorCode(s3err.ErrAccessDenied))
assert.Equal(t, "RequestTimeTooSkewed", iamAuthErrorCode(s3err.ErrRequestTimeTooSkewed))
assert.Equal(t, "IncompleteSignature", iamAuthErrorCode(s3err.ErrMissingFields))
}