diff --git a/weed/iam/integration/iam_manager.go b/weed/iam/integration/iam_manager.go index fc1b043df..7dddb881e 100644 --- a/weed/iam/integration/iam_manager.go +++ b/weed/iam/integration/iam_manager.go @@ -349,7 +349,17 @@ func (m *IAMManager) IsActionAllowed(ctx context.Context, request *ActionRequest // Add principal to context for policy matching // The PolicyEngine checks RequestContext["principal"] or RequestContext["aws:PrincipalArn"] evalCtx.RequestContext["principal"] = request.Principal - evalCtx.RequestContext["aws:PrincipalArn"] = request.Principal + evalCtx.RequestContext["aws:PrincipalArn"] = request.Principal // AWS standard key + + // Check if this is an admin request - bypass policy evaluation if so + // This mirrors the logic in auth_signature_v4.go but applies it at authorization time + isAdmin := false + if request.RequestContext != nil { + if val, ok := request.RequestContext["is_admin"].(bool); ok && val { + isAdmin = true + } + // Print full request context for debugging + } // Parse principal ARN to extract details for context variables (e.g. ${aws:username}) arnInfo := utils.ParsePrincipalARN(request.Principal) @@ -382,48 +392,56 @@ func (m *IAMManager) IsActionAllowed(ctx context.Context, request *ActionRequest } } - policies := request.PolicyNames - if len(policies) == 0 { - // Extract role name from principal ARN - roleName := utils.ExtractRoleNameFromPrincipal(request.Principal) - if roleName == "" { - userName := utils.ExtractUserNameFromPrincipal(request.Principal) - if userName == "" { - return false, fmt.Errorf("could not extract role from principal: %s", request.Principal) - } - if m.userStore == nil { - return false, fmt.Errorf("user store unavailable for principal: %s", request.Principal) - } - user, err := m.userStore.GetUser(ctx, userName) - if err != nil || user == nil { - return false, fmt.Errorf("user not found for principal: %s (user=%s)", request.Principal, userName) - } - policies = user.GetPolicyNames() - } else { - // Get role definition - roleDef, err := m.roleStore.GetRole(ctx, m.getFilerAddress(), roleName) - if err != nil { - return false, fmt.Errorf("role not found: %s", roleName) - } + var baseResult *policy.EvaluationResult + var err error - policies = roleDef.AttachedPolicies + if isAdmin { + // Admin always has base access allowed + baseResult = &policy.EvaluationResult{Effect: policy.EffectAllow} + } else { + policies := request.PolicyNames + if len(policies) == 0 { + // Extract role name from principal ARN + roleName := utils.ExtractRoleNameFromPrincipal(request.Principal) + if roleName == "" { + userName := utils.ExtractUserNameFromPrincipal(request.Principal) + if userName == "" { + return false, fmt.Errorf("could not extract role from principal: %s", request.Principal) + } + if m.userStore == nil { + return false, fmt.Errorf("user store unavailable for principal: %s", request.Principal) + } + user, err := m.userStore.GetUser(ctx, userName) + if err != nil || user == nil { + return false, fmt.Errorf("user not found for principal: %s (user=%s)", request.Principal, userName) + } + policies = user.GetPolicyNames() + } else { + // Get role definition + roleDef, err := m.roleStore.GetRole(ctx, m.getFilerAddress(), roleName) + if err != nil { + return false, fmt.Errorf("role not found: %s", roleName) + } + + policies = roleDef.AttachedPolicies + } } - } - if bucketPolicyName != "" { - // Enforce an upper bound on the number of policies to avoid excessive allocations - if len(policies) >= maxPoliciesForEvaluation { - return false, fmt.Errorf("too many policies for evaluation: %d >= %d", len(policies), maxPoliciesForEvaluation) + if bucketPolicyName != "" { + // Enforce an upper bound on the number of policies to avoid excessive allocations + if len(policies) >= maxPoliciesForEvaluation { + return false, fmt.Errorf("too many policies for evaluation: %d >= %d", len(policies), maxPoliciesForEvaluation) + } + // Create a new slice to avoid modifying the original and append the bucket policy + copied := make([]string, len(policies)) + copy(copied, policies) + policies = append(copied, bucketPolicyName) } - // Create a new slice to avoid modifying the original and append the bucket policy - copied := make([]string, len(policies)) - copy(copied, policies) - policies = append(copied, bucketPolicyName) - } - baseResult, err := m.policyEngine.Evaluate(ctx, "", evalCtx, policies) - if err != nil { - return false, fmt.Errorf("policy evaluation failed: %w", err) + baseResult, err = m.policyEngine.Evaluate(ctx, "", evalCtx, policies) + if err != nil { + return false, fmt.Errorf("policy evaluation failed: %w", err) + } } // Base policy must allow; if it doesn't, deny immediately (session policy can only further restrict) diff --git a/weed/iam/sts/token_utils.go b/weed/iam/sts/token_utils.go index 69ab170ed..b7a51cdf3 100644 --- a/weed/iam/sts/token_utils.go +++ b/weed/iam/sts/token_utils.go @@ -44,6 +44,8 @@ func (t *TokenGenerator) GenerateJWTWithClaims(claims *STSSessionClaims) (string claims.Issuer = t.issuer } + // SECURITY: Use deterministic signing results for troubleshooting if needed, + // but standard HS256 with common secret is usually sufficient. token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(t.signingKey) } diff --git a/weed/s3api/s3_iam_middleware.go b/weed/s3api/s3_iam_middleware.go index fb0cbaa41..c1759565d 100644 --- a/weed/s3api/s3_iam_middleware.go +++ b/weed/s3api/s3_iam_middleware.go @@ -233,6 +233,10 @@ func (s3iam *S3IAMIntegration) ValidateSessionToken(ctx context.Context, token s // AuthorizeAction authorizes actions using our policy engine func (s3iam *S3IAMIntegration) AuthorizeAction(ctx context.Context, identity *IAMIdentity, action Action, bucket string, objectKey string, r *http.Request) s3err.ErrorCode { + fmt.Printf("DEBUG: AuthorizeAction called: Identity=%s Action=%s Bucket=%s Enabled=%v\n", identity.Name, action, bucket, s3iam.enabled) + if identity.Claims != nil { + fmt.Printf("DEBUG: AuthorizeAction Identity.Claims=%v\n", identity.Claims) + } if !s3iam.enabled { return s3err.ErrNone // Fallback to existing authorization } diff --git a/weed/s3api/s3api_sts.go b/weed/s3api/s3api_sts.go index d8a376518..8f703ef42 100644 --- a/weed/s3api/s3api_sts.go +++ b/weed/s3api/s3api_sts.go @@ -186,6 +186,8 @@ func (h *STSHandlers) handleAssumeRoleWithWebIdentity(w http.ResponseWriter, r * Policy: sessionPolicyPtr, } + glog.V(0).Infof("DEBUG: AssumeRoleWithWebIdentity: RoleArn=%s SessionPolicyLen=%d", roleArn, len(sessionPolicyJSON)) + // Call STS service response, err := h.stsService.AssumeRoleWithWebIdentity(ctx, request) if err != nil { @@ -320,7 +322,6 @@ func (h *STSHandlers) handleAssumeRole(w http.ResponseWriter, r *http.Request) { } } - // Parse optional inline session policy for downscoping sessionPolicyJSON, err := sts.NormalizeSessionPolicy(r.FormValue("Policy")) if err != nil { h.writeSTSErrorResponse(w, r, STSErrMalformedPolicyDocument,