package s3api import ( "fmt" "github.com/seaweedfs/seaweedfs/weed/iam/policy" "github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine" ) // ConvertPolicyDocumentToPolicyEngine converts a policy.PolicyDocument to policy_engine.PolicyDocument // This function provides efficient type conversion without JSON marshaling overhead. // It handles the differences between the two types: // - Converts []string fields to StringOrStringSlice // - Maps Condition types // - Handles optional fields (Id, NotPrincipal, NotAction, NotResource are ignored in policy_engine) func ConvertPolicyDocumentToPolicyEngine(src *policy.PolicyDocument) *policy_engine.PolicyDocument { if src == nil { return nil } dest := &policy_engine.PolicyDocument{ Version: src.Version, Statement: make([]policy_engine.PolicyStatement, len(src.Statement)), } for i, srcStmt := range src.Statement { dest.Statement[i] = convertStatement(&srcStmt) } return dest } // convertStatement converts a policy.Statement to policy_engine.PolicyStatement func convertStatement(src *policy.Statement) policy_engine.PolicyStatement { stmt := policy_engine.PolicyStatement{ Sid: src.Sid, Effect: policy_engine.PolicyEffect(src.Effect), } // Convert Action ([]string to StringOrStringSlice) if len(src.Action) > 0 { stmt.Action = policy_engine.NewStringOrStringSlice(src.Action...) } // Convert Resource ([]string to StringOrStringSlice) if len(src.Resource) > 0 { stmt.Resource = policy_engine.NewStringOrStringSlice(src.Resource...) } // Convert Principal (interface{} to *StringOrStringSlice) if src.Principal != nil { stmt.Principal = convertPrincipal(src.Principal) } // Convert Condition (map[string]map[string]interface{} to PolicyConditions) if len(src.Condition) > 0 { stmt.Condition = convertCondition(src.Condition) } return stmt } // convertPrincipal converts a Principal field to *StringOrStringSlice func convertPrincipal(principal interface{}) *policy_engine.StringOrStringSlice { if principal == nil { return nil } switch p := principal.(type) { case string: result := policy_engine.NewStringOrStringSlice(p) return &result case []string: result := policy_engine.NewStringOrStringSlice(p...) return &result case []interface{}: // Convert []interface{} to []string strs := make([]string, 0, len(p)) for _, v := range p { if str, ok := v.(string); ok { strs = append(strs, str) } } if len(strs) > 0 { result := policy_engine.NewStringOrStringSlice(strs...) return &result } case map[string]interface{}: // Handle AWS-style principal with service/user keys // Example: {"AWS": "arn:aws:iam::123456789012:user/Alice"} strs := make([]string, 0) for _, v := range p { switch val := v.(type) { case string: strs = append(strs, val) case []string: strs = append(strs, val...) case []interface{}: for _, item := range val { if str, ok := item.(string); ok { strs = append(strs, str) } } } } if len(strs) > 0 { result := policy_engine.NewStringOrStringSlice(strs...) return &result } } return nil } // convertCondition converts policy conditions to PolicyConditions func convertCondition(src map[string]map[string]interface{}) policy_engine.PolicyConditions { if len(src) == 0 { return nil } dest := make(policy_engine.PolicyConditions) for condType, condBlock := range src { destBlock := make(map[string]policy_engine.StringOrStringSlice) for key, value := range condBlock { destBlock[key] = convertConditionValue(value) } dest[condType] = destBlock } return dest } // convertConditionValue converts a condition value to StringOrStringSlice func convertConditionValue(value interface{}) policy_engine.StringOrStringSlice { switch v := value.(type) { case string: return policy_engine.NewStringOrStringSlice(v) case []string: return policy_engine.NewStringOrStringSlice(v...) case []interface{}: strs := make([]string, 0, len(v)) for _, item := range v { if str, ok := item.(string); ok { strs = append(strs, str) } } return policy_engine.NewStringOrStringSlice(strs...) default: // For non-string types, convert to string // This handles numbers, booleans, etc. return policy_engine.NewStringOrStringSlice(convertToString(v)) } } // convertToString converts any value to string representation func convertToString(value interface{}) string { switch v := value.(type) { case string: return v case bool: if v { return "true" } return "false" case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: // Use fmt.Sprint for numeric types return fmt.Sprint(v) default: return "" } }