You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							768 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							768 lines
						
					
					
						
							21 KiB
						
					
					
				| 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 | |
| }
 |