[s3acl] Step1: move s3account.AccountManager into to iam.S3ApiConfiguration (#4859)

* move s3account.AccountManager into to iam.S3ApiConfiguration and switch to Interface

https://github.com/seaweedfs/seaweedfs/issues/4519

* fix: test bucket acl default and
adjust the variable names

* fix: s3 api config test

---------

Co-authored-by: Konstantin Lebedev <9497591+kmlebedev@users.noreply.github.co>
Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com>
This commit is contained in:
Konstantin Lebedev
2023-09-25 20:34:12 +05:00
committed by GitHub
parent c9177c92e5
commit f8b94cac0e
17 changed files with 400 additions and 235 deletions

View File

@@ -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
}