Revert "Fix IAM defaults and s3tables identities"

This reverts commit bf71fe0039.
This commit is contained in:
Chris Lu
2026-02-18 16:23:13 -08:00
parent bf71fe0039
commit eda4a000cc
5 changed files with 12 additions and 260 deletions

View File

@@ -168,46 +168,26 @@ func (h *S3TablesHandler) HandleRequest(w http.ResponseWriter, r *http.Request,
// Principal/authorization helpers
// getAccountID returns a stable caller identifier for ownership and permission checks.
// Prefer JWT/STS user claims to avoid collapsing all assumed-role callers to the same
// account ID (e.g. 000000000000), which would effectively grant cross-user access.
// getAccountID returns the authenticated account ID from the request or the handler's default.
// This is also used as the principal for permission checks, ensuring alignment between
// the caller identity and ownership verification when IAM is enabled.
func (h *S3TablesHandler) getAccountID(r *http.Request) string {
identityRaw := s3_constants.GetIdentityFromContext(r)
if identityRaw != nil {
// Use reflection to access identity fields and avoid import cycles.
// Use reflection to access the Account.Id field to avoid import cycle
val := reflect.ValueOf(identityRaw)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() == reflect.Struct {
// Prefer stable user claims from JWT/STS identities.
claimsField := val.FieldByName("Claims")
if claimsField.IsValid() && claimsField.Kind() == reflect.Map && !claimsField.IsNil() && claimsField.Type().Key().Kind() == reflect.String {
for _, claimKey := range []string{"preferred_username", "sub", "email"} {
claimVal := claimsField.MapIndex(reflect.ValueOf(claimKey))
if !claimVal.IsValid() {
continue
}
if claimVal.Kind() == reflect.Interface && !claimVal.IsNil() {
claimVal = claimVal.Elem()
}
if claimVal.Kind() == reflect.String {
if principal := normalizePrincipalID(claimVal.String()); principal != "" {
return principal
}
}
}
}
accountField := val.FieldByName("Account")
if accountField.IsValid() && !accountField.IsNil() {
accountVal := accountField.Elem()
if accountVal.Kind() == reflect.Struct {
idField := accountVal.FieldByName("Id")
if idField.IsValid() && idField.Kind() == reflect.String {
if principal := normalizePrincipalID(idField.String()); principal != "" {
return principal
}
id := idField.String()
return id
}
}
}
@@ -215,35 +195,15 @@ func (h *S3TablesHandler) getAccountID(r *http.Request) string {
}
if identityName := s3_constants.GetIdentityNameFromContext(r); identityName != "" {
if principal := normalizePrincipalID(identityName); principal != "" {
return principal
}
return identityName
}
if accountID := r.Header.Get(s3_constants.AmzAccountId); accountID != "" {
if principal := normalizePrincipalID(accountID); principal != "" {
return principal
}
return accountID
}
return h.accountID
}
func normalizePrincipalID(id string) string {
id = strings.TrimSpace(id)
if id == "" {
return ""
}
// If this is an ARN (common for assumed roles), use the trailing segment as a
// stable-ish principal key instead of embedding the full ARN in ownership fields.
if strings.HasPrefix(id, "arn:") {
if idx := strings.LastIndex(id, "/"); idx >= 0 && idx+1 < len(id) {
return strings.TrimSpace(id[idx+1:])
}
return ""
}
return id
}
// getIdentityActions extracts the action list from the identity object in the request context.
// Uses reflection to avoid import cycles with s3api package.
func getIdentityActions(r *http.Request) []string {

View File

@@ -1,81 +0,0 @@
package s3tables
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
)
type testIdentityAccount struct {
Id string
}
type testIdentity struct {
Account *testIdentityAccount
Claims map[string]interface{}
}
func TestGetAccountIDPrefersClaimsOverAccountID(t *testing.T) {
h := NewS3TablesHandler()
id := &testIdentity{
Account: &testIdentityAccount{Id: s3_constants.AccountAdminId},
Claims: map[string]interface{}{
"preferred_username": "alice",
"sub": "alice-sub",
},
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req = req.WithContext(s3_constants.SetIdentityInContext(req.Context(), id))
got := h.getAccountID(req)
if got != "alice" {
t.Fatalf("expected preferred_username claim to be used, got %q", got)
}
}
func TestGetAccountIDUsesSubWhenPreferredUsernameMissing(t *testing.T) {
h := NewS3TablesHandler()
id := &testIdentity{
Account: &testIdentityAccount{Id: s3_constants.AccountAdminId},
Claims: map[string]interface{}{
"sub": "user-123",
},
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req = req.WithContext(s3_constants.SetIdentityInContext(req.Context(), id))
got := h.getAccountID(req)
if got != "user-123" {
t.Fatalf("expected sub claim to be used, got %q", got)
}
}
func TestGetAccountIDFallsBackToAccountID(t *testing.T) {
h := NewS3TablesHandler()
id := &testIdentity{
Account: &testIdentityAccount{Id: s3_constants.AccountAdminId},
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req = req.WithContext(s3_constants.SetIdentityInContext(req.Context(), id))
got := h.getAccountID(req)
if got != s3_constants.AccountAdminId {
t.Fatalf("expected fallback account id %q, got %q", s3_constants.AccountAdminId, got)
}
}
func TestGetAccountIDNormalizesArnIdentityName(t *testing.T) {
h := NewS3TablesHandler()
req := httptest.NewRequest(http.MethodGet, "/", nil)
req = req.WithContext(s3_constants.SetIdentityNameInContext(req.Context(), "arn:aws:sts::123456789012:assumed-role/S3UserRole/alice-session"))
got := h.getAccountID(req)
if got != "alice-session" {
t.Fatalf("expected ARN session suffix, got %q", got)
}
}

View File

@@ -208,10 +208,6 @@ func hasIdentityPermission(operation string, ctx *PolicyContext) bool {
candidates = append(candidates, operation+":"+ctx.TableBucketName, fullAction+":"+ctx.TableBucketName)
}
for _, action := range ctx.IdentityActions {
// Legacy static identities may still use broad admin markers.
if action == "*" || action == "Admin" || action == string(s3_constants.ACTION_ADMIN) || action == "s3:*" || action == "s3tables:*" {
return true
}
for _, candidate := range candidates {
if action == candidate {
return true