diff options
Diffstat (limited to 'weed/s3api/policy_engine/conditions.go')
| -rw-r--r-- | weed/s3api/policy_engine/conditions.go | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/weed/s3api/policy_engine/conditions.go b/weed/s3api/policy_engine/conditions.go new file mode 100644 index 000000000..fc8005fd0 --- /dev/null +++ b/weed/s3api/policy_engine/conditions.go @@ -0,0 +1,768 @@ +package policy_engine + +import ( + "fmt" + "net" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/seaweedfs/seaweedfs/weed/glog" +) + +// LRUNode represents a node in the doubly-linked list for efficient LRU operations +type LRUNode struct { + key string + value []string + prev *LRUNode + next *LRUNode +} + +// NormalizedValueCache provides size-limited caching for normalized values with efficient LRU eviction +type NormalizedValueCache struct { + mu sync.RWMutex + cache map[string]*LRUNode + maxSize int + head *LRUNode // Most recently used + tail *LRUNode // Least recently used +} + +// NewNormalizedValueCache creates a new normalized value cache with configurable size +func NewNormalizedValueCache(maxSize int) *NormalizedValueCache { + if maxSize <= 0 { + maxSize = 1000 // Default size + } + + // Create dummy head and tail nodes for easier list manipulation + head := &LRUNode{} + tail := &LRUNode{} + head.next = tail + tail.prev = head + + return &NormalizedValueCache{ + cache: make(map[string]*LRUNode), + maxSize: maxSize, + head: head, + tail: tail, + } +} + +// Get retrieves a cached value and updates access order in O(1) time +func (c *NormalizedValueCache) Get(key string) ([]string, bool) { + c.mu.Lock() + defer c.mu.Unlock() + + if node, exists := c.cache[key]; exists { + // Move to head (most recently used) - O(1) operation + c.moveToHead(node) + return node.value, true + } + return nil, false +} + +// Set stores a value in the cache with size limit enforcement in O(1) time +func (c *NormalizedValueCache) Set(key string, value []string) { + c.mu.Lock() + defer c.mu.Unlock() + + if node, exists := c.cache[key]; exists { + // Update existing node and move to head + node.value = value + c.moveToHead(node) + return + } + + // Create new node + newNode := &LRUNode{ + key: key, + value: value, + } + + // If at max size, evict least recently used + if len(c.cache) >= c.maxSize { + c.evictLeastRecentlyUsed() + } + + // Add to cache and move to head + c.cache[key] = newNode + c.addToHead(newNode) +} + +// moveToHead moves a node to the head of the list (most recently used) - O(1) +func (c *NormalizedValueCache) moveToHead(node *LRUNode) { + c.removeNode(node) + c.addToHead(node) +} + +// addToHead adds a node right after the head - O(1) +func (c *NormalizedValueCache) addToHead(node *LRUNode) { + node.prev = c.head + node.next = c.head.next + c.head.next.prev = node + c.head.next = node +} + +// removeNode removes a node from the list - O(1) +func (c *NormalizedValueCache) removeNode(node *LRUNode) { + node.prev.next = node.next + node.next.prev = node.prev +} + +// removeTail removes the last node before tail (least recently used) - O(1) +func (c *NormalizedValueCache) removeTail() *LRUNode { + lastNode := c.tail.prev + c.removeNode(lastNode) + return lastNode +} + +// evictLeastRecentlyUsed removes the least recently used item in O(1) time +func (c *NormalizedValueCache) evictLeastRecentlyUsed() { + tail := c.removeTail() + delete(c.cache, tail.key) +} + +// Clear clears all cached values +func (c *NormalizedValueCache) Clear() { + c.mu.Lock() + defer c.mu.Unlock() + c.cache = make(map[string]*LRUNode) + c.head.next = c.tail + c.tail.prev = c.head +} + +// GetStats returns cache statistics +func (c *NormalizedValueCache) GetStats() (size int, maxSize int) { + c.mu.RLock() + defer c.mu.RUnlock() + return len(c.cache), c.maxSize +} + +// Global cache instance with size limit +var normalizedValueCache = NewNormalizedValueCache(1000) + +// getCachedNormalizedValues returns cached normalized values or caches new ones +func getCachedNormalizedValues(value interface{}) []string { + // Create a string key for caching - more efficient than fmt.Sprintf + typeStr := reflect.TypeOf(value).String() + cacheKey := typeStr + ":" + fmt.Sprint(value) + + // Try to get from cache + if cached, exists := normalizedValueCache.Get(cacheKey); exists { + return cached + } + + // Not in cache, normalize and store + // Use the error-handling version for better error reporting + normalized, err := normalizeToStringSliceWithError(value) + if err != nil { + glog.Warningf("Failed to normalize policy value %v: %v", value, err) + // Fallback to string conversion for backward compatibility + normalized = []string{fmt.Sprintf("%v", value)} + } + + normalizedValueCache.Set(cacheKey, normalized) + + return normalized +} + +// ConditionEvaluator evaluates policy conditions +type ConditionEvaluator interface { + Evaluate(conditionValue interface{}, contextValues []string) bool +} + +// StringEqualsEvaluator evaluates StringEquals conditions +type StringEqualsEvaluator struct{} + +func (e *StringEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + for _, contextValue := range contextValues { + if expected == contextValue { + return true + } + } + } + return false +} + +// StringNotEqualsEvaluator evaluates StringNotEquals conditions +type StringNotEqualsEvaluator struct{} + +func (e *StringNotEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + for _, contextValue := range contextValues { + if expected == contextValue { + return false + } + } + } + return true +} + +// StringLikeEvaluator evaluates StringLike conditions (supports wildcards) +type StringLikeEvaluator struct{} + +func (e *StringLikeEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + patterns := getCachedNormalizedValues(conditionValue) + for _, pattern := range patterns { + for _, contextValue := range contextValues { + if MatchesWildcard(pattern, contextValue) { + return true + } + } + } + return false +} + +// StringNotLikeEvaluator evaluates StringNotLike conditions +type StringNotLikeEvaluator struct{} + +func (e *StringNotLikeEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + patterns := getCachedNormalizedValues(conditionValue) + for _, pattern := range patterns { + for _, contextValue := range contextValues { + if MatchesWildcard(pattern, contextValue) { + return false + } + } + } + return true +} + +// NumericEqualsEvaluator evaluates NumericEquals conditions +type NumericEqualsEvaluator struct{} + +func (e *NumericEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedFloat, err := strconv.ParseFloat(expected, 64) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextFloat, err := strconv.ParseFloat(contextValue, 64) + if err != nil { + continue + } + if expectedFloat == contextFloat { + return true + } + } + } + return false +} + +// NumericNotEqualsEvaluator evaluates NumericNotEquals conditions +type NumericNotEqualsEvaluator struct{} + +func (e *NumericNotEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedFloat, err := strconv.ParseFloat(expected, 64) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextFloat, err := strconv.ParseFloat(contextValue, 64) + if err != nil { + continue + } + if expectedFloat == contextFloat { + return false + } + } + } + return true +} + +// NumericLessThanEvaluator evaluates NumericLessThan conditions +type NumericLessThanEvaluator struct{} + +func (e *NumericLessThanEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedFloat, err := strconv.ParseFloat(expected, 64) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextFloat, err := strconv.ParseFloat(contextValue, 64) + if err != nil { + continue + } + if contextFloat < expectedFloat { + return true + } + } + } + return false +} + +// NumericLessThanEqualsEvaluator evaluates NumericLessThanEquals conditions +type NumericLessThanEqualsEvaluator struct{} + +func (e *NumericLessThanEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedFloat, err := strconv.ParseFloat(expected, 64) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextFloat, err := strconv.ParseFloat(contextValue, 64) + if err != nil { + continue + } + if contextFloat <= expectedFloat { + return true + } + } + } + return false +} + +// NumericGreaterThanEvaluator evaluates NumericGreaterThan conditions +type NumericGreaterThanEvaluator struct{} + +func (e *NumericGreaterThanEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedFloat, err := strconv.ParseFloat(expected, 64) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextFloat, err := strconv.ParseFloat(contextValue, 64) + if err != nil { + continue + } + if contextFloat > expectedFloat { + return true + } + } + } + return false +} + +// NumericGreaterThanEqualsEvaluator evaluates NumericGreaterThanEquals conditions +type NumericGreaterThanEqualsEvaluator struct{} + +func (e *NumericGreaterThanEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedFloat, err := strconv.ParseFloat(expected, 64) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextFloat, err := strconv.ParseFloat(contextValue, 64) + if err != nil { + continue + } + if contextFloat >= expectedFloat { + return true + } + } + } + return false +} + +// DateEqualsEvaluator evaluates DateEquals conditions +type DateEqualsEvaluator struct{} + +func (e *DateEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedTime, err := time.Parse(time.RFC3339, expected) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextTime, err := time.Parse(time.RFC3339, contextValue) + if err != nil { + continue + } + if expectedTime.Equal(contextTime) { + return true + } + } + } + return false +} + +// DateNotEqualsEvaluator evaluates DateNotEquals conditions +type DateNotEqualsEvaluator struct{} + +func (e *DateNotEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedTime, err := time.Parse(time.RFC3339, expected) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextTime, err := time.Parse(time.RFC3339, contextValue) + if err != nil { + continue + } + if expectedTime.Equal(contextTime) { + return false + } + } + } + return true +} + +// DateLessThanEvaluator evaluates DateLessThan conditions +type DateLessThanEvaluator struct{} + +func (e *DateLessThanEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedTime, err := time.Parse(time.RFC3339, expected) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextTime, err := time.Parse(time.RFC3339, contextValue) + if err != nil { + continue + } + if contextTime.Before(expectedTime) { + return true + } + } + } + return false +} + +// DateLessThanEqualsEvaluator evaluates DateLessThanEquals conditions +type DateLessThanEqualsEvaluator struct{} + +func (e *DateLessThanEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedTime, err := time.Parse(time.RFC3339, expected) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextTime, err := time.Parse(time.RFC3339, contextValue) + if err != nil { + continue + } + if contextTime.Before(expectedTime) || contextTime.Equal(expectedTime) { + return true + } + } + } + return false +} + +// DateGreaterThanEvaluator evaluates DateGreaterThan conditions +type DateGreaterThanEvaluator struct{} + +func (e *DateGreaterThanEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedTime, err := time.Parse(time.RFC3339, expected) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextTime, err := time.Parse(time.RFC3339, contextValue) + if err != nil { + continue + } + if contextTime.After(expectedTime) { + return true + } + } + } + return false +} + +// DateGreaterThanEqualsEvaluator evaluates DateGreaterThanEquals conditions +type DateGreaterThanEqualsEvaluator struct{} + +func (e *DateGreaterThanEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedTime, err := time.Parse(time.RFC3339, expected) + if err != nil { + continue + } + for _, contextValue := range contextValues { + contextTime, err := time.Parse(time.RFC3339, contextValue) + if err != nil { + continue + } + if contextTime.After(expectedTime) || contextTime.Equal(expectedTime) { + return true + } + } + } + return false +} + +// BoolEvaluator evaluates Bool conditions +type BoolEvaluator struct{} + +func (e *BoolEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + for _, contextValue := range contextValues { + if strings.ToLower(expected) == strings.ToLower(contextValue) { + return true + } + } + } + return false +} + +// IpAddressEvaluator evaluates IpAddress conditions +type IpAddressEvaluator struct{} + +func (e *IpAddressEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + _, expectedNet, err := net.ParseCIDR(expected) + if err != nil { + // Try parsing as single IP + expectedIP := net.ParseIP(expected) + if expectedIP == nil { + glog.V(3).Infof("Failed to parse expected IP address: %s", expected) + continue + } + for _, contextValue := range contextValues { + contextIP := net.ParseIP(contextValue) + if contextIP == nil { + glog.V(3).Infof("Failed to parse IP address: %s", contextValue) + continue + } + if contextIP.Equal(expectedIP) { + return true + } + } + } else { + // CIDR network + for _, contextValue := range contextValues { + contextIP := net.ParseIP(contextValue) + if contextIP == nil { + glog.V(3).Infof("Failed to parse IP address: %s", contextValue) + continue + } + if expectedNet.Contains(contextIP) { + return true + } + } + } + } + return false +} + +// NotIpAddressEvaluator evaluates NotIpAddress conditions +type NotIpAddressEvaluator struct{} + +func (e *NotIpAddressEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + _, expectedNet, err := net.ParseCIDR(expected) + if err != nil { + // Try parsing as single IP + expectedIP := net.ParseIP(expected) + if expectedIP == nil { + glog.V(3).Infof("Failed to parse expected IP address: %s", expected) + continue + } + for _, contextValue := range contextValues { + contextIP := net.ParseIP(contextValue) + if contextIP == nil { + glog.V(3).Infof("Failed to parse IP address: %s", contextValue) + continue + } + if contextIP.Equal(expectedIP) { + return false + } + } + } else { + // CIDR network + for _, contextValue := range contextValues { + contextIP := net.ParseIP(contextValue) + if contextIP == nil { + glog.V(3).Infof("Failed to parse IP address: %s", contextValue) + continue + } + if expectedNet.Contains(contextIP) { + return false + } + } + } + } + return true +} + +// ArnEqualsEvaluator evaluates ArnEquals conditions +type ArnEqualsEvaluator struct{} + +func (e *ArnEqualsEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + for _, contextValue := range contextValues { + if expected == contextValue { + return true + } + } + } + return false +} + +// ArnLikeEvaluator evaluates ArnLike conditions +type ArnLikeEvaluator struct{} + +func (e *ArnLikeEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + patterns := getCachedNormalizedValues(conditionValue) + for _, pattern := range patterns { + for _, contextValue := range contextValues { + if MatchesWildcard(pattern, contextValue) { + return true + } + } + } + return false +} + +// NullEvaluator evaluates Null conditions +type NullEvaluator struct{} + +func (e *NullEvaluator) Evaluate(conditionValue interface{}, contextValues []string) bool { + expectedValues := getCachedNormalizedValues(conditionValue) + for _, expected := range expectedValues { + expectedBool := strings.ToLower(expected) == "true" + contextExists := len(contextValues) > 0 + if expectedBool && !contextExists { + return true // Key should be null and it is + } + if !expectedBool && contextExists { + return true // Key should not be null and it isn't + } + } + return false +} + +// GetConditionEvaluator returns the appropriate evaluator for a condition operator +func GetConditionEvaluator(operator string) (ConditionEvaluator, error) { + switch operator { + case "StringEquals": + return &StringEqualsEvaluator{}, nil + case "StringNotEquals": + return &StringNotEqualsEvaluator{}, nil + case "StringLike": + return &StringLikeEvaluator{}, nil + case "StringNotLike": + return &StringNotLikeEvaluator{}, nil + case "NumericEquals": + return &NumericEqualsEvaluator{}, nil + case "NumericNotEquals": + return &NumericNotEqualsEvaluator{}, nil + case "NumericLessThan": + return &NumericLessThanEvaluator{}, nil + case "NumericLessThanEquals": + return &NumericLessThanEqualsEvaluator{}, nil + case "NumericGreaterThan": + return &NumericGreaterThanEvaluator{}, nil + case "NumericGreaterThanEquals": + return &NumericGreaterThanEqualsEvaluator{}, nil + case "DateEquals": + return &DateEqualsEvaluator{}, nil + case "DateNotEquals": + return &DateNotEqualsEvaluator{}, nil + case "DateLessThan": + return &DateLessThanEvaluator{}, nil + case "DateLessThanEquals": + return &DateLessThanEqualsEvaluator{}, nil + case "DateGreaterThan": + return &DateGreaterThanEvaluator{}, nil + case "DateGreaterThanEquals": + return &DateGreaterThanEqualsEvaluator{}, nil + case "Bool": + return &BoolEvaluator{}, nil + case "IpAddress": + return &IpAddressEvaluator{}, nil + case "NotIpAddress": + return &NotIpAddressEvaluator{}, nil + case "ArnEquals": + return &ArnEqualsEvaluator{}, nil + case "ArnLike": + return &ArnLikeEvaluator{}, nil + case "Null": + return &NullEvaluator{}, nil + default: + return nil, fmt.Errorf("unsupported condition operator: %s", operator) + } +} + +// EvaluateConditions evaluates all conditions in a policy statement +func EvaluateConditions(conditions PolicyConditions, contextValues map[string][]string) bool { + if len(conditions) == 0 { + return true // No conditions means always true + } + + for operator, conditionMap := range conditions { + conditionEvaluator, err := GetConditionEvaluator(operator) + if err != nil { + glog.Warningf("Unsupported condition operator: %s", operator) + continue + } + + for key, value := range conditionMap { + contextVals, exists := contextValues[key] + if !exists { + contextVals = []string{} + } + + if !conditionEvaluator.Evaluate(value.Strings(), contextVals) { + return false // If any condition fails, the whole condition block fails + } + } + } + + return true +} + +// EvaluateConditionsLegacy evaluates conditions using the old interface{} format for backward compatibility +func EvaluateConditionsLegacy(conditions map[string]interface{}, contextValues map[string][]string) bool { + if len(conditions) == 0 { + return true // No conditions means always true + } + + for operator, conditionMap := range conditions { + conditionEvaluator, err := GetConditionEvaluator(operator) + if err != nil { + glog.Warningf("Unsupported condition operator: %s", operator) + continue + } + + conditionMapTyped, ok := conditionMap.(map[string]interface{}) + if !ok { + glog.Warningf("Invalid condition format for operator: %s", operator) + continue + } + + for key, value := range conditionMapTyped { + contextVals, exists := contextValues[key] + if !exists { + contextVals = []string{} + } + + if !conditionEvaluator.Evaluate(value, contextVals) { + return false // If any condition fails, the whole condition block fails + } + } + } + + return true +} |
