Browse Source

add tests

Signed-off-by: changlin.shi <changlin.shi@ly.com>
pull/3831/head
changlin.shi 3 years ago
parent
commit
293113d1b8
  1. 32
      weed/s3api/s3acl/acl_helper.go
  2. 576
      weed/s3api/s3acl/acl_helper_test.go

32
weed/s3api/s3acl/acl_helper.go

@ -32,12 +32,12 @@ func ExtractAcl(r *http.Request, accountManager *s3account.AccountManager, owner
var acp s3.AccessControlPolicy
err := xmlutil.UnmarshalXML(&acp, xml.NewDecoder(r.Body), "")
if err != nil {
if err != nil || acp.Owner == nil || acp.Owner.ID == nil {
return nil, s3err.ErrInvalidRequest
}
//owner should present && owner is immutable
if acp.Owner == nil || acp.Owner.ID == nil || *acp.Owner.ID != ownerId {
if *acp.Owner.ID != ownerId {
glog.V(3).Infof("set acl denied! owner account is not consistent, request account id: %s, expect account id: %s", accountId, ownerId)
return nil, s3err.ErrAccessDenied
}
@ -251,7 +251,7 @@ func ParseCannedAclHeader(bucketOwnership, bucketOwnerId, accountId, cannedAcl s
case s3_constants.CannedAclAwsExecRead:
err = s3err.ErrNotImplemented
default:
err = s3err.ErrNotImplemented
err = s3err.ErrInvalidRequest
}
return
}
@ -373,7 +373,7 @@ func SetAcpOwnerHeader(r *http.Request, acpOwnerId string) {
func GetAcpOwner(entryExtended map[string][]byte, defaultOwner string) string {
ownerIdBytes, ok := entryExtended[s3_constants.ExtAmzOwnerKey]
if ok {
if ok && len(ownerIdBytes) > 0 {
return string(ownerIdBytes)
}
return defaultOwner
@ -393,7 +393,7 @@ func SetAcpGrantsHeader(r *http.Request, acpGrants []*s3.Grant) {
// GetAcpGrants return grants parsed from entry
func GetAcpGrants(entryExtended map[string][]byte) []*s3.Grant {
acpBytes, ok := entryExtended[s3_constants.ExtAmzAclKey]
if ok {
if ok && len(acpBytes) > 0 {
var grants []*s3.Grant
err := json.Unmarshal(acpBytes, &grants)
if err == nil {
@ -405,13 +405,23 @@ func GetAcpGrants(entryExtended map[string][]byte) []*s3.Grant {
// AssembleEntryWithAcp fill entry with owner and grants
func AssembleEntryWithAcp(objectEntry *filer_pb.Entry, objectOwner string, grants []*s3.Grant) s3err.ErrorCode {
objectEntry.Extended[s3_constants.ExtAmzOwnerKey] = []byte(objectOwner)
grantsBytes, err := json.Marshal(grants)
if err != nil {
glog.Warning("assemble acp to entry:", err)
return s3err.ErrInvalidRequest
if objectEntry.Extended == nil {
objectEntry.Extended = make(map[string][]byte, 0)
}
if len(objectOwner) > 0 {
objectEntry.Extended[s3_constants.ExtAmzOwnerKey] = []byte(objectOwner)
}
if len(grants) > 0 {
grantsBytes, err := json.Marshal(grants)
if err != nil {
glog.Warning("assemble acp to entry:", err)
return s3err.ErrInvalidRequest
}
objectEntry.Extended[s3_constants.ExtAmzAclKey] = grantsBytes
}
objectEntry.Extended[s3_constants.ExtAmzAclKey] = grantsBytes
return s3err.ErrNone
}

576
weed/s3api/s3acl/acl_helper_test.go

@ -1,80 +1,534 @@
package s3acl
import (
"bytes"
"encoding/json"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3account"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
"io"
"net/http"
"testing"
)
func TestParseAclHeaders(t *testing.T) {
accountManager := &s3account.AccountManager{
var (
accountManager = &s3account.AccountManager{
IdNameMapping: map[string]string{
s3account.AccountAdmin.Id: s3account.AccountAdmin.Name,
s3account.AccountAnonymous.Id: s3account.AccountAnonymous.Name,
"accountA": "accountA",
"accountB": "accountB",
},
EmailIdMapping: map[string]string{
s3account.AccountAdmin.EmailAddress: s3account.AccountAdmin.Id,
s3account.AccountAnonymous.EmailAddress: s3account.AccountAnonymous.Id,
"accountA@example.com": "accountA",
"accountBexample.com": "accountB",
},
}
)
func TestGetAccountId(t *testing.T) {
req := &http.Request{
Header: make(map[string][]string, 0),
}
//case1
//accountId: "admin"
req.Header.Set(s3_constants.AmzAccountId, s3account.AccountAdmin.Id)
if GetAccountId(req) != s3account.AccountAdmin.Id {
t.Fatal("expect accountId: admin")
}
//case2
//accountId: "anoymous"
req.Header.Set(s3_constants.AmzAccountId, s3account.AccountAnonymous.Id)
if GetAccountId(req) != s3account.AccountAnonymous.Id {
t.Fatal("expect accountId: anonymous")
}
//case3
//accountId is nil => "anonymous"
req.Header.Del(s3_constants.AmzAccountId)
if GetAccountId(req) != s3account.AccountAnonymous.Id {
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, 0),
}
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, 0),
}
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, 0),
}
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, 0),
}
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,
})
}
//good value
grants := make([]*s3.Grant, 0)
validHeaderValue := `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="anonymous", emailAddress="admin@example.com"`
errCode := ParseCustomAclHeader(validHeaderValue, s3_constants.PermissionFullControl, &grants)
if errCode != s3err.ErrNone {
t.Fatal(errCode)
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)
}
}
_, errCode = ValidateAndTransferGrants(accountManager, grants)
if errCode != s3err.ErrNone {
t.Fatal(errCode)
}
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"
//bad case: acl header format error
grants = make([]*s3.Grant, 0)
formatErrCase := `uri, id="anonymous", emailAddress="admin@example.com"`
errCode = ParseCustomAclHeader(formatErrCase, s3_constants.PermissionFullControl, &grants)
if errCode != s3err.ErrInvalidRequest {
t.Fatal(errCode)
{
//case1 (good case)
//parse custom acl
req := &http.Request{
Header: make(map[string][]string, 0),
}
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: &s3account.AccountAnonymous.Id,
},
Permission: &s3_constants.PermissionFullControl,
},
{
Grantee: &s3.Grantee{
Type: &s3_constants.GrantTypeCanonicalUser,
ID: &s3account.AccountAdmin.Id,
},
Permission: &s3_constants.PermissionFullControl,
},
},
})
}
{
//case2 (good case)
//parse canned acl (ownership=ObjectWriter)
req := &http.Request{
Header: make(map[string][]string, 0),
}
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, 0),
}
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, 0),
}
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, 0),
}
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, 0),
}
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)
}
}
}
//bad case: email not exists
grants = make([]*s3.Grant, 0)
badCaseOfEmail := `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="anonymous", emailAddress="admin@example1.com"`
errCode = ParseCustomAclHeader(badCaseOfEmail, s3_constants.PermissionFullControl, &grants)
if errCode != s3err.ErrNone {
t.Fatal(errCode)
func grantsEquals(a, b []*s3.Grant) bool {
if len(a) != len(b) {
return false
}
_, errCode = ValidateAndTransferGrants(accountManager, grants)
if errCode != s3err.ErrInvalidRequest {
t.Fatal(errCode)
for i, grant := range a {
if !GrantEquals(grant, b[i]) {
return false
}
}
return true
}
//bad case: account id not exists
grants = make([]*s3.Grant, 0)
badCaseOfAccountId := "uri=\"http://acs.amazonaws.com/groups/global/AllUsers\", id=\"xxxxxx\", emailAddress=\"admin@example.com\""
errCode = ParseCustomAclHeader(badCaseOfAccountId, s3_constants.PermissionFullControl, &grants)
if errCode != s3err.ErrNone {
t.Fatal(errCode)
func TestDetermineReqGrants(t *testing.T) {
{
//case1: request account is anonymous
accountId := s3account.AccountAnonymous.Id
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")
}
}
_, errCode = ValidateAndTransferGrants(accountManager, grants)
if errCode != s3err.ErrInvalidRequest {
t.Fatal(errCode)
{
//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
expectOwner := "accountS"
expectGrants := []*s3.Grant{
{
Permission: &s3_constants.PermissionRead,
Grantee: &s3.Grantee{
Type: &s3_constants.GrantTypeGroup,
ID: &s3account.AccountAdmin.Id,
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")
}
//bad case: group url not valid
grants = make([]*s3.Grant, 0)
badCaseOfURL := "uri=\"http://acs.amazonaws.com/groups/global/AllUsers111xxxx\", id=\"anonymous\", emailAddress=\"admin@example.com\""
errCode = ParseCustomAclHeader(badCaseOfURL, s3_constants.PermissionFullControl, &grants)
if errCode != s3err.ErrNone {
t.Fatal(errCode)
resultGrants := GetAcpGrants(entry.Extended)
if !grantsEquals(resultGrants, expectGrants) {
t.Fatal("grants not expect")
}
}
_, errCode = ValidateAndTransferGrants(accountManager, grants)
if errCode != s3err.ErrInvalidRequest {
t.Fatal(errCode)
{
//case2
entry := &filer_pb.Entry{}
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) {
@ -218,3 +672,37 @@ func TestGrantEquals(t *testing.T) {
}
}
}
func TestSetAcpOwnerHeader(t *testing.T) {
ownerId := "accountZ"
req := &http.Request{
Header: make(map[string][]string, 0),
}
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, 0),
}
grants := []*s3.Grant{
{
Permission: &s3_constants.PermissionRead,
Grantee: &s3.Grantee{
Type: &s3_constants.GrantTypeGroup,
ID: &s3account.AccountAdmin.Id,
URI: &s3_constants.GranteeGroupAllUsers,
},
},
}
SetAcpGrantsHeader(req, grants)
grantsJson, _ := json.Marshal(grants)
if req.Header.Get(s3_constants.ExtAmzAclKey) != string(grantsJson) {
t.Fatalf("owner unexpect")
}
}
Loading…
Cancel
Save