diff options
Diffstat (limited to 'weed/s3api')
| -rw-r--r-- | weed/s3api/auth_credentials.go | 137 | ||||
| -rw-r--r-- | weed/s3api/auth_credentials_test.go | 37 | ||||
| -rw-r--r-- | weed/s3api/bucket_metadata.go | 15 | ||||
| -rw-r--r-- | weed/s3api/bucket_metadata_test.go | 42 | ||||
| -rw-r--r-- | weed/s3api/s3_constants/s3_acp.go | 6 | ||||
| -rw-r--r-- | weed/s3api/s3account/s3_account.go | 70 | ||||
| -rw-r--r-- | weed/s3api/s3api_acl_helper.go (renamed from weed/s3api/s3acl/acl_helper.go) | 28 | ||||
| -rw-r--r-- | weed/s3api/s3api_acl_helper_test.go (renamed from weed/s3api/s3acl/acl_helper_test.go) | 75 | ||||
| -rw-r--r-- | weed/s3api/s3api_acp.go | 5 | ||||
| -rw-r--r-- | weed/s3api/s3api_bucket_handlers.go | 11 | ||||
| -rw-r--r-- | weed/s3api/s3api_server.go | 3 |
11 files changed, 238 insertions, 191 deletions
diff --git a/weed/s3api/auth_credentials.go b/weed/s3api/auth_credentials.go index 38ff2b5ca..a2b1fd90f 100644 --- a/weed/s3api/auth_credentials.go +++ b/weed/s3api/auth_credentials.go @@ -7,8 +7,6 @@ import ( "strings" "sync" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" - "github.com/seaweedfs/seaweedfs/weed/filer" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/pb" @@ -29,6 +27,8 @@ type IdentityAccessManagement struct { identities []*Identity accessKeyIdent map[string]*Identity + accounts map[string]*Account + emailAccount map[string]*Account hashes map[string]*sync.Pool hashCounters map[string]*int32 identityAnonymous *Identity @@ -39,20 +39,50 @@ type IdentityAccessManagement struct { type Identity struct { Name string - AccountId string + Account *Account Credentials []*Credential Actions []Action } -func (i *Identity) isAnonymous() bool { - return i.Name == s3account.AccountAnonymous.Name +// Account represents a system user, a system user can +// configure multiple IAM-Users, IAM-Users can configure +// permissions respectively, and each IAM-User can +// configure multiple security credentials +type Account struct { + //Name is also used to display the "DisplayName" as the owner of the bucket or object + DisplayName string + EmailAddress string + + //Id is used to identify an Account when granting cross-account access(ACLs) to buckets and objects + Id string } +// Predefined Accounts +var ( + // AccountAdmin is used as the default account for IAM-Credentials access without Account configured + AccountAdmin = Account{ + DisplayName: "admin", + EmailAddress: "admin@example.com", + Id: s3_constants.AccountAdminId, + } + + // AccountAnonymous is used to represent the account for anonymous access + AccountAnonymous = Account{ + DisplayName: "anonymous", + EmailAddress: "anonymous@example.com", + Id: s3_constants.AccountAnonymousId, + } +) + type Credential struct { AccessKey string SecretKey string } +func (i *Identity) isAnonymous() bool { + return i.Account.Id == s3_constants.AccountAnonymousId +} + func (action Action) isAdmin() bool { return strings.HasPrefix(string(action), s3_constants.ACTION_ADMIN) } @@ -65,14 +95,19 @@ func (action Action) overBucket(bucket string) bool { return strings.HasSuffix(string(action), ":"+bucket) || strings.HasSuffix(string(action), ":*") } +// "Permission": "FULL_CONTROL"|"WRITE"|"WRITE_ACP"|"READ"|"READ_ACP" func (action Action) getPermission() Permission { switch act := strings.Split(string(action), ":")[0]; act { case s3_constants.ACTION_ADMIN: return Permission("FULL_CONTROL") case s3_constants.ACTION_WRITE: return Permission("WRITE") + case s3_constants.ACTION_WRITE_ACP: + return Permission("WRITE_ACP") case s3_constants.ACTION_READ: return Permission("READ") + case s3_constants.ACTION_READ_ACP: + return Permission("READ_ACP") default: return Permission("") } @@ -138,26 +173,69 @@ func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3Api var identities []*Identity var identityAnonymous *Identity accessKeyIdent := make(map[string]*Identity) + accounts := make(map[string]*Account) + emailAccount := make(map[string]*Account) + foundAccountAdmin := false + foundAccountAnonymous := false + + for _, account := range config.Accounts { + switch account.Id { + case AccountAdmin.Id: + AccountAdmin = Account{ + Id: account.Id, + DisplayName: account.DisplayName, + EmailAddress: account.EmailAddress, + } + accounts[account.Id] = &AccountAdmin + foundAccountAdmin = true + case AccountAnonymous.Id: + AccountAnonymous = Account{ + Id: account.Id, + DisplayName: account.DisplayName, + EmailAddress: account.EmailAddress, + } + accounts[account.Id] = &AccountAnonymous + foundAccountAnonymous = true + default: + t := Account{ + Id: account.Id, + DisplayName: account.DisplayName, + EmailAddress: account.EmailAddress, + } + accounts[account.Id] = &t + } + if account.EmailAddress != "" { + emailAccount[account.EmailAddress] = accounts[account.Id] + } + } + if !foundAccountAdmin { + accounts[AccountAdmin.Id] = &AccountAdmin + emailAccount[AccountAdmin.EmailAddress] = &AccountAdmin + } + if !foundAccountAnonymous { + accounts[AccountAnonymous.Id] = &AccountAnonymous + emailAccount[AccountAnonymous.EmailAddress] = &AccountAnonymous + } for _, ident := range config.Identities { t := &Identity{ Name: ident.Name, - AccountId: s3account.AccountAdmin.Id, Credentials: nil, Actions: nil, } - - if ident.Name == s3account.AccountAnonymous.Name { - if ident.AccountId != "" && ident.AccountId != s3account.AccountAnonymous.Id { - glog.Warningf("anonymous identity is associated with a non-anonymous account ID, the association is invalid") - } - t.AccountId = s3account.AccountAnonymous.Id + switch { + case ident.Name == AccountAnonymous.Id: + t.Account = &AccountAnonymous identityAnonymous = t - } else { - if len(ident.AccountId) > 0 { - t.AccountId = ident.AccountId + case ident.Account == nil: + t.Account = &AccountAdmin + default: + if account, ok := accounts[ident.Account.Id]; ok { + t.Account = account + } else { + t.Account = &AccountAdmin + glog.Warningf("identity %s is associated with a non exist account ID, the association is invalid", ident.Name) } } - for _, action := range ident.Actions { t.Actions = append(t.Actions, Action(action)) } @@ -170,15 +248,19 @@ func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3Api } identities = append(identities, t) } + iam.m.Lock() // atomically switch iam.identities = identities iam.identityAnonymous = identityAnonymous + iam.accounts = accounts + iam.emailAccount = emailAccount iam.accessKeyIdent = accessKeyIdent if !iam.isAuthEnabled { // one-directional, no toggling iam.isAuthEnabled = len(identities) > 0 } iam.m.Unlock() + return nil } @@ -209,6 +291,24 @@ func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, foun return nil, false } +func (iam *IdentityAccessManagement) GetAccountNameById(canonicalId string) string { + iam.m.RLock() + defer iam.m.RUnlock() + if account, ok := iam.accounts[canonicalId]; ok { + return account.DisplayName + } + return "" +} + +func (iam *IdentityAccessManagement) GetAccountIdByEmail(email string) string { + iam.m.RLock() + defer iam.m.RUnlock() + if account, ok := iam.emailAccount[email]; ok { + return account.Id + } + return "" +} + func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if !iam.isEnabled() { @@ -287,9 +387,8 @@ func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) return identity, s3err.ErrAccessDenied } - if !identity.isAnonymous() { - r.Header.Set(s3_constants.AmzAccountId, identity.AccountId) - } + r.Header.Set(s3_constants.AmzAccountId, identity.Account.Id) + return identity, s3err.ErrNone } diff --git a/weed/s3api/auth_credentials_test.go b/weed/s3api/auth_credentials_test.go index 0bbcce706..f9f87fc54 100644 --- a/weed/s3api/auth_credentials_test.go +++ b/weed/s3api/auth_credentials_test.go @@ -2,14 +2,12 @@ package s3api import ( . "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "github.com/stretchr/testify/assert" "reflect" "testing" - jsonpb "google.golang.org/protobuf/encoding/protojson" - "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" + jsonpb "google.golang.org/protobuf/encoding/protojson" ) func TestIdentityListFileFormat(t *testing.T) { @@ -146,11 +144,22 @@ func TestCanDo(t *testing.T) { } type LoadS3ApiConfigurationTestCase struct { + pbAccount *iam_pb.Account pbIdent *iam_pb.Identity expectIdent *Identity } func TestLoadS3ApiConfiguration(t *testing.T) { + specifiedAccount := Account{ + Id: "specifiedAccountID", + DisplayName: "specifiedAccountName", + EmailAddress: "specifiedAccounEmail@example.com", + } + pbSpecifiedAccount := iam_pb.Account{ + Id: "specifiedAccountID", + DisplayName: "specifiedAccountName", + EmailAddress: "specifiedAccounEmail@example.com", + } testCases := map[string]*LoadS3ApiConfigurationTestCase{ "notSpecifyAccountId": { pbIdent: &iam_pb.Identity{ @@ -167,8 +176,8 @@ func TestLoadS3ApiConfiguration(t *testing.T) { }, }, expectIdent: &Identity{ - Name: "notSpecifyAccountId", - AccountId: s3account.AccountAdmin.Id, + Name: "notSpecifyAccountId", + Account: &AccountAdmin, Actions: []Action{ "Read", "Write", @@ -182,17 +191,18 @@ func TestLoadS3ApiConfiguration(t *testing.T) { }, }, "specifiedAccountID": { + pbAccount: &pbSpecifiedAccount, pbIdent: &iam_pb.Identity{ - Name: "specifiedAccountID", - AccountId: "specifiedAccountID", + Name: "specifiedAccountID", + Account: &pbSpecifiedAccount, Actions: []string{ "Read", "Write", }, }, expectIdent: &Identity{ - Name: "specifiedAccountID", - AccountId: "specifiedAccountID", + Name: "specifiedAccountID", + Account: &specifiedAccount, Actions: []Action{ "Read", "Write", @@ -208,8 +218,8 @@ func TestLoadS3ApiConfiguration(t *testing.T) { }, }, expectIdent: &Identity{ - Name: "anonymous", - AccountId: "anonymous", + Name: "anonymous", + Account: &AccountAnonymous, Actions: []Action{ "Read", "Write", @@ -223,6 +233,9 @@ func TestLoadS3ApiConfiguration(t *testing.T) { } for _, v := range testCases { config.Identities = append(config.Identities, v.pbIdent) + if v.pbAccount != nil { + config.Accounts = append(config.Accounts, v.pbAccount) + } } iam := IdentityAccessManagement{} @@ -234,7 +247,7 @@ func TestLoadS3ApiConfiguration(t *testing.T) { for _, ident := range iam.identities { tc := testCases[ident.Name] if !reflect.DeepEqual(ident, tc.expectIdent) { - t.Error("not expect") + t.Errorf("not expect for ident name %s", ident.Name) } } } diff --git a/weed/s3api/bucket_metadata.go b/weed/s3api/bucket_metadata.go index f4088e6b3..d1f487104 100644 --- a/weed/s3api/bucket_metadata.go +++ b/weed/s3api/bucket_metadata.go @@ -6,7 +6,6 @@ import ( "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/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "github.com/seaweedfs/seaweedfs/weed/util" "math" @@ -19,7 +18,7 @@ var loadBucketMetadataFromFiler = func(r *BucketRegistry, bucketName string) (*B return nil, err } - return buildBucketMetadata(r.s3a.accountManager, entry), nil + return buildBucketMetadata(r.s3a.iam, entry), nil } type BucketMetaData struct { @@ -73,13 +72,13 @@ func (r *BucketRegistry) init() error { } func (r *BucketRegistry) LoadBucketMetadata(entry *filer_pb.Entry) { - bucketMetadata := buildBucketMetadata(r.s3a.accountManager, entry) + bucketMetadata := buildBucketMetadata(r.s3a.iam, entry) r.metadataCacheLock.Lock() defer r.metadataCacheLock.Unlock() r.metadataCache[entry.Name] = bucketMetadata } -func buildBucketMetadata(accountManager *s3account.AccountManager, entry *filer_pb.Entry) *BucketMetaData { +func buildBucketMetadata(accountManager AccountManager, entry *filer_pb.Entry) *BucketMetaData { entryJson, _ := json.Marshal(entry) glog.V(3).Infof("build bucket metadata,entry=%s", entryJson) bucketMetadata := &BucketMetaData{ @@ -90,8 +89,8 @@ func buildBucketMetadata(accountManager *s3account.AccountManager, entry *filer_ // Default owner: `AccountAdmin` Owner: &s3.Owner{ - ID: &s3account.AccountAdmin.Id, - DisplayName: &s3account.AccountAdmin.Name, + ID: &AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, }, } if entry.Extended != nil { @@ -112,8 +111,8 @@ func buildBucketMetadata(accountManager *s3account.AccountManager, entry *filer_ acpOwnerBytes, ok := entry.Extended[s3_constants.ExtAmzOwnerKey] if ok && len(acpOwnerBytes) > 0 { ownerAccountId := string(acpOwnerBytes) - ownerAccountName, exists := accountManager.IdNameMapping[ownerAccountId] - if !exists { + ownerAccountName := accountManager.GetAccountNameById(ownerAccountId) + if ownerAccountName == "" { glog.Warningf("owner[id=%s] is invalid, bucket: %s", ownerAccountId, bucketMetadata.Name) } else { bucketMetadata.Owner = &s3.Owner{ diff --git a/weed/s3api/bucket_metadata_test.go b/weed/s3api/bucket_metadata_test.go index f852a272a..16d20b8fe 100644 --- a/weed/s3api/bucket_metadata_test.go +++ b/weed/s3api/bucket_metadata_test.go @@ -5,8 +5,8 @@ import ( "fmt" "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/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "reflect" "sync" @@ -31,7 +31,7 @@ var ( Name: "entryWithValidAcp", Extended: map[string][]byte{ s3_constants.ExtOwnershipKey: []byte(s3_constants.OwnershipBucketOwnerEnforced), - s3_constants.ExtAmzOwnerKey: []byte(s3account.AccountAdmin.Name), + s3_constants.ExtAmzOwnerKey: []byte(AccountAdmin.DisplayName), s3_constants.ExtAmzAclKey: goodEntryAcl, }, } @@ -88,8 +88,8 @@ var tcs = []*BucketMetadataTestCase{ Name: badEntry.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -99,8 +99,8 @@ var tcs = []*BucketMetadataTestCase{ Name: goodEntry.Name, ObjectOwnership: s3_constants.OwnershipBucketOwnerEnforced, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: s3_constants.PublicRead, }, @@ -110,8 +110,8 @@ var tcs = []*BucketMetadataTestCase{ Name: ownershipEmptyStr.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -121,8 +121,8 @@ var tcs = []*BucketMetadataTestCase{ Name: ownershipValid.Name, ObjectOwnership: s3_constants.OwnershipBucketOwnerEnforced, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -132,8 +132,8 @@ var tcs = []*BucketMetadataTestCase{ Name: acpEmptyStr.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -143,8 +143,8 @@ var tcs = []*BucketMetadataTestCase{ Name: acpEmptyObject.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -154,8 +154,8 @@ var tcs = []*BucketMetadataTestCase{ Name: acpOwnerNil.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: make([]*s3.Grant, 0), }, @@ -163,14 +163,10 @@ var tcs = []*BucketMetadataTestCase{ } func TestBuildBucketMetadata(t *testing.T) { - accountManager := &s3account.AccountManager{ - IdNameMapping: map[string]string{ - s3account.AccountAdmin.Id: s3account.AccountAdmin.Name, - s3account.AccountAnonymous.Id: s3account.AccountAnonymous.Name, - }, - } + iam := &IdentityAccessManagement{} + _ = iam.loadS3ApiConfiguration(&iam_pb.S3ApiConfiguration{}) for _, tc := range tcs { - resultBucketMetadata := buildBucketMetadata(accountManager, tc.filerEntry) + resultBucketMetadata := buildBucketMetadata(iam, tc.filerEntry) if !reflect.DeepEqual(resultBucketMetadata, tc.expectBucketMetadata) { t.Fatalf("result is unexpect: \nresult: %v, \nexpect: %v", resultBucketMetadata, tc.expectBucketMetadata) } diff --git a/weed/s3api/s3_constants/s3_acp.go b/weed/s3api/s3_constants/s3_acp.go new file mode 100644 index 000000000..d24e07e24 --- /dev/null +++ b/weed/s3api/s3_constants/s3_acp.go @@ -0,0 +1,6 @@ +package s3_constants + +const ( + AccountAnonymousId = "anonymous" + AccountAdminId = "admin" +) diff --git a/weed/s3api/s3account/s3_account.go b/weed/s3api/s3account/s3_account.go deleted file mode 100644 index 9b1b01123..000000000 --- a/weed/s3api/s3account/s3_account.go +++ /dev/null @@ -1,70 +0,0 @@ -package s3account - -import ( - "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" - "sync" -) - -//Predefined Accounts -var ( - // AccountAdmin is used as the default account for IAM-Credentials access without Account configured - AccountAdmin = Account{ - Name: "admin", - EmailAddress: "admin@example.com", - Id: "admin", - } - - // AccountAnonymous is used to represent the account for anonymous access - AccountAnonymous = Account{ - Name: "anonymous", - EmailAddress: "anonymous@example.com", - Id: "anonymous", - } -) - -//Account represents a system user, a system user can -//configure multiple IAM-Users, IAM-Users can configure -//permissions respectively, and each IAM-User can -//configure multiple security credentials -type Account struct { - //Name is also used to display the "DisplayName" as the owner of the bucket or object - Name string - EmailAddress string - - //Id is used to identify an Account when granting cross-account access(ACLs) to buckets and objects - Id string -} - -type AccountManager struct { - sync.Mutex - filerClient filer_pb.FilerClient - - IdNameMapping map[string]string - EmailIdMapping map[string]string -} - -func NewAccountManager(filerClient filer_pb.FilerClient) *AccountManager { - am := &AccountManager{ - filerClient: filerClient, - IdNameMapping: make(map[string]string), - EmailIdMapping: make(map[string]string), - } - am.initialize() - return am -} - -func (am *AccountManager) GetAccountNameById(canonicalId string) string { - return am.IdNameMapping[canonicalId] -} - -func (am *AccountManager) GetAccountIdByEmail(email string) string { - return am.EmailIdMapping[email] -} - -func (am *AccountManager) initialize() { - // load predefined Accounts - for _, account := range []Account{AccountAdmin, AccountAnonymous} { - am.IdNameMapping[account.Id] = account.Name - am.EmailIdMapping[account.EmailAddress] = account.Id - } -} diff --git a/weed/s3api/s3acl/acl_helper.go b/weed/s3api/s3api_acl_helper.go index bb956368e..0332b6a39 100644 --- a/weed/s3api/s3acl/acl_helper.go +++ b/weed/s3api/s3api_acl_helper.go @@ -1,4 +1,4 @@ -package s3acl +package s3api import ( "encoding/json" @@ -8,25 +8,29 @@ import ( "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/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "github.com/seaweedfs/seaweedfs/weed/util" "net/http" "strings" ) +type AccountManager interface { + GetAccountNameById(canonicalId string) string + GetAccountIdByEmail(email string) string +} + // GetAccountId get AccountId from request headers, AccountAnonymousId will be return if not presen func GetAccountId(r *http.Request) string { id := r.Header.Get(s3_constants.AmzAccountId) if len(id) == 0 { - return s3account.AccountAnonymous.Id + return s3_constants.AccountAnonymousId } else { return id } } // ExtractAcl extracts the acl from the request body, or from the header if request body is empty -func ExtractAcl(r *http.Request, accountManager *s3account.AccountManager, ownership, bucketOwnerId, ownerId, accountId string) (grants []*s3.Grant, errCode s3err.ErrorCode) { +func ExtractAcl(r *http.Request, accountManager AccountManager, ownership, bucketOwnerId, ownerId, accountId string) (grants []*s3.Grant, errCode s3err.ErrorCode) { if r.Body != nil && r.Body != http.NoBody { defer util.CloseRequest(r) @@ -50,7 +54,7 @@ func ExtractAcl(r *http.Request, accountManager *s3account.AccountManager, owner } // ParseAndValidateAclHeadersOrElseDefault will callParseAndValidateAclHeaders to get Grants, if empty, it will return Grant that grant `accountId` with `FullControl` permission -func ParseAndValidateAclHeadersOrElseDefault(r *http.Request, accountManager *s3account.AccountManager, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) { +func ParseAndValidateAclHeadersOrElseDefault(r *http.Request, accountManager AccountManager, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) { ownerId, grants, errCode = ParseAndValidateAclHeaders(r, accountManager, ownership, bucketOwnerId, accountId, putAcl) if errCode != s3err.ErrNone { return @@ -69,7 +73,7 @@ func ParseAndValidateAclHeadersOrElseDefault(r *http.Request, accountManager *s3 } // ParseAndValidateAclHeaders parse and validate acl from header -func ParseAndValidateAclHeaders(r *http.Request, accountManager *s3account.AccountManager, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) { +func ParseAndValidateAclHeaders(r *http.Request, accountManager AccountManager, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) { ownerId, grants, errCode = ParseAclHeaders(r, ownership, bucketOwnerId, accountId, putAcl) if errCode != s3err.ErrNone { return @@ -257,7 +261,7 @@ func ParseCannedAclHeader(bucketOwnership, bucketOwnerId, accountId, cannedAcl s } // ValidateAndTransferGrants validate grant & transfer Email-Grant to Id-Grant -func ValidateAndTransferGrants(accountManager *s3account.AccountManager, grants []*s3.Grant) ([]*s3.Grant, s3err.ErrorCode) { +func ValidateAndTransferGrants(accountManager AccountManager, grants []*s3.Grant) ([]*s3.Grant, s3err.ErrorCode) { var result []*s3.Grant for _, grant := range grants { grantee := grant.Grantee @@ -283,8 +287,8 @@ func ValidateAndTransferGrants(accountManager *s3account.AccountManager, grants glog.Warning("invalid canonical grantee! account id is nil") return nil, s3err.ErrInvalidRequest } - _, ok := accountManager.IdNameMapping[*grantee.ID] - if !ok { + name := accountManager.GetAccountNameById(*grantee.ID) + if len(name) == 0 { glog.Warningf("invalid canonical grantee! account id[%s] is not exists", *grantee.ID) return nil, s3err.ErrInvalidRequest } @@ -294,8 +298,8 @@ func ValidateAndTransferGrants(accountManager *s3account.AccountManager, grants glog.Warning("invalid email grantee! email address is nil") return nil, s3err.ErrInvalidRequest } - accountId, ok := accountManager.EmailIdMapping[*grantee.EmailAddress] - if !ok { + accountId := accountManager.GetAccountIdByEmail(*grantee.EmailAddress) + if len(accountId) == 0 { glog.Warningf("invalid email grantee! email address[%s] is not exists", *grantee.EmailAddress) return nil, s3err.ErrInvalidRequest } @@ -348,7 +352,7 @@ func DetermineReqGrants(accountId, aclAction string) (grants []*s3.Grant) { }) // group grantee (AuthenticateUsers) - if accountId != s3account.AccountAnonymous.Id { + if accountId != s3_constants.AccountAnonymousId { grants = append(grants, &s3.Grant{ Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, diff --git a/weed/s3api/s3acl/acl_helper_test.go b/weed/s3api/s3api_acl_helper_test.go index ce177595b..81ce25575 100644 --- a/weed/s3api/s3acl/acl_helper_test.go +++ b/weed/s3api/s3api_acl_helper_test.go @@ -1,34 +1,38 @@ -package s3acl +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/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "io" "net/http" "testing" ) -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", +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{ @@ -36,22 +40,22 @@ func TestGetAccountId(t *testing.T) { } //case1 //accountId: "admin" - req.Header.Set(s3_constants.AmzAccountId, s3account.AccountAdmin.Id) - if GetAccountId(req) != s3account.AccountAdmin.Id { + 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, s3account.AccountAnonymous.Id) - if GetAccountId(req) != s3account.AccountAnonymous.Id { + 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) != s3account.AccountAnonymous.Id { + if GetAccountId(req) != s3_constants.AccountAnonymousId { t.Fatal("expect accountId: anonymous") } } @@ -64,7 +68,6 @@ func TestExtractAcl(t *testing.T) { } testCases := make([]*Case, 0) accountAdminId := "admin" - { //case1 (good case) //parse acp from request body @@ -249,14 +252,14 @@ func TestParseAndValidateAclHeaders(t *testing.T) { { Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeCanonicalUser, - ID: &s3account.AccountAnonymous.Id, + ID: aws.String(s3_constants.AccountAnonymousId), }, Permission: &s3_constants.PermissionFullControl, }, { Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeCanonicalUser, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), }, Permission: &s3_constants.PermissionFullControl, }, @@ -391,7 +394,7 @@ func grantsEquals(a, b []*s3.Grant) bool { func TestDetermineReqGrants(t *testing.T) { { //case1: request account is anonymous - accountId := s3account.AccountAnonymous.Id + accountId := s3_constants.AccountAnonymousId reqPermission := s3_constants.PermissionRead resultGrants := DetermineReqGrants(accountId, reqPermission) @@ -496,7 +499,7 @@ func TestAssembleEntryWithAcp(t *testing.T) { Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), URI: &s3_constants.GranteeGroupAllUsers, }, }, @@ -569,13 +572,13 @@ func TestGrantEquals(t *testing.T) { GrantEquals(&s3.Grant{ Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ - ID: &s3account.AccountAdmin.Id, - EmailAddress: &s3account.AccountAdmin.EmailAddress, + ID: aws.String(s3_constants.AccountAdminId), + //EmailAddress: &s3account.AccountAdmin.EmailAddress, }, }, &s3.Grant{ Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), }, }): true, @@ -623,13 +626,13 @@ func TestGrantEquals(t *testing.T) { Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), }, }, &s3.Grant{ Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), }, }): true, @@ -637,14 +640,14 @@ func TestGrantEquals(t *testing.T) { Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), URI: &s3_constants.GranteeGroupAllUsers, }, }, &s3.Grant{ Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), }, }): false, @@ -652,7 +655,7 @@ func TestGrantEquals(t *testing.T) { Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), URI: &s3_constants.GranteeGroupAllUsers, }, }, &s3.Grant{ @@ -692,7 +695,7 @@ func TestSetAcpGrantsHeader(t *testing.T) { Permission: &s3_constants.PermissionRead, Grantee: &s3.Grantee{ Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, + ID: aws.String(s3_constants.AccountAdminId), URI: &s3_constants.GranteeGroupAllUsers, }, }, diff --git a/weed/s3api/s3api_acp.go b/weed/s3api/s3api_acp.go index 7a76c2a67..0a79990f5 100644 --- a/weed/s3api/s3api_acp.go +++ b/weed/s3api/s3api_acp.go @@ -2,7 +2,6 @@ package s3api import ( "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "net/http" ) @@ -10,7 +9,7 @@ import ( func getAccountId(r *http.Request) string { id := r.Header.Get(s3_constants.AmzAccountId) if len(id) == 0 { - return s3account.AccountAnonymous.Id + return AccountAnonymous.Id } else { return id } @@ -22,7 +21,7 @@ func (s3a *S3ApiServer) checkAccessByOwnership(r *http.Request, bucket string) s return errCode } accountId := getAccountId(r) - if accountId == s3account.AccountAdmin.Id || accountId == *metadata.Owner.ID { + if accountId == AccountAdmin.Id || accountId == *metadata.Owner.ID { return s3err.ErrNone } return s3err.ErrAccessDenied diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index d2e987a25..3bf5a598b 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -259,17 +259,18 @@ func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Reque return } - identityId := r.Header.Get(s3_constants.AmzIdentityId) + amzAccountId := r.Header.Get(s3_constants.AmzAccountId) + amzDisplayName := s3a.iam.GetAccountNameById(amzAccountId) response := AccessControlPolicy{ Owner: CanonicalUser{ - ID: identityId, - DisplayName: identityId, + ID: amzAccountId, + DisplayName: amzDisplayName, }, } response.AccessControlList.Grant = append(response.AccessControlList.Grant, Grant{ Grantee: Grantee{ - ID: identityId, - DisplayName: identityId, + ID: amzAccountId, + DisplayName: amzDisplayName, Type: "CanonicalUser", XMLXSI: "CanonicalUser", XMLNS: "http://www.w3.org/2001/XMLSchema-instance"}, diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index 77c488f4d..e38aae23d 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/seaweedfs/seaweedfs/weed/filer" "github.com/seaweedfs/seaweedfs/weed/pb/s3_pb" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "net" "net/http" "strings" @@ -42,7 +41,6 @@ type S3ApiServer struct { randomClientId int32 filerGuard *security.Guard client *http.Client - accountManager *s3account.AccountManager bucketRegistry *BucketRegistry } @@ -63,7 +61,6 @@ func NewS3ApiServer(router *mux.Router, option *S3ApiServerOption) (s3ApiServer filerGuard: security.NewGuard([]string{}, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec), cb: NewCircuitBreaker(option), } - s3ApiServer.accountManager = s3account.NewAccountManager(s3ApiServer) s3ApiServer.bucketRegistry = NewBucketRegistry(s3ApiServer) if option.LocalFilerSocket == "" { s3ApiServer.client = &http.Client{Transport: &http.Transport{ |
