LHHDZ
5 months ago
39 changed files with 2796 additions and 1092 deletions
-
9weed/filer/filer_conf.go
-
2weed/pb/iam.proto
-
68weed/pb/iam_pb/iam.pb.go
-
54weed/s3api/auth_credentials.go
-
4weed/s3api/auth_credentials_subscribe.go
-
9weed/s3api/auth_credentials_test.go
-
10weed/s3api/auth_signature_v4.go
-
6weed/s3api/auth_signature_v4_test.go
-
73weed/s3api/bucket_metadata.go
-
52weed/s3api/bucket_metadata_test.go
-
33weed/s3api/chunked_reader_v4.go
-
29weed/s3api/filer_multipart.go
-
4weed/s3api/s3_constants/acp_ownership.go
-
7weed/s3api/s3_constants/extend_key.go
-
3weed/s3api/s3_constants/header.go
-
416weed/s3api/s3acl/s3api_acl_helper.go
-
1477weed/s3api/s3acl/s3api_acl_helper_test.go
-
709weed/s3api/s3api_acl_helper_test.go
-
400weed/s3api/s3api_acp.go
-
3weed/s3api/s3api_auth.go
-
160weed/s3api/s3api_bucket_handlers.go
-
2weed/s3api/s3api_circuit_breaker.go
-
81weed/s3api/s3api_object_handlers.go
-
5weed/s3api/s3api_object_handlers_copy.go
-
14weed/s3api/s3api_object_handlers_list.go
-
72weed/s3api/s3api_object_handlers_multipart.go
-
36weed/s3api/s3api_object_handlers_put.go
-
16weed/s3api/s3api_object_handlers_skip.go
-
42weed/s3api/s3api_server.go
-
25weed/s3api/s3err/s3api_errors.go
-
32weed/server/filer_server_handlers_read.go
-
6weed/server/filer_server_handlers_write_autochunk.go
-
2weed/shell/command_collection_delete.go
-
2weed/shell/command_fs_mv.go
-
2weed/shell/command_fs_rm.go
-
2weed/shell/command_s3_bucket_delete.go
-
2weed/shell/command_volume_balance_test.go
-
2weed/shell/command_volume_delete.go
-
17weed/util/http/http_global_client_util.go
@ -1,7 +1,8 @@ |
|||||
package s3_constants |
package s3_constants |
||||
|
|
||||
const ( |
const ( |
||||
ExtAmzOwnerKey = "Seaweed-X-Amz-Owner" |
|
||||
ExtAmzAclKey = "Seaweed-X-Amz-Acl" |
|
||||
ExtOwnershipKey = "Seaweed-X-Amz-Ownership" |
|
||||
|
ExtAmzOwnerKey = "Seaweed-X-Amz-Owner" |
||||
|
ExtAmzMultipartInitiator = "Seaweed-X-Amz-Multipart-Initiator" |
||||
|
ExtAmzAclKey = "Seaweed-X-Amz-Acl" |
||||
|
ExtOwnershipKey = "Seaweed-X-Amz-Ownership" |
||||
) |
) |
1477
weed/s3api/s3acl/s3api_acl_helper_test.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,709 +0,0 @@ |
|||||
package s3api |
|
||||
|
|
||||
import ( |
|
||||
"bytes" |
|
||||
"encoding/json" |
|
||||
"github.com/aws/aws-sdk-go/aws" |
|
||||
"github.com/aws/aws-sdk-go/service/s3" |
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" |
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" |
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err" |
|
||||
"io" |
|
||||
"net/http" |
|
||||
"testing" |
|
||||
) |
|
||||
|
|
||||
var accountManager *IdentityAccessManagement |
|
||||
|
|
||||
func init() { |
|
||||
accountManager = &IdentityAccessManagement{} |
|
||||
_ = accountManager.loadS3ApiConfiguration(&iam_pb.S3ApiConfiguration{ |
|
||||
Accounts: []*iam_pb.Account{ |
|
||||
{ |
|
||||
Id: "accountA", |
|
||||
DisplayName: "accountAName", |
|
||||
EmailAddress: "accountA@example.com", |
|
||||
}, |
|
||||
{ |
|
||||
Id: "accountB", |
|
||||
DisplayName: "accountBName", |
|
||||
EmailAddress: "accountB@example.com", |
|
||||
}, |
|
||||
}, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
func TestGetAccountId(t *testing.T) { |
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
//case1
|
|
||||
//accountId: "admin"
|
|
||||
req.Header.Set(s3_constants.AmzAccountId, s3_constants.AccountAdminId) |
|
||||
if GetAccountId(req) != s3_constants.AccountAdminId { |
|
||||
t.Fatal("expect accountId: admin") |
|
||||
} |
|
||||
|
|
||||
//case2
|
|
||||
//accountId: "anoymous"
|
|
||||
req.Header.Set(s3_constants.AmzAccountId, s3_constants.AccountAnonymousId) |
|
||||
if GetAccountId(req) != s3_constants.AccountAnonymousId { |
|
||||
t.Fatal("expect accountId: anonymous") |
|
||||
} |
|
||||
|
|
||||
//case3
|
|
||||
//accountId is nil => "anonymous"
|
|
||||
req.Header.Del(s3_constants.AmzAccountId) |
|
||||
if GetAccountId(req) != s3_constants.AccountAnonymousId { |
|
||||
t.Fatal("expect accountId: anonymous") |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func TestExtractAcl(t *testing.T) { |
|
||||
type Case struct { |
|
||||
id int |
|
||||
resultErrCode, expectErrCode s3err.ErrorCode |
|
||||
resultGrants, expectGrants []*s3.Grant |
|
||||
} |
|
||||
testCases := make([]*Case, 0) |
|
||||
accountAdminId := "admin" |
|
||||
{ |
|
||||
//case1 (good case)
|
|
||||
//parse acp from request body
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
req.Body = io.NopCloser(bytes.NewReader([]byte(` |
|
||||
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> |
|
||||
<Owner> |
|
||||
<ID>admin</ID> |
|
||||
<DisplayName>admin</DisplayName> |
|
||||
</Owner> |
|
||||
<AccessControlList> |
|
||||
<Grant> |
|
||||
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser"> |
|
||||
<ID>admin</ID> |
|
||||
</Grantee> |
|
||||
<Permission>FULL_CONTROL</Permission> |
|
||||
</Grant> |
|
||||
<Grant> |
|
||||
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group"> |
|
||||
<URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
|
|
||||
</Grantee> |
|
||||
<Permission>FULL_CONTROL</Permission> |
|
||||
</Grant> |
|
||||
</AccessControlList> |
|
||||
</AccessControlPolicy> |
|
||||
`))) |
|
||||
objectWriter := "accountA" |
|
||||
grants, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
1, |
|
||||
errCode, s3err.ErrNone, |
|
||||
grants, []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &accountAdminId, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
}, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
//case2 (good case)
|
|
||||
//parse acp from header (cannedAcl)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
req.Body = nil |
|
||||
req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclPrivate) |
|
||||
objectWriter := "accountA" |
|
||||
grants, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
2, |
|
||||
errCode, s3err.ErrNone, |
|
||||
grants, []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &objectWriter, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
}, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
//case3 (bad case)
|
|
||||
//parse acp from request body (content is invalid)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
req.Body = io.NopCloser(bytes.NewReader([]byte("zdfsaf"))) |
|
||||
req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclPrivate) |
|
||||
objectWriter := "accountA" |
|
||||
_, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
id: 3, |
|
||||
resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
//case4 (bad case)
|
|
||||
//parse acp from header (cannedAcl is invalid)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
req.Body = nil |
|
||||
req.Header.Set(s3_constants.AmzCannedAcl, "dfaksjfk") |
|
||||
objectWriter := "accountA" |
|
||||
_, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, "", objectWriter) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
id: 4, |
|
||||
resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, |
|
||||
}) |
|
||||
|
|
||||
{ |
|
||||
//case5 (bad case)
|
|
||||
//parse acp from request body: owner is inconsistent
|
|
||||
req.Body = io.NopCloser(bytes.NewReader([]byte(` |
|
||||
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> |
|
||||
<Owner> |
|
||||
<ID>admin</ID> |
|
||||
<DisplayName>admin</DisplayName> |
|
||||
</Owner> |
|
||||
<AccessControlList> |
|
||||
<Grant> |
|
||||
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser"> |
|
||||
<ID>admin</ID> |
|
||||
</Grantee> |
|
||||
<Permission>FULL_CONTROL</Permission> |
|
||||
</Grant> |
|
||||
<Grant> |
|
||||
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group"> |
|
||||
<URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
|
|
||||
</Grantee> |
|
||||
<Permission>FULL_CONTROL</Permission> |
|
||||
</Grant> |
|
||||
</AccessControlList> |
|
||||
</AccessControlPolicy> |
|
||||
`))) |
|
||||
objectWriter = "accountA" |
|
||||
_, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, objectWriter, objectWriter) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
id: 5, |
|
||||
resultErrCode: errCode, expectErrCode: s3err.ErrAccessDenied, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
for _, tc := range testCases { |
|
||||
if tc.resultErrCode != tc.expectErrCode { |
|
||||
t.Fatalf("case[%d]: errorCode not expect", tc.id) |
|
||||
} |
|
||||
if !grantsEquals(tc.resultGrants, tc.expectGrants) { |
|
||||
t.Fatalf("case[%d]: grants not expect", tc.id) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func TestParseAndValidateAclHeaders(t *testing.T) { |
|
||||
type Case struct { |
|
||||
id int |
|
||||
resultOwner, expectOwner string |
|
||||
resultErrCode, expectErrCode s3err.ErrorCode |
|
||||
resultGrants, expectGrants []*s3.Grant |
|
||||
} |
|
||||
testCases := make([]*Case, 0) |
|
||||
bucketOwner := "admin" |
|
||||
|
|
||||
{ |
|
||||
//case1 (good case)
|
|
||||
//parse custom acl
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
objectWriter := "accountA" |
|
||||
req.Header.Set(s3_constants.AmzAclFullControl, `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="anonymous", emailAddress="admin@example.com"`) |
|
||||
ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
1, |
|
||||
ownerId, objectWriter, |
|
||||
errCode, s3err.ErrNone, |
|
||||
grants, []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: aws.String(s3_constants.AccountAnonymousId), |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
}, |
|
||||
}) |
|
||||
} |
|
||||
{ |
|
||||
//case2 (good case)
|
|
||||
//parse canned acl (ownership=ObjectWriter)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
objectWriter := "accountA" |
|
||||
req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclBucketOwnerFullControl) |
|
||||
ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
2, |
|
||||
ownerId, objectWriter, |
|
||||
errCode, s3err.ErrNone, |
|
||||
grants, []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &objectWriter, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &bucketOwner, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
}, |
|
||||
}) |
|
||||
} |
|
||||
{ |
|
||||
//case3 (good case)
|
|
||||
//parse canned acl (ownership=OwnershipBucketOwnerPreferred)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
objectWriter := "accountA" |
|
||||
req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclBucketOwnerFullControl) |
|
||||
ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipBucketOwnerPreferred, bucketOwner, objectWriter, false) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
3, |
|
||||
ownerId, bucketOwner, |
|
||||
errCode, s3err.ErrNone, |
|
||||
grants, []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &bucketOwner, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
}, |
|
||||
}) |
|
||||
} |
|
||||
{ |
|
||||
//case4 (bad case)
|
|
||||
//parse custom acl (grantee id not exists)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
objectWriter := "accountA" |
|
||||
req.Header.Set(s3_constants.AmzAclFullControl, `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="notExistsAccount", emailAddress="admin@example.com"`) |
|
||||
_, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
id: 4, |
|
||||
resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
//case5 (bad case)
|
|
||||
//parse custom acl (invalid format)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
objectWriter := "accountA" |
|
||||
req.Header.Set(s3_constants.AmzAclFullControl, `uri="http:sfasf"`) |
|
||||
_, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
id: 5, |
|
||||
resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
//case6 (bad case)
|
|
||||
//parse canned acl (invalid value)
|
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
objectWriter := "accountA" |
|
||||
req.Header.Set(s3_constants.AmzCannedAcl, `uri="http:sfasf"`) |
|
||||
_, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) |
|
||||
testCases = append(testCases, &Case{ |
|
||||
id: 5, |
|
||||
resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
for _, tc := range testCases { |
|
||||
if tc.expectErrCode != tc.resultErrCode { |
|
||||
t.Errorf("case[%d]: errCode unexpect", tc.id) |
|
||||
} |
|
||||
if tc.resultOwner != tc.expectOwner { |
|
||||
t.Errorf("case[%d]: ownerId unexpect", tc.id) |
|
||||
} |
|
||||
if !grantsEquals(tc.resultGrants, tc.expectGrants) { |
|
||||
t.Fatalf("case[%d]: grants not expect", tc.id) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func grantsEquals(a, b []*s3.Grant) bool { |
|
||||
if len(a) != len(b) { |
|
||||
return false |
|
||||
} |
|
||||
for i, grant := range a { |
|
||||
if !GrantEquals(grant, b[i]) { |
|
||||
return false |
|
||||
} |
|
||||
} |
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
func TestDetermineReqGrants(t *testing.T) { |
|
||||
{ |
|
||||
//case1: request account is anonymous
|
|
||||
accountId := s3_constants.AccountAnonymousId |
|
||||
reqPermission := s3_constants.PermissionRead |
|
||||
|
|
||||
resultGrants := DetermineReqGrants(accountId, reqPermission) |
|
||||
expectGrants := []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
Permission: &reqPermission, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &accountId, |
|
||||
}, |
|
||||
Permission: &reqPermission, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &accountId, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
} |
|
||||
if !grantsEquals(resultGrants, expectGrants) { |
|
||||
t.Fatalf("grants not expect") |
|
||||
} |
|
||||
} |
|
||||
{ |
|
||||
//case2: request account is not anonymous (Iam authed)
|
|
||||
accountId := "accountX" |
|
||||
reqPermission := s3_constants.PermissionRead |
|
||||
|
|
||||
resultGrants := DetermineReqGrants(accountId, reqPermission) |
|
||||
expectGrants := []*s3.Grant{ |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
Permission: &reqPermission, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &accountId, |
|
||||
}, |
|
||||
Permission: &reqPermission, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeCanonicalUser, |
|
||||
ID: &accountId, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAuthenticatedUsers, |
|
||||
}, |
|
||||
Permission: &reqPermission, |
|
||||
}, |
|
||||
{ |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAuthenticatedUsers, |
|
||||
}, |
|
||||
Permission: &s3_constants.PermissionFullControl, |
|
||||
}, |
|
||||
} |
|
||||
if !grantsEquals(resultGrants, expectGrants) { |
|
||||
t.Fatalf("grants not expect") |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func TestAssembleEntryWithAcp(t *testing.T) { |
|
||||
defaultOwner := "admin" |
|
||||
|
|
||||
//case1
|
|
||||
//assemble with non-empty grants
|
|
||||
expectOwner := "accountS" |
|
||||
expectGrants := []*s3.Grant{ |
|
||||
{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
entry := &filer_pb.Entry{} |
|
||||
AssembleEntryWithAcp(entry, expectOwner, expectGrants) |
|
||||
|
|
||||
resultOwner := GetAcpOwner(entry.Extended, defaultOwner) |
|
||||
if resultOwner != expectOwner { |
|
||||
t.Fatalf("owner not expect") |
|
||||
} |
|
||||
|
|
||||
resultGrants := GetAcpGrants(entry.Extended) |
|
||||
if !grantsEquals(resultGrants, expectGrants) { |
|
||||
t.Fatal("grants not expect") |
|
||||
} |
|
||||
|
|
||||
//case2
|
|
||||
//assemble with empty grants (override)
|
|
||||
AssembleEntryWithAcp(entry, "", nil) |
|
||||
resultOwner = GetAcpOwner(entry.Extended, defaultOwner) |
|
||||
if resultOwner != defaultOwner { |
|
||||
t.Fatalf("owner not expect") |
|
||||
} |
|
||||
|
|
||||
resultGrants = GetAcpGrants(entry.Extended) |
|
||||
if len(resultGrants) != 0 { |
|
||||
t.Fatal("grants not expect") |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
func TestGrantEquals(t *testing.T) { |
|
||||
testCases := map[bool]bool{ |
|
||||
GrantEquals(nil, nil): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{}, nil): false, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{}, &s3.Grant{}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
}, &s3.Grant{}): false, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{}, |
|
||||
}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{}, |
|
||||
}): false, |
|
||||
|
|
||||
//type not present, compare other fields of grant is meaningless
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
//EmailAddress: &s3account.AccountAdmin.EmailAddress,
|
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
}, |
|
||||
}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
}, |
|
||||
}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionWrite, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}): false, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
}, |
|
||||
}): true, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
}, |
|
||||
}): false, |
|
||||
|
|
||||
GrantEquals(&s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}, &s3.Grant{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}): true, |
|
||||
} |
|
||||
|
|
||||
for tc, expect := range testCases { |
|
||||
if tc != expect { |
|
||||
t.Fatal("TestGrantEquals not expect!") |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func TestSetAcpOwnerHeader(t *testing.T) { |
|
||||
ownerId := "accountZ" |
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
SetAcpOwnerHeader(req, ownerId) |
|
||||
|
|
||||
if req.Header.Get(s3_constants.ExtAmzOwnerKey) != ownerId { |
|
||||
t.Fatalf("owner unexpect") |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func TestSetAcpGrantsHeader(t *testing.T) { |
|
||||
req := &http.Request{ |
|
||||
Header: make(map[string][]string), |
|
||||
} |
|
||||
grants := []*s3.Grant{ |
|
||||
{ |
|
||||
Permission: &s3_constants.PermissionRead, |
|
||||
Grantee: &s3.Grantee{ |
|
||||
Type: &s3_constants.GrantTypeGroup, |
|
||||
ID: aws.String(s3_constants.AccountAdminId), |
|
||||
URI: &s3_constants.GranteeGroupAllUsers, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
SetAcpGrantsHeader(req, grants) |
|
||||
|
|
||||
grantsJson, _ := json.Marshal(grants) |
|
||||
if req.Header.Get(s3_constants.ExtAmzAclKey) != string(grantsJson) { |
|
||||
t.Fatalf("owner unexpect") |
|
||||
} |
|
||||
} |
|
@ -1,28 +1,406 @@ |
|||||
package s3api |
package s3api |
||||
|
|
||||
import ( |
import ( |
||||
|
"net/http" |
||||
|
"path/filepath" |
||||
|
|
||||
|
"github.com/aws/aws-sdk-go/service/s3" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/glog" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" |
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3acl" |
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err" |
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err" |
||||
"net/http" |
|
||||
|
"github.com/seaweedfs/seaweedfs/weed/util" |
||||
) |
) |
||||
|
|
||||
func getAccountId(r *http.Request) string { |
|
||||
id := r.Header.Get(s3_constants.AmzAccountId) |
|
||||
if len(id) == 0 { |
|
||||
return AccountAnonymous.Id |
|
||||
} else { |
|
||||
return id |
|
||||
|
func (s3a *S3ApiServer) checkAccessByOwnership(r *http.Request, bucket string) s3err.ErrorCode { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
requestAccountId := s3acl.GetAccountId(r) |
||||
|
if s3acl.ValidateAccount(requestAccountId, *bucketMetadata.Owner.ID) { |
||||
|
return s3err.ErrNone |
||||
} |
} |
||||
|
return s3err.ErrAccessDenied |
||||
} |
} |
||||
|
|
||||
func (s3a *S3ApiServer) checkAccessByOwnership(r *http.Request, bucket string) s3err.ErrorCode { |
|
||||
metadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
|
||||
|
// Check access for PutBucketAclHandler
|
||||
|
func (s3a *S3ApiServer) checkAccessForPutBucketAcl(requestAccountId, bucket string) (*BucketMetaData, s3err.ErrorCode) { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return nil, errCode |
||||
|
} |
||||
|
|
||||
|
if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
return nil, s3err.AccessControlListNotSupported |
||||
|
} |
||||
|
|
||||
|
if s3acl.ValidateAccount(requestAccountId, *bucketMetadata.Owner.ID) { |
||||
|
return bucketMetadata, s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
if len(bucketMetadata.Acl) > 0 { |
||||
|
reqGrants := s3acl.DetermineRequiredGrants(requestAccountId, s3_constants.PermissionWriteAcp) |
||||
|
for _, bucketGrant := range bucketMetadata.Acl { |
||||
|
for _, reqGrant := range reqGrants { |
||||
|
if s3acl.GrantEquals(bucketGrant, reqGrant) { |
||||
|
return bucketMetadata, s3err.ErrNone |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
glog.V(3).Infof("acl denied! request account id: %s", requestAccountId) |
||||
|
return nil, s3err.ErrAccessDenied |
||||
|
} |
||||
|
|
||||
|
func updateBucketEntry(s3a *S3ApiServer, entry *filer_pb.Entry) error { |
||||
|
return s3a.updateEntry(s3a.option.BucketsPath, entry) |
||||
|
} |
||||
|
|
||||
|
// Check Bucket/BucketAcl Read related access
|
||||
|
// includes:
|
||||
|
// - HeadBucketHandler
|
||||
|
// - GetBucketAclHandler
|
||||
|
// - ListObjectsV1Handler
|
||||
|
// - ListObjectsV2Handler
|
||||
|
// - ListMultipartUploadsHandler
|
||||
|
func (s3a *S3ApiServer) checkAccessForReadBucket(r *http.Request, bucket, aclAction string) (*BucketMetaData, s3err.ErrorCode) { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return nil, errCode |
||||
|
} |
||||
|
|
||||
|
if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
return bucketMetadata, s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
requestAccountId := s3acl.GetAccountId(r) |
||||
|
if s3acl.ValidateAccount(requestAccountId, *bucketMetadata.Owner.ID) { |
||||
|
return bucketMetadata, s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
if len(bucketMetadata.Acl) > 0 { |
||||
|
reqGrants := s3acl.DetermineRequiredGrants(requestAccountId, aclAction) |
||||
|
for _, bucketGrant := range bucketMetadata.Acl { |
||||
|
for _, reqGrant := range reqGrants { |
||||
|
if s3acl.GrantEquals(bucketGrant, reqGrant) { |
||||
|
return bucketMetadata, s3err.ErrNone |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
glog.V(3).Infof("acl denied! request account id: %s", requestAccountId) |
||||
|
return nil, s3err.ErrAccessDenied |
||||
|
} |
||||
|
|
||||
|
// Check ObjectAcl-Read related access
|
||||
|
// includes:
|
||||
|
// - GetObjectAclHandler
|
||||
|
func (s3a *S3ApiServer) checkAccessForReadObjectAcl(r *http.Request, bucket, object string) (acp *s3.AccessControlPolicy, errCode s3err.ErrorCode) { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return nil, errCode |
||||
|
} |
||||
|
|
||||
|
getAcpFunc := func() (*s3.AccessControlPolicy, s3err.ErrorCode) { |
||||
|
entry, err := getObjectEntry(s3a, bucket, object) |
||||
|
if err != nil { |
||||
|
if err == filer_pb.ErrNotFound { |
||||
|
return nil, s3err.ErrNoSuchKey |
||||
|
} else { |
||||
|
return nil, s3err.ErrInternalError |
||||
|
} |
||||
|
} |
||||
|
if entry.IsDirectory { |
||||
|
return nil, s3err.ErrExistingObjectIsDirectory |
||||
|
} |
||||
|
acpOwnerId := s3acl.GetAcpOwner(entry.Extended, *bucketMetadata.Owner.ID) |
||||
|
acpOwnerName := s3a.iam.GetAccountNameById(acpOwnerId) |
||||
|
acpGrants := s3acl.GetAcpGrants(&acpOwnerId, entry.Extended) |
||||
|
acp = &s3.AccessControlPolicy{ |
||||
|
Owner: &s3.Owner{ |
||||
|
ID: &acpOwnerId, |
||||
|
DisplayName: &acpOwnerName, |
||||
|
}, |
||||
|
Grants: acpGrants, |
||||
|
} |
||||
|
return acp, s3err.ErrNone |
||||
|
} |
||||
|
if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
return getAcpFunc() |
||||
|
} |
||||
|
requestAccountId := s3acl.GetAccountId(r) |
||||
|
acp, errCode = getAcpFunc() |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return nil, errCode |
||||
|
} |
||||
|
if s3acl.ValidateAccount(requestAccountId, *acp.Owner.ID) { |
||||
|
return acp, s3err.ErrNone |
||||
|
} |
||||
|
if acp.Grants != nil { |
||||
|
reqGrants := s3acl.DetermineRequiredGrants(requestAccountId, s3_constants.PermissionReadAcp) |
||||
|
for _, requiredGrant := range reqGrants { |
||||
|
for _, grant := range acp.Grants { |
||||
|
if s3acl.GrantEquals(requiredGrant, grant) { |
||||
|
return acp, s3err.ErrNone |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
glog.V(3).Infof("CheckAccessForReadObjectAcl denied! request account id: %s", requestAccountId) |
||||
|
return nil, s3err.ErrAccessDenied |
||||
|
} |
||||
|
|
||||
|
// Check Object-Read related access
|
||||
|
// includes:
|
||||
|
// - GetObjectHandler
|
||||
|
//
|
||||
|
// offload object access validation to Filer layer
|
||||
|
// - CheckObjectAccessForReadObject
|
||||
|
func (s3a *S3ApiServer) checkBucketAccessForReadObject(r *http.Request, bucket string) s3err.ErrorCode { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
if errCode != s3err.ErrNone { |
if errCode != s3err.ErrNone { |
||||
return errCode |
return errCode |
||||
} |
} |
||||
accountId := getAccountId(r) |
|
||||
if accountId == AccountAdmin.Id || accountId == *metadata.Owner.ID { |
|
||||
|
|
||||
|
if bucketMetadata.ObjectOwnership != s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
//offload object acl validation to filer layer
|
||||
|
_, defaultErrorCode := s3a.checkAccessForReadBucket(r, bucket, s3_constants.PermissionRead) |
||||
|
if defaultErrorCode != s3err.ErrNone { |
||||
|
r.Header.Set(s3_constants.XAmzBucketAccessDenied, "true") |
||||
|
} |
||||
|
r.Header.Set(s3_constants.XAmzBucketOwnerId, *bucketMetadata.Owner.ID) |
||||
|
} |
||||
|
|
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
// Check ObjectAcl-Write related access
|
||||
|
// includes:
|
||||
|
// - PutObjectAclHandler
|
||||
|
func (s3a *S3ApiServer) checkAccessForWriteObjectAcl(r *http.Request, bucket, object string) (*filer_pb.Entry, string, []*s3.Grant, s3err.ErrorCode) { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return nil, "", nil, errCode |
||||
|
} |
||||
|
|
||||
|
requestAccountId := s3acl.GetAccountId(r) |
||||
|
reqOwnerId, grants, errCode := s3acl.ExtractObjectAcl(r, s3a.iam, bucketMetadata.ObjectOwnership, *bucketMetadata.Owner.ID, requestAccountId, false) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return nil, "", nil, errCode |
||||
|
} |
||||
|
|
||||
|
if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
return nil, "", nil, s3err.AccessControlListNotSupported |
||||
|
} |
||||
|
|
||||
|
//object acl
|
||||
|
objectEntry, err := getObjectEntry(s3a, bucket, object) |
||||
|
if err != nil { |
||||
|
if err == filer_pb.ErrNotFound { |
||||
|
return nil, "", nil, s3err.ErrNoSuchKey |
||||
|
} |
||||
|
return nil, "", nil, s3err.ErrInternalError |
||||
|
} |
||||
|
|
||||
|
if objectEntry.IsDirectory { |
||||
|
return nil, "", nil, s3err.ErrExistingObjectIsDirectory |
||||
|
} |
||||
|
|
||||
|
objectOwner := s3acl.GetAcpOwner(objectEntry.Extended, *bucketMetadata.Owner.ID) |
||||
|
//object owner is immutable
|
||||
|
if reqOwnerId != "" && reqOwnerId != objectOwner { |
||||
|
return nil, "", nil, s3err.ErrAccessDenied |
||||
|
} |
||||
|
if s3acl.ValidateAccount(requestAccountId, objectOwner) { |
||||
|
return objectEntry, objectOwner, grants, s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
objectGrants := s3acl.GetAcpGrants(nil, objectEntry.Extended) |
||||
|
if objectGrants != nil { |
||||
|
requiredGrants := s3acl.DetermineRequiredGrants(requestAccountId, s3_constants.PermissionWriteAcp) |
||||
|
for _, objectGrant := range objectGrants { |
||||
|
for _, requiredGrant := range requiredGrants { |
||||
|
if s3acl.GrantEquals(objectGrant, requiredGrant) { |
||||
|
return objectEntry, objectOwner, grants, s3err.ErrNone |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
glog.V(3).Infof("checkAccessForWriteObjectAcl denied! request account id: %s", requestAccountId) |
||||
|
return nil, "", nil, s3err.ErrAccessDenied |
||||
|
} |
||||
|
|
||||
|
func updateObjectEntry(s3a *S3ApiServer, bucket, object string, entry *filer_pb.Entry) error { |
||||
|
dir, _ := filepath.Split(object) |
||||
|
return s3a.updateEntry(util.Join(s3a.option.BucketsPath, bucket, dir), entry) |
||||
|
} |
||||
|
|
||||
|
// CheckAccessForPutObject Check ACL for PutObject API
|
||||
|
// includes:
|
||||
|
// - PutObjectHandler
|
||||
|
func (s3a *S3ApiServer) CheckAccessForPutObject(r *http.Request, bucket, object string) s3err.ErrorCode { |
||||
|
accountId := s3acl.GetAccountId(r) |
||||
|
return s3a.checkAccessForPutObject(r, bucket, object, accountId) |
||||
|
} |
||||
|
|
||||
|
// CheckAccessForPutObjectPartHandler Check Acl for Upload object part
|
||||
|
// includes:
|
||||
|
// - PutObjectPartHandler
|
||||
|
func (s3a *S3ApiServer) CheckAccessForPutObjectPartHandler(r *http.Request, bucket string) s3err.ErrorCode { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
accountId := s3acl.GetAccountId(r) |
||||
|
if !CheckBucketAccess(accountId, bucketMetadata, s3_constants.PermissionWrite) { |
||||
|
return s3err.ErrAccessDenied |
||||
|
} |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
// CheckAccessForNewMultipartUpload Check Acl for API
|
||||
|
// includes:
|
||||
|
// - NewMultipartUploadHandler
|
||||
|
func (s3a *S3ApiServer) CheckAccessForNewMultipartUpload(r *http.Request, bucket, object string) (s3err.ErrorCode, string) { |
||||
|
accountId := s3acl.GetAccountId(r) |
||||
|
if accountId == AccountAnonymous.Id { |
||||
|
return s3err.ErrAccessDenied, "" |
||||
|
} |
||||
|
errCode := s3a.checkAccessForPutObject(r, bucket, object, accountId) |
||||
|
return errCode, accountId |
||||
|
} |
||||
|
|
||||
|
func (s3a *S3ApiServer) CheckAccessForAbortMultipartUpload(r *http.Request, bucket, object string) s3err.ErrorCode { |
||||
|
return s3a.CheckAccessWithBucketOwnerAndInitiator(r, bucket, object) |
||||
|
} |
||||
|
|
||||
|
func (s3a *S3ApiServer) CheckAccessForCompleteMultipartUpload(r *http.Request, bucket, object string) s3err.ErrorCode { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
|
||||
|
if bucketMetadata.ObjectOwnership != s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
accountId := s3acl.GetAccountId(r) |
||||
|
if !CheckBucketAccess(accountId, bucketMetadata, s3_constants.PermissionWrite) { |
||||
|
return s3err.ErrAccessDenied |
||||
|
} |
||||
|
} |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
func (s3a *S3ApiServer) CheckAccessForListMultipartUploadParts(r *http.Request, bucket, object string) s3err.ErrorCode { |
||||
|
return s3a.CheckAccessWithBucketOwnerAndInitiator(r, bucket, object) |
||||
|
} |
||||
|
|
||||
|
// CheckAccessWithBucketOwnerAndInitiator Check Access Permission with 'bucketOwner' and 'multipartUpload initiator'
|
||||
|
func (s3a *S3ApiServer) CheckAccessWithBucketOwnerAndInitiator(r *http.Request, bucket, object string) s3err.ErrorCode { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
|
||||
|
//bucket access allowed
|
||||
|
accountId := s3acl.GetAccountId(r) |
||||
|
if s3acl.ValidateAccount(*bucketMetadata.Owner.ID, accountId) { |
||||
return s3err.ErrNone |
return s3err.ErrNone |
||||
} |
} |
||||
|
|
||||
|
//multipart initiator allowed
|
||||
|
entry, err := getMultipartUpload(s3a, bucket, object) |
||||
|
if err != nil { |
||||
|
if err != filer_pb.ErrNotFound { |
||||
|
return s3err.ErrInternalError |
||||
|
} |
||||
|
} else { |
||||
|
uploadInitiator, ok := entry.Extended[s3_constants.ExtAmzMultipartInitiator] |
||||
|
if !ok || accountId == string(uploadInitiator) { |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
} |
||||
|
glog.V(3).Infof("CheckAccessWithBucketOwnerAndInitiator denied! request account id: %s", accountId) |
||||
return s3err.ErrAccessDenied |
return s3err.ErrAccessDenied |
||||
} |
} |
||||
|
|
||||
|
func (s3a *S3ApiServer) checkAccessForPutObject(r *http.Request, bucket, object, requestAccountId string) s3err.ErrorCode { |
||||
|
bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
|
||||
|
// if ownership is 'OwnershipBucketOwnerEnforced', acl is not supportedG
|
||||
|
if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { |
||||
|
_, _, errCode := s3acl.ExtractObjectAcl(r, s3a.iam, bucketMetadata.ObjectOwnership, *bucketMetadata.Owner.ID, requestAccountId, true) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
requestOwnerId, grants, errCode := s3acl.ExtractObjectAcl(r, s3a.iam, bucketMetadata.ObjectOwnership, *bucketMetadata.Owner.ID, requestAccountId, true) |
||||
|
if errCode != s3err.ErrNone { |
||||
|
return errCode |
||||
|
} |
||||
|
|
||||
|
if !CheckBucketAccess(requestAccountId, bucketMetadata, s3_constants.PermissionWrite) { |
||||
|
return s3err.ErrAccessDenied |
||||
|
} |
||||
|
|
||||
|
if requestOwnerId == "" { |
||||
|
requestOwnerId = requestAccountId |
||||
|
} |
||||
|
entry, err := getObjectEntry(s3a, bucket, object) |
||||
|
if err != nil { |
||||
|
if err == filer_pb.ErrNotFound { |
||||
|
s3acl.SetAcpOwnerHeader(r, requestOwnerId) |
||||
|
s3acl.SetAcpGrantsHeader(r, grants) |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
return s3err.ErrInternalError |
||||
|
} |
||||
|
|
||||
|
objectOwnerId := s3acl.GetAcpOwner(entry.Extended, *bucketMetadata.Owner.ID) |
||||
|
|
||||
|
//object owner is immutable
|
||||
|
if !s3acl.ValidateAccount(requestOwnerId, objectOwnerId, *bucketMetadata.Owner.ID) { |
||||
|
return s3err.ErrAccessDenied |
||||
|
} |
||||
|
|
||||
|
s3acl.SetAcpOwnerHeader(r, objectOwnerId) |
||||
|
s3acl.SetAcpGrantsHeader(r, grants) |
||||
|
return s3err.ErrNone |
||||
|
} |
||||
|
|
||||
|
func CheckBucketAccess(requestAccountId string, bucketMetadata *BucketMetaData, permission string) bool { |
||||
|
if s3acl.ValidateAccount(requestAccountId, *bucketMetadata.Owner.ID) { |
||||
|
return true |
||||
|
} else { |
||||
|
if len(bucketMetadata.Acl) > 0 { |
||||
|
reqGrants := s3acl.DetermineRequiredGrants(requestAccountId, permission) |
||||
|
for _, bucketGrant := range bucketMetadata.Acl { |
||||
|
for _, requiredGrant := range reqGrants { |
||||
|
if s3acl.GrantEquals(bucketGrant, requiredGrant) { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
glog.V(3).Infof("CheckBucketAccess denied! request account id: %s", requestAccountId) |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func getObjectEntry(s3a *S3ApiServer, bucket, object string) (*filer_pb.Entry, error) { |
||||
|
return s3a.getEntry(util.Join(s3a.option.BucketsPath, bucket), object) |
||||
|
} |
||||
|
|
||||
|
func getMultipartUpload(s3a *S3ApiServer, bucket, object string) (*filer_pb.Entry, error) { |
||||
|
return s3a.getEntry(s3a.genUploadsFolder(bucket), object) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue