minio/pkg/bucket/policy/condition/func.go

225 lines
6.0 KiB
Go

// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package condition
import (
"encoding/json"
"fmt"
"sort"
)
// Function - condition function interface.
type Function interface {
// evaluate() - evaluates this condition function with given values.
evaluate(values map[string][]string) bool
// key() - returns condition key used in this function.
key() Key
// name() - returns condition name of this function.
name() name
// String() - returns string representation of function.
String() string
// toMap - returns map representation of this function.
toMap() map[Key]ValueSet
}
// Functions - list of functions.
type Functions []Function
// Evaluate - evaluates all functions with given values map. Each function is evaluated
// sequencely and next function is called only if current function succeeds.
func (functions Functions) Evaluate(values map[string][]string) bool {
for _, f := range functions {
if !f.evaluate(values) {
return false
}
}
return true
}
// Keys - returns list of keys used in all functions.
func (functions Functions) Keys() KeySet {
keySet := NewKeySet()
for _, f := range functions {
keySet.Add(f.key())
}
return keySet
}
// Clone clones Functions structure
func (functions Functions) Clone() Functions {
funcs := []Function{}
for _, f := range functions {
vfn := conditionFuncMap[f.name()]
for key, values := range f.toMap() {
function, _ := vfn(key, values.Clone())
funcs = append(funcs, function)
}
}
return funcs
}
// Equals returns true if two Functions structures are equal
func (functions Functions) Equals(funcs Functions) bool {
if len(functions) != len(funcs) {
return false
}
for _, fi := range functions {
fistr := fi.String()
found := false
for _, fj := range funcs {
if fistr == fj.String() {
found = true
break
}
}
if !found {
return false
}
}
return true
}
// MarshalJSON - encodes Functions to JSON data.
func (functions Functions) MarshalJSON() ([]byte, error) {
nm := make(map[name]map[Key]ValueSet)
for _, f := range functions {
if _, ok := nm[f.name()]; ok {
for k, v := range f.toMap() {
nm[f.name()][k] = v
}
} else {
nm[f.name()] = f.toMap()
}
}
return json.Marshal(nm)
}
func (functions Functions) String() string {
funcStrings := []string{}
for _, f := range functions {
s := fmt.Sprintf("%v", f)
funcStrings = append(funcStrings, s)
}
sort.Strings(funcStrings)
return fmt.Sprintf("%v", funcStrings)
}
var conditionFuncMap = map[name]func(Key, ValueSet) (Function, error){
stringEquals: newStringEqualsFunc,
stringNotEquals: newStringNotEqualsFunc,
stringEqualsIgnoreCase: newStringEqualsIgnoreCaseFunc,
stringNotEqualsIgnoreCase: newStringNotEqualsIgnoreCaseFunc,
binaryEquals: newBinaryEqualsFunc,
stringLike: newStringLikeFunc,
stringNotLike: newStringNotLikeFunc,
ipAddress: newIPAddressFunc,
notIPAddress: newNotIPAddressFunc,
null: newNullFunc,
boolean: newBooleanFunc,
numericEquals: newNumericEqualsFunc,
numericNotEquals: newNumericNotEqualsFunc,
numericLessThan: newNumericLessThanFunc,
numericLessThanEquals: newNumericLessThanEqualsFunc,
numericGreaterThan: newNumericGreaterThanFunc,
numericGreaterThanEquals: newNumericGreaterThanEqualsFunc,
dateEquals: newDateEqualsFunc,
dateNotEquals: newDateNotEqualsFunc,
dateLessThan: newDateLessThanFunc,
dateLessThanEquals: newDateLessThanEqualsFunc,
dateGreaterThan: newDateGreaterThanFunc,
dateGreaterThanEquals: newDateGreaterThanEqualsFunc,
// Add new conditions here.
}
// UnmarshalJSON - decodes JSON data to Functions.
func (functions *Functions) UnmarshalJSON(data []byte) error {
// As string kind, int kind then json.Unmarshaler is checked at
// https://github.com/golang/go/blob/master/src/encoding/json/decode.go#L618
// UnmarshalJSON() is not called for types extending string
// see https://play.golang.org/p/HrSsKksHvrS, better way to do is
// https://play.golang.org/p/y9ElWpBgVAB
//
// Due to this issue, name and Key types cannot be used as map keys below.
nm := make(map[string]map[string]ValueSet)
if err := json.Unmarshal(data, &nm); err != nil {
return err
}
if len(nm) == 0 {
return fmt.Errorf("condition must not be empty")
}
funcs := []Function{}
for nameString, args := range nm {
n, err := parseName(nameString)
if err != nil {
return err
}
for keyString, values := range args {
key, err := parseKey(keyString)
if err != nil {
return err
}
vfn, ok := conditionFuncMap[n]
if !ok {
return fmt.Errorf("condition %v is not handled", n)
}
f, err := vfn(key, values)
if err != nil {
return err
}
funcs = append(funcs, f)
}
}
*functions = funcs
return nil
}
// GobEncode - encodes Functions to gob data.
func (functions Functions) GobEncode() ([]byte, error) {
return functions.MarshalJSON()
}
// GobDecode - decodes gob data to Functions.
func (functions *Functions) GobDecode(data []byte) error {
return functions.UnmarshalJSON(data)
}
// NewFunctions - returns new Functions with given function list.
func NewFunctions(functions ...Function) Functions {
return Functions(functions)
}