diff --git a/weed/s3api/s3api_embedded_iam.go b/weed/s3api/s3api_embedded_iam.go index 4b837117f4..1c6aa1bec0 100644 --- a/weed/s3api/s3api_embedded_iam.go +++ b/weed/s3api/s3api_embedded_iam.go @@ -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 diff --git a/weed/s3api/s3api_embedded_iam_test.go b/weed/s3api/s3api_embedded_iam_test.go index 2e7f9206af..4269958de9 100644 --- a/weed/s3api/s3api_embedded_iam_test.go +++ b/weed/s3api/s3api_embedded_iam_test.go @@ -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()