1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 07:57:07 +00:00
kyverno/pkg/engine/pattern.go
2019-05-27 18:07:24 +03:00

294 lines
6.7 KiB
Go

package engine
import (
"fmt"
"log"
"math"
"regexp"
"strconv"
"strings"
"github.com/minio/minio/pkg/wildcard"
)
// Operator is string alias that represents selection operators enum
type Operator string
const (
// Equal stands for ==
Equal Operator = ""
// MoreEqual stands for >=
MoreEqual Operator = ">="
// LessEqual stands for <=
LessEqual Operator = "<="
// NotEqual stands for !
NotEqual Operator = "!"
// More stands for >
More Operator = ">"
// Less stands for <
Less Operator = "<"
)
// ValidateValueWithPattern validates value with operators and wildcards
func ValidateValueWithPattern(value, pattern interface{}) bool {
switch typedPattern := pattern.(type) {
case bool:
typedValue, ok := value.(bool)
if !ok {
log.Printf("Expected bool, found %T", value)
return false
}
return typedPattern == typedValue
case int:
return validateValueWithIntPattern(value, int64(typedPattern))
case int64:
return validateValueWithIntPattern(value, typedPattern)
case float64:
return validateValueWithFloatPattern(value, typedPattern)
case string:
return validateValueWithStringPatterns(value, typedPattern)
case nil:
return validateValueWithNilPattern(value)
case map[string]interface{}, []interface{}:
log.Println("Maps and arrays as patterns are not supported")
return false
default:
log.Printf("Unknown type as pattern: %T\n", pattern)
return false
}
}
func validateValueWithIntPattern(value interface{}, pattern int64) bool {
switch typedValue := value.(type) {
case int:
return int64(typedValue) == pattern
case int64:
return typedValue == pattern
case float64:
// check that float has no fraction
if typedValue == math.Trunc(typedValue) {
return int64(typedValue) == pattern
}
log.Printf("Expected int, found float: %f\n", typedValue)
return false
default:
log.Printf("Expected int, found: %T\n", value)
return false
}
}
func validateValueWithFloatPattern(value interface{}, pattern float64) bool {
switch typedValue := value.(type) {
case int:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int(pattern) == value
}
log.Printf("Expected float, found int: %d\n", typedValue)
return false
case float64:
return typedValue == pattern
default:
log.Printf("Expected float, found: %T\n", value)
return false
}
}
func validateValueWithNilPattern(value interface{}) bool {
switch typed := value.(type) {
case float64:
return typed == 0.0
case int:
return typed == 0
case int64:
return typed == 0
case string:
return typed == ""
case bool:
return typed == false
case nil:
return true
case map[string]interface{}, []interface{}:
log.Println("Maps and arrays could not be checked with nil pattern")
return false
default:
log.Printf("Unknown type as value when checking for nil pattern: %T\n", value)
return false
}
}
func validateValueWithStringPatterns(value interface{}, pattern string) bool {
statements := strings.Split(pattern, "|")
for _, statement := range statements {
statement = strings.Trim(statement, " ")
if validateValueWithStringPattern(value, statement) {
return true
}
}
return false
}
func validateValueWithStringPattern(value interface{}, pattern string) bool {
operator := getOperatorFromStringPattern(pattern)
pattern = pattern[len(operator):]
number, str := getNumberAndStringPartsFromPattern(pattern)
if "" == number {
return validateString(value, str, operator)
}
validateNumberWithStr(value, number, str, operator)
return true
}
func validateString(value interface{}, pattern string, operator Operator) bool {
if NotEqual == operator || Equal == operator {
strValue, ok := value.(string)
if !ok {
log.Printf("Expected string, found %T\n", value)
return false
}
wildcardResult := wildcard.Match(pattern, strValue)
if NotEqual == operator {
return !wildcardResult
}
return wildcardResult
}
log.Println("Operators >, >=, <, <= are not applicable to strings")
return false
}
func validateNumberWithStr(value interface{}, patternNumber, patternStr string, operator Operator) bool {
patternParsedNumber, err := parseNumber(patternNumber)
if err != nil {
return false
}
if "" != patternStr {
typedValue, ok := value.(string)
if !ok {
log.Printf("Number must have suffix: %s", patternStr)
return false
}
valueNumber, valueStr := getNumberAndStringPartsFromPattern(typedValue)
if !wildcard.Match(patternStr, valueStr) {
log.Printf("Suffix %s has not passed wildcard check: %s", valueStr, patternStr)
return false
}
valueParsedNumber, err := parseNumber(valueNumber)
if err != nil {
return false
}
return validateNumber(valueParsedNumber, patternParsedNumber, operator)
}
return validateNumber(value, patternParsedNumber, operator)
}
func validateNumber(value, pattern interface{}, operator Operator) bool {
var floatPattern, floatValue float64
switch typed := value.(type) {
case float64:
floatValue = typed
case int64:
floatValue = float64(typed)
case int:
floatValue = float64(typed)
default:
return false
}
switch typed := pattern.(type) {
case float64:
floatPattern = typed
case int64:
floatPattern = float64(typed)
case int:
floatPattern = float64(typed)
default:
return false
}
switch operator {
case Equal:
return floatValue == floatPattern
case NotEqual:
return floatValue != floatPattern
case More:
return floatValue > floatPattern
case MoreEqual:
return floatValue >= floatPattern
case Less:
return floatValue < floatPattern
case LessEqual:
return floatValue <= floatPattern
}
return false
}
func getOperatorFromStringPattern(pattern string) Operator {
if pattern[:len(MoreEqual)] == string(MoreEqual) {
return MoreEqual
}
if pattern[:len(LessEqual)] == string(LessEqual) {
return LessEqual
}
if pattern[:len(More)] == string(More) {
return More
}
if pattern[:len(Less)] == string(Less) {
return Less
}
if pattern[:len(NotEqual)] == string(NotEqual) {
return NotEqual
}
return ""
}
func getNumberAndStringPartsFromPattern(pattern string) (number, str string) {
regexpStr := `^(\d*(\.\d+)?)(.*)`
re := regexp.MustCompile(regexpStr)
matches := re.FindAllStringSubmatch(pattern, -1)
match := matches[0]
return match[1], match[3]
}
func checkForWildcard(value, pattern string) error {
if !wildcard.Match(pattern, value) {
return fmt.Errorf("wildcard check has failed. Pattern: \"%s\". Value: \"%s\"", pattern, value)
}
return nil
}
func parseNumber(number string) (interface{}, error) {
var err error
if floatValue, err := strconv.ParseFloat(number, 64); err == nil {
return floatValue, nil
}
if intValue, err := strconv.ParseInt(number, 10, 64); err == nil {
return intValue, nil
}
return nil, err
}