Browse Source

unified IAM authorization system

pull/7160/head
chrislu 1 month ago
parent
commit
c66e1f5ec2
  1. 58
      weed/s3api/auth_credentials.go
  2. 15
      weed/s3api/auth_credentials_test.go
  3. 4
      weed/s3api/s3_jwt_auth_test.go
  4. 4
      weed/s3api/s3_multipart_iam_test.go
  5. 4
      weed/s3api/s3_presigned_url_iam_test.go

58
weed/s3api/auth_credentials.go

@ -60,6 +60,7 @@ type Identity struct {
Account *Account Account *Account
Credentials []*Credential Credentials []*Credential
Actions []Action Actions []Action
PrincipalArn string // ARN for IAM authorization (e.g., "arn:seaweed:iam::user/username")
} }
// Account represents a system user, a system user can // Account represents a system user, a system user can
@ -302,9 +303,10 @@ func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3Api
for _, ident := range config.Identities { for _, ident := range config.Identities {
glog.V(3).Infof("loading identity %s", ident.Name) glog.V(3).Infof("loading identity %s", ident.Name)
t := &Identity{ t := &Identity{
Name: ident.Name,
Credentials: nil,
Actions: nil,
Name: ident.Name,
Credentials: nil,
Actions: nil,
PrincipalArn: generatePrincipalArn(ident.Name),
} }
switch { switch {
case ident.Name == AccountAnonymous.Id: case ident.Name == AccountAnonymous.Id:
@ -376,6 +378,19 @@ func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, foun
return nil, false return nil, false
} }
// generatePrincipalArn generates an ARN for a user identity
func generatePrincipalArn(identityName string) string {
// Handle special cases
switch identityName {
case AccountAnonymous.Id:
return "arn:seaweed:iam::user/anonymous"
case AccountAdmin.Id:
return "arn:seaweed:iam::user/admin"
default:
return fmt.Sprintf("arn:seaweed:iam::user/%s", identityName)
}
}
func (iam *IdentityAccessManagement) GetAccountNameById(canonicalId string) string { func (iam *IdentityAccessManagement) GetAccountNameById(canonicalId string) string {
iam.m.RLock() iam.m.RLock()
defer iam.m.RUnlock() defer iam.m.RUnlock()
@ -487,14 +502,14 @@ func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action)
if action == s3_constants.ACTION_LIST && bucket == "" { if action == s3_constants.ACTION_LIST && bucket == "" {
// ListBuckets operation - authorization handled per-bucket in the handler // ListBuckets operation - authorization handled per-bucket in the handler
} else { } else {
// Use enhanced authorization if IAM integration is available
sessionToken := r.Header.Get("X-SeaweedFS-Session-Token")
if iam.iamIntegration != nil && sessionToken != "" {
// Use enhanced IAM authorization if available, otherwise fall back to legacy authorization
if iam.iamIntegration != nil {
// Always use IAM when available for unified authorization
if errCode := iam.authorizeWithIAM(r, identity, action, bucket, object); errCode != s3err.ErrNone { if errCode := iam.authorizeWithIAM(r, identity, action, bucket, object); errCode != s3err.ErrNone {
return identity, errCode return identity, errCode
} }
} else { } else {
// Fall back to existing authorization
// Fall back to existing authorization when IAM is not configured
if !identity.canDo(action, bucket, object) { if !identity.canDo(action, bucket, object) {
return identity, s3err.ErrAccessDenied return identity, s3err.ErrAccessDenied
} }
@ -635,21 +650,30 @@ func (iam *IdentityAccessManagement) authenticateJWTWithIAM(r *http.Request) (*I
func (iam *IdentityAccessManagement) authorizeWithIAM(r *http.Request, identity *Identity, action Action, bucket string, object string) s3err.ErrorCode { func (iam *IdentityAccessManagement) authorizeWithIAM(r *http.Request, identity *Identity, action Action, bucket string, object string) s3err.ErrorCode {
ctx := r.Context() ctx := r.Context()
// Get session info from request headers
// Get session info from request headers (for JWT-based authentication)
sessionToken := r.Header.Get("X-SeaweedFS-Session-Token") sessionToken := r.Header.Get("X-SeaweedFS-Session-Token")
principal := r.Header.Get("X-SeaweedFS-Principal") principal := r.Header.Get("X-SeaweedFS-Principal")
if sessionToken == "" || principal == "" {
glog.V(3).Info("No session information for IAM authorization")
return s3err.ErrAccessDenied
}
// Create IAMIdentity for authorization // Create IAMIdentity for authorization
iamIdentity := &IAMIdentity{ iamIdentity := &IAMIdentity{
Name: identity.Name,
Principal: principal,
SessionToken: sessionToken,
Account: identity.Account,
Name: identity.Name,
Account: identity.Account,
}
// Handle both session-based (JWT) and static-key-based (V4 signature) principals
if sessionToken != "" && principal != "" {
// JWT-based authentication - use session token and principal from headers
iamIdentity.Principal = principal
iamIdentity.SessionToken = sessionToken
glog.V(3).Infof("Using JWT-based IAM authorization for principal: %s", principal)
} else if identity.PrincipalArn != "" {
// V4 signature authentication - use principal ARN from identity
iamIdentity.Principal = identity.PrincipalArn
iamIdentity.SessionToken = "" // No session token for static credentials
glog.V(3).Infof("Using V4 signature IAM authorization for principal: %s", identity.PrincipalArn)
} else {
glog.V(3).Info("No valid principal information for IAM authorization")
return s3err.ErrAccessDenied
} }
// Use IAM integration for authorization // Use IAM integration for authorization

15
weed/s3api/auth_credentials_test.go

@ -191,8 +191,9 @@ func TestLoadS3ApiConfiguration(t *testing.T) {
}, },
}, },
expectIdent: &Identity{ expectIdent: &Identity{
Name: "notSpecifyAccountId",
Account: &AccountAdmin,
Name: "notSpecifyAccountId",
Account: &AccountAdmin,
PrincipalArn: "arn:seaweed:iam::user/notSpecifyAccountId",
Actions: []Action{ Actions: []Action{
"Read", "Read",
"Write", "Write",
@ -216,8 +217,9 @@ func TestLoadS3ApiConfiguration(t *testing.T) {
}, },
}, },
expectIdent: &Identity{ expectIdent: &Identity{
Name: "specifiedAccountID",
Account: &specifiedAccount,
Name: "specifiedAccountID",
Account: &specifiedAccount,
PrincipalArn: "arn:seaweed:iam::user/specifiedAccountID",
Actions: []Action{ Actions: []Action{
"Read", "Read",
"Write", "Write",
@ -233,8 +235,9 @@ func TestLoadS3ApiConfiguration(t *testing.T) {
}, },
}, },
expectIdent: &Identity{ expectIdent: &Identity{
Name: "anonymous",
Account: &AccountAnonymous,
Name: "anonymous",
Account: &AccountAnonymous,
PrincipalArn: "arn:seaweed:iam::user/anonymous",
Actions: []Action{ Actions: []Action{
"Read", "Read",
"Write", "Write",

4
weed/s3api/s3_jwt_auth_test.go

@ -282,7 +282,9 @@ func setupTestIAMManager(t *testing.T) *integration.IAMManager {
}, },
} }
err := manager.Initialize(config)
err := manager.Initialize(config, func() string {
return "localhost:8888" // Mock filer address for testing
})
require.NoError(t, err) require.NoError(t, err)
// Set up test identity providers // Set up test identity providers

4
weed/s3api/s3_multipart_iam_test.go

@ -473,7 +473,9 @@ func setupTestIAMManagerForMultipart(t *testing.T) *integration.IAMManager {
}, },
} }
err := manager.Initialize(config)
err := manager.Initialize(config, func() string {
return "localhost:8888" // Mock filer address for testing
})
require.NoError(t, err) require.NoError(t, err)
// Set up test identity providers // Set up test identity providers

4
weed/s3api/s3_presigned_url_iam_test.go

@ -434,7 +434,9 @@ func setupTestIAMManagerForPresigned(t *testing.T) *integration.IAMManager {
}, },
} }
err := manager.Initialize(config)
err := manager.Initialize(config, func() string {
return "localhost:8888" // Mock filer address for testing
})
require.NoError(t, err) require.NoError(t, err)
// Set up test identity providers // Set up test identity providers

Loading…
Cancel
Save