perf(s3.iam.GetUser): Make the API default to the request username if not specified (#9746)

* perf(s3.iam.GetUser): Make the API default to the request username if not
specified

This makes the Embedded S3 IAM API align with the documented behavior of the AWS IAM
API as per AWS Docs: https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetUser.html

BREAKING CHANGE: This changes the default behavior of the Embedded IAM API to use the
username of the user holding the accesskey used to make the request in
the GetUsername request handler.

* test: cover GetUser implicit username default

---------

Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
Elias Paitz
2026-05-30 10:51:03 -07:00
committed by GitHub
co-authored by Chris Lu
parent 4bf27278fa
commit 30f49013e1
2 changed files with 41 additions and 1 deletions
+1 -1
View File
@@ -2854,7 +2854,7 @@ func (e *EmbeddedIamApi) DoActions(w http.ResponseWriter, r *http.Request) {
// Handle implicit username for HTTP requests
switch r.Form.Get("Action") {
case "ListAccessKeys", "CreateAccessKey", "DeleteAccessKey", "UpdateAccessKey", "ListUserPolicies":
case "ListAccessKeys", "CreateAccessKey", "DeleteAccessKey", "UpdateAccessKey", "ListUserPolicies", "GetUser":
e.handleImplicitUsername(r, values)
case "CreateServiceAccount":
createdBy := s3_constants.GetIdentityNameFromContext(r)
+40
View File
@@ -339,6 +339,46 @@ func TestEmbeddedIamGetUser(t *testing.T) {
assert.Equal(t, "TestUser", *out.GetUserResult.User.UserName)
}
// TestEmbeddedIamGetUserImplicitUsername verifies GetUser without a UserName defaults
// to the user that signed the request rather than returning NoSuchEntity for an empty
// username, matching documented AWS IAM behavior.
func TestEmbeddedIamGetUserImplicitUsername(t *testing.T) {
const accessKey = UserAccessKeyPrefix + "TESTFAKEKEY000001"
api := NewEmbeddedIamApiForTest()
api.mockConfig = &iam_pb.S3ApiConfiguration{
Identities: []*iam_pb.Identity{
{
Name: "TestUser",
Credentials: []*iam_pb.Credential{
{AccessKey: accessKey, SecretKey: "testsecretfake"},
},
},
},
}
// handleImplicitUsername resolves the username via iam.LookupByAccessKey,
// so the iam state must know about the credential.
if err := api.iam.LoadS3ApiConfigurationFromBytes(mustMarshalJSON(api.mockConfig)); err != nil {
t.Fatalf("failed to load iam config: %v", err)
}
// GetUser request with NO UserName, but signed by accessKey.
form := url.Values{}
form.Set("Action", "GetUser")
req, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential="+accessKey+
"/20220420/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=fakesig")
out := iamGetUserResponse{}
rr, err := executeEmbeddedIamRequest(api, req, &out)
require.NoError(t, err)
require.Equal(t, http.StatusOK, rr.Code, "body=%s", rr.Body.String())
require.NotNil(t, out.GetUserResult.User.UserName)
assert.Equal(t, "TestUser", *out.GetUserResult.User.UserName)
}
// TestEmbeddedIamCreatePolicy tests creating a policy via the embedded IAM API
func TestEmbeddedIamCreatePolicy(t *testing.T) {
api := NewEmbeddedIamApiForTest()