mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-17 03:28:32 +02:00
s3api: support group inline policies + Condition enforcement (#9569)
* test(s3api): cover IAM inline policy aws:SourceIp + group inline gap Unit tests under weed/s3api/ drive PutUserPolicy / PutGroupPolicy → reload → VerifyActionPermission with a synthetic 127.0.0.1 request and assert that the policy's IpAddress condition flips the outcome. The user-policy cases pass on master (hydrateRuntimePolicies already routes inline docs through the policy engine, so Condition blocks are honored end- to-end). The group-policy case fails: PutGroupPolicy still returns NotImplemented, so a group inline doc never lands in the engine. Integration counterparts live under test/s3/iam/ and exercise the same paths against a live SeaweedFS S3+IAM endpoint. * s3api: support group inline policies + Condition enforcement PutGroupPolicy/GetGroupPolicy/DeleteGroupPolicy/ListGroupPolicies used to return NotImplemented in embedded IAM mode, so anything attached to a group as an inline doc — including aws:SourceIp or any other Condition — was simply unreachable. Wire the four endpoints to the credential-store methods that were already in place (memory, postgres, filer_etc all implement GroupInlinePolicyStore). On every config reload, hydrateRuntimePolicies now also walks LoadGroupInlinePolicies, registers each doc in the IAM policy engine under __inline_group_policy__/<group>/<policy>, and appends that key to Group.PolicyNames so evaluateIAMPolicies picks it up through its existing group walk. PutGroupPolicy/DeleteGroupPolicy are added to the ReloadConfiguration trigger list in DoActions. Side fix: MemoryStore.LoadConfiguration now surfaces store.groups too. Without it iam.groups never repopulated on a memory-store reload, so group policy evaluation silently no-op'd whether the policy was inline or attached. The existing tests didn't notice because no test reloaded through cm after creating a group. The NotImplemented unit test is inverted to drive the new round-trip. * s3api: drop redundant refreshIAMConfiguration from Put/DeleteGroupPolicy DoActions already triggers ReloadConfiguration for both actions via the explicit reload list, so calling refreshIAMConfiguration inline runs the load twice per request. Per PR review. * s3api: scope group-policy resource names per test; tighten deny polling - Integration test resource names get a per-test suffix so retried or parallel CI jobs don't trip EntityAlreadyExists / BucketAlreadyExists. - Deny-path Eventually loops gate on AccessDenied via a typed helper rather than any non-nil error; transient setup errors no longer end the wait prematurely. - ListGroupPolicies returns ServiceFailure when the credential manager is nil, matching Put/Get/DeleteGroupPolicy. * test(s3 iam): cover both IPv4 and IPv6 loopback in allow CIDRs CI runners with happy-eyeballs resolve `localhost` to ::1 first, in which case a 127.0.0.0/8-only allow would silently never match and the deny-driven enforcement test would hang for the allow case. Add ::1/128 to every loopback-matching policy so the allow path works regardless of which loopback family the SDK lands on.
This commit is contained in:
1 parent
77ac781bbd
commit
285025eb73
6 files changed
+809
-61
No files matched your search
@@ -72,6 +72,10 @@ func NewEmbeddedIamApiForTest() *EmbeddedIamApiForTest {
|
||||
for i, p := range config.Policies {
|
||||
s3cfg.Policies[i] = proto.Clone(p).(*iam_pb.Policy)
|
||||
}
|
||||
s3cfg.Groups = make([]*iam_pb.Group, len(config.Groups))
|
||||
for i, g := range config.Groups {
|
||||
s3cfg.Groups[i] = proto.Clone(g).(*iam_pb.Group)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -619,37 +623,47 @@ func TestEmbeddedIamListUserPolicies(t *testing.T) {
|
||||
assert.Equal(t, http.StatusNotFound, rr3.Code)
|
||||
}
|
||||
|
||||
// TestEmbeddedIamGroupInlinePoliciesNotImplemented tests that group inline policies
|
||||
// return NotImplemented in embedded IAM mode.
|
||||
func TestEmbeddedIamGroupInlinePoliciesNotImplemented(t *testing.T) {
|
||||
// TestEmbeddedIamGroupInlinePolicies_CRUD exercises the Put/Get/List/Delete
|
||||
// round-trip for group inline policies and verifies that an unknown policy
|
||||
// surfaces as NoSuchEntity (not the legacy NotImplemented).
|
||||
func TestEmbeddedIamGroupInlinePolicies_CRUD(t *testing.T) {
|
||||
api := NewEmbeddedIamApiForTest()
|
||||
s3cfg := &iam_pb.S3ApiConfiguration{
|
||||
Groups: []*iam_pb.Group{
|
||||
{Name: "developers", Members: []string{"alice"}},
|
||||
},
|
||||
}
|
||||
api.mockConfig = s3cfg
|
||||
|
||||
notImpl := s3err.GetAPIError(s3err.ErrNotImplemented).Code
|
||||
policyDoc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::*"}]}`
|
||||
|
||||
_, iamErr := api.PutGroupPolicy(s3cfg, url.Values{
|
||||
"GroupName": {"developers"},
|
||||
"PolicyName": {"DevPolicy"},
|
||||
"PolicyDocument": {`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::*"}]}`},
|
||||
"PolicyDocument": {policyDoc},
|
||||
})
|
||||
assert.NotNil(t, iamErr)
|
||||
assert.Equal(t, notImpl, iamErr.Code)
|
||||
require.Nil(t, iamErr, "PutGroupPolicy must succeed; got: %+v", iamErr)
|
||||
|
||||
_, iamErr = api.GetGroupPolicy(s3cfg, url.Values{"GroupName": {"developers"}, "PolicyName": {"DevPolicy"}})
|
||||
assert.NotNil(t, iamErr)
|
||||
assert.Equal(t, notImpl, iamErr.Code)
|
||||
listResp, iamErr := api.ListGroupPolicies(s3cfg, url.Values{"GroupName": {"developers"}})
|
||||
require.Nil(t, iamErr)
|
||||
assert.Equal(t, []string{"DevPolicy"}, listResp.ListGroupPoliciesResult.PolicyNames)
|
||||
|
||||
getResp, iamErr := api.GetGroupPolicy(s3cfg, url.Values{"GroupName": {"developers"}, "PolicyName": {"DevPolicy"}})
|
||||
require.Nil(t, iamErr)
|
||||
assert.Equal(t, "developers", getResp.GetGroupPolicyResult.GroupName)
|
||||
assert.Equal(t, "DevPolicy", getResp.GetGroupPolicyResult.PolicyName)
|
||||
assert.Contains(t, getResp.GetGroupPolicyResult.PolicyDocument, "s3:GetObject")
|
||||
|
||||
_, iamErr = api.DeleteGroupPolicy(s3cfg, url.Values{"GroupName": {"developers"}, "PolicyName": {"DevPolicy"}})
|
||||
assert.NotNil(t, iamErr)
|
||||
assert.Equal(t, notImpl, iamErr.Code)
|
||||
require.Nil(t, iamErr)
|
||||
|
||||
_, iamErr = api.ListGroupPolicies(s3cfg, url.Values{"GroupName": {"developers"}})
|
||||
assert.NotNil(t, iamErr)
|
||||
assert.Equal(t, notImpl, iamErr.Code)
|
||||
_, iamErr = api.GetGroupPolicy(s3cfg, url.Values{"GroupName": {"developers"}, "PolicyName": {"DevPolicy"}})
|
||||
require.NotNil(t, iamErr)
|
||||
assert.Equal(t, iam.ErrCodeNoSuchEntityException, iamErr.Code)
|
||||
|
||||
_, iamErr = api.GetGroupPolicy(s3cfg, url.Values{"GroupName": {"ghosts"}, "PolicyName": {"DevPolicy"}})
|
||||
require.NotNil(t, iamErr, "GetGroupPolicy on a missing group must error")
|
||||
assert.Equal(t, iam.ErrCodeNoSuchEntityException, iamErr.Code)
|
||||
}
|
||||
|
||||
// TestEmbeddedIamAttachUserPolicy tests attaching a managed policy to a user.
|
||||
|
||||
Reference in new issue
Block a user