mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-06 14:31:57 +02:00
* s3api: persist ACLs on PutObject uploads Signed-off-by: zhaoyuchen <43179751+zhao-yc@users.noreply.github.com> * s3api: fix PutObject ACL edge cases found in review - Only enforce BucketOwnerEnforced when explicitly configured; buckets without a stored ownership control keep accepting upload ACLs - Ignore ACL query parameters on SigV2 requests, which do not sign them - Mirror signed-query ACL values into headers after authentication so grant parsing and resolveFileMode agree on presigned uploads - Validate only caller-supplied grantees against the account registry; default grants now work for accounts outside the local registry - Reject unknown grantee keys and accept comma-separated grantee lists without spaces in ParseCustomAclHeader - Guard against identities without an account * s3api: harden upload ACL parsing and authorization Signed-off-by: zhaoyuchen <43179751+zhao-yc@users.noreply.github.com> * s3api: evaluate upload ACL grantees individually in policies A comma-joined grant header or a signed query parameter reached policy conditions as one value, so a deny on a later grantee did not fire. Split grant headers into per-grantee values for policy evaluation and share the grantee pair parser with ParseCustomAclHeader. * s3api: keep raw grant header values visible to policy conditions Exact-match conditions written against the signed header value stopped matching once grantees were split for evaluation. Preserve the original wire values alongside the per-grantee values so deny policies fire on either granularity. * s3api: evaluate upload ACL grants as one canonical list in policies Conditions on s3:x-amz-grant-* now see a single comma-separated canonical grant list identical for a single line, repeated header lines, or a signed query parameter. This keeps StringEquals allows and exact-list or allowlist (StringNotEquals) denies accurate regardless of wire encoding. * s3api: preserve upload ACL denies and align policy checks Signed-off-by: zhaoyuchen <43179751+zhao-yc@users.noreply.github.com> * s3api: retain upload owner grants and literal policy values Signed-off-by: zhaoyuchen <43179751+zhao-yc@users.noreply.github.com> --------- Signed-off-by: zhaoyuchen <43179751+zhao-yc@users.noreply.github.com> Co-authored-by: Chris Lu <chris.lu@gmail.com>
62 lines
2.8 KiB
Go
62 lines
2.8 KiB
Go
package s3api
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestParseCustomAclHeaderList covers the wire syntax separately from account
|
|
// resolution, including quoted delimiters and rejection without partial grants.
|
|
func TestParseCustomAclHeaderList(t *testing.T) {
|
|
tests := []struct {
|
|
name, input string
|
|
values []string
|
|
invalid bool
|
|
}{
|
|
{name: "absent"},
|
|
{name: "single", input: `id="alice"`, values: []string{"alice"}},
|
|
{name: "comma without space", input: `id="alice",id="bob"`, values: []string{"alice", "bob"}},
|
|
{name: "comma with space", input: `id="alice", id="bob"`, values: []string{"alice", "bob"}},
|
|
{name: "optional whitespace", input: " id = \"alice\" ,\t id=\"bob\" ", values: []string{"alice", "bob"}},
|
|
{name: "quoted comma and equals", input: `id="a,b=c",id="bob"`, values: []string{"a,b=c", "bob"}},
|
|
{name: "escaped quote", input: `id="a\"b",id="bob"`, values: []string{`a"b`, "bob"}},
|
|
{name: "email", input: `emailAddress="a=b@example.com"`, values: []string{"a=b@example.com"}},
|
|
{name: "group", input: `uri="http://acs.amazonaws.com/groups/global/AllUsers"`, values: []string{s3_constants.GranteeGroupAllUsers}},
|
|
{name: "unknown type", input: `account="alice"`, invalid: true},
|
|
{name: "mixed unknown type", input: `id="alice",principal="bob"`, invalid: true},
|
|
{name: "empty grantee", input: `id=""`, invalid: true},
|
|
{name: "unquoted", input: `id=alice`, invalid: true},
|
|
{name: "unterminated", input: `id="alice`, invalid: true},
|
|
{name: "trailing comma", input: `id="alice",`, invalid: true},
|
|
{name: "empty element", input: `id="alice",,id="bob"`, invalid: true},
|
|
{name: "missing comma", input: `id="alice" id="bob"`, invalid: true},
|
|
{name: "invalid escape", input: `id="a\q"`, invalid: true},
|
|
{name: "whitespace only", input: " ", invalid: true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
original := &s3.Grant{Permission: aws.String(s3_constants.PermissionFullControl)}
|
|
grants := []*s3.Grant{original}
|
|
code := ParseCustomAclHeader(tt.input, s3_constants.PermissionRead, &grants)
|
|
if tt.invalid {
|
|
require.Equal(t, s3err.ErrInvalidRequest, code)
|
|
require.Equal(t, []*s3.Grant{original}, grants, "invalid lists must not leave partial grants")
|
|
return
|
|
}
|
|
require.Equal(t, s3err.ErrNone, code)
|
|
require.Len(t, grants, 1+len(tt.values))
|
|
for i, value := range tt.values {
|
|
grant := grants[i+1]
|
|
actual := aws.StringValue(grant.Grantee.ID) + aws.StringValue(grant.Grantee.EmailAddress) + aws.StringValue(grant.Grantee.URI)
|
|
require.Equal(t, value, actual)
|
|
require.Equal(t, s3_constants.PermissionRead, aws.StringValue(grant.Permission))
|
|
}
|
|
})
|
|
}
|
|
}
|