mirror of https://github.com/minio/minio.git
Add numeric/date policy conditions (#9233)
add new policy conditions - NumericEquals - NumericNotEquals - NumericLessThan - NumericLessThanEquals - NumericGreaterThan - NumericGreaterThanEquals - DateEquals - DateNotEquals - DateLessThan - DateLessThanEquals - DateGreaterThan - DateGreaterThanEquals
This commit is contained in:
parent
c8243706b4
commit
d8af244708
1
Makefile
1
Makefile
|
@ -101,3 +101,4 @@ clean:
|
|||
@rm -rvf minio
|
||||
@rm -rvf build
|
||||
@rm -rvf release
|
||||
@rm -rvf .verify*
|
||||
|
|
|
@ -30,33 +30,49 @@ MINIO=( "$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server )
|
|||
|
||||
function start_minio_3_node() {
|
||||
declare -a minio_pids
|
||||
declare -a ARGS
|
||||
export MINIO_ACCESS_KEY=minio
|
||||
export MINIO_SECRET_KEY=minio123
|
||||
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
for i in $(seq 1 3); do
|
||||
ARGS+=("http://127.0.0.1:$[8000+$i]${WORK_DIR}/$i/1/ http://127.0.0.1:$[8000+$i]${WORK_DIR}/$i/2/ http://127.0.0.1:$[8000+$i]${WORK_DIR}/$i/3/ http://127.0.0.1:$[8000+$i]${WORK_DIR}/$i/4/ http://127.0.0.1:$[8000+$i]${WORK_DIR}/$i/5/ http://127.0.0.1:$[8000+$i]${WORK_DIR}/$i/6/")
|
||||
ARGS+=("http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/1/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/2/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/3/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/4/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/5/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/6/")
|
||||
done
|
||||
|
||||
"${MINIO[@]}" --address ":8001" ${ARGS[@]} > "${WORK_DIR}/dist-minio-8001.log" 2>&1 &
|
||||
"${MINIO[@]}" --address ":$[$start_port+1]" ${ARGS[@]} > "${WORK_DIR}/dist-minio-server1.log" 2>&1 &
|
||||
minio_pids[0]=$!
|
||||
disown "${minio_pids[0]}"
|
||||
|
||||
"${MINIO[@]}" --address ":8002" ${ARGS[@]} > "${WORK_DIR}/dist-minio-8002.log" 2>&1 &
|
||||
"${MINIO[@]}" --address ":$[$start_port+2]" ${ARGS[@]} > "${WORK_DIR}/dist-minio-server2.log" 2>&1 &
|
||||
minio_pids[1]=$!
|
||||
disown "${minio_pids[1]}"
|
||||
|
||||
"${MINIO[@]}" --address ":8003" ${ARGS[@]} > "${WORK_DIR}/dist-minio-8003.log" 2>&1 &
|
||||
"${MINIO[@]}" --address ":$[$start_port+3]" ${ARGS[@]} > "${WORK_DIR}/dist-minio-server3.log" 2>&1 &
|
||||
minio_pids[2]=$!
|
||||
disown "${minio_pids[2]}"
|
||||
|
||||
sleep "$1"
|
||||
echo "${minio_pids[@]}"
|
||||
for pid in "${minio_pids[@]}"; do
|
||||
if ! kill "$pid"; then
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
# forcibly killing, to proceed further properly.
|
||||
kill -9 "$pid"
|
||||
sleep 1 # wait 1sec per pid
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
function check_online() {
|
||||
for i in $(seq 1 3); do
|
||||
if grep -q 'Server switching to safe mode' ${WORK_DIR}/dist-minio-$[8000+$i].log; then
|
||||
echo "1"
|
||||
fi
|
||||
done
|
||||
if grep -q 'Server switching to safe mode' ${WORK_DIR}/dist-minio-*.log; then
|
||||
echo "1"
|
||||
fi
|
||||
}
|
||||
|
||||
function purge()
|
||||
|
@ -75,50 +91,21 @@ function __init__()
|
|||
}
|
||||
|
||||
function perform_test() {
|
||||
minio_pids=( $(start_minio_3_node 60) )
|
||||
for pid in "${minio_pids[@]}"; do
|
||||
if ! kill "$pid"; then
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-$[8000+$i].log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
# forcibly killing, to proceed further properly.
|
||||
kill -9 "$pid"
|
||||
done
|
||||
start_minio_3_node 60
|
||||
|
||||
echo "Testing Distributed Erasure setup healing of drives"
|
||||
echo "Remove the contents of the disks belonging to '${1}' erasure set"
|
||||
|
||||
rm -rf ${WORK_DIR}/${1}/*/
|
||||
|
||||
minio_pids=( $(start_minio_3_node 60) )
|
||||
for pid in "${minio_pids[@]}"; do
|
||||
if ! kill "$pid"; then
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-$[8000+$i].log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
# forcibly killing, to proceed further properly.
|
||||
# if the previous kill is taking time.
|
||||
kill -9 "$pid"
|
||||
done
|
||||
start_minio_3_node 60
|
||||
|
||||
rv=$(check_online)
|
||||
if [ "$rv" == "1" ]; then
|
||||
for pid in "${minio_pids[@]}"; do
|
||||
kill -9 "$pid"
|
||||
done
|
||||
pkill -9 minio
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-$[8000+$i].log"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
|
|
|
@ -491,7 +491,7 @@ func isPutActionAllowed(atype authType, bucketName, objectName string, r *http.R
|
|||
if cred.AccessKey == "" {
|
||||
if globalPolicySys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Action: policy.PutObjectAction,
|
||||
Action: policy.Action(action),
|
||||
BucketName: bucketName,
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
|
|
|
@ -1312,7 +1312,6 @@ var iamAccountWriteAccessActions = iampolicy.NewActionSet(
|
|||
)
|
||||
|
||||
var iamAccountOtherAccessActions = iampolicy.NewActionSet(
|
||||
iampolicy.BypassGovernanceModeAction,
|
||||
iampolicy.BypassGovernanceRetentionAction,
|
||||
iampolicy.PutObjectRetentionAction,
|
||||
iampolicy.GetObjectRetentionAction,
|
||||
|
|
|
@ -19,6 +19,7 @@ package cmd
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
|
@ -251,7 +252,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error
|
|||
configFile := path.Join(bucketConfigPrefix, bucket.Name, bucketObjectLockEnabledConfigFile)
|
||||
bucketObjLockData, err := readConfig(ctx, objAPI, configFile)
|
||||
if err != nil {
|
||||
if err == errConfigNotFound {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
|
@ -266,7 +267,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error
|
|||
configFile = path.Join(bucketConfigPrefix, bucket.Name, objectLockConfig)
|
||||
configData, err := readConfig(ctx, objAPI, configFile)
|
||||
if err != nil {
|
||||
if err == errConfigNotFound {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
globalBucketObjectLockConfig.Set(bucket.Name, objectlock.Retention{})
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -22,14 +22,17 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
miniogopolicy "github.com/minio/minio-go/v6/pkg/policy"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
)
|
||||
|
||||
|
@ -145,14 +148,14 @@ func NewPolicySys() *PolicySys {
|
|||
|
||||
func getConditionValues(request *http.Request, locationConstraint string, username string, claims map[string]interface{}) map[string][]string {
|
||||
currTime := UTCNow()
|
||||
principalType := func() string {
|
||||
if username != "" {
|
||||
return "User"
|
||||
}
|
||||
return "Anonymous"
|
||||
}()
|
||||
|
||||
principalType := "Anonymous"
|
||||
if username != "" {
|
||||
principalType = "User"
|
||||
}
|
||||
|
||||
args := map[string][]string{
|
||||
"CurrenTime": {currTime.Format(event.AMZTimeFormat)},
|
||||
"CurrentTime": {currTime.Format(time.RFC3339)},
|
||||
"EpochTime": {fmt.Sprintf("%d", currTime.Unix())},
|
||||
"principaltype": {principalType},
|
||||
"SecureTransport": {fmt.Sprintf("%t", request.TLS != nil)},
|
||||
|
@ -163,26 +166,56 @@ func getConditionValues(request *http.Request, locationConstraint string, userna
|
|||
"username": {username},
|
||||
}
|
||||
|
||||
for key, values := range request.Header {
|
||||
if existingValues, found := args[key]; found {
|
||||
args[key] = append(existingValues, values...)
|
||||
} else {
|
||||
args[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
for key, values := range request.URL.Query() {
|
||||
if existingValues, found := args[key]; found {
|
||||
args[key] = append(existingValues, values...)
|
||||
} else {
|
||||
args[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
if locationConstraint != "" {
|
||||
args["LocationConstraint"] = []string{locationConstraint}
|
||||
}
|
||||
|
||||
// TODO: support object-lock-remaining-retention-days
|
||||
cloneHeader := request.Header.Clone()
|
||||
|
||||
for _, objLock := range []string{
|
||||
xhttp.AmzObjectLockMode,
|
||||
xhttp.AmzObjectLockLegalHold,
|
||||
xhttp.AmzObjectLockRetainUntilDate,
|
||||
} {
|
||||
if values, ok := cloneHeader[objLock]; ok {
|
||||
args[strings.TrimPrefix(objLock, "X-Amz-")] = values
|
||||
}
|
||||
cloneHeader.Del(objLock)
|
||||
}
|
||||
|
||||
for key, values := range cloneHeader {
|
||||
if existingValues, found := args[key]; found {
|
||||
args[key] = append(existingValues, values...)
|
||||
} else {
|
||||
args[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
var cloneURLValues = url.Values{}
|
||||
for k, v := range request.URL.Query() {
|
||||
cloneURLValues[k] = v
|
||||
}
|
||||
|
||||
for _, objLock := range []string{
|
||||
xhttp.AmzObjectLockMode,
|
||||
xhttp.AmzObjectLockLegalHold,
|
||||
xhttp.AmzObjectLockRetainUntilDate,
|
||||
} {
|
||||
if values, ok := cloneURLValues[objLock]; ok {
|
||||
args[strings.TrimPrefix(objLock, "X-Amz-")] = values
|
||||
}
|
||||
cloneURLValues.Del(objLock)
|
||||
}
|
||||
|
||||
for key, values := range cloneURLValues {
|
||||
if existingValues, found := args[key]; found {
|
||||
args[key] = append(existingValues, values...)
|
||||
} else {
|
||||
args[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
// JWT specific values
|
||||
for k, v := range claims {
|
||||
vStr, ok := v.(string)
|
||||
|
@ -190,6 +223,7 @@ func getConditionValues(request *http.Request, locationConstraint string, userna
|
|||
args[k] = []string{vStr}
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
|
|
|
@ -89,8 +89,6 @@ const (
|
|||
// GetBucketLifecycleAction - GetBucketLifecycle Rest API action.
|
||||
GetBucketLifecycleAction = "s3:GetLifecycleConfiguration"
|
||||
|
||||
// BypassGovernanceModeAction - bypass governance mode for DeleteObject Rest API action.
|
||||
BypassGovernanceModeAction = "s3:BypassGovernanceMode"
|
||||
// BypassGovernanceRetentionAction - bypass governance retention for PutObjectRetention, PutObject and DeleteObject Rest API action.
|
||||
BypassGovernanceRetentionAction = "s3:BypassGovernanceRetention"
|
||||
// PutObjectRetentionAction - PutObjectRetention Rest API action.
|
||||
|
@ -127,7 +125,6 @@ var supportedObjectActions = map[Action]struct{}{
|
|||
GetObjectAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutObjectAction: {},
|
||||
BypassGovernanceModeAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
PutObjectRetentionAction: {},
|
||||
GetObjectRetentionAction: {},
|
||||
|
@ -172,7 +169,6 @@ var supportedActions = map[Action]struct{}{
|
|||
PutObjectLegalHoldAction: {},
|
||||
PutBucketObjectLockConfigurationAction: {},
|
||||
GetBucketObjectLockConfigurationAction: {},
|
||||
BypassGovernanceModeAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
|
@ -263,13 +259,31 @@ var actionConditionKeyMap = map[Action]condition.KeySet{
|
|||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3XAmzMetadataDirective,
|
||||
condition.S3XAmzStorageClass,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
PutObjectRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
GetObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectLegalHoldAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
BypassGovernanceRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
PutObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
BypassGovernanceModeAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
BypassGovernanceRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
|
|
@ -111,6 +111,10 @@ func validateBinaryEqualsValues(n name, key Key, values set.StringSet) error {
|
|||
if s != "COPY" && s != "REPLACE" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzMetadataDirective, n)
|
||||
}
|
||||
case S3XAmzContentSha256:
|
||||
if s == "" {
|
||||
return fmt.Errorf("invalid empty value for '%v' for %v condition", S3XAmzContentSha256, n)
|
||||
}
|
||||
}
|
||||
values.Add(s)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,10 @@ func (f booleanFunc) evaluate(values map[string][]string) bool {
|
|||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return f.value == requestValue[0]
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package condition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func toDateEqualsFuncString(n name, key Key, value time.Time) string {
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, value.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
// dateEqualsFunc - String equals function. It checks whether value by Key in given
|
||||
// values map is in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is in values.
|
||||
type dateEqualsFunc struct {
|
||||
k Key
|
||||
value time.Time
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f dateEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return f.value.Equal(t)
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f dateEqualsFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "DateEquals" condition name.
|
||||
func (f dateEqualsFunc) name() name {
|
||||
return dateEquals
|
||||
}
|
||||
|
||||
func (f dateEqualsFunc) String() string {
|
||||
return toDateEqualsFuncString(dateEquals, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f dateEqualsFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
values.Add(NewStringValue(f.value.Format(time.RFC3339)))
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// dateNotEqualsFunc - String not equals function. It checks whether value by Key in
|
||||
// given values is NOT in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT in values.
|
||||
type dateNotEqualsFunc struct {
|
||||
dateEqualsFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f dateNotEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
return !f.dateEqualsFunc.evaluate(values)
|
||||
}
|
||||
|
||||
// name() - returns "DateNotEquals" condition name.
|
||||
func (f dateNotEqualsFunc) name() name {
|
||||
return dateNotEquals
|
||||
}
|
||||
|
||||
func (f dateNotEqualsFunc) String() string {
|
||||
return toDateEqualsFuncString(dateNotEquals, f.dateEqualsFunc.k, f.dateEqualsFunc.value)
|
||||
}
|
||||
|
||||
func valueToTime(n name, values ValueSet) (v time.Time, err error) {
|
||||
if len(values) != 1 {
|
||||
return v, fmt.Errorf("only one value is allowed for %s condition", n)
|
||||
}
|
||||
|
||||
for vs := range values {
|
||||
switch vs.GetType() {
|
||||
case reflect.String:
|
||||
s, err := vs.GetString()
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
if v, err = time.Parse(time.RFC3339, s); err != nil {
|
||||
return v, fmt.Errorf("value %s must be a time.Time string for %s condition: %w", vs, n, err)
|
||||
}
|
||||
default:
|
||||
return v, fmt.Errorf("value %s must be a time.Time for %s condition", vs, n)
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
|
||||
}
|
||||
|
||||
// newDateEqualsFunc - returns new DateEquals function.
|
||||
func newDateEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToTime(dateEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDateEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewDateEqualsFunc - returns new DateEquals function.
|
||||
func NewDateEqualsFunc(key Key, value time.Time) (Function, error) {
|
||||
return &dateEqualsFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// newDateNotEqualsFunc - returns new DateNotEquals function.
|
||||
func newDateNotEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToTime(dateNotEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDateNotEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewDateNotEqualsFunc - returns new DateNotEquals function.
|
||||
func NewDateNotEqualsFunc(key Key, value time.Time) (Function, error) {
|
||||
return &dateNotEqualsFunc{dateEqualsFunc{key, value}}, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package condition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func toDateGreaterThanFuncString(n name, key Key, value time.Time) string {
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, value.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
// dateGreaterThanFunc - String equals function. It checks whether value by Key in given
|
||||
// values map is in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is in values.
|
||||
type dateGreaterThanFunc struct {
|
||||
k Key
|
||||
value time.Time
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f dateGreaterThanFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.After(f.value)
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f dateGreaterThanFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "DateGreaterThan" condition name.
|
||||
func (f dateGreaterThanFunc) name() name {
|
||||
return dateGreaterThan
|
||||
}
|
||||
|
||||
func (f dateGreaterThanFunc) String() string {
|
||||
return toDateGreaterThanFuncString(dateGreaterThan, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f dateGreaterThanFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
values.Add(NewStringValue(f.value.Format(time.RFC3339)))
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// dateNotEqualsFunc - String not equals function. It checks whether value by Key in
|
||||
// given values is NOT in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT in values.
|
||||
type dateGreaterThanEqualsFunc struct {
|
||||
dateGreaterThanFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f dateGreaterThanEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.After(f.value) || t.Equal(f.value)
|
||||
}
|
||||
|
||||
// name() - returns "DateNotEquals" condition name.
|
||||
func (f dateGreaterThanEqualsFunc) name() name {
|
||||
return dateGreaterThanEquals
|
||||
}
|
||||
|
||||
func (f dateGreaterThanEqualsFunc) String() string {
|
||||
return toDateGreaterThanFuncString(dateNotEquals, f.dateGreaterThanFunc.k, f.dateGreaterThanFunc.value)
|
||||
}
|
||||
|
||||
// newDateGreaterThanFunc - returns new DateGreaterThan function.
|
||||
func newDateGreaterThanFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToTime(dateGreaterThan, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDateGreaterThanFunc(key, v)
|
||||
}
|
||||
|
||||
// NewDateGreaterThanFunc - returns new DateGreaterThan function.
|
||||
func NewDateGreaterThanFunc(key Key, value time.Time) (Function, error) {
|
||||
return &dateGreaterThanFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// newDateNotEqualsFunc - returns new DateNotEquals function.
|
||||
func newDateGreaterThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToTime(dateNotEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDateGreaterThanEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewDateGreaterThanEqualsFunc - returns new DateNotEquals function.
|
||||
func NewDateGreaterThanEqualsFunc(key Key, value time.Time) (Function, error) {
|
||||
return &dateGreaterThanEqualsFunc{dateGreaterThanFunc{key, value}}, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package condition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func toDateLessThanFuncString(n name, key Key, value time.Time) string {
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, value.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
// dateLessThanFunc - String equals function. It checks whether value by Key in given
|
||||
// values map is in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is in values.
|
||||
type dateLessThanFunc struct {
|
||||
k Key
|
||||
value time.Time
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f dateLessThanFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.Before(f.value)
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f dateLessThanFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "DateLessThan" condition name.
|
||||
func (f dateLessThanFunc) name() name {
|
||||
return dateLessThan
|
||||
}
|
||||
|
||||
func (f dateLessThanFunc) String() string {
|
||||
return toDateLessThanFuncString(dateLessThan, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f dateLessThanFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
values.Add(NewStringValue(f.value.Format(time.RFC3339)))
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// dateNotEqualsFunc - String not equals function. It checks whether value by Key in
|
||||
// given values is NOT in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT in values.
|
||||
type dateLessThanEqualsFunc struct {
|
||||
dateLessThanFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f dateLessThanEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.Before(f.value) || t.Equal(f.value)
|
||||
}
|
||||
|
||||
// name() - returns "DateNotEquals" condition name.
|
||||
func (f dateLessThanEqualsFunc) name() name {
|
||||
return dateLessThanEquals
|
||||
}
|
||||
|
||||
func (f dateLessThanEqualsFunc) String() string {
|
||||
return toDateLessThanFuncString(dateNotEquals, f.dateLessThanFunc.k, f.dateLessThanFunc.value)
|
||||
}
|
||||
|
||||
// newDateLessThanFunc - returns new DateLessThan function.
|
||||
func newDateLessThanFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToTime(dateLessThan, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDateLessThanFunc(key, v)
|
||||
}
|
||||
|
||||
// NewDateLessThanFunc - returns new DateLessThan function.
|
||||
func NewDateLessThanFunc(key Key, value time.Time) (Function, error) {
|
||||
return &dateLessThanFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// newDateNotEqualsFunc - returns new DateNotEquals function.
|
||||
func newDateLessThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToTime(dateNotEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDateLessThanEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewDateLessThanEqualsFunc - returns new DateNotEquals function.
|
||||
func NewDateLessThanEqualsFunc(key Key, value time.Time) (Function, error) {
|
||||
return &dateLessThanEqualsFunc{dateLessThanFunc{key, value}}, nil
|
||||
}
|
|
@ -106,6 +106,18 @@ var conditionFuncMap = map[name]func(Key, ValueSet) (Function, error){
|
|||
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.
|
||||
}
|
||||
|
||||
|
|
|
@ -268,12 +268,7 @@ func TestFunctionsUnmarshalJSON(t *testing.T) {
|
|||
|
||||
case3Data := []byte(`{}`)
|
||||
|
||||
// Remove this test after supporting date conditions.
|
||||
case4Data := []byte(`{
|
||||
"DateEquals": { "aws:CurrentTime": "2013-06-30T00:00:00Z" }
|
||||
}`)
|
||||
|
||||
case5Data := []byte(`{
|
||||
"StringLike": {
|
||||
"s3:x-amz-metadata-directive": "REPL*"
|
||||
},
|
||||
|
@ -336,10 +331,8 @@ func TestFunctionsUnmarshalJSON(t *testing.T) {
|
|||
{case2Data, NewFunctions(func6), false},
|
||||
// empty condition error.
|
||||
{case3Data, nil, true},
|
||||
// unsupported condition error.
|
||||
{case4Data, nil, true},
|
||||
// Success case multiple keys, same condition.
|
||||
{case5Data, NewFunctions(func1, func2_1, func2_2, func2_3, func3, func4, func5, func6, func7), false},
|
||||
{case4Data, NewFunctions(func1, func2_1, func2_2, func2_3, func3, func4, func5, func6, func7), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
|
|
|
@ -43,6 +43,9 @@ const (
|
|||
// PutObject API only.
|
||||
S3XAmzMetadataDirective Key = "s3:x-amz-metadata-directive"
|
||||
|
||||
// S3XAmzContentSha256 - set a static content-sha256 for all calls for a given action.
|
||||
S3XAmzContentSha256 = "s3:x-amz-content-sha256"
|
||||
|
||||
// S3XAmzStorageClass - key representing x-amz-storage-class HTTP header applicable to PutObject API
|
||||
// only.
|
||||
S3XAmzStorageClass Key = "s3:x-amz-storage-class"
|
||||
|
@ -59,6 +62,24 @@ const (
|
|||
// S3MaxKeys - key representing max-keys query parameter of ListBucket API only.
|
||||
S3MaxKeys Key = "s3:max-keys"
|
||||
|
||||
// S3ObjectLockRemainingRetentionDays - key representing object-lock-remaining-retention-days
|
||||
// Enables enforcement of an object relative to the remaining retention days, you can set
|
||||
// minimum and maximum allowable retention periods for a bucket using a bucket policy.
|
||||
// This key are specific for s3:PutObjectRetention API.
|
||||
S3ObjectLockRemainingRetentionDays Key = "s3:object-lock-remaining-retention-days"
|
||||
|
||||
// S3ObjectLockMode - key representing object-lock-mode
|
||||
// Enables enforcement of the specified object retention mode
|
||||
S3ObjectLockMode Key = "s3:object-lock-mode"
|
||||
|
||||
// S3ObjectLockRetainUntilDate - key representing object-lock-retain-util-date
|
||||
// Enables enforcement of a specific retain-until-date
|
||||
S3ObjectLockRetainUntilDate Key = "s3:object-lock-retain-until-date"
|
||||
|
||||
// S3ObjectLockLegalHold - key representing object-local-legal-hold
|
||||
// Enables enforcement of the specified object legal hold status
|
||||
S3ObjectLockLegalHold Key = "s3:object-lock-legal-hold"
|
||||
|
||||
// AWSReferer - key representing Referer header of any API.
|
||||
AWSReferer Key = "aws:Referer"
|
||||
|
||||
|
@ -94,10 +115,15 @@ var AllSupportedKeys = append([]Key{
|
|||
S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
S3XAmzMetadataDirective,
|
||||
S3XAmzStorageClass,
|
||||
S3XAmzContentSha256,
|
||||
S3LocationConstraint,
|
||||
S3Prefix,
|
||||
S3Delimiter,
|
||||
S3MaxKeys,
|
||||
S3ObjectLockRemainingRetentionDays,
|
||||
S3ObjectLockMode,
|
||||
S3ObjectLockLegalHold,
|
||||
S3ObjectLockRetainUntilDate,
|
||||
AWSReferer,
|
||||
AWSSourceIP,
|
||||
AWSUserAgent,
|
||||
|
@ -121,6 +147,7 @@ var CommonKeys = append([]Key{
|
|||
AWSPrincipalType,
|
||||
AWSUserID,
|
||||
AWSUsername,
|
||||
S3XAmzContentSha256,
|
||||
}, JWTKeys...)
|
||||
|
||||
func substFuncFromValues(values map[string][]string) func(string) string {
|
||||
|
|
|
@ -35,6 +35,18 @@ const (
|
|||
notIPAddress = "NotIpAddress"
|
||||
null = "Null"
|
||||
boolean = "Bool"
|
||||
numericEquals = "NumericEquals"
|
||||
numericNotEquals = "NumericNotEquals"
|
||||
numericLessThan = "NumericLessThan"
|
||||
numericLessThanEquals = "NumericLessThanEquals"
|
||||
numericGreaterThan = "NumericGreaterThan"
|
||||
numericGreaterThanEquals = "NumericGreaterThanEquals"
|
||||
dateEquals = "DateEquals"
|
||||
dateNotEquals = "DateNotEquals"
|
||||
dateLessThan = "DateLessThan"
|
||||
dateLessThanEquals = "DateLessThanEquals"
|
||||
dateGreaterThan = "DateGreaterThan"
|
||||
dateGreaterThanEquals = "DateGreaterThanEquals"
|
||||
)
|
||||
|
||||
var supportedConditions = []name{
|
||||
|
@ -49,6 +61,18 @@ var supportedConditions = []name{
|
|||
notIPAddress,
|
||||
null,
|
||||
boolean,
|
||||
numericEquals,
|
||||
numericNotEquals,
|
||||
numericLessThan,
|
||||
numericLessThanEquals,
|
||||
numericGreaterThan,
|
||||
numericGreaterThanEquals,
|
||||
dateEquals,
|
||||
dateNotEquals,
|
||||
dateLessThan,
|
||||
dateLessThanEquals,
|
||||
dateGreaterThan,
|
||||
dateGreaterThanEquals,
|
||||
// Add new conditions here.
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package condition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func toNumericEqualsFuncString(n name, key Key, value int) string {
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, value)
|
||||
}
|
||||
|
||||
// numericEqualsFunc - String equals function. It checks whether value by Key in given
|
||||
// values map is in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is in values.
|
||||
type numericEqualsFunc struct {
|
||||
k Key
|
||||
value int
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f numericEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rvInt, err := strconv.Atoi(requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return f.value == rvInt
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f numericEqualsFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "NumericEquals" condition name.
|
||||
func (f numericEqualsFunc) name() name {
|
||||
return numericEquals
|
||||
}
|
||||
|
||||
func (f numericEqualsFunc) String() string {
|
||||
return toNumericEqualsFuncString(numericEquals, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f numericEqualsFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
values.Add(NewIntValue(f.value))
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// numericNotEqualsFunc - String not equals function. It checks whether value by Key in
|
||||
// given values is NOT in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT in values.
|
||||
type numericNotEqualsFunc struct {
|
||||
numericEqualsFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f numericNotEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
return !f.numericEqualsFunc.evaluate(values)
|
||||
}
|
||||
|
||||
// name() - returns "NumericNotEquals" condition name.
|
||||
func (f numericNotEqualsFunc) name() name {
|
||||
return numericNotEquals
|
||||
}
|
||||
|
||||
func (f numericNotEqualsFunc) String() string {
|
||||
return toNumericEqualsFuncString(numericNotEquals, f.numericEqualsFunc.k, f.numericEqualsFunc.value)
|
||||
}
|
||||
|
||||
func valueToInt(n name, values ValueSet) (v int, err error) {
|
||||
if len(values) != 1 {
|
||||
return -1, fmt.Errorf("only one value is allowed for %s condition", n)
|
||||
}
|
||||
|
||||
for vs := range values {
|
||||
switch vs.GetType() {
|
||||
case reflect.Int:
|
||||
if v, err = vs.GetInt(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
case reflect.String:
|
||||
s, err := vs.GetString()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if v, err = strconv.Atoi(s); err != nil {
|
||||
return -1, fmt.Errorf("value %s must be a int for %s condition: %w", vs, n, err)
|
||||
}
|
||||
default:
|
||||
return -1, fmt.Errorf("value %s must be a int for %s condition", vs, n)
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
|
||||
}
|
||||
|
||||
// newNumericEqualsFunc - returns new NumericEquals function.
|
||||
func newNumericEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToInt(numericEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNumericEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewNumericEqualsFunc - returns new NumericEquals function.
|
||||
func NewNumericEqualsFunc(key Key, value int) (Function, error) {
|
||||
return &numericEqualsFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// newNumericNotEqualsFunc - returns new NumericNotEquals function.
|
||||
func newNumericNotEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToInt(numericNotEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNumericNotEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewNumericNotEqualsFunc - returns new NumericNotEquals function.
|
||||
func NewNumericNotEqualsFunc(key Key, value int) (Function, error) {
|
||||
return &numericNotEqualsFunc{numericEqualsFunc{key, value}}, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package condition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func toNumericGreaterThanFuncString(n name, key Key, value int) string {
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, value)
|
||||
}
|
||||
|
||||
// numericGreaterThanFunc - String equals function. It checks whether value by Key in given
|
||||
// values map is in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is in values.
|
||||
type numericGreaterThanFunc struct {
|
||||
k Key
|
||||
value int
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f numericGreaterThanFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rvInt, err := strconv.Atoi(requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return rvInt > f.value
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f numericGreaterThanFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "NumericGreaterThan" condition name.
|
||||
func (f numericGreaterThanFunc) name() name {
|
||||
return numericGreaterThan
|
||||
}
|
||||
|
||||
func (f numericGreaterThanFunc) String() string {
|
||||
return toNumericGreaterThanFuncString(numericGreaterThan, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f numericGreaterThanFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
values.Add(NewIntValue(f.value))
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// numericGreaterThanEqualsFunc - String not equals function. It checks whether value by Key in
|
||||
// given values is NOT in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT in values.
|
||||
type numericGreaterThanEqualsFunc struct {
|
||||
numericGreaterThanFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f numericGreaterThanEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rvInt, err := strconv.Atoi(requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return rvInt >= f.value
|
||||
}
|
||||
|
||||
// name() - returns "NumericGreaterThanEquals" condition name.
|
||||
func (f numericGreaterThanEqualsFunc) name() name {
|
||||
return numericGreaterThanEquals
|
||||
}
|
||||
|
||||
func (f numericGreaterThanEqualsFunc) String() string {
|
||||
return toNumericGreaterThanFuncString(numericGreaterThanEquals, f.numericGreaterThanFunc.k, f.numericGreaterThanFunc.value)
|
||||
}
|
||||
|
||||
// newNumericGreaterThanFunc - returns new NumericGreaterThan function.
|
||||
func newNumericGreaterThanFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToInt(numericGreaterThan, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNumericGreaterThanFunc(key, v)
|
||||
}
|
||||
|
||||
// NewNumericGreaterThanFunc - returns new NumericGreaterThan function.
|
||||
func NewNumericGreaterThanFunc(key Key, value int) (Function, error) {
|
||||
return &numericGreaterThanFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// newNumericGreaterThanEqualsFunc - returns new NumericGreaterThanEquals function.
|
||||
func newNumericGreaterThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToInt(numericGreaterThanEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNumericGreaterThanEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewNumericGreaterThanEqualsFunc - returns new NumericGreaterThanEquals function.
|
||||
func NewNumericGreaterThanEqualsFunc(key Key, value int) (Function, error) {
|
||||
return &numericGreaterThanEqualsFunc{numericGreaterThanFunc{key, value}}, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package condition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func toNumericLessThanFuncString(n name, key Key, value int) string {
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, value)
|
||||
}
|
||||
|
||||
// numericLessThanFunc - String equals function. It checks whether value by Key in given
|
||||
// values map is in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is in values.
|
||||
type numericLessThanFunc struct {
|
||||
k Key
|
||||
value int
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f numericLessThanFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rvInt, err := strconv.Atoi(requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return rvInt < f.value
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f numericLessThanFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "NumericLessThan" condition name.
|
||||
func (f numericLessThanFunc) name() name {
|
||||
return numericLessThan
|
||||
}
|
||||
|
||||
func (f numericLessThanFunc) String() string {
|
||||
return toNumericLessThanFuncString(numericLessThan, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f numericLessThanFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
values.Add(NewIntValue(f.value))
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// numericLessThanEqualsFunc - String not equals function. It checks whether value by Key in
|
||||
// given values is NOT in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT in values.
|
||||
type numericLessThanEqualsFunc struct {
|
||||
numericLessThanFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f numericLessThanEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if len(requestValue) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rvInt, err := strconv.Atoi(requestValue[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return rvInt <= f.value
|
||||
}
|
||||
|
||||
// name() - returns "NumericLessThanEquals" condition name.
|
||||
func (f numericLessThanEqualsFunc) name() name {
|
||||
return numericLessThanEquals
|
||||
}
|
||||
|
||||
func (f numericLessThanEqualsFunc) String() string {
|
||||
return toNumericLessThanFuncString(numericLessThanEquals, f.numericLessThanFunc.k, f.numericLessThanFunc.value)
|
||||
}
|
||||
|
||||
// newNumericLessThanFunc - returns new NumericLessThan function.
|
||||
func newNumericLessThanFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToInt(numericLessThan, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNumericLessThanFunc(key, v)
|
||||
}
|
||||
|
||||
// NewNumericLessThanFunc - returns new NumericLessThan function.
|
||||
func NewNumericLessThanFunc(key Key, value int) (Function, error) {
|
||||
return &numericLessThanFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// newNumericLessThanEqualsFunc - returns new NumericLessThanEquals function.
|
||||
func newNumericLessThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
v, err := valueToInt(numericLessThanEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNumericLessThanEqualsFunc(key, v)
|
||||
}
|
||||
|
||||
// NewNumericLessThanEqualsFunc - returns new NumericLessThanEquals function.
|
||||
func NewNumericLessThanEqualsFunc(key Key, value int) (Function, error) {
|
||||
return &numericLessThanEqualsFunc{numericLessThanFunc{key, value}}, nil
|
||||
}
|
|
@ -143,6 +143,10 @@ func validateStringEqualsValues(n name, key Key, values set.StringSet) error {
|
|||
if s != "COPY" && s != "REPLACE" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzMetadataDirective, n)
|
||||
}
|
||||
case S3XAmzContentSha256:
|
||||
if s == "" {
|
||||
return fmt.Errorf("invalid empty value for '%v' for %v condition", S3XAmzContentSha256, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,9 +90,6 @@ const (
|
|||
// PutObjectAction - PutObject Rest API action.
|
||||
PutObjectAction = "s3:PutObject"
|
||||
|
||||
// BypassGovernanceModeAction - bypass governance mode for DeleteObject Rest API action.
|
||||
BypassGovernanceModeAction = "s3:BypassGovernanceMode"
|
||||
|
||||
// BypassGovernanceRetentionAction - bypass governance retention for PutObjectRetention, PutObject and DeleteObject Rest API action.
|
||||
BypassGovernanceRetentionAction = "s3:BypassGovernanceRetention"
|
||||
|
||||
|
@ -162,7 +159,6 @@ var supportedActions = map[Action]struct{}{
|
|||
PutObjectLegalHoldAction: {},
|
||||
PutBucketObjectLockConfigurationAction: {},
|
||||
GetBucketObjectLockConfigurationAction: {},
|
||||
BypassGovernanceModeAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
|
@ -179,7 +175,6 @@ var supportedObjectActions = map[Action]struct{}{
|
|||
GetObjectAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutObjectAction: {},
|
||||
BypassGovernanceModeAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
PutObjectRetentionAction: {},
|
||||
GetObjectRetentionAction: {},
|
||||
|
@ -301,13 +296,32 @@ var actionConditionKeyMap = map[Action]condition.KeySet{
|
|||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3XAmzMetadataDirective,
|
||||
condition.S3XAmzStorageClass,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
PutObjectRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
GetObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectLegalHoldAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
BypassGovernanceRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
PutObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
BypassGovernanceModeAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
BypassGovernanceRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
|
Loading…
Reference in New Issue