mirror of
https://github.com/minio/minio.git
synced 2024-12-23 21:55:53 -05:00
move to iam, bucket policy from minio/pkg (#12400)
This commit is contained in:
parent
3350dbc50d
commit
fdc2020b10
@ -25,7 +25,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// Data types used for returning dummy access control
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -37,7 +37,7 @@ import (
|
||||
"github.com/minio/minio/cmd/config/storageclass"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
func validateAdminReqConfigKV(ctx context.Context, w http.ResponseWriter, r *http.Request) (auth.Credentials, ObjectLayer) {
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
"github.com/minio/minio/cmd/config/dns"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
func validateAdminUsersReq(ctx context.Context, w http.ResponseWriter, r *http.Request, action iampolicy.AdminAction) (ObjectLayer, auth.Credentials) {
|
||||
|
@ -46,9 +46,9 @@ import (
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/dsync"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -38,10 +38,10 @@ import (
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/versioning"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// APIError structure
|
||||
|
@ -37,10 +37,10 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/etag"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// Verify if request has JWT.
|
||||
|
@ -29,7 +29,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// Test get request auth type.
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -44,14 +44,14 @@ import (
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
"github.com/minio/minio/pkg/sync/errgroup"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,8 +26,8 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/sync/errgroup"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
func concurrentDecryptETag(ctx context.Context, objects []ObjectInfo) {
|
||||
|
@ -30,12 +30,12 @@ import (
|
||||
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/minio/pkg/bucket/versioning"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
"github.com/minio/minio/pkg/sync/errgroup"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// BucketMetadataSys captures all bucket metadata for a given cluster.
|
||||
|
@ -36,12 +36,12 @@ import (
|
||||
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/minio/pkg/bucket/versioning"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/fips"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
|
@ -25,8 +25,8 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,8 +26,8 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// BucketObjectLockSys - map of bucket and retention configuration.
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -29,8 +29,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy/condition"
|
||||
)
|
||||
|
||||
func getAnonReadOnlyBucketPolicy(bucketName string) *policy.Policy {
|
||||
|
@ -29,8 +29,8 @@ import (
|
||||
miniogopolicy "github.com/minio/minio-go/v7/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/handlers"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// PolicySys - policy subsystem.
|
||||
|
@ -38,7 +38,7 @@ import (
|
||||
"github.com/minio/minio/pkg/bucket/bandwidth"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// gets replication config associated to a given bucket name.
|
||||
|
@ -25,8 +25,8 @@ import (
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/versioning"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,9 +32,9 @@ import (
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
"github.com/minio/pkg/env"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// Config - OpenID Config
|
||||
|
@ -25,9 +25,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/minio/cmd/config"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
"github.com/minio/pkg/env"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// Env IAM OPA URL
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// Data types used for returning dummy tagging XML.
|
||||
|
@ -40,11 +40,11 @@ import (
|
||||
"github.com/minio/minio/cmd/config"
|
||||
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/color"
|
||||
xioutil "github.com/minio/minio/pkg/ioutil"
|
||||
"github.com/minio/minio/pkg/lock"
|
||||
"github.com/minio/minio/pkg/mountinfo"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/mimedb"
|
||||
)
|
||||
|
||||
|
@ -26,8 +26,8 @@ import (
|
||||
"github.com/minio/minio-go/v7/pkg/tags"
|
||||
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/versioning"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
@ -45,8 +45,8 @@ import (
|
||||
minio "github.com/minio/minio/cmd"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/env"
|
||||
)
|
||||
|
||||
|
@ -42,8 +42,8 @@ import (
|
||||
minio "github.com/minio/minio/cmd"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/env"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/iterator"
|
||||
|
@ -37,7 +37,7 @@ import (
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -33,8 +33,8 @@ import (
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||
etcd "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
@ -29,8 +29,8 @@ import (
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// IAMObjectStore implements IAMStorageAPI
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// UsersSysType - defines the type of users and groups system that is
|
||||
|
@ -24,8 +24,8 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
policy "github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
policy "github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
func (api objectAPIHandlers) ListenNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
@ -40,10 +40,10 @@ import (
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
bucketBandwidth "github.com/minio/minio/pkg/bucket/bandwidth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
"github.com/minio/minio/pkg/sync/errgroup"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// NotificationSys - notification system.
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio-go/v7/pkg/encrypt"
|
||||
"github.com/minio/minio-go/v7/pkg/tags"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// CheckPreconditionFn returns true if precondition check failed.
|
||||
|
@ -47,18 +47,18 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/minio/pkg/etag"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/fips"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/ioutil"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
"github.com/minio/minio/pkg/s3select"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
|
@ -23,8 +23,8 @@ import (
|
||||
|
||||
miniogopolicy "github.com/minio/minio-go/v7/pkg/policy"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy/condition"
|
||||
)
|
||||
|
||||
func TestPolicySysIsAllowed(t *testing.T) {
|
||||
|
@ -35,7 +35,7 @@ import (
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// API suite container common to both FS and Erasure.
|
||||
|
@ -31,7 +31,7 @@ import (
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
|
@ -65,8 +65,8 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/cmd/rest"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
)
|
||||
|
||||
// TestMain to set up global env.
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -48,14 +48,14 @@ import (
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/replication"
|
||||
"github.com/minio/minio/pkg/etag"
|
||||
"github.com/minio/minio/pkg/event"
|
||||
"github.com/minio/minio/pkg/handlers"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/ioutil"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/minio/rpc/json2"
|
||||
)
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -47,7 +47,7 @@ require (
|
||||
github.com/minio/madmin-go v1.0.2
|
||||
github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78
|
||||
github.com/minio/parquet-go v1.0.0
|
||||
github.com/minio/pkg v1.0.2
|
||||
github.com/minio/pkg v1.0.3
|
||||
github.com/minio/rpc v1.0.0
|
||||
github.com/minio/selfupdate v0.3.1
|
||||
github.com/minio/sha256-simd v1.0.0
|
||||
|
4
go.sum
4
go.sum
@ -504,8 +504,8 @@ github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78 h1:v7OMbUnWky
|
||||
github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78/go.mod h1:mTh2uJuAbEqdhMVl6CMIIZLUeiMiWtJR4JB8/5g2skw=
|
||||
github.com/minio/parquet-go v1.0.0 h1:fcWsEvub04Nsl/4hiRBDWlbqd6jhacQieV07a+nhiIk=
|
||||
github.com/minio/parquet-go v1.0.0/go.mod h1:aQlkSOfOq2AtQKkuou3mosNVMwNokd+faTacxxk/oHA=
|
||||
github.com/minio/pkg v1.0.2 h1:vUlNMJbOgP/Hi/ekN+tl1xTOm3Q39gPr5XurDVOgvBA=
|
||||
github.com/minio/pkg v1.0.2/go.mod h1:e9WOU0bav8jd8AzloFjCTSiXSNqnXqxbzGwlH+2rQnI=
|
||||
github.com/minio/pkg v1.0.3 h1:tUhM6lG/BdNB0+5f2RbE4ifCAYwMs6cRJnZ/AY0WIeQ=
|
||||
github.com/minio/pkg v1.0.3/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
|
||||
github.com/minio/rpc v1.0.0 h1:tJCHyLfQF6k6HlMQFpKy2FO/7lc2WP8gLDGMZp18E70=
|
||||
github.com/minio/rpc v1.0.0/go.mod h1:b9xqF7J0xeMXr0cM4pnBlP7Te7PDsG5JrRxl5dG6Ldk=
|
||||
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
||||
|
@ -1,426 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
)
|
||||
|
||||
// Action - policy action.
|
||||
// Refer https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazons3.html
|
||||
// for more information about available actions.
|
||||
type Action string
|
||||
|
||||
const (
|
||||
// AbortMultipartUploadAction - AbortMultipartUpload Rest API action.
|
||||
AbortMultipartUploadAction Action = "s3:AbortMultipartUpload"
|
||||
|
||||
// CreateBucketAction - CreateBucket Rest API action.
|
||||
CreateBucketAction = "s3:CreateBucket"
|
||||
|
||||
// DeleteBucketAction - DeleteBucket Rest API action.
|
||||
DeleteBucketAction = "s3:DeleteBucket"
|
||||
|
||||
// ForceDeleteBucketAction - DeleteBucket Rest API action when x-minio-force-delete flag
|
||||
// is specified.
|
||||
ForceDeleteBucketAction = "s3:ForceDeleteBucket"
|
||||
|
||||
// DeleteBucketPolicyAction - DeleteBucketPolicy Rest API action.
|
||||
DeleteBucketPolicyAction = "s3:DeleteBucketPolicy"
|
||||
|
||||
// DeleteObjectAction - DeleteObject Rest API action.
|
||||
DeleteObjectAction = "s3:DeleteObject"
|
||||
|
||||
// GetBucketLocationAction - GetBucketLocation Rest API action.
|
||||
GetBucketLocationAction = "s3:GetBucketLocation"
|
||||
|
||||
// GetBucketNotificationAction - GetBucketNotification Rest API action.
|
||||
GetBucketNotificationAction = "s3:GetBucketNotification"
|
||||
|
||||
// GetBucketPolicyAction - GetBucketPolicy Rest API action.
|
||||
GetBucketPolicyAction = "s3:GetBucketPolicy"
|
||||
|
||||
// GetObjectAction - GetObject Rest API action.
|
||||
GetObjectAction = "s3:GetObject"
|
||||
|
||||
// HeadBucketAction - HeadBucket Rest API action. This action is unused in minio.
|
||||
HeadBucketAction = "s3:HeadBucket"
|
||||
|
||||
// ListAllMyBucketsAction - ListAllMyBuckets (List buckets) Rest API action.
|
||||
ListAllMyBucketsAction = "s3:ListAllMyBuckets"
|
||||
|
||||
// ListBucketAction - ListBucket Rest API action.
|
||||
ListBucketAction = "s3:ListBucket"
|
||||
|
||||
// GetBucketPolicyStatusAction - Retrieves the policy status for a bucket.
|
||||
GetBucketPolicyStatusAction = "s3:GetBucketPolicyStatus"
|
||||
|
||||
// ListBucketMultipartUploadsAction - ListMultipartUploads Rest API action.
|
||||
ListBucketMultipartUploadsAction = "s3:ListBucketMultipartUploads"
|
||||
|
||||
// ListBucketVersionsAction - ListBucket versions Rest API action.
|
||||
ListBucketVersionsAction = "s3:ListBucketVersions"
|
||||
|
||||
// ListenNotificationAction - ListenNotification Rest API action.
|
||||
// This is MinIO extension.
|
||||
ListenNotificationAction = "s3:ListenNotification"
|
||||
|
||||
// ListenBucketNotificationAction - ListenBucketNotification Rest API action.
|
||||
// This is MinIO extension.
|
||||
ListenBucketNotificationAction = "s3:ListenBucketNotification"
|
||||
|
||||
// ListMultipartUploadPartsAction - ListParts Rest API action.
|
||||
ListMultipartUploadPartsAction = "s3:ListMultipartUploadParts"
|
||||
|
||||
// PutBucketNotificationAction - PutObjectNotification Rest API action.
|
||||
PutBucketNotificationAction = "s3:PutBucketNotification"
|
||||
|
||||
// PutBucketPolicyAction - PutBucketPolicy Rest API action.
|
||||
PutBucketPolicyAction = "s3:PutBucketPolicy"
|
||||
|
||||
// PutObjectAction - PutObject Rest API action.
|
||||
PutObjectAction = "s3:PutObject"
|
||||
|
||||
// PutBucketLifecycleAction - PutBucketLifecycle Rest API action.
|
||||
PutBucketLifecycleAction = "s3:PutLifecycleConfiguration"
|
||||
|
||||
// GetBucketLifecycleAction - GetBucketLifecycle Rest API action.
|
||||
GetBucketLifecycleAction = "s3:GetLifecycleConfiguration"
|
||||
|
||||
// BypassGovernanceRetentionAction - bypass governance retention for PutObjectRetention, PutObject and DeleteObject Rest API action.
|
||||
BypassGovernanceRetentionAction = "s3:BypassGovernanceRetention"
|
||||
// PutObjectRetentionAction - PutObjectRetention Rest API action.
|
||||
PutObjectRetentionAction = "s3:PutObjectRetention"
|
||||
|
||||
// GetObjectRetentionAction - GetObjectRetention, GetObject, HeadObject Rest API action.
|
||||
GetObjectRetentionAction = "s3:GetObjectRetention"
|
||||
// GetObjectLegalHoldAction - GetObjectLegalHold, GetObject Rest API action.
|
||||
GetObjectLegalHoldAction = "s3:GetObjectLegalHold"
|
||||
// PutObjectLegalHoldAction - PutObjectLegalHold, PutObject Rest API action.
|
||||
PutObjectLegalHoldAction = "s3:PutObjectLegalHold"
|
||||
// GetBucketObjectLockConfigurationAction - GetObjectLockConfiguration Rest API action
|
||||
GetBucketObjectLockConfigurationAction = "s3:GetBucketObjectLockConfiguration"
|
||||
// PutBucketObjectLockConfigurationAction - PutObjectLockConfiguration Rest API action
|
||||
PutBucketObjectLockConfigurationAction = "s3:PutBucketObjectLockConfiguration"
|
||||
|
||||
// GetBucketTaggingAction - GetTagging Rest API action
|
||||
GetBucketTaggingAction = "s3:GetBucketTagging"
|
||||
// PutBucketTaggingAction - PutTagging Rest API action
|
||||
PutBucketTaggingAction = "s3:PutBucketTagging"
|
||||
|
||||
// GetObjectTaggingAction - Get Object Tags API action
|
||||
GetObjectTaggingAction = "s3:GetObjectTagging"
|
||||
// PutObjectTaggingAction - Put Object Tags API action
|
||||
PutObjectTaggingAction = "s3:PutObjectTagging"
|
||||
// DeleteObjectTaggingAction - Delete Object Tags API action
|
||||
DeleteObjectTaggingAction = "s3:DeleteObjectTagging"
|
||||
|
||||
// PutBucketEncryptionAction - PutBucketEncryption REST API action
|
||||
PutBucketEncryptionAction = "s3:PutEncryptionConfiguration"
|
||||
// GetBucketEncryptionAction - GetBucketEncryption REST API action
|
||||
GetBucketEncryptionAction = "s3:GetEncryptionConfiguration"
|
||||
|
||||
// PutBucketVersioningAction - PutBucketVersioning REST API action
|
||||
PutBucketVersioningAction = "s3:PutBucketVersioning"
|
||||
// GetBucketVersioningAction - GetBucketVersioning REST API action
|
||||
GetBucketVersioningAction = "s3:GetBucketVersioning"
|
||||
|
||||
// DeleteObjectVersionAction - DeleteObjectVersion Rest API action.
|
||||
DeleteObjectVersionAction = "s3:DeleteObjectVersion"
|
||||
|
||||
// DeleteObjectVersionTaggingAction - DeleteObjectVersionTagging Rest API action.
|
||||
DeleteObjectVersionTaggingAction = "s3:DeleteObjectVersionTagging"
|
||||
|
||||
// GetObjectVersionAction - GetObjectVersionAction Rest API action.
|
||||
GetObjectVersionAction = "s3:GetObjectVersion"
|
||||
|
||||
// GetObjectVersionTaggingAction - GetObjectVersionTagging Rest API action.
|
||||
GetObjectVersionTaggingAction = "s3:GetObjectVersionTagging"
|
||||
|
||||
// PutObjectVersionTaggingAction - PutObjectVersionTagging Rest API action.
|
||||
PutObjectVersionTaggingAction = "s3:PutObjectVersionTagging"
|
||||
|
||||
// GetReplicationConfigurationAction - GetReplicationConfiguration REST API action
|
||||
GetReplicationConfigurationAction = "s3:GetReplicationConfiguration"
|
||||
// PutReplicationConfigurationAction - PutReplicationConfiguration REST API action
|
||||
PutReplicationConfigurationAction = "s3:PutReplicationConfiguration"
|
||||
|
||||
// ReplicateObjectAction - ReplicateObject REST API action
|
||||
ReplicateObjectAction = "s3:ReplicateObject"
|
||||
|
||||
// ReplicateDeleteAction - ReplicateDelete REST API action
|
||||
ReplicateDeleteAction = "s3:ReplicateDelete"
|
||||
|
||||
// ReplicateTagsAction - ReplicateTags REST API action
|
||||
ReplicateTagsAction = "s3:ReplicateTags"
|
||||
|
||||
// GetObjectVersionForReplicationAction - GetObjectVersionForReplication REST API action
|
||||
GetObjectVersionForReplicationAction = "s3:GetObjectVersionForReplication"
|
||||
|
||||
// RestoreObjectAction - RestoreObject REST API action
|
||||
RestoreObjectAction = "s3:RestoreObject"
|
||||
)
|
||||
|
||||
// List of all supported object actions.
|
||||
var supportedObjectActions = map[Action]struct{}{
|
||||
AbortMultipartUploadAction: {},
|
||||
DeleteObjectAction: {},
|
||||
GetObjectAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutObjectAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
PutObjectRetentionAction: {},
|
||||
GetObjectRetentionAction: {},
|
||||
PutObjectLegalHoldAction: {},
|
||||
GetObjectLegalHoldAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
DeleteObjectTaggingAction: {},
|
||||
GetObjectVersionAction: {},
|
||||
GetObjectVersionTaggingAction: {},
|
||||
DeleteObjectVersionAction: {},
|
||||
DeleteObjectVersionTaggingAction: {},
|
||||
PutObjectVersionTaggingAction: {},
|
||||
ReplicateObjectAction: {},
|
||||
ReplicateDeleteAction: {},
|
||||
ReplicateTagsAction: {},
|
||||
GetObjectVersionForReplicationAction: {},
|
||||
RestoreObjectAction: {},
|
||||
}
|
||||
|
||||
// isObjectAction - returns whether action is object type or not.
|
||||
func (action Action) isObjectAction() bool {
|
||||
_, ok := supportedObjectActions[action]
|
||||
return ok
|
||||
}
|
||||
|
||||
// List of all supported actions.
|
||||
var supportedActions = map[Action]struct{}{
|
||||
AbortMultipartUploadAction: {},
|
||||
CreateBucketAction: {},
|
||||
DeleteBucketAction: {},
|
||||
ForceDeleteBucketAction: {},
|
||||
DeleteBucketPolicyAction: {},
|
||||
DeleteObjectAction: {},
|
||||
GetBucketLocationAction: {},
|
||||
GetBucketNotificationAction: {},
|
||||
GetBucketPolicyAction: {},
|
||||
GetObjectAction: {},
|
||||
HeadBucketAction: {},
|
||||
ListAllMyBucketsAction: {},
|
||||
ListBucketAction: {},
|
||||
GetBucketPolicyStatusAction: {},
|
||||
ListBucketVersionsAction: {},
|
||||
ListBucketMultipartUploadsAction: {},
|
||||
ListenNotificationAction: {},
|
||||
ListenBucketNotificationAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutBucketNotificationAction: {},
|
||||
PutBucketPolicyAction: {},
|
||||
PutObjectAction: {},
|
||||
GetBucketLifecycleAction: {},
|
||||
PutBucketLifecycleAction: {},
|
||||
PutObjectRetentionAction: {},
|
||||
GetObjectRetentionAction: {},
|
||||
GetObjectLegalHoldAction: {},
|
||||
PutObjectLegalHoldAction: {},
|
||||
PutBucketObjectLockConfigurationAction: {},
|
||||
GetBucketObjectLockConfigurationAction: {},
|
||||
PutBucketTaggingAction: {},
|
||||
GetBucketTaggingAction: {},
|
||||
GetObjectVersionAction: {},
|
||||
GetObjectVersionTaggingAction: {},
|
||||
DeleteObjectVersionAction: {},
|
||||
DeleteObjectVersionTaggingAction: {},
|
||||
PutObjectVersionTaggingAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
DeleteObjectTaggingAction: {},
|
||||
PutBucketEncryptionAction: {},
|
||||
GetBucketEncryptionAction: {},
|
||||
PutBucketVersioningAction: {},
|
||||
GetBucketVersioningAction: {},
|
||||
GetReplicationConfigurationAction: {},
|
||||
PutReplicationConfigurationAction: {},
|
||||
ReplicateObjectAction: {},
|
||||
ReplicateDeleteAction: {},
|
||||
ReplicateTagsAction: {},
|
||||
GetObjectVersionForReplicationAction: {},
|
||||
RestoreObjectAction: {},
|
||||
}
|
||||
|
||||
// IsValid - checks if action is valid or not.
|
||||
func (action Action) IsValid() bool {
|
||||
_, ok := supportedActions[action]
|
||||
return ok
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes Action to JSON data.
|
||||
func (action Action) MarshalJSON() ([]byte, error) {
|
||||
if action.IsValid() {
|
||||
return json.Marshal(string(action))
|
||||
}
|
||||
|
||||
return nil, Errorf("invalid action '%v'", action)
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to Action.
|
||||
func (action *Action) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a := Action(s)
|
||||
if !a.IsValid() {
|
||||
return Errorf("invalid action '%v'", s)
|
||||
}
|
||||
|
||||
*action = a
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseAction(s string) (Action, error) {
|
||||
action := Action(s)
|
||||
|
||||
if action.IsValid() {
|
||||
return action, nil
|
||||
}
|
||||
|
||||
return action, Errorf("unsupported action '%v'", s)
|
||||
}
|
||||
|
||||
// actionConditionKeyMap - holds mapping of supported condition key for an action.
|
||||
var actionConditionKeyMap = map[Action]condition.KeySet{
|
||||
AbortMultipartUploadAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
CreateBucketAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
DeleteObjectAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
GetBucketLocationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
GetBucketPolicyStatusAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
GetObjectAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
HeadBucketAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
ListAllMyBucketsAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
ListBucketAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3Prefix,
|
||||
condition.S3Delimiter,
|
||||
condition.S3MaxKeys,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
ListBucketVersionsAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3Prefix,
|
||||
condition.S3Delimiter,
|
||||
condition.S3MaxKeys,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
ListBucketMultipartUploadsAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
ListenNotificationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
ListenBucketNotificationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
ListMultipartUploadPartsAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
PutObjectAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzCopySource,
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3XAmzMetadataDirective,
|
||||
condition.S3XAmzStorageClass,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
||||
// LockLegalHold is not supported with PutObjectRetentionAction
|
||||
PutObjectRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
GetObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectLegalHoldAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
||||
BypassGovernanceRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
GetBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetBucketTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutBucketTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
DeleteObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
PutObjectVersionTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectVersionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectVersionTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
DeleteObjectVersionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
DeleteObjectVersionTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetReplicationConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
PutReplicationConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
ReplicateObjectAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
ReplicateDeleteAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
ReplicateTagsAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
GetObjectVersionForReplicationAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
RestoreObjectAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestActionIsObjectAction(t *testing.T) {
|
||||
testCases := []struct {
|
||||
action Action
|
||||
expectedResult bool
|
||||
}{
|
||||
{AbortMultipartUploadAction, true},
|
||||
{DeleteObjectAction, true},
|
||||
{GetObjectAction, true},
|
||||
{ListMultipartUploadPartsAction, true},
|
||||
{PutObjectAction, true},
|
||||
{CreateBucketAction, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.action.isObjectAction()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
action Action
|
||||
expectedResult bool
|
||||
}{
|
||||
{AbortMultipartUploadAction, true},
|
||||
{Action("foo"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.action.IsValid()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
action Action
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{PutObjectAction, []byte(`"s3:PutObject"`), false},
|
||||
{Action("foo"), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.action)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult Action
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"s3:PutObject"`), PutObjectAction, false},
|
||||
{[]byte(`"foo"`), Action(""), true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Action
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
// ActionSet - set of actions.
|
||||
type ActionSet map[Action]struct{}
|
||||
|
||||
// Add - add action to the set.
|
||||
func (actionSet ActionSet) Add(action Action) {
|
||||
actionSet[action] = struct{}{}
|
||||
}
|
||||
|
||||
// Contains - checks given action exists in the action set.
|
||||
func (actionSet ActionSet) Contains(action Action) bool {
|
||||
_, found := actionSet[action]
|
||||
return found
|
||||
}
|
||||
|
||||
// Equals - checks whether given action set is equal to current action set or not.
|
||||
func (actionSet ActionSet) Equals(sactionSet ActionSet) bool {
|
||||
// If length of set is not equal to length of given set, the
|
||||
// set is not equal to given set.
|
||||
if len(actionSet) != len(sactionSet) {
|
||||
return false
|
||||
}
|
||||
|
||||
// As both sets are equal in length, check each elements are equal.
|
||||
for k := range actionSet {
|
||||
if _, ok := sactionSet[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersection - returns actions available in both ActionSet.
|
||||
func (actionSet ActionSet) Intersection(sset ActionSet) ActionSet {
|
||||
nset := NewActionSet()
|
||||
for k := range actionSet {
|
||||
if _, ok := sset[k]; ok {
|
||||
nset.Add(k)
|
||||
}
|
||||
}
|
||||
|
||||
return nset
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes ActionSet to JSON data.
|
||||
func (actionSet ActionSet) MarshalJSON() ([]byte, error) {
|
||||
if len(actionSet) == 0 {
|
||||
return nil, Errorf("empty actions not allowed")
|
||||
}
|
||||
|
||||
return json.Marshal(actionSet.ToSlice())
|
||||
}
|
||||
|
||||
func (actionSet ActionSet) String() string {
|
||||
actions := []string{}
|
||||
for action := range actionSet {
|
||||
actions = append(actions, string(action))
|
||||
}
|
||||
sort.Strings(actions)
|
||||
|
||||
return fmt.Sprintf("%v", actions)
|
||||
}
|
||||
|
||||
// ToSlice - returns slice of actions from the action set.
|
||||
func (actionSet ActionSet) ToSlice() []Action {
|
||||
actions := []Action{}
|
||||
for action := range actionSet {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
// Clone clones ActionSet structure
|
||||
func (actionSet ActionSet) Clone() ActionSet {
|
||||
return NewActionSet(actionSet.ToSlice()...)
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to ActionSet.
|
||||
func (actionSet *ActionSet) UnmarshalJSON(data []byte) error {
|
||||
var sset set.StringSet
|
||||
if err := json.Unmarshal(data, &sset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sset) == 0 {
|
||||
return Errorf("empty actions not allowed")
|
||||
}
|
||||
|
||||
*actionSet = make(ActionSet)
|
||||
for _, s := range sset.ToSlice() {
|
||||
action, err := parseAction(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actionSet.Add(action)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewActionSet - creates new action set.
|
||||
func NewActionSet(actions ...Action) ActionSet {
|
||||
actionSet := make(ActionSet)
|
||||
for _, action := range actions {
|
||||
actionSet.Add(action)
|
||||
}
|
||||
|
||||
return actionSet
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestActionSetAdd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ActionSet
|
||||
action Action
|
||||
expectedResult ActionSet
|
||||
}{
|
||||
{NewActionSet(), PutObjectAction, NewActionSet(PutObjectAction)},
|
||||
{NewActionSet(PutObjectAction), PutObjectAction, NewActionSet(PutObjectAction)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
testCase.set.Add(testCase.action)
|
||||
|
||||
if !reflect.DeepEqual(testCase.expectedResult, testCase.set) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetContains(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ActionSet
|
||||
action Action
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewActionSet(PutObjectAction), PutObjectAction, true},
|
||||
{NewActionSet(PutObjectAction, GetObjectAction), PutObjectAction, true},
|
||||
{NewActionSet(PutObjectAction, GetObjectAction), AbortMultipartUploadAction, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.Contains(testCase.action)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetIntersection(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ActionSet
|
||||
setToIntersect ActionSet
|
||||
expectedResult ActionSet
|
||||
}{
|
||||
{NewActionSet(), NewActionSet(PutObjectAction), NewActionSet()},
|
||||
{NewActionSet(PutObjectAction), NewActionSet(), NewActionSet()},
|
||||
{NewActionSet(PutObjectAction), NewActionSet(PutObjectAction, GetObjectAction), NewActionSet(PutObjectAction)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.Intersection(testCase.setToIntersect)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
actionSet ActionSet
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewActionSet(PutObjectAction), []byte(`["s3:PutObject"]`), false},
|
||||
{NewActionSet(), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.actionSet)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetToSlice(t *testing.T) {
|
||||
testCases := []struct {
|
||||
actionSet ActionSet
|
||||
expectedResult []Action
|
||||
}{
|
||||
{NewActionSet(PutObjectAction), []Action{PutObjectAction}},
|
||||
{NewActionSet(), []Action{}},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.actionSet.ToSlice()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult ActionSet
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"s3:PutObject"`), NewActionSet(PutObjectAction), false},
|
||||
{[]byte(`["s3:PutObject"]`), NewActionSet(PutObjectAction), false},
|
||||
{[]byte(`["s3:PutObject", "s3:GetObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false},
|
||||
{[]byte(`["s3:PutObject", "s3:GetObject", "s3:PutObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false},
|
||||
{[]byte(`[]`), NewActionSet(), true}, // Empty array.
|
||||
{[]byte(`"foo"`), nil, true}, // Invalid action.
|
||||
{[]byte(`["s3:PutObject", "foo"]`), nil, true}, // Invalid action.
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := make(ActionSet)
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
// 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/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
func toBinaryEqualsFuncString(n name, key Key, values set.StringSet) string {
|
||||
valueStrings := values.ToSlice()
|
||||
sort.Strings(valueStrings)
|
||||
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
||||
}
|
||||
|
||||
// binaryEqualsFunc - 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 binaryEqualsFunc struct {
|
||||
k Key
|
||||
values set.StringSet
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f binaryEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||
return !fvalues.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f binaryEqualsFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "BinaryEquals" condition name.
|
||||
func (f binaryEqualsFunc) name() name {
|
||||
return binaryEquals
|
||||
}
|
||||
|
||||
func (f binaryEqualsFunc) String() string {
|
||||
return toBinaryEqualsFuncString(binaryEquals, f.k, f.values)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f binaryEqualsFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
for _, value := range f.values.ToSlice() {
|
||||
values.Add(NewStringValue(base64.StdEncoding.EncodeToString([]byte(value))))
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
func validateBinaryEqualsValues(n name, key Key, values set.StringSet) error {
|
||||
vslice := values.ToSlice()
|
||||
for _, s := range vslice {
|
||||
sbytes, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values.Remove(s)
|
||||
s = string(sbytes)
|
||||
switch key {
|
||||
case S3XAmzCopySource:
|
||||
bucket, object := path2BucketAndObject(s)
|
||||
if object == "" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzCopySource, n)
|
||||
}
|
||||
if err = s3utils.CheckValidBucketName(bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
case S3XAmzServerSideEncryption, S3XAmzServerSideEncryptionCustomerAlgorithm:
|
||||
if s != "AES256" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzServerSideEncryption, n)
|
||||
}
|
||||
case S3XAmzMetadataDirective:
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newBinaryEqualsFunc - returns new BinaryEquals function.
|
||||
func newBinaryEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(binaryEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewBinaryEqualsFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewBinaryEqualsFunc - returns new BinaryEquals function.
|
||||
func NewBinaryEqualsFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateBinaryEqualsValues(binaryEquals, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &binaryEqualsFunc{key, sset}, nil
|
||||
}
|
@ -1,383 +0,0 @@
|
||||
// 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/base64"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBinaryEqualsFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{}, false},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
||||
{case2Function, map[string][]string{}, false},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
||||
{case3Function, map[string][]string{}, false},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
||||
{case4Function, map[string][]string{}, false},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryEqualsFuncKey(t *testing.T) {
|
||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryEqualsFuncToMap(t *testing.T) {
|
||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))),
|
||||
}
|
||||
|
||||
case2Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))),
|
||||
}
|
||||
|
||||
case4Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))),
|
||||
}
|
||||
|
||||
case6Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))),
|
||||
}
|
||||
|
||||
case8Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&binaryEqualsFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBinaryEqualsFunc(t *testing.T) {
|
||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket")))), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("SSE-C")))), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("DUPLICATE")))), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newBinaryEqualsFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// booleanFunc - Bool condition function. It checks whether Key is true or false.
|
||||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_Boolean
|
||||
type booleanFunc struct {
|
||||
k Key
|
||||
value string
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether Key is present in given values or not.
|
||||
// Depending on condition boolean value, this function returns true or false.
|
||||
func (f booleanFunc) 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
|
||||
}
|
||||
|
||||
return f.value == requestValue[0]
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f booleanFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "Bool" condition name.
|
||||
func (f booleanFunc) name() name {
|
||||
return boolean
|
||||
}
|
||||
|
||||
func (f booleanFunc) String() string {
|
||||
return fmt.Sprintf("%v:%v:%v", boolean, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f booleanFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: NewValueSet(NewStringValue(f.value)),
|
||||
}
|
||||
}
|
||||
|
||||
func newBooleanFunc(key Key, values ValueSet) (Function, error) {
|
||||
if key != AWSSecureTransport {
|
||||
return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSecureTransport, boolean)
|
||||
}
|
||||
|
||||
if len(values) != 1 {
|
||||
return nil, fmt.Errorf("only one value is allowed for boolean condition")
|
||||
}
|
||||
|
||||
var value Value
|
||||
for v := range values {
|
||||
value = v
|
||||
switch v.GetType() {
|
||||
case reflect.Bool:
|
||||
if _, err := v.GetBool(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.String:
|
||||
s, err := v.GetString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = strconv.ParseBool(s); err != nil {
|
||||
return nil, fmt.Errorf("value must be a boolean string for boolean condition")
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("value must be a boolean for boolean condition")
|
||||
}
|
||||
}
|
||||
|
||||
return &booleanFunc{key, value.String()}, nil
|
||||
}
|
||||
|
||||
// NewBoolFunc - returns new Bool function.
|
||||
func NewBoolFunc(key Key, value string) (Function, error) {
|
||||
return &booleanFunc{key, value}, nil
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBooleanFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"SecureTransport": {"true"}}, true},
|
||||
{case2Function, map[string][]string{"SecureTransport": {"false"}}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBooleanFuncKey(t *testing.T) {
|
||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, AWSSecureTransport},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBooleanFuncToMap(t *testing.T) {
|
||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
AWSSecureTransport: NewValueSet(NewStringValue("true")),
|
||||
}
|
||||
|
||||
case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
AWSSecureTransport: NewValueSet(NewStringValue("false")),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBooleanFunc(t *testing.T) {
|
||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{AWSSecureTransport, NewValueSet(NewBoolValue(true)), case1Function, false},
|
||||
{AWSSecureTransport, NewValueSet(NewStringValue("false")), case2Function, false},
|
||||
// Multiple values error.
|
||||
{AWSSecureTransport, NewValueSet(NewStringValue("true"), NewStringValue("false")), nil, true},
|
||||
// Invalid boolean string error.
|
||||
{AWSSecureTransport, NewValueSet(NewStringValue("foo")), nil, true},
|
||||
// Invalid value error.
|
||||
{AWSSecureTransport, NewValueSet(NewIntValue(7)), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newBooleanFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
// 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)
|
||||
}
|
@ -1,354 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFunctionsEvaluate(t *testing.T) {
|
||||
func1, err := newNullFunc(S3XAmzCopySource, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func3, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func4, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Function := NewFunctions(func1, func2, func3, func4)
|
||||
|
||||
testCases := []struct {
|
||||
functions Functions
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/myobject"},
|
||||
"SourceIp": {"192.168.1.10"},
|
||||
}, false},
|
||||
{case1Function, map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/myobject"},
|
||||
"SourceIp": {"192.168.1.10"},
|
||||
"Refer": {"http://example.org/"},
|
||||
}, false},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{"SourceIp": {"192.168.1.10"}}, false},
|
||||
{case1Function, map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/yourobject"},
|
||||
"SourceIp": {"192.168.1.10"},
|
||||
}, false},
|
||||
{case1Function, map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/myobject"},
|
||||
"SourceIp": {"192.168.2.10"},
|
||||
}, false},
|
||||
{case1Function, map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/myobject"},
|
||||
"Refer": {"http://example.org/"},
|
||||
}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.functions.Evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionsKeys(t *testing.T) {
|
||||
func1, err := newNullFunc(S3XAmzCopySource, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func3, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func4, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
functions Functions
|
||||
expectedResult KeySet
|
||||
}{
|
||||
{NewFunctions(func1, func2, func3, func4), NewKeySet(S3XAmzCopySource, AWSSourceIP)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.functions.Keys()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionsMarshalJSON(t *testing.T) {
|
||||
func1, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func3, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func4, err := newNotIPAddressFunc(AWSSourceIP,
|
||||
NewValueSet(NewStringValue("10.1.10.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func5, err := newStringNotLikeFunc(S3XAmzStorageClass, NewValueSet(NewStringValue("STANDARD")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func6, err := newNullFunc(S3XAmzServerSideEncryptionCustomerAlgorithm, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func7, err := newIPAddressFunc(AWSSourceIP,
|
||||
NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := []byte(`{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]},"NotIpAddress":{"aws:SourceIp":["10.1.10.0/24"]},"Null":{"s3:x-amz-server-side-encryption-customer-algorithm":[true]},"StringEquals":{"s3:x-amz-copy-source":["mybucket/myobject"]},"StringLike":{"s3:x-amz-metadata-directive":["REPL*"]},"StringNotEquals":{"s3:x-amz-server-side-encryption":["AES256"]},"StringNotLike":{"s3:x-amz-storage-class":["STANDARD"]}}`)
|
||||
|
||||
case2Result := []byte(`{"Null":{"s3:x-amz-server-side-encryption-customer-algorithm":[true]}}`)
|
||||
|
||||
testCases := []struct {
|
||||
functions Functions
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewFunctions(func1, func2, func3, func4, func5, func6, func7), case1Result, false},
|
||||
{NewFunctions(func6), case2Result, false},
|
||||
{NewFunctions(), []byte(`{}`), false},
|
||||
{nil, []byte(`{}`), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.functions)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionsUnmarshalJSON(t *testing.T) {
|
||||
case1Data := []byte(`{
|
||||
"StringLike": {
|
||||
"s3:x-amz-metadata-directive": "REPL*"
|
||||
},
|
||||
"StringEquals": {
|
||||
"s3:x-amz-copy-source": "mybucket/myobject"
|
||||
},
|
||||
"StringNotEquals": {
|
||||
"s3:x-amz-server-side-encryption": "AES256"
|
||||
},
|
||||
"NotIpAddress": {
|
||||
"aws:SourceIp": [
|
||||
"10.1.10.0/24",
|
||||
"10.10.1.0/24"
|
||||
]
|
||||
},
|
||||
"StringNotLike": {
|
||||
"s3:x-amz-storage-class": "STANDARD"
|
||||
},
|
||||
"Null": {
|
||||
"s3:x-amz-server-side-encryption-customer-algorithm": true
|
||||
},
|
||||
"IpAddress": {
|
||||
"aws:SourceIp": [
|
||||
"192.168.1.0/24",
|
||||
"192.168.2.0/24"
|
||||
]
|
||||
}
|
||||
}`)
|
||||
func1, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func3, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func4, err := newNotIPAddressFunc(AWSSourceIP,
|
||||
NewValueSet(NewStringValue("10.1.10.0/24"), NewStringValue("10.10.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func5, err := newStringNotLikeFunc(S3XAmzStorageClass, NewValueSet(NewStringValue("STANDARD")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func6, err := newNullFunc(S3XAmzServerSideEncryptionCustomerAlgorithm, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func7, err := newIPAddressFunc(AWSSourceIP,
|
||||
NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("192.168.2.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Data := []byte(`{
|
||||
"Null": {
|
||||
"s3:x-amz-server-side-encryption-customer-algorithm": true
|
||||
},
|
||||
"Null": {
|
||||
"s3:x-amz-server-side-encryption-customer-algorithm": "true"
|
||||
}
|
||||
}`)
|
||||
|
||||
case3Data := []byte(`{}`)
|
||||
|
||||
case4Data := []byte(`{
|
||||
"StringLike": {
|
||||
"s3:x-amz-metadata-directive": "REPL*"
|
||||
},
|
||||
"StringEquals": {
|
||||
"s3:x-amz-copy-source": "mybucket/myobject",
|
||||
"s3:prefix": [
|
||||
"",
|
||||
"home/"
|
||||
],
|
||||
"s3:delimiter": [
|
||||
"/"
|
||||
]
|
||||
},
|
||||
"StringNotEquals": {
|
||||
"s3:x-amz-server-side-encryption": "AES256"
|
||||
},
|
||||
"NotIpAddress": {
|
||||
"aws:SourceIp": [
|
||||
"10.1.10.0/24",
|
||||
"10.10.1.0/24"
|
||||
]
|
||||
},
|
||||
"StringNotLike": {
|
||||
"s3:x-amz-storage-class": "STANDARD"
|
||||
},
|
||||
"Null": {
|
||||
"s3:x-amz-server-side-encryption-customer-algorithm": true
|
||||
},
|
||||
"IpAddress": {
|
||||
"aws:SourceIp": [
|
||||
"192.168.1.0/24",
|
||||
"192.168.2.0/24"
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
func2_1, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2_2, err := newStringEqualsFunc(S3Prefix, NewValueSet(NewStringValue(""), NewStringValue("home/")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2_3, err := newStringEqualsFunc(S3Delimiter, NewValueSet(NewStringValue("/")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult Functions
|
||||
expectErr bool
|
||||
}{
|
||||
// Success case, basic conditions.
|
||||
{case1Data, NewFunctions(func1, func2, func3, func4, func5, func6, func7), false},
|
||||
// Duplicate conditions, success case only one value is preserved.
|
||||
{case2Data, NewFunctions(func6), false},
|
||||
// empty condition error.
|
||||
{case3Data, nil, true},
|
||||
// Success case multiple keys, same condition.
|
||||
{case4Data, NewFunctions(func1, func2_1, func2_2, func2_3, func3, func4, func5, func6, func7), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := new(Functions)
|
||||
err := json.Unmarshal(testCase.data, result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if (*result).String() != testCase.expectedResult.String() {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, *result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,187 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func toIPAddressFuncString(n name, key Key, values []*net.IPNet) string {
|
||||
valueStrings := []string{}
|
||||
for _, value := range values {
|
||||
valueStrings = append(valueStrings, value.String())
|
||||
}
|
||||
sort.Strings(valueStrings)
|
||||
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
||||
}
|
||||
|
||||
// ipAddressFunc - IP address function. It checks whether value by Key in given
|
||||
// values is in IP network. Here Key must be AWSSourceIP.
|
||||
// For example,
|
||||
// - if values = [192.168.1.0/24], at evaluate() it returns whether IP address
|
||||
// in value map for AWSSourceIP falls in the network 192.168.1.10/24.
|
||||
type ipAddressFunc struct {
|
||||
k Key
|
||||
values []*net.IPNet
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether IP address in values map for AWSSourceIP
|
||||
// falls in one of network or not.
|
||||
func (f ipAddressFunc) evaluate(values map[string][]string) bool {
|
||||
IPs := []net.IP{}
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
for _, s := range requestValue {
|
||||
IP := net.ParseIP(s)
|
||||
if IP == nil {
|
||||
panic(fmt.Errorf("invalid IP address '%v'", s))
|
||||
}
|
||||
|
||||
IPs = append(IPs, IP)
|
||||
}
|
||||
|
||||
for _, IP := range IPs {
|
||||
for _, IPNet := range f.values {
|
||||
if IPNet.Contains(IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
// Key is always AWSSourceIP.
|
||||
func (f ipAddressFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "IpAddress" condition name.
|
||||
func (f ipAddressFunc) name() name {
|
||||
return ipAddress
|
||||
}
|
||||
|
||||
func (f ipAddressFunc) String() string {
|
||||
return toIPAddressFuncString(ipAddress, f.k, f.values)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f ipAddressFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
for _, value := range f.values {
|
||||
values.Add(NewStringValue(value.String()))
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// notIPAddressFunc - Not IP address function. It checks whether value by Key in given
|
||||
// values is NOT in IP network. Here Key must be AWSSourceIP.
|
||||
// For example,
|
||||
// - if values = [192.168.1.0/24], at evaluate() it returns whether IP address
|
||||
// in value map for AWSSourceIP does not fall in the network 192.168.1.10/24.
|
||||
type notIPAddressFunc struct {
|
||||
ipAddressFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether IP address in values map for AWSSourceIP
|
||||
// does not fall in one of network.
|
||||
func (f notIPAddressFunc) evaluate(values map[string][]string) bool {
|
||||
return !f.ipAddressFunc.evaluate(values)
|
||||
}
|
||||
|
||||
// name() - returns "NotIpAddress" condition name.
|
||||
func (f notIPAddressFunc) name() name {
|
||||
return notIPAddress
|
||||
}
|
||||
|
||||
func (f notIPAddressFunc) String() string {
|
||||
return toIPAddressFuncString(notIPAddress, f.ipAddressFunc.k, f.ipAddressFunc.values)
|
||||
}
|
||||
|
||||
func valuesToIPNets(n name, values ValueSet) ([]*net.IPNet, error) {
|
||||
IPNets := []*net.IPNet{}
|
||||
for v := range values {
|
||||
s, err := v.GetString()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("value %v must be string representation of CIDR for %v condition", v, n)
|
||||
}
|
||||
|
||||
var IPNet *net.IPNet
|
||||
_, IPNet, err = net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("value %v must be CIDR string for %v condition", s, n)
|
||||
}
|
||||
|
||||
IPNets = append(IPNets, IPNet)
|
||||
}
|
||||
|
||||
return IPNets, nil
|
||||
}
|
||||
|
||||
// newIPAddressFunc - returns new IP address function.
|
||||
func newIPAddressFunc(key Key, values ValueSet) (Function, error) {
|
||||
IPNets, err := valuesToIPNets(ipAddress, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewIPAddressFunc(key, IPNets...)
|
||||
}
|
||||
|
||||
// NewIPAddressFunc - returns new IP address function.
|
||||
func NewIPAddressFunc(key Key, IPNets ...*net.IPNet) (Function, error) {
|
||||
if key != AWSSourceIP {
|
||||
return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSourceIP, ipAddress)
|
||||
}
|
||||
|
||||
return &ipAddressFunc{key, IPNets}, nil
|
||||
}
|
||||
|
||||
// newNotIPAddressFunc - returns new Not IP address function.
|
||||
func newNotIPAddressFunc(key Key, values ValueSet) (Function, error) {
|
||||
IPNets, err := valuesToIPNets(notIPAddress, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNotIPAddressFunc(key, IPNets...)
|
||||
}
|
||||
|
||||
// NewNotIPAddressFunc - returns new Not IP address function.
|
||||
func NewNotIPAddressFunc(key Key, IPNets ...*net.IPNet) (Function, error) {
|
||||
if key != AWSSourceIP {
|
||||
return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSourceIP, notIPAddress)
|
||||
}
|
||||
|
||||
return ¬IPAddressFunc{ipAddressFunc{key, IPNets}}, nil
|
||||
}
|
@ -1,279 +0,0 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIPAddressFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"SourceIp": {"192.168.1.10"}}, true},
|
||||
{case1Function, map[string][]string{"SourceIp": {"192.168.2.10"}}, false},
|
||||
{case1Function, map[string][]string{}, false},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPAddressFuncKey(t *testing.T) {
|
||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, AWSSourceIP},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPAddressFuncToMap(t *testing.T) {
|
||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24")),
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{&ipAddressFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotIPAddressFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"SourceIp": {"192.168.2.10"}}, true},
|
||||
{case1Function, map[string][]string{}, true},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
{case1Function, map[string][]string{"SourceIp": {"192.168.1.10"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotIPAddressFuncKey(t *testing.T) {
|
||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, AWSSourceIP},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotIPAddressFuncToMap(t *testing.T) {
|
||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24")),
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{¬IPAddressFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewIPAddressFunc(t *testing.T) {
|
||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")), case1Function, false},
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")), case2Function, false},
|
||||
// Unsupported key error.
|
||||
{S3Prefix, NewValueSet(NewStringValue("192.168.1.0/24")), nil, true},
|
||||
// Invalid value error.
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("node1.example.org")), nil, true},
|
||||
// Invalid CIDR format error.
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0.0/24")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newIPAddressFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if result.String() != testCase.expectedResult.String() {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewNotIPAddressFunc(t *testing.T) {
|
||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")), case1Function, false},
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")), case2Function, false},
|
||||
// Unsupported key error.
|
||||
{S3Prefix, NewValueSet(NewStringValue("192.168.1.0/24")), nil, true},
|
||||
// Invalid value error.
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("node1.example.org")), nil, true},
|
||||
// Invalid CIDR format error.
|
||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0.0/24")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newNotIPAddressFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if result.String() != testCase.expectedResult.String() {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
// 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
|
||||
|
||||
// JWT claims supported substitutions.
|
||||
// https://www.iana.org/assignments/jwt/jwt.xhtml#claims
|
||||
const (
|
||||
// JWTSub - JWT subject claim substitution.
|
||||
JWTSub Key = "jwt:sub"
|
||||
|
||||
// JWTIss issuer claim substitution.
|
||||
JWTIss Key = "jwt:iss"
|
||||
|
||||
// JWTAud audience claim substitution.
|
||||
JWTAud Key = "jwt:aud"
|
||||
|
||||
// JWTJti JWT unique identifier claim substitution.
|
||||
JWTJti Key = "jwt:jti"
|
||||
|
||||
JWTUpn Key = "jwt:upn"
|
||||
JWTName Key = "jwt:name"
|
||||
JWTGroups Key = "jwt:groups"
|
||||
JWTGivenName Key = "jwt:given_name"
|
||||
JWTFamilyName Key = "jwt:family_name"
|
||||
JWTMiddleName Key = "jwt:middle_name"
|
||||
JWTNickName Key = "jwt:nickname"
|
||||
JWTPrefUsername Key = "jwt:preferred_username"
|
||||
JWTProfile Key = "jwt:profile"
|
||||
JWTPicture Key = "jwt:picture"
|
||||
JWTWebsite Key = "jwt:website"
|
||||
JWTEmail Key = "jwt:email"
|
||||
JWTGender Key = "jwt:gender"
|
||||
JWTBirthdate Key = "jwt:birthdate"
|
||||
JWTPhoneNumber Key = "jwt:phone_number"
|
||||
JWTAddress Key = "jwt:address"
|
||||
JWTScope Key = "jwt:scope"
|
||||
JWTClientID Key = "jwt:client_id"
|
||||
)
|
||||
|
||||
// JWTKeys - Supported JWT keys, non-exhaustive list please
|
||||
// expand as new claims are standardized.
|
||||
var JWTKeys = []Key{
|
||||
JWTSub,
|
||||
JWTIss,
|
||||
JWTAud,
|
||||
JWTJti,
|
||||
JWTName,
|
||||
JWTUpn,
|
||||
JWTGroups,
|
||||
JWTGivenName,
|
||||
JWTFamilyName,
|
||||
JWTMiddleName,
|
||||
JWTNickName,
|
||||
JWTPrefUsername,
|
||||
JWTProfile,
|
||||
JWTPicture,
|
||||
JWTWebsite,
|
||||
JWTEmail,
|
||||
JWTGender,
|
||||
JWTBirthdate,
|
||||
JWTPhoneNumber,
|
||||
JWTAddress,
|
||||
JWTScope,
|
||||
JWTClientID,
|
||||
}
|
@ -1,322 +0,0 @@
|
||||
// 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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Key - conditional key which is used to fetch values for any condition.
|
||||
// Refer https://docs.aws.amazon.com/IAM/latest/UserGuide/list_s3.html
|
||||
// for more information about available condition keys.
|
||||
type Key string
|
||||
|
||||
const (
|
||||
// S3XAmzCopySource - key representing x-amz-copy-source HTTP header applicable to PutObject API only.
|
||||
S3XAmzCopySource Key = "s3:x-amz-copy-source"
|
||||
|
||||
// S3XAmzServerSideEncryption - key representing x-amz-server-side-encryption HTTP header applicable
|
||||
// to PutObject API only.
|
||||
S3XAmzServerSideEncryption Key = "s3:x-amz-server-side-encryption"
|
||||
|
||||
// S3XAmzServerSideEncryptionCustomerAlgorithm - key representing
|
||||
// x-amz-server-side-encryption-customer-algorithm HTTP header applicable to PutObject API only.
|
||||
S3XAmzServerSideEncryptionCustomerAlgorithm Key = "s3:x-amz-server-side-encryption-customer-algorithm"
|
||||
|
||||
// S3XAmzMetadataDirective - key representing x-amz-metadata-directive HTTP header applicable to
|
||||
// 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"
|
||||
|
||||
// S3LocationConstraint - key representing LocationConstraint XML tag of CreateBucket API only.
|
||||
S3LocationConstraint Key = "s3:LocationConstraint"
|
||||
|
||||
// S3Prefix - key representing prefix query parameter of ListBucket API only.
|
||||
S3Prefix Key = "s3:prefix"
|
||||
|
||||
// S3Delimiter - key representing delimiter query parameter of ListBucket API only.
|
||||
S3Delimiter Key = "s3:delimiter"
|
||||
|
||||
// S3VersionID - Enables you to limit the permission for the
|
||||
// s3:PutObjectVersionTagging action to a specific object version.
|
||||
S3VersionID Key = "s3:versionid"
|
||||
|
||||
// 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"
|
||||
|
||||
// AWSSourceIP - key representing client's IP address (not intermittent proxies) of any API.
|
||||
AWSSourceIP Key = "aws:SourceIp"
|
||||
|
||||
// AWSUserAgent - key representing UserAgent header for any API.
|
||||
AWSUserAgent Key = "aws:UserAgent"
|
||||
|
||||
// AWSSecureTransport - key representing if the clients request is authenticated or not.
|
||||
AWSSecureTransport Key = "aws:SecureTransport"
|
||||
|
||||
// AWSCurrentTime - key representing the current time.
|
||||
AWSCurrentTime Key = "aws:CurrentTime"
|
||||
|
||||
// AWSEpochTime - key representing the current epoch time.
|
||||
AWSEpochTime Key = "aws:EpochTime"
|
||||
|
||||
// AWSPrincipalType - user principal type currently supported values are "User" and "Anonymous".
|
||||
AWSPrincipalType Key = "aws:principaltype"
|
||||
|
||||
// AWSUserID - user unique ID, in MinIO this value is same as your user Access Key.
|
||||
AWSUserID Key = "aws:userid"
|
||||
|
||||
// AWSUsername - user friendly name, in MinIO this value is same as your user Access Key.
|
||||
AWSUsername Key = "aws:username"
|
||||
|
||||
// S3SignatureVersion - identifies the version of AWS Signature that you want to support for authenticated requests.
|
||||
S3SignatureVersion = "s3:signatureversion"
|
||||
|
||||
// S3AuthType - optionally use this condition key to restrict incoming requests to use a specific authentication method.
|
||||
S3AuthType = "s3:authType"
|
||||
)
|
||||
|
||||
// AllSupportedKeys - is list of all all supported keys.
|
||||
var AllSupportedKeys = append([]Key{
|
||||
S3SignatureVersion,
|
||||
S3AuthType,
|
||||
S3XAmzCopySource,
|
||||
S3XAmzServerSideEncryption,
|
||||
S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
S3XAmzMetadataDirective,
|
||||
S3XAmzStorageClass,
|
||||
S3XAmzContentSha256,
|
||||
S3LocationConstraint,
|
||||
S3Prefix,
|
||||
S3Delimiter,
|
||||
S3MaxKeys,
|
||||
S3VersionID,
|
||||
S3ObjectLockRemainingRetentionDays,
|
||||
S3ObjectLockMode,
|
||||
S3ObjectLockLegalHold,
|
||||
S3ObjectLockRetainUntilDate,
|
||||
AWSReferer,
|
||||
AWSSourceIP,
|
||||
AWSUserAgent,
|
||||
AWSSecureTransport,
|
||||
AWSCurrentTime,
|
||||
AWSEpochTime,
|
||||
AWSPrincipalType,
|
||||
AWSUserID,
|
||||
AWSUsername,
|
||||
LDAPUser,
|
||||
LDAPUsername,
|
||||
// Add new supported condition keys.
|
||||
}, JWTKeys...)
|
||||
|
||||
// CommonKeys - is list of all common condition keys.
|
||||
var CommonKeys = append([]Key{
|
||||
S3SignatureVersion,
|
||||
S3AuthType,
|
||||
S3XAmzContentSha256,
|
||||
S3LocationConstraint,
|
||||
AWSReferer,
|
||||
AWSSourceIP,
|
||||
AWSUserAgent,
|
||||
AWSSecureTransport,
|
||||
AWSCurrentTime,
|
||||
AWSEpochTime,
|
||||
AWSPrincipalType,
|
||||
AWSUserID,
|
||||
AWSUsername,
|
||||
LDAPUser,
|
||||
LDAPUsername,
|
||||
}, JWTKeys...)
|
||||
|
||||
func substFuncFromValues(values map[string][]string) func(string) string {
|
||||
return func(v string) string {
|
||||
for _, key := range CommonKeys {
|
||||
// Empty values are not supported for policy variables.
|
||||
if rvalues, ok := values[key.Name()]; ok && rvalues[0] != "" {
|
||||
v = strings.Replace(v, key.VarName(), rvalues[0], -1)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid - checks if key is valid or not.
|
||||
func (key Key) IsValid() bool {
|
||||
for _, supKey := range AllSupportedKeys {
|
||||
if supKey == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes Key to JSON data.
|
||||
func (key Key) MarshalJSON() ([]byte, error) {
|
||||
if !key.IsValid() {
|
||||
return nil, fmt.Errorf("unknown key %v", key)
|
||||
}
|
||||
|
||||
return json.Marshal(string(key))
|
||||
}
|
||||
|
||||
// VarName - returns variable key name, such as "${aws:username}"
|
||||
func (key Key) VarName() string {
|
||||
return fmt.Sprintf("${%s}", key)
|
||||
}
|
||||
|
||||
// Name - returns key name which is stripped value of prefixes "aws:" and "s3:"
|
||||
func (key Key) Name() string {
|
||||
keyString := string(key)
|
||||
|
||||
if strings.HasPrefix(keyString, "aws:") {
|
||||
return strings.TrimPrefix(keyString, "aws:")
|
||||
} else if strings.HasPrefix(keyString, "jwt:") {
|
||||
return strings.TrimPrefix(keyString, "jwt:")
|
||||
} else if strings.HasPrefix(keyString, "ldap:") {
|
||||
return strings.TrimPrefix(keyString, "ldap:")
|
||||
}
|
||||
return strings.TrimPrefix(keyString, "s3:")
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to Key.
|
||||
func (key *Key) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parsedKey, err := parseKey(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*key = parsedKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseKey(s string) (Key, error) {
|
||||
key := Key(s)
|
||||
|
||||
if key.IsValid() {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return key, fmt.Errorf("invalid condition key '%v'", s)
|
||||
}
|
||||
|
||||
// KeySet - set representation of slice of keys.
|
||||
type KeySet map[Key]struct{}
|
||||
|
||||
// Add - add a key to key set.
|
||||
func (set KeySet) Add(key Key) {
|
||||
set[key] = struct{}{}
|
||||
}
|
||||
|
||||
// Merge merges two key sets, duplicates are overwritten
|
||||
func (set KeySet) Merge(mset KeySet) {
|
||||
for k, v := range mset {
|
||||
set[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Difference - returns a key set contains difference of two keys.
|
||||
// Example:
|
||||
// keySet1 := ["one", "two", "three"]
|
||||
// keySet2 := ["two", "four", "three"]
|
||||
// keySet1.Difference(keySet2) == ["one"]
|
||||
func (set KeySet) Difference(sset KeySet) KeySet {
|
||||
nset := make(KeySet)
|
||||
|
||||
for k := range set {
|
||||
if _, ok := sset[k]; !ok {
|
||||
nset.Add(k)
|
||||
}
|
||||
}
|
||||
|
||||
return nset
|
||||
}
|
||||
|
||||
// IsEmpty - returns whether key set is empty or not.
|
||||
func (set KeySet) IsEmpty() bool {
|
||||
return len(set) == 0
|
||||
}
|
||||
|
||||
func (set KeySet) String() string {
|
||||
return fmt.Sprintf("%v", set.ToSlice())
|
||||
}
|
||||
|
||||
// ToSlice - returns slice of keys.
|
||||
func (set KeySet) ToSlice() []Key {
|
||||
keys := []Key{}
|
||||
|
||||
for key := range set {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// NewKeySet - returns new KeySet contains given keys.
|
||||
func NewKeySet(keys ...Key) KeySet {
|
||||
set := make(KeySet)
|
||||
for _, key := range keys {
|
||||
set.Add(key)
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// AllSupportedAdminKeys - is list of all admin supported keys.
|
||||
var AllSupportedAdminKeys = []Key{
|
||||
AWSReferer,
|
||||
AWSSourceIP,
|
||||
AWSUserAgent,
|
||||
AWSSecureTransport,
|
||||
AWSCurrentTime,
|
||||
AWSEpochTime,
|
||||
// Add new supported condition keys.
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKeyIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key Key
|
||||
expectedResult bool
|
||||
}{
|
||||
{S3XAmzCopySource, true},
|
||||
{S3XAmzServerSideEncryption, true},
|
||||
{S3XAmzServerSideEncryptionCustomerAlgorithm, true},
|
||||
{S3XAmzMetadataDirective, true},
|
||||
{S3XAmzStorageClass, true},
|
||||
{S3LocationConstraint, true},
|
||||
{S3Prefix, true},
|
||||
{S3Delimiter, true},
|
||||
{S3MaxKeys, true},
|
||||
{AWSReferer, true},
|
||||
{AWSSourceIP, true},
|
||||
{Key("foo"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.key.IsValid()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key Key
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, []byte(`"s3:x-amz-copy-source"`), false},
|
||||
{Key("foo"), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.key)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: key: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key Key
|
||||
expectedResult string
|
||||
}{
|
||||
{S3XAmzCopySource, "x-amz-copy-source"},
|
||||
{AWSReferer, "Referer"},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.key.Name()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedKey Key
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"s3:x-amz-copy-source"`), S3XAmzCopySource, false},
|
||||
{[]byte(`"foo"`), Key(""), true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var key Key
|
||||
err := json.Unmarshal(testCase.data, &key)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if testCase.expectedKey != key {
|
||||
t.Fatalf("case %v: key: expected: %v, got: %v\n", i+1, testCase.expectedKey, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeySetAdd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set KeySet
|
||||
key Key
|
||||
expectedResult KeySet
|
||||
}{
|
||||
{NewKeySet(), S3XAmzCopySource, NewKeySet(S3XAmzCopySource)},
|
||||
{NewKeySet(S3XAmzCopySource), S3XAmzCopySource, NewKeySet(S3XAmzCopySource)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
testCase.set.Add(testCase.key)
|
||||
|
||||
if !reflect.DeepEqual(testCase.expectedResult, testCase.set) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeySetDifference(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set KeySet
|
||||
setToDiff KeySet
|
||||
expectedResult KeySet
|
||||
}{
|
||||
{NewKeySet(), NewKeySet(S3XAmzCopySource), NewKeySet()},
|
||||
{NewKeySet(S3Prefix, S3Delimiter, S3MaxKeys), NewKeySet(S3Delimiter, S3MaxKeys), NewKeySet(S3Prefix)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.Difference(testCase.setToDiff)
|
||||
|
||||
if !reflect.DeepEqual(testCase.expectedResult, result) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeySetIsEmpty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set KeySet
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewKeySet(), true},
|
||||
{NewKeySet(S3Delimiter), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.IsEmpty()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeySetString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set KeySet
|
||||
expectedResult string
|
||||
}{
|
||||
{NewKeySet(), `[]`},
|
||||
{NewKeySet(S3Delimiter), `[s3:delimiter]`},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.String()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeySetToSlice(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set KeySet
|
||||
expectedResult []Key
|
||||
}{
|
||||
{NewKeySet(), []Key{}},
|
||||
{NewKeySet(S3Delimiter), []Key{S3Delimiter}},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.ToSlice()
|
||||
|
||||
if !reflect.DeepEqual(testCase.expectedResult, result) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// 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
|
||||
|
||||
const (
|
||||
// LDAPUser - LDAP user DN, in MinIO this value is equal to user DN of the authenticated user.
|
||||
LDAPUser Key = "ldap:user"
|
||||
|
||||
// LDAPUsername - LDAP username, in MinIO is the authenticated simply user.
|
||||
LDAPUsername Key = "ldap:username"
|
||||
)
|
@ -1,124 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
type name string
|
||||
|
||||
const (
|
||||
stringEquals name = "StringEquals"
|
||||
stringNotEquals = "StringNotEquals"
|
||||
stringEqualsIgnoreCase = "StringEqualsIgnoreCase"
|
||||
stringNotEqualsIgnoreCase = "StringNotEqualsIgnoreCase"
|
||||
stringLike = "StringLike"
|
||||
stringNotLike = "StringNotLike"
|
||||
binaryEquals = "BinaryEquals"
|
||||
ipAddress = "IpAddress"
|
||||
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{
|
||||
stringEquals,
|
||||
stringNotEquals,
|
||||
stringEqualsIgnoreCase,
|
||||
stringNotEqualsIgnoreCase,
|
||||
binaryEquals,
|
||||
stringLike,
|
||||
stringNotLike,
|
||||
ipAddress,
|
||||
notIPAddress,
|
||||
null,
|
||||
boolean,
|
||||
numericEquals,
|
||||
numericNotEquals,
|
||||
numericLessThan,
|
||||
numericLessThanEquals,
|
||||
numericGreaterThan,
|
||||
numericGreaterThanEquals,
|
||||
dateEquals,
|
||||
dateNotEquals,
|
||||
dateLessThan,
|
||||
dateLessThanEquals,
|
||||
dateGreaterThan,
|
||||
dateGreaterThanEquals,
|
||||
// Add new conditions here.
|
||||
}
|
||||
|
||||
// IsValid - checks if name is valid or not.
|
||||
func (n name) IsValid() bool {
|
||||
for _, supn := range supportedConditions {
|
||||
if n == supn {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes name to JSON data.
|
||||
func (n name) MarshalJSON() ([]byte, error) {
|
||||
if !n.IsValid() {
|
||||
return nil, fmt.Errorf("invalid name %v", n)
|
||||
}
|
||||
|
||||
return json.Marshal(string(n))
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to condition name.
|
||||
func (n *name) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parsedName, err := parseName(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*n = parsedName
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseName(s string) (name, error) {
|
||||
n := name(s)
|
||||
|
||||
if n.IsValid() {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
return n, fmt.Errorf("invalid condition name '%v'", s)
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNameIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
n name
|
||||
expectedResult bool
|
||||
}{
|
||||
{stringEquals, true},
|
||||
{stringNotEquals, true},
|
||||
{stringLike, true},
|
||||
{stringNotLike, true},
|
||||
{ipAddress, true},
|
||||
{notIPAddress, true},
|
||||
{null, true},
|
||||
{name("foo"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.n.IsValid()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
n name
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{stringEquals, []byte(`"StringEquals"`), false},
|
||||
{stringNotEquals, []byte(`"StringNotEquals"`), false},
|
||||
{stringLike, []byte(`"StringLike"`), false},
|
||||
{stringNotLike, []byte(`"StringNotLike"`), false},
|
||||
{ipAddress, []byte(`"IpAddress"`), false},
|
||||
{notIPAddress, []byte(`"NotIpAddress"`), false},
|
||||
{null, []byte(`"Null"`), false},
|
||||
{name("foo"), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.n)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult name
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"StringEquals"`), stringEquals, false},
|
||||
{[]byte(`"foo"`), name(""), true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result name
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if testCase.expectErr != expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// nullFunc - Null condition function. It checks whether Key is not present in given
|
||||
// values or not.
|
||||
// For example,
|
||||
// 1. if Key = S3XAmzCopySource and Value = true, at evaluate() it returns whether
|
||||
// S3XAmzCopySource is NOT in given value map or not.
|
||||
// 2. if Key = S3XAmzCopySource and Value = false, at evaluate() it returns whether
|
||||
// S3XAmzCopySource is in given value map or not.
|
||||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_Null
|
||||
type nullFunc struct {
|
||||
k Key
|
||||
value bool
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether Key is present in given values or not.
|
||||
// Depending on condition boolean value, this function returns true or false.
|
||||
func (f nullFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
if f.value {
|
||||
return len(requestValue) == 0
|
||||
}
|
||||
|
||||
return len(requestValue) != 0
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f nullFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "Null" condition name.
|
||||
func (f nullFunc) name() name {
|
||||
return null
|
||||
}
|
||||
|
||||
func (f nullFunc) String() string {
|
||||
return fmt.Sprintf("%v:%v:%v", null, f.k, f.value)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f nullFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: NewValueSet(NewBoolValue(f.value)),
|
||||
}
|
||||
}
|
||||
|
||||
func newNullFunc(key Key, values ValueSet) (Function, error) {
|
||||
if len(values) != 1 {
|
||||
return nil, fmt.Errorf("only one value is allowed for Null condition")
|
||||
}
|
||||
|
||||
var value bool
|
||||
for v := range values {
|
||||
switch v.GetType() {
|
||||
case reflect.Bool:
|
||||
value, _ = v.GetBool()
|
||||
case reflect.String:
|
||||
var err error
|
||||
s, _ := v.GetString()
|
||||
if value, err = strconv.ParseBool(s); err != nil {
|
||||
return nil, fmt.Errorf("value must be a boolean string for Null condition")
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("value must be a boolean for Null condition")
|
||||
}
|
||||
}
|
||||
|
||||
return &nullFunc{key, value}, nil
|
||||
}
|
||||
|
||||
// NewNullFunc - returns new Null function.
|
||||
func NewNullFunc(key Key, value bool) (Function, error) {
|
||||
return &nullFunc{key, value}, nil
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNullFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(false)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"prefix": {"true"}}, false},
|
||||
{case1Function, map[string][]string{"prefix": {"false"}}, false},
|
||||
{case1Function, map[string][]string{"prefix": {"mybucket/foo"}}, false},
|
||||
{case1Function, map[string][]string{}, true},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
{case2Function, map[string][]string{"prefix": {"true"}}, true},
|
||||
{case2Function, map[string][]string{"prefix": {"false"}}, true},
|
||||
{case2Function, map[string][]string{"prefix": {"mybucket/foo"}}, true},
|
||||
{case2Function, map[string][]string{}, false},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNullFuncKey(t *testing.T) {
|
||||
case1Function, err := newNullFunc(S3XAmzCopySource, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNullFuncToMap(t *testing.T) {
|
||||
case1Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3Prefix: NewValueSet(NewBoolValue(true)),
|
||||
}
|
||||
|
||||
case2Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(false)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3Prefix: NewValueSet(NewBoolValue(false)),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{&nullFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewNullFunc(t *testing.T) {
|
||||
case1Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(true)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(false)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3Prefix, NewValueSet(NewBoolValue(true)), case1Function, false},
|
||||
{S3Prefix, NewValueSet(NewStringValue("false")), case2Function, false},
|
||||
// Multiple values error.
|
||||
{S3Prefix, NewValueSet(NewBoolValue(true), NewBoolValue(false)), nil, true},
|
||||
// Invalid boolean string error.
|
||||
{S3Prefix, NewValueSet(NewStringValue("foo")), nil, true},
|
||||
// Invalid value error.
|
||||
{S3Prefix, NewValueSet(NewIntValue(7)), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newNullFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
func toStringEqualsFuncString(n name, key Key, values set.StringSet) string {
|
||||
valueStrings := values.ToSlice()
|
||||
sort.Strings(valueStrings)
|
||||
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
||||
}
|
||||
|
||||
// stringEqualsFunc - 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 stringEqualsFunc struct {
|
||||
k Key
|
||||
values set.StringSet
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values.
|
||||
func (f stringEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||
return !fvalues.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f stringEqualsFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "StringEquals" condition name.
|
||||
func (f stringEqualsFunc) name() name {
|
||||
return stringEquals
|
||||
}
|
||||
|
||||
func (f stringEqualsFunc) String() string {
|
||||
return toStringEqualsFuncString(stringEquals, f.k, f.values)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f stringEqualsFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
for _, value := range f.values.ToSlice() {
|
||||
values.Add(NewStringValue(value))
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// stringNotEqualsFunc - 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 stringNotEqualsFunc struct {
|
||||
stringEqualsFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f stringNotEqualsFunc) evaluate(values map[string][]string) bool {
|
||||
return !f.stringEqualsFunc.evaluate(values)
|
||||
}
|
||||
|
||||
// name() - returns "StringNotEquals" condition name.
|
||||
func (f stringNotEqualsFunc) name() name {
|
||||
return stringNotEquals
|
||||
}
|
||||
|
||||
func (f stringNotEqualsFunc) String() string {
|
||||
return toStringEqualsFuncString(stringNotEquals, f.stringEqualsFunc.k, f.stringEqualsFunc.values)
|
||||
}
|
||||
|
||||
func valuesToStringSlice(n name, values ValueSet) ([]string, error) {
|
||||
valueStrings := []string{}
|
||||
|
||||
for value := range values {
|
||||
s, err := value.GetString()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("value must be a string for %v condition", n)
|
||||
}
|
||||
|
||||
valueStrings = append(valueStrings, s)
|
||||
}
|
||||
|
||||
return valueStrings, nil
|
||||
}
|
||||
|
||||
func validateStringEqualsValues(n name, key Key, values set.StringSet) error {
|
||||
for _, s := range values.ToSlice() {
|
||||
switch key {
|
||||
case S3XAmzCopySource:
|
||||
bucket, object := path2BucketAndObject(s)
|
||||
if object == "" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzCopySource, n)
|
||||
}
|
||||
if err := s3utils.CheckValidBucketName(bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
case S3XAmzServerSideEncryption, S3XAmzServerSideEncryptionCustomerAlgorithm:
|
||||
if s != "AES256" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzServerSideEncryption, n)
|
||||
}
|
||||
case S3XAmzMetadataDirective:
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newStringEqualsFunc - returns new StringEquals function.
|
||||
func newStringEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(stringEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStringEqualsFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewStringEqualsFunc - returns new StringEquals function.
|
||||
func NewStringEqualsFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateStringEqualsValues(stringEquals, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stringEqualsFunc{key, sset}, nil
|
||||
}
|
||||
|
||||
// newStringNotEqualsFunc - returns new StringNotEquals function.
|
||||
func newStringNotEqualsFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(stringNotEquals, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStringNotEqualsFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewStringNotEqualsFunc - returns new StringNotEquals function.
|
||||
func NewStringNotEqualsFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateStringEqualsValues(stringNotEquals, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stringNotEqualsFunc{stringEqualsFunc{key, sset}}, nil
|
||||
}
|
@ -1,709 +0,0 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringEqualsFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{}, false},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
||||
{case2Function, map[string][]string{}, false},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
||||
{case3Function, map[string][]string{}, false},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
||||
{case4Function, map[string][]string{}, false},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringEqualsFuncKey(t *testing.T) {
|
||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringEqualsFuncToMap(t *testing.T) {
|
||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
||||
}
|
||||
|
||||
case6Function, err := newStringEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
||||
}
|
||||
|
||||
case8Function, err := newStringEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&stringEqualsFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotEqualsFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{}, true},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
||||
{case2Function, map[string][]string{}, true},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
||||
{case3Function, map[string][]string{}, true},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
||||
{case4Function, map[string][]string{}, true},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotEqualsFuncKey(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotEqualsFuncToMap(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&stringNotEqualsFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStringEqualsFunc(t *testing.T) {
|
||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newStringEqualsFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStringNotEqualsFunc(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotEqualsFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newStringNotEqualsFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
func toStringEqualsIgnoreCaseFuncString(n name, key Key, values set.StringSet) string {
|
||||
valueStrings := values.ToSlice()
|
||||
sort.Strings(valueStrings)
|
||||
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
||||
}
|
||||
|
||||
// stringEqualsIgnoreCaseFunc - 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 stringEqualsIgnoreCaseFunc struct {
|
||||
k Key
|
||||
values set.StringSet
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
||||
// condition values, ignores case.
|
||||
func (f stringEqualsIgnoreCaseFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||
|
||||
for _, v := range requestValue {
|
||||
if !fvalues.FuncMatch(strings.EqualFold, v).IsEmpty() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f stringEqualsIgnoreCaseFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "StringEqualsIgnoreCase" condition name.
|
||||
func (f stringEqualsIgnoreCaseFunc) name() name {
|
||||
return stringEqualsIgnoreCase
|
||||
}
|
||||
|
||||
func (f stringEqualsIgnoreCaseFunc) String() string {
|
||||
return toStringEqualsIgnoreCaseFuncString(stringEqualsIgnoreCase, f.k, f.values)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f stringEqualsIgnoreCaseFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
for _, value := range f.values.ToSlice() {
|
||||
values.Add(NewStringValue(value))
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// stringNotEqualsIgnoreCaseFunc - 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 stringNotEqualsIgnoreCaseFunc struct {
|
||||
stringEqualsIgnoreCaseFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
||||
// condition values.
|
||||
func (f stringNotEqualsIgnoreCaseFunc) evaluate(values map[string][]string) bool {
|
||||
return !f.stringEqualsIgnoreCaseFunc.evaluate(values)
|
||||
}
|
||||
|
||||
// name() - returns "StringNotEqualsIgnoreCase" condition name.
|
||||
func (f stringNotEqualsIgnoreCaseFunc) name() name {
|
||||
return stringNotEqualsIgnoreCase
|
||||
}
|
||||
|
||||
func (f stringNotEqualsIgnoreCaseFunc) String() string {
|
||||
return toStringEqualsIgnoreCaseFuncString(stringNotEqualsIgnoreCase, f.stringEqualsIgnoreCaseFunc.k, f.stringEqualsIgnoreCaseFunc.values)
|
||||
}
|
||||
|
||||
func validateStringEqualsIgnoreCaseValues(n name, key Key, values set.StringSet) error {
|
||||
return validateStringEqualsValues(n, key, values)
|
||||
}
|
||||
|
||||
// newStringEqualsIgnoreCaseFunc - returns new StringEqualsIgnoreCase function.
|
||||
func newStringEqualsIgnoreCaseFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(stringEqualsIgnoreCase, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStringEqualsIgnoreCaseFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewStringEqualsIgnoreCaseFunc - returns new StringEqualsIgnoreCase function.
|
||||
func NewStringEqualsIgnoreCaseFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateStringEqualsIgnoreCaseValues(stringEqualsIgnoreCase, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stringEqualsIgnoreCaseFunc{key, sset}, nil
|
||||
}
|
||||
|
||||
// newStringNotEqualsIgnoreCaseFunc - returns new StringNotEqualsIgnoreCase function.
|
||||
func newStringNotEqualsIgnoreCaseFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(stringNotEqualsIgnoreCase, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStringNotEqualsIgnoreCaseFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewStringNotEqualsIgnoreCaseFunc - returns new StringNotEqualsIgnoreCase function.
|
||||
func NewStringNotEqualsIgnoreCaseFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateStringEqualsIgnoreCaseValues(stringNotEqualsIgnoreCase, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stringNotEqualsIgnoreCaseFunc{stringEqualsIgnoreCaseFunc{key, sset}}, nil
|
||||
}
|
@ -1,711 +0,0 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringEqualsIgnoreCaseFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{}, false},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"aes256"}}, true},
|
||||
{case2Function, map[string][]string{}, false},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"replace"}}, true},
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
||||
{case3Function, map[string][]string{}, false},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
||||
{case4Function, map[string][]string{}, false},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringEqualsIgnoreCaseFuncKey(t *testing.T) {
|
||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringEqualsIgnoreCaseFuncToMap(t *testing.T) {
|
||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
||||
}
|
||||
|
||||
case6Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
||||
}
|
||||
|
||||
case8Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&stringEqualsFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotEqualsIgnoreCaseFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{}, true},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
||||
{case2Function, map[string][]string{}, true},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
||||
{case3Function, map[string][]string{}, true},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
||||
{case4Function, map[string][]string{}, true},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotEqualsIgnoreCaseFuncKey(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotEqualsIgnoreCaseFuncToMap(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&stringNotEqualsFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStringEqualsIgnoreCaseFunc(t *testing.T) {
|
||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newStringEqualsIgnoreCaseFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStringNotEqualsIgnoreCaseFunc(t *testing.T) {
|
||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/myobject"),
|
||||
NewStringValue("yourbucket/myobject"),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES256"),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPLACE"),
|
||||
NewStringValue("COPY"),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-1"),
|
||||
NewStringValue("us-west-1"),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newStringNotEqualsIgnoreCaseFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
func toStringLikeFuncString(n name, key Key, values set.StringSet) string {
|
||||
valueStrings := values.ToSlice()
|
||||
sort.Strings(valueStrings)
|
||||
|
||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
||||
}
|
||||
|
||||
// stringLikeFunc - String like function. It checks whether value by Key in given
|
||||
// values map is widcard matching in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo*"], at evaluate() it returns whether string
|
||||
// in value map for Key is wildcard matching in values.
|
||||
type stringLikeFunc struct {
|
||||
k Key
|
||||
values set.StringSet
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is wildcard
|
||||
// matching in condition values.
|
||||
func (f stringLikeFunc) evaluate(values map[string][]string) bool {
|
||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
||||
if !ok {
|
||||
requestValue = values[f.k.Name()]
|
||||
}
|
||||
|
||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||
|
||||
for _, v := range requestValue {
|
||||
if !fvalues.FuncMatch(wildcard.Match, v).IsEmpty() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// key() - returns condition key which is used by this condition function.
|
||||
func (f stringLikeFunc) key() Key {
|
||||
return f.k
|
||||
}
|
||||
|
||||
// name() - returns "StringLike" function name.
|
||||
func (f stringLikeFunc) name() name {
|
||||
return stringLike
|
||||
}
|
||||
|
||||
func (f stringLikeFunc) String() string {
|
||||
return toStringLikeFuncString(stringLike, f.k, f.values)
|
||||
}
|
||||
|
||||
// toMap - returns map representation of this function.
|
||||
func (f stringLikeFunc) toMap() map[Key]ValueSet {
|
||||
if !f.k.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := NewValueSet()
|
||||
for _, value := range f.values.ToSlice() {
|
||||
values.Add(NewStringValue(value))
|
||||
}
|
||||
|
||||
return map[Key]ValueSet{
|
||||
f.k: values,
|
||||
}
|
||||
}
|
||||
|
||||
// stringNotLikeFunc - String not like function. It checks whether value by Key in given
|
||||
// values map is NOT widcard matching in condition values.
|
||||
// For example,
|
||||
// - if values = ["mybucket/foo*"], at evaluate() it returns whether string
|
||||
// in value map for Key is NOT wildcard matching in values.
|
||||
type stringNotLikeFunc struct {
|
||||
stringLikeFunc
|
||||
}
|
||||
|
||||
// evaluate() - evaluates to check whether value by Key in given values is NOT wildcard
|
||||
// matching in condition values.
|
||||
func (f stringNotLikeFunc) evaluate(values map[string][]string) bool {
|
||||
return !f.stringLikeFunc.evaluate(values)
|
||||
}
|
||||
|
||||
// name() - returns "StringNotLike" function name.
|
||||
func (f stringNotLikeFunc) name() name {
|
||||
return stringNotLike
|
||||
}
|
||||
|
||||
func (f stringNotLikeFunc) String() string {
|
||||
return toStringLikeFuncString(stringNotLike, f.stringLikeFunc.k, f.stringLikeFunc.values)
|
||||
}
|
||||
|
||||
func validateStringLikeValues(n name, key Key, values set.StringSet) error {
|
||||
for _, s := range values.ToSlice() {
|
||||
switch key {
|
||||
case S3XAmzCopySource:
|
||||
bucket, object := path2BucketAndObject(s)
|
||||
if object == "" {
|
||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzCopySource, n)
|
||||
}
|
||||
if err := s3utils.CheckValidBucketName(bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newStringLikeFunc - returns new StringLike function.
|
||||
func newStringLikeFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(stringLike, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStringLikeFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewStringLikeFunc - returns new StringLike function.
|
||||
func NewStringLikeFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateStringLikeValues(stringLike, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stringLikeFunc{key, sset}, nil
|
||||
}
|
||||
|
||||
// newStringNotLikeFunc - returns new StringNotLike function.
|
||||
func newStringNotLikeFunc(key Key, values ValueSet) (Function, error) {
|
||||
valueStrings, err := valuesToStringSlice(stringNotLike, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStringNotLikeFunc(key, valueStrings...)
|
||||
}
|
||||
|
||||
// NewStringNotLikeFunc - returns new StringNotLike function.
|
||||
func NewStringNotLikeFunc(key Key, values ...string) (Function, error) {
|
||||
sset := set.CreateStringSet(values...)
|
||||
if err := validateStringLikeValues(stringNotLike, key, sset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stringNotLikeFunc{stringLikeFunc{key, sset}}, nil
|
||||
}
|
@ -1,799 +0,0 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringLikeFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, true},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{}, false},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, false},
|
||||
{case2Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
||||
{case2Function, map[string][]string{}, false},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, true},
|
||||
{case3Function, map[string][]string{}, false},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, false},
|
||||
{case4Function, map[string][]string{}, false},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, true},
|
||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
||||
{case5Function, map[string][]string{}, false},
|
||||
{case5Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, false},
|
||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
||||
{case6Function, map[string][]string{}, false},
|
||||
{case6Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, true},
|
||||
{case7Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
||||
{case7Function, map[string][]string{}, false},
|
||||
{case7Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
|
||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, false},
|
||||
{case8Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
||||
{case8Function, map[string][]string{}, false},
|
||||
{case8Function, map[string][]string{"delimiter": {"/"}}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringLikeFuncKey(t *testing.T) {
|
||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringLikeFuncToMap(t *testing.T) {
|
||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/*")),
|
||||
}
|
||||
|
||||
case2Function, err := newStringLikeFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES*")),
|
||||
}
|
||||
|
||||
case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPL*")),
|
||||
}
|
||||
|
||||
case6Function, err := newStringLikeFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-*")),
|
||||
}
|
||||
|
||||
case8Function, err := newStringLikeFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&stringLikeFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotLikeFuncEvaluate(t *testing.T) {
|
||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
values map[string][]string
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, false},
|
||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
||||
{case1Function, map[string][]string{}, true},
|
||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, true},
|
||||
{case2Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
||||
{case2Function, map[string][]string{}, true},
|
||||
{case2Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, false},
|
||||
{case3Function, map[string][]string{}, true},
|
||||
{case3Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, true},
|
||||
{case4Function, map[string][]string{}, true},
|
||||
{case4Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, false},
|
||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
||||
{case5Function, map[string][]string{}, true},
|
||||
{case5Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, true},
|
||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
||||
{case6Function, map[string][]string{}, true},
|
||||
{case6Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, false},
|
||||
{case7Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
||||
{case7Function, map[string][]string{}, true},
|
||||
{case7Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
|
||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, true},
|
||||
{case8Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
||||
{case8Function, map[string][]string{}, true},
|
||||
{case8Function, map[string][]string{"delimiter": {"/"}}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.evaluate(testCase.values)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotLikeFuncKey(t *testing.T) {
|
||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
function Function
|
||||
expectedResult Key
|
||||
}{
|
||||
{case1Function, S3XAmzCopySource},
|
||||
{case2Function, S3XAmzServerSideEncryption},
|
||||
{case3Function, S3XAmzMetadataDirective},
|
||||
{case4Function, S3LocationConstraint},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.function.key()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringNotLikeFuncToMap(t *testing.T) {
|
||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case1Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/*")),
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotLikeFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Result := map[Key]ValueSet{
|
||||
S3XAmzCopySource: NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
),
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES*")),
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Result := map[Key]ValueSet{
|
||||
S3XAmzServerSideEncryption: NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
),
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPL*")),
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Result := map[Key]ValueSet{
|
||||
S3XAmzMetadataDirective: NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
),
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-*")),
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotLikeFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Result := map[Key]ValueSet{
|
||||
S3LocationConstraint: NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
f Function
|
||||
expectedResult map[Key]ValueSet
|
||||
}{
|
||||
{case1Function, case1Result},
|
||||
{case2Function, case2Result},
|
||||
{case3Function, case3Result},
|
||||
{case4Function, case4Result},
|
||||
{case5Function, case5Result},
|
||||
{case6Function, case6Result},
|
||||
{case7Function, case7Result},
|
||||
{case8Function, case8Result},
|
||||
{&stringNotLikeFunc{}, nil},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.f.toMap()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStringLikeFunc(t *testing.T) {
|
||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringLikeFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringLikeFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringLikeFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newStringLikeFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStringNotLikeFunc(t *testing.T) {
|
||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case2Function, err := newStringNotLikeFunc(S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case5Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case6Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case7Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case8Function, err := newStringNotLikeFunc(S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
key Key
|
||||
values ValueSet
|
||||
expectedResult Function
|
||||
expectErr bool
|
||||
}{
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")), case1Function, false},
|
||||
{S3XAmzCopySource,
|
||||
NewValueSet(
|
||||
NewStringValue("mybucket/*"),
|
||||
NewStringValue("yourbucket/myobject*"),
|
||||
), case2Function, false},
|
||||
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")), case3Function, false},
|
||||
{S3XAmzServerSideEncryption,
|
||||
NewValueSet(
|
||||
NewStringValue("AES*"),
|
||||
), case4Function, false},
|
||||
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")), case5Function, false},
|
||||
{S3XAmzMetadataDirective,
|
||||
NewValueSet(
|
||||
NewStringValue("REPL*"),
|
||||
NewStringValue("COPY*"),
|
||||
), case6Function, false},
|
||||
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")), case7Function, false},
|
||||
{S3LocationConstraint,
|
||||
NewValueSet(
|
||||
NewStringValue("eu-west-*"),
|
||||
NewStringValue("us-west-*"),
|
||||
), case8Function, false},
|
||||
|
||||
// Unsupported value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
||||
|
||||
// Invalid value error.
|
||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := newStringNotLikeFunc(testCase.key, testCase.values)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Splits an incoming path into bucket and object components.
|
||||
func path2BucketAndObject(path string) (bucket, object string) {
|
||||
// Skip the first element if it is '/', split the rest.
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
pathComponents := strings.SplitN(path, "/", 2)
|
||||
|
||||
// Save the bucket and object extracted from path.
|
||||
switch len(pathComponents) {
|
||||
case 1:
|
||||
bucket = pathComponents[0]
|
||||
case 2:
|
||||
bucket = pathComponents[0]
|
||||
object = pathComponents[1]
|
||||
}
|
||||
return bucket, object
|
||||
}
|
||||
|
||||
// Value - is enum type of string, int or bool.
|
||||
type Value struct {
|
||||
t reflect.Kind
|
||||
s string
|
||||
i int
|
||||
b bool
|
||||
}
|
||||
|
||||
// GetBool - gets stored bool value.
|
||||
func (v Value) GetBool() (bool, error) {
|
||||
var err error
|
||||
|
||||
if v.t != reflect.Bool {
|
||||
err = fmt.Errorf("not a bool Value")
|
||||
}
|
||||
|
||||
return v.b, err
|
||||
}
|
||||
|
||||
// GetInt - gets stored int value.
|
||||
func (v Value) GetInt() (int, error) {
|
||||
var err error
|
||||
|
||||
if v.t != reflect.Int {
|
||||
err = fmt.Errorf("not a int Value")
|
||||
}
|
||||
|
||||
return v.i, err
|
||||
}
|
||||
|
||||
// GetString - gets stored string value.
|
||||
func (v Value) GetString() (string, error) {
|
||||
var err error
|
||||
|
||||
if v.t != reflect.String {
|
||||
err = fmt.Errorf("not a string Value")
|
||||
}
|
||||
|
||||
return v.s, err
|
||||
}
|
||||
|
||||
// GetType - gets enum type.
|
||||
func (v Value) GetType() reflect.Kind {
|
||||
return v.t
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes Value to JSON data.
|
||||
func (v Value) MarshalJSON() ([]byte, error) {
|
||||
switch v.t {
|
||||
case reflect.String:
|
||||
return json.Marshal(v.s)
|
||||
case reflect.Int:
|
||||
return json.Marshal(v.i)
|
||||
case reflect.Bool:
|
||||
return json.Marshal(v.b)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown value kind %v", v.t)
|
||||
}
|
||||
|
||||
// StoreBool - stores bool value.
|
||||
func (v *Value) StoreBool(b bool) {
|
||||
*v = Value{t: reflect.Bool, b: b}
|
||||
}
|
||||
|
||||
// StoreInt - stores int value.
|
||||
func (v *Value) StoreInt(i int) {
|
||||
*v = Value{t: reflect.Int, i: i}
|
||||
}
|
||||
|
||||
// StoreString - stores string value.
|
||||
func (v *Value) StoreString(s string) {
|
||||
*v = Value{t: reflect.String, s: s}
|
||||
}
|
||||
|
||||
// String - returns string representation of value.
|
||||
func (v Value) String() string {
|
||||
switch v.t {
|
||||
case reflect.String:
|
||||
return v.s
|
||||
case reflect.Int:
|
||||
return strconv.Itoa(v.i)
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(v.b)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data.
|
||||
func (v *Value) UnmarshalJSON(data []byte) error {
|
||||
var b bool
|
||||
if err := json.Unmarshal(data, &b); err == nil {
|
||||
v.StoreBool(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
var i int
|
||||
if err := json.Unmarshal(data, &i); err == nil {
|
||||
v.StoreInt(i)
|
||||
return nil
|
||||
}
|
||||
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
v.StoreString(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown json data '%v'", data)
|
||||
}
|
||||
|
||||
// NewBoolValue - returns new bool value.
|
||||
func NewBoolValue(b bool) Value {
|
||||
value := &Value{}
|
||||
value.StoreBool(b)
|
||||
return *value
|
||||
}
|
||||
|
||||
// NewIntValue - returns new int value.
|
||||
func NewIntValue(i int) Value {
|
||||
value := &Value{}
|
||||
value.StoreInt(i)
|
||||
return *value
|
||||
}
|
||||
|
||||
// NewStringValue - returns new string value.
|
||||
func NewStringValue(s string) Value {
|
||||
value := &Value{}
|
||||
value.StoreString(s)
|
||||
return *value
|
||||
}
|
@ -1,261 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValueGetBool(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult bool
|
||||
expectErr bool
|
||||
}{
|
||||
{NewBoolValue(true), true, false},
|
||||
{NewIntValue(7), false, true},
|
||||
{Value{}, false, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := testCase.value.GetBool()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueGetInt(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult int
|
||||
expectErr bool
|
||||
}{
|
||||
{NewIntValue(7), 7, false},
|
||||
{NewBoolValue(true), 0, true},
|
||||
{Value{}, 0, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := testCase.value.GetInt()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueGetString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult string
|
||||
expectErr bool
|
||||
}{
|
||||
{NewStringValue("foo"), "foo", false},
|
||||
{NewBoolValue(true), "", true},
|
||||
{Value{}, "", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := testCase.value.GetString()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueGetType(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult reflect.Kind
|
||||
}{
|
||||
{NewBoolValue(true), reflect.Bool},
|
||||
{NewIntValue(7), reflect.Int},
|
||||
{NewStringValue("foo"), reflect.String},
|
||||
{Value{}, reflect.Invalid},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.value.GetType()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewBoolValue(true), []byte("true"), false},
|
||||
{NewIntValue(7), []byte("7"), false},
|
||||
{NewStringValue("foo"), []byte(`"foo"`), false},
|
||||
{Value{}, nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.value)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueStoreBool(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value bool
|
||||
expectedResult Value
|
||||
}{
|
||||
{false, NewBoolValue(false)},
|
||||
{true, NewBoolValue(true)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Value
|
||||
result.StoreBool(testCase.value)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueStoreInt(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value int
|
||||
expectedResult Value
|
||||
}{
|
||||
{0, NewIntValue(0)},
|
||||
{7, NewIntValue(7)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Value
|
||||
result.StoreInt(testCase.value)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueStoreString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value string
|
||||
expectedResult Value
|
||||
}{
|
||||
{"", NewStringValue("")},
|
||||
{"foo", NewStringValue("foo")},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Value
|
||||
result.StoreString(testCase.value)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult string
|
||||
}{
|
||||
{NewBoolValue(true), "true"},
|
||||
{NewIntValue(7), "7"},
|
||||
{NewStringValue("foo"), "foo"},
|
||||
{Value{}, ""},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.value.String()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult Value
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte("true"), NewBoolValue(true), false},
|
||||
{[]byte("7"), NewIntValue(7), false},
|
||||
{[]byte(`"foo"`), NewStringValue("foo"), false},
|
||||
{[]byte("True"), Value{}, true},
|
||||
{[]byte("7.1"), Value{}, true},
|
||||
{[]byte(`["foo"]`), Value{}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Value
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// ValueSet - unique list of values.
|
||||
type ValueSet map[Value]struct{}
|
||||
|
||||
// Add - adds given value to value set.
|
||||
func (set ValueSet) Add(value Value) {
|
||||
set[value] = struct{}{}
|
||||
}
|
||||
|
||||
// ToSlice converts ValueSet to a slice of Value
|
||||
func (set ValueSet) ToSlice() []Value {
|
||||
var values []Value
|
||||
for k := range set {
|
||||
values = append(values, k)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes ValueSet to JSON data.
|
||||
func (set ValueSet) MarshalJSON() ([]byte, error) {
|
||||
var values []Value
|
||||
for k := range set {
|
||||
values = append(values, k)
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return nil, fmt.Errorf("invalid value set %v", set)
|
||||
}
|
||||
|
||||
return json.Marshal(values)
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data.
|
||||
func (set *ValueSet) UnmarshalJSON(data []byte) error {
|
||||
var v Value
|
||||
if err := json.Unmarshal(data, &v); err == nil {
|
||||
*set = make(ValueSet)
|
||||
set.Add(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
var values []Value
|
||||
if err := json.Unmarshal(data, &values); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(values) < 1 {
|
||||
return fmt.Errorf("invalid value")
|
||||
}
|
||||
|
||||
*set = make(ValueSet)
|
||||
for _, v = range values {
|
||||
if _, found := (*set)[v]; found {
|
||||
return fmt.Errorf("duplicate value found '%v'", v)
|
||||
}
|
||||
|
||||
set.Add(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone clones ValueSet structure
|
||||
func (set ValueSet) Clone() ValueSet {
|
||||
return NewValueSet(set.ToSlice()...)
|
||||
}
|
||||
|
||||
// NewValueSet - returns new value set containing given values.
|
||||
func NewValueSet(values ...Value) ValueSet {
|
||||
set := make(ValueSet)
|
||||
|
||||
for _, value := range values {
|
||||
set.Add(value)
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
// 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValueSetAdd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value Value
|
||||
expectedResult ValueSet
|
||||
}{
|
||||
{NewBoolValue(true), NewValueSet(NewBoolValue(true))},
|
||||
{NewIntValue(7), NewValueSet(NewIntValue(7))},
|
||||
{NewStringValue("foo"), NewValueSet(NewStringValue("foo"))},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := NewValueSet()
|
||||
result.Add(testCase.value)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueSetMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ValueSet
|
||||
expectedResult string
|
||||
expectErr bool
|
||||
}{
|
||||
{NewValueSet(NewBoolValue(true)), `[true]`, false},
|
||||
{NewValueSet(NewIntValue(7)), `[7]`, false},
|
||||
{NewValueSet(NewStringValue("foo")), `["foo"]`, false},
|
||||
{NewValueSet(NewBoolValue(true)), `[true]`, false},
|
||||
{NewValueSet(NewStringValue("7")), `["7"]`, false},
|
||||
{NewValueSet(NewStringValue("foo")), `["foo"]`, false},
|
||||
{make(ValueSet), "", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.set)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if string(result) != testCase.expectedResult {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueSetUnmarshalJSON(t *testing.T) {
|
||||
set1 := NewValueSet(
|
||||
NewBoolValue(true),
|
||||
NewStringValue("false"),
|
||||
NewIntValue(7),
|
||||
NewStringValue("7"),
|
||||
NewStringValue("foo"),
|
||||
NewStringValue("192.168.1.100/24"),
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult ValueSet
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`true`), NewValueSet(NewBoolValue(true)), false},
|
||||
{[]byte(`7`), NewValueSet(NewIntValue(7)), false},
|
||||
{[]byte(`"foo"`), NewValueSet(NewStringValue("foo")), false},
|
||||
{[]byte(`[true]`), NewValueSet(NewBoolValue(true)), false},
|
||||
{[]byte(`[7]`), NewValueSet(NewIntValue(7)), false},
|
||||
{[]byte(`["foo"]`), NewValueSet(NewStringValue("foo")), false},
|
||||
{[]byte(`[true, "false", 7, "7", "foo", "192.168.1.100/24"]`), set1, false},
|
||||
{[]byte(`{}`), nil, true}, // Unsupported data.
|
||||
{[]byte(`[]`), nil, true}, // Empty array.
|
||||
{[]byte(`[7, 7, true]`), nil, true}, // Duplicate value.
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := make(ValueSet)
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
// Effect - policy statement effect Allow or Deny.
|
||||
type Effect string
|
||||
|
||||
const (
|
||||
// Allow - allow effect.
|
||||
Allow Effect = "Allow"
|
||||
|
||||
// Deny - deny effect.
|
||||
Deny = "Deny"
|
||||
)
|
||||
|
||||
// IsAllowed - returns if given check is allowed or not.
|
||||
func (effect Effect) IsAllowed(b bool) bool {
|
||||
if effect == Allow {
|
||||
return b
|
||||
}
|
||||
|
||||
return !b
|
||||
}
|
||||
|
||||
// IsValid - checks if Effect is valid or not
|
||||
func (effect Effect) IsValid() bool {
|
||||
switch effect {
|
||||
case Allow, Deny:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEffectIsAllowed(t *testing.T) {
|
||||
testCases := []struct {
|
||||
effect Effect
|
||||
check bool
|
||||
expectedResult bool
|
||||
}{
|
||||
{Allow, false, false},
|
||||
{Allow, true, true},
|
||||
{Deny, false, true},
|
||||
{Deny, true, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.effect.IsAllowed(testCase.check)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEffectIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
effect Effect
|
||||
expectedResult bool
|
||||
}{
|
||||
{Allow, true},
|
||||
{Deny, true},
|
||||
{Effect(""), false},
|
||||
{Effect("foo"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.effect.IsValid()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Error is the generic type for any error happening during policy
|
||||
// parsing.
|
||||
type Error struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// Errorf - formats according to a format specifier and returns
|
||||
// the string as a value that satisfies error of type policy.Error
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
return Error{err: fmt.Errorf(format, a...)}
|
||||
}
|
||||
|
||||
// Unwrap the internal error.
|
||||
func (e Error) Unwrap() error { return e.err }
|
||||
|
||||
// Error 'error' compatible method.
|
||||
func (e Error) Error() string {
|
||||
if e.err == nil {
|
||||
return "policy: cause <nil>"
|
||||
}
|
||||
return e.err.Error()
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ID - policy ID.
|
||||
type ID string
|
||||
|
||||
// IsValid - checks if ID is valid or not.
|
||||
func (id ID) IsValid() bool {
|
||||
return utf8.ValidString(string(id))
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIDIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
id ID
|
||||
expectedResult bool
|
||||
}{
|
||||
{ID("DenyEncryptionSt1"), true},
|
||||
{ID(""), true},
|
||||
{ID("aa\xe2"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.id.IsValid()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Errorf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
// DefaultVersion - default policy version as per AWS S3 specification.
|
||||
const DefaultVersion = "2012-10-17"
|
||||
|
||||
// Args - arguments to policy to check whether it is allowed
|
||||
type Args struct {
|
||||
AccountName string `json:"account"`
|
||||
Groups []string `json:"groups"`
|
||||
Action Action `json:"action"`
|
||||
BucketName string `json:"bucket"`
|
||||
ConditionValues map[string][]string `json:"conditions"`
|
||||
IsOwner bool `json:"owner"`
|
||||
ObjectName string `json:"object"`
|
||||
}
|
||||
|
||||
// Policy - bucket policy.
|
||||
type Policy struct {
|
||||
ID ID `json:"ID,omitempty"`
|
||||
Version string
|
||||
Statements []Statement `json:"Statement"`
|
||||
}
|
||||
|
||||
// IsAllowed - checks given policy args is allowed to continue the Rest API.
|
||||
func (policy Policy) IsAllowed(args Args) bool {
|
||||
// Check all deny statements. If any one statement denies, return false.
|
||||
for _, statement := range policy.Statements {
|
||||
if statement.Effect == Deny {
|
||||
if !statement.IsAllowed(args) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For owner, its allowed by default.
|
||||
if args.IsOwner {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check all allow statements. If any one statement allows, return true.
|
||||
for _, statement := range policy.Statements {
|
||||
if statement.Effect == Allow {
|
||||
if statement.IsAllowed(args) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEmpty - returns whether policy is empty or not.
|
||||
func (policy Policy) IsEmpty() bool {
|
||||
return len(policy.Statements) == 0
|
||||
}
|
||||
|
||||
// isValid - checks if Policy is valid or not.
|
||||
func (policy Policy) isValid() error {
|
||||
if policy.Version != DefaultVersion && policy.Version != "" {
|
||||
return Errorf("invalid version '%v'", policy.Version)
|
||||
}
|
||||
|
||||
for _, statement := range policy.Statements {
|
||||
if err := statement.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes Policy to JSON data.
|
||||
func (policy Policy) MarshalJSON() ([]byte, error) {
|
||||
if err := policy.isValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// subtype to avoid recursive call to MarshalJSON()
|
||||
type subPolicy Policy
|
||||
return json.Marshal(subPolicy(policy))
|
||||
}
|
||||
|
||||
// Merge merges two policies documents and drop
|
||||
// duplicate statements if any.
|
||||
func (policy Policy) Merge(input Policy) Policy {
|
||||
var mergedPolicy Policy
|
||||
if policy.Version != "" {
|
||||
mergedPolicy.Version = policy.Version
|
||||
} else {
|
||||
mergedPolicy.Version = input.Version
|
||||
}
|
||||
for _, st := range policy.Statements {
|
||||
mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone())
|
||||
}
|
||||
for _, st := range input.Statements {
|
||||
mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone())
|
||||
}
|
||||
mergedPolicy.dropDuplicateStatements()
|
||||
return mergedPolicy
|
||||
}
|
||||
|
||||
func (policy *Policy) dropDuplicateStatements() {
|
||||
redo:
|
||||
for i := range policy.Statements {
|
||||
for j, statement := range policy.Statements[i+1:] {
|
||||
if !policy.Statements[i].Equals(statement) {
|
||||
continue
|
||||
}
|
||||
policy.Statements = append(policy.Statements[:j], policy.Statements[j+1:]...)
|
||||
goto redo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to Policy.
|
||||
func (policy *Policy) UnmarshalJSON(data []byte) error {
|
||||
// subtype to avoid recursive call to UnmarshalJSON()
|
||||
type subPolicy Policy
|
||||
var sp subPolicy
|
||||
if err := json.Unmarshal(data, &sp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := Policy(sp)
|
||||
if err := p.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.dropDuplicateStatements()
|
||||
|
||||
*policy = p
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate - validates all statements are for given bucket or not.
|
||||
func (policy Policy) Validate(bucketName string) error {
|
||||
if err := policy.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, statement := range policy.Statements {
|
||||
if err := statement.Validate(bucketName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseConfig - parses data in given reader to Policy.
|
||||
func ParseConfig(reader io.Reader, bucketName string) (*Policy, error) {
|
||||
var policy Policy
|
||||
|
||||
decoder := json.NewDecoder(reader)
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&policy); err != nil {
|
||||
return nil, Errorf("%w", err)
|
||||
}
|
||||
|
||||
err := policy.Validate(bucketName)
|
||||
return &policy, err
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,103 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
// Principal - policy principal.
|
||||
type Principal struct {
|
||||
AWS set.StringSet
|
||||
}
|
||||
|
||||
// IsValid - checks whether Principal is valid or not.
|
||||
func (p Principal) IsValid() bool {
|
||||
return len(p.AWS) != 0
|
||||
}
|
||||
|
||||
// Equals - returns true if principals are equal.
|
||||
func (p Principal) Equals(pp Principal) bool {
|
||||
return p.AWS.Equals(pp.AWS)
|
||||
}
|
||||
|
||||
// Intersection - returns principals available in both Principal.
|
||||
func (p Principal) Intersection(principal Principal) set.StringSet {
|
||||
return p.AWS.Intersection(principal.AWS)
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes Principal to JSON data.
|
||||
func (p Principal) MarshalJSON() ([]byte, error) {
|
||||
if !p.IsValid() {
|
||||
return nil, Errorf("invalid principal %v", p)
|
||||
}
|
||||
|
||||
// subtype to avoid recursive call to MarshalJSON()
|
||||
type subPrincipal Principal
|
||||
sp := subPrincipal(p)
|
||||
return json.Marshal(sp)
|
||||
}
|
||||
|
||||
// Match - matches given principal is wildcard matching with Principal.
|
||||
func (p Principal) Match(principal string) bool {
|
||||
for _, pattern := range p.AWS.ToSlice() {
|
||||
if wildcard.MatchSimple(pattern, principal) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to Principal.
|
||||
func (p *Principal) UnmarshalJSON(data []byte) error {
|
||||
// subtype to avoid recursive call to UnmarshalJSON()
|
||||
type subPrincipal Principal
|
||||
var sp subPrincipal
|
||||
|
||||
if err := json.Unmarshal(data, &sp); err != nil {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s != "*" {
|
||||
return Errorf("invalid principal '%v'", s)
|
||||
}
|
||||
|
||||
sp.AWS = set.CreateStringSet("*")
|
||||
}
|
||||
|
||||
*p = Principal(sp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone clones Principal structure
|
||||
func (p Principal) Clone() Principal {
|
||||
return NewPrincipal(p.AWS.ToSlice()...)
|
||||
|
||||
}
|
||||
|
||||
// NewPrincipal - creates new Principal.
|
||||
func NewPrincipal(principals ...string) Principal {
|
||||
return Principal{AWS: set.CreateStringSet(principals...)}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
func TestPrincipalIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
principal Principal
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewPrincipal("*"), true},
|
||||
{NewPrincipal("arn:aws:iam::AccountNumber:root"), true},
|
||||
{NewPrincipal(), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.principal.IsValid()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrincipalIntersection(t *testing.T) {
|
||||
testCases := []struct {
|
||||
principal Principal
|
||||
principalToIntersect Principal
|
||||
expectedResult set.StringSet
|
||||
}{
|
||||
{NewPrincipal("*"), NewPrincipal("*"), set.CreateStringSet("*")},
|
||||
{NewPrincipal("arn:aws:iam::AccountNumber:root"), NewPrincipal("arn:aws:iam::AccountNumber:myuser"), set.CreateStringSet()},
|
||||
{NewPrincipal(), NewPrincipal("*"), set.CreateStringSet()},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.principal.Intersection(testCase.principalToIntersect)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrincipalMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
principal Principal
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewPrincipal("*"), []byte(`{"AWS":["*"]}`), false},
|
||||
{NewPrincipal("arn:aws:iam::AccountNumber:*"), []byte(`{"AWS":["arn:aws:iam::AccountNumber:*"]}`), false},
|
||||
{NewPrincipal(), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.principal)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrincipalMatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
principals Principal
|
||||
principal string
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewPrincipal("*"), "AccountNumber", true},
|
||||
{NewPrincipal("arn:aws:iam::*"), "arn:aws:iam::AccountNumber:root", true},
|
||||
{NewPrincipal("arn:aws:iam::AccountNumber:*"), "arn:aws:iam::TestAccountNumber:root", false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.principals.Match(testCase.principal)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrincipalUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult Principal
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"*"`), NewPrincipal("*"), false},
|
||||
{[]byte(`{"AWS": "*"}`), NewPrincipal("*"), false},
|
||||
{[]byte(`{"AWS": "arn:aws:iam::AccountNumber:*"}`), NewPrincipal("arn:aws:iam::AccountNumber:*"), false},
|
||||
{[]byte(`"arn:aws:iam::AccountNumber:*"`), NewPrincipal(), true},
|
||||
{[]byte(`["arn:aws:iam::AccountNumber:*", "arn:aws:iam:AnotherAccount:*"]`), NewPrincipal(), true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Principal
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
// ResourceARNPrefix - resource ARN prefix as per AWS S3 specification.
|
||||
const ResourceARNPrefix = "arn:aws:s3:::"
|
||||
|
||||
// Resource - resource in policy statement.
|
||||
type Resource struct {
|
||||
BucketName string
|
||||
Pattern string
|
||||
}
|
||||
|
||||
func (r Resource) isBucketPattern() bool {
|
||||
return !strings.Contains(r.Pattern, "/")
|
||||
}
|
||||
|
||||
func (r Resource) isObjectPattern() bool {
|
||||
return strings.Contains(r.Pattern, "/") || strings.Contains(r.BucketName, "*")
|
||||
}
|
||||
|
||||
// IsValid - checks whether Resource is valid or not.
|
||||
func (r Resource) IsValid() bool {
|
||||
return r.BucketName != "" && r.Pattern != ""
|
||||
}
|
||||
|
||||
// Match - matches object name with resource pattern.
|
||||
func (r Resource) Match(resource string, conditionValues map[string][]string) bool {
|
||||
pattern := r.Pattern
|
||||
for _, key := range condition.CommonKeys {
|
||||
// Empty values are not supported for policy variables.
|
||||
if rvalues, ok := conditionValues[key.Name()]; ok && rvalues[0] != "" {
|
||||
pattern = strings.Replace(pattern, key.VarName(), rvalues[0], -1)
|
||||
}
|
||||
}
|
||||
|
||||
return wildcard.Match(pattern, resource)
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes Resource to JSON data.
|
||||
func (r Resource) MarshalJSON() ([]byte, error) {
|
||||
if !r.IsValid() {
|
||||
return nil, Errorf("invalid resource %v", r)
|
||||
}
|
||||
|
||||
return json.Marshal(r.String())
|
||||
}
|
||||
|
||||
func (r Resource) String() string {
|
||||
return ResourceARNPrefix + r.Pattern
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to Resource.
|
||||
func (r *Resource) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parsedResource, err := parseResource(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*r = parsedResource
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate - validates Resource is for given bucket or not.
|
||||
func (r Resource) Validate(bucketName string) error {
|
||||
if !r.IsValid() {
|
||||
return Errorf("invalid resource")
|
||||
}
|
||||
|
||||
if !wildcard.Match(r.BucketName, bucketName) {
|
||||
return Errorf("bucket name does not match")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseResource - parses string to Resource.
|
||||
func parseResource(s string) (Resource, error) {
|
||||
if !strings.HasPrefix(s, ResourceARNPrefix) {
|
||||
return Resource{}, Errorf("invalid resource '%v'", s)
|
||||
}
|
||||
|
||||
pattern := strings.TrimPrefix(s, ResourceARNPrefix)
|
||||
tokens := strings.SplitN(pattern, "/", 2)
|
||||
bucketName := tokens[0]
|
||||
if bucketName == "" {
|
||||
return Resource{}, Errorf("invalid resource format '%v'", s)
|
||||
}
|
||||
|
||||
return Resource{
|
||||
BucketName: bucketName,
|
||||
Pattern: pattern,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewResource - creates new resource.
|
||||
func NewResource(bucketName, keyName string) Resource {
|
||||
pattern := bucketName
|
||||
if keyName != "" {
|
||||
if !strings.HasPrefix(keyName, "/") {
|
||||
pattern += "/"
|
||||
}
|
||||
|
||||
pattern += keyName
|
||||
}
|
||||
|
||||
return Resource{
|
||||
BucketName: bucketName,
|
||||
Pattern: pattern,
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResourceIsBucketPattern(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource Resource
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResource("*", ""), true},
|
||||
{NewResource("mybucket", ""), true},
|
||||
{NewResource("mybucket*", ""), true},
|
||||
{NewResource("mybucket?0", ""), true},
|
||||
{NewResource("", "*"), false},
|
||||
{NewResource("*", "*"), false},
|
||||
{NewResource("mybucket", "*"), false},
|
||||
{NewResource("mybucket*", "/myobject"), false},
|
||||
{NewResource("mybucket?0", "/2010/photos/*"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resource.isBucketPattern()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceIsObjectPattern(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource Resource
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResource("*", ""), true},
|
||||
{NewResource("mybucket*", ""), true},
|
||||
{NewResource("", "*"), true},
|
||||
{NewResource("*", "*"), true},
|
||||
{NewResource("mybucket", "*"), true},
|
||||
{NewResource("mybucket*", "/myobject"), true},
|
||||
{NewResource("mybucket?0", "/2010/photos/*"), true},
|
||||
{NewResource("mybucket", ""), false},
|
||||
{NewResource("mybucket?0", ""), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resource.isObjectPattern()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource Resource
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResource("*", ""), true},
|
||||
{NewResource("mybucket*", ""), true},
|
||||
{NewResource("*", "*"), true},
|
||||
{NewResource("mybucket", "*"), true},
|
||||
{NewResource("mybucket*", "/myobject"), true},
|
||||
{NewResource("mybucket?0", "/2010/photos/*"), true},
|
||||
{NewResource("mybucket", ""), true},
|
||||
{NewResource("mybucket?0", ""), true},
|
||||
{NewResource("", ""), false},
|
||||
{NewResource("", "*"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resource.IsValid()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceMatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource Resource
|
||||
objectName string
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResource("*", ""), "mybucket", true},
|
||||
{NewResource("*", ""), "mybucket/myobject", true},
|
||||
{NewResource("mybucket*", ""), "mybucket", true},
|
||||
{NewResource("mybucket*", ""), "mybucket/myobject", true},
|
||||
{NewResource("", "*"), "/myobject", true},
|
||||
{NewResource("*", "*"), "mybucket/myobject", true},
|
||||
{NewResource("mybucket", "*"), "mybucket/myobject", true},
|
||||
{NewResource("mybucket*", "/myobject"), "mybucket/myobject", true},
|
||||
{NewResource("mybucket*", "/myobject"), "mybucket100/myobject", true},
|
||||
{NewResource("mybucket?0", "/2010/photos/*"), "mybucket20/2010/photos/1.jpg", true},
|
||||
{NewResource("mybucket", ""), "mybucket", true},
|
||||
{NewResource("mybucket?0", ""), "mybucket30", true},
|
||||
{NewResource("", "*"), "mybucket/myobject", false},
|
||||
{NewResource("*", "*"), "mybucket", false},
|
||||
{NewResource("mybucket", "*"), "mybucket10/myobject", false},
|
||||
{NewResource("mybucket?0", "/2010/photos/*"), "mybucket0/2010/photos/1.jpg", false},
|
||||
{NewResource("mybucket", ""), "mybucket/myobject", false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resource.Match(testCase.objectName, nil)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource Resource
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewResource("*", ""), []byte(`"arn:aws:s3:::*"`), false},
|
||||
{NewResource("mybucket*", ""), []byte(`"arn:aws:s3:::mybucket*"`), false},
|
||||
{NewResource("mybucket", ""), []byte(`"arn:aws:s3:::mybucket"`), false},
|
||||
{NewResource("*", "*"), []byte(`"arn:aws:s3:::*/*"`), false},
|
||||
{NewResource("mybucket", "*"), []byte(`"arn:aws:s3:::mybucket/*"`), false},
|
||||
{NewResource("mybucket*", "myobject"), []byte(`"arn:aws:s3:::mybucket*/myobject"`), false},
|
||||
{NewResource("mybucket?0", "/2010/photos/*"), []byte(`"arn:aws:s3:::mybucket?0/2010/photos/*"`), false},
|
||||
{Resource{}, nil, true},
|
||||
{NewResource("", "*"), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.resource)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult Resource
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"arn:aws:s3:::*"`), NewResource("*", ""), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket*"`), NewResource("mybucket*", ""), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket"`), NewResource("mybucket", ""), false},
|
||||
{[]byte(`"arn:aws:s3:::*/*"`), NewResource("*", "*"), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket/*"`), NewResource("mybucket", "*"), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket*/myobject"`), NewResource("mybucket*", "myobject"), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket?0/2010/photos/*"`), NewResource("mybucket?0", "/2010/photos/*"), false},
|
||||
{[]byte(`"mybucket/myobject*"`), Resource{}, true},
|
||||
{[]byte(`"arn:aws:s3:::/*"`), Resource{}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Resource
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource Resource
|
||||
bucketName string
|
||||
expectErr bool
|
||||
}{
|
||||
{NewResource("mybucket", "/myobject*"), "mybucket", false},
|
||||
{NewResource("", "/myobject*"), "yourbucket", true},
|
||||
{NewResource("mybucket", "/myobject*"), "yourbucket", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.resource.Validate(testCase.bucketName)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
// ResourceSet - set of resources in policy statement.
|
||||
type ResourceSet map[Resource]struct{}
|
||||
|
||||
// bucketResourceExists - checks if at least one bucket resource exists in the set.
|
||||
func (resourceSet ResourceSet) bucketResourceExists() bool {
|
||||
for resource := range resourceSet {
|
||||
if resource.isBucketPattern() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// objectResourceExists - checks if at least one object resource exists in the set.
|
||||
func (resourceSet ResourceSet) objectResourceExists() bool {
|
||||
for resource := range resourceSet {
|
||||
if resource.isObjectPattern() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Add - adds resource to resource set.
|
||||
func (resourceSet ResourceSet) Add(resource Resource) {
|
||||
resourceSet[resource] = struct{}{}
|
||||
}
|
||||
|
||||
// Equals - checks whether given resource set is equal to current resource set or not.
|
||||
func (resourceSet ResourceSet) Equals(sresourceSet ResourceSet) bool {
|
||||
// If length of set is not equal to length of given set, the
|
||||
// set is not equal to given set.
|
||||
if len(resourceSet) != len(sresourceSet) {
|
||||
return false
|
||||
}
|
||||
|
||||
// As both sets are equal in length, check each elements are equal.
|
||||
for k := range resourceSet {
|
||||
if _, ok := sresourceSet[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersection - returns resouces available in both ResourcsSet.
|
||||
func (resourceSet ResourceSet) Intersection(sset ResourceSet) ResourceSet {
|
||||
nset := NewResourceSet()
|
||||
for k := range resourceSet {
|
||||
if _, ok := sset[k]; ok {
|
||||
nset.Add(k)
|
||||
}
|
||||
}
|
||||
|
||||
return nset
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes ResourceSet to JSON data.
|
||||
func (resourceSet ResourceSet) MarshalJSON() ([]byte, error) {
|
||||
if len(resourceSet) == 0 {
|
||||
return nil, Errorf("empty resources not allowed")
|
||||
}
|
||||
|
||||
resources := []Resource{}
|
||||
for resource := range resourceSet {
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
|
||||
return json.Marshal(resources)
|
||||
}
|
||||
|
||||
// Match - matches object name with anyone of resource pattern in resource set.
|
||||
func (resourceSet ResourceSet) Match(resource string, conditionValues map[string][]string) bool {
|
||||
for r := range resourceSet {
|
||||
if r.Match(resource, conditionValues) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (resourceSet ResourceSet) String() string {
|
||||
resources := []string{}
|
||||
for resource := range resourceSet {
|
||||
resources = append(resources, resource.String())
|
||||
}
|
||||
sort.Strings(resources)
|
||||
|
||||
return fmt.Sprintf("%v", resources)
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to ResourceSet.
|
||||
func (resourceSet *ResourceSet) UnmarshalJSON(data []byte) error {
|
||||
var sset set.StringSet
|
||||
if err := json.Unmarshal(data, &sset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*resourceSet = make(ResourceSet)
|
||||
for _, s := range sset.ToSlice() {
|
||||
resource, err := parseResource(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, found := (*resourceSet)[resource]; found {
|
||||
return Errorf("duplicate resource '%v' found", s)
|
||||
}
|
||||
|
||||
resourceSet.Add(resource)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate - validates ResourceSet is for given bucket or not.
|
||||
func (resourceSet ResourceSet) Validate(bucketName string) error {
|
||||
for resource := range resourceSet {
|
||||
if err := resource.Validate(bucketName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToSlice - returns slice of resources from the resource set.
|
||||
func (resourceSet ResourceSet) ToSlice() []Resource {
|
||||
resources := []Resource{}
|
||||
for resource := range resourceSet {
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
// Clone clones ResourceSet structure
|
||||
func (resourceSet ResourceSet) Clone() ResourceSet {
|
||||
return NewResourceSet(resourceSet.ToSlice()...)
|
||||
}
|
||||
|
||||
// NewResourceSet - creates new resource set.
|
||||
func NewResourceSet(resources ...Resource) ResourceSet {
|
||||
resourceSet := make(ResourceSet)
|
||||
for _, resource := range resources {
|
||||
resourceSet.Add(resource)
|
||||
}
|
||||
|
||||
return resourceSet
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResourceSetBucketResourceExists(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceSet ResourceSet
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResourceSet(NewResource("*", "")), true},
|
||||
{NewResourceSet(NewResource("mybucket", "")), true},
|
||||
{NewResourceSet(NewResource("mybucket*", "")), true},
|
||||
{NewResourceSet(NewResource("mybucket?0", "")), true},
|
||||
{NewResourceSet(NewResource("mybucket", "/2010/photos/*"), NewResource("mybucket", "")), true},
|
||||
{NewResourceSet(NewResource("", "*")), false},
|
||||
{NewResourceSet(NewResource("*", "*")), false},
|
||||
{NewResourceSet(NewResource("mybucket", "*")), false},
|
||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), false},
|
||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*")), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resourceSet.bucketResourceExists()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetObjectResourceExists(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceSet ResourceSet
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResourceSet(NewResource("*", "")), true},
|
||||
{NewResourceSet(NewResource("mybucket*", "")), true},
|
||||
{NewResourceSet(NewResource("", "*")), true},
|
||||
{NewResourceSet(NewResource("*", "*")), true},
|
||||
{NewResourceSet(NewResource("mybucket", "*")), true},
|
||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), true},
|
||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*")), true},
|
||||
{NewResourceSet(NewResource("mybucket", ""), NewResource("mybucket", "/2910/photos/*")), true},
|
||||
{NewResourceSet(NewResource("mybucket", "")), false},
|
||||
{NewResourceSet(NewResource("mybucket?0", "")), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resourceSet.objectResourceExists()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetAdd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceSet ResourceSet
|
||||
resource Resource
|
||||
expectedResult ResourceSet
|
||||
}{
|
||||
{NewResourceSet(), NewResource("mybucket", "/myobject*"),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*"))},
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
NewResource("mybucket", "/yourobject*"),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*"),
|
||||
NewResource("mybucket", "/yourobject*"))},
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
NewResource("mybucket", "/myobject*"),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*"))},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
testCase.resourceSet.Add(testCase.resource)
|
||||
|
||||
if !reflect.DeepEqual(testCase.resourceSet, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, testCase.resourceSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetIntersection(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ResourceSet
|
||||
setToIntersect ResourceSet
|
||||
expectedResult ResourceSet
|
||||
}{
|
||||
{NewResourceSet(), NewResourceSet(NewResource("mybucket", "/myobject*")), NewResourceSet()},
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")), NewResourceSet(), NewResourceSet()},
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*"), NewResource("mybucket", "/yourobject*")),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*"))},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.Intersection(testCase.setToIntersect)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resoruceSet ResourceSet
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
[]byte(`["arn:aws:s3:::mybucket/myobject*"]`), false},
|
||||
{NewResourceSet(NewResource("mybucket", "/photos/myobject*")),
|
||||
[]byte(`["arn:aws:s3:::mybucket/photos/myobject*"]`), false},
|
||||
{NewResourceSet(), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.resoruceSet)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetMatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceSet ResourceSet
|
||||
resource string
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewResourceSet(NewResource("*", "")), "mybucket", true},
|
||||
{NewResourceSet(NewResource("*", "")), "mybucket/myobject", true},
|
||||
{NewResourceSet(NewResource("mybucket*", "")), "mybucket", true},
|
||||
{NewResourceSet(NewResource("mybucket*", "")), "mybucket/myobject", true},
|
||||
{NewResourceSet(NewResource("", "*")), "/myobject", true},
|
||||
{NewResourceSet(NewResource("*", "*")), "mybucket/myobject", true},
|
||||
{NewResourceSet(NewResource("mybucket", "*")), "mybucket/myobject", true},
|
||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), "mybucket/myobject", true},
|
||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), "mybucket100/myobject", true},
|
||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*")), "mybucket20/2010/photos/1.jpg", true},
|
||||
{NewResourceSet(NewResource("mybucket", "")), "mybucket", true},
|
||||
{NewResourceSet(NewResource("mybucket?0", "")), "mybucket30", true},
|
||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*"),
|
||||
NewResource("mybucket", "/2010/photos/*")), "mybucket/2010/photos/1.jpg", true},
|
||||
{NewResourceSet(NewResource("", "*")), "mybucket/myobject", false},
|
||||
{NewResourceSet(NewResource("*", "*")), "mybucket", false},
|
||||
{NewResourceSet(NewResource("mybucket", "*")), "mybucket10/myobject", false},
|
||||
{NewResourceSet(NewResource("mybucket", "")), "mybucket/myobject", false},
|
||||
{NewResourceSet(), "mybucket/myobject", false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.resourceSet.Match(testCase.resource, nil)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult ResourceSet
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`"arn:aws:s3:::mybucket/myobject*"`),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket/photos/myobject*"`),
|
||||
NewResourceSet(NewResource("mybucket", "/photos/myobject*")), false},
|
||||
{[]byte(`"arn:aws:s3:::mybucket"`), NewResourceSet(NewResource("mybucket", "")), false},
|
||||
{[]byte(`"mybucket/myobject*"`), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result ResourceSet
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSetValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceSet ResourceSet
|
||||
bucketName string
|
||||
expectErr bool
|
||||
}{
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")), "mybucket", false},
|
||||
{NewResourceSet(NewResource("", "/myobject*")), "yourbucket", true},
|
||||
{NewResourceSet(NewResource("mybucket", "/myobject*")), "yourbucket", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.resourceSet.Validate(testCase.bucketName)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
)
|
||||
|
||||
// Statement - policy statement.
|
||||
type Statement struct {
|
||||
SID ID `json:"Sid,omitempty"`
|
||||
Effect Effect `json:"Effect"`
|
||||
Principal Principal `json:"Principal"`
|
||||
Actions ActionSet `json:"Action"`
|
||||
Resources ResourceSet `json:"Resource"`
|
||||
Conditions condition.Functions `json:"Condition,omitempty"`
|
||||
}
|
||||
|
||||
// Equals checks if two statements are equal
|
||||
func (statement Statement) Equals(st Statement) bool {
|
||||
if statement.Effect != st.Effect {
|
||||
return false
|
||||
}
|
||||
if !statement.Principal.Equals(st.Principal) {
|
||||
return false
|
||||
}
|
||||
if !statement.Actions.Equals(st.Actions) {
|
||||
return false
|
||||
}
|
||||
if !statement.Resources.Equals(st.Resources) {
|
||||
return false
|
||||
}
|
||||
if !statement.Conditions.Equals(st.Conditions) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsAllowed - checks given policy args is allowed to continue the Rest API.
|
||||
func (statement Statement) IsAllowed(args Args) bool {
|
||||
check := func() bool {
|
||||
if !statement.Principal.Match(args.AccountName) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !statement.Actions.Contains(args.Action) {
|
||||
return false
|
||||
}
|
||||
|
||||
resource := args.BucketName
|
||||
if args.ObjectName != "" {
|
||||
if !strings.HasPrefix(args.ObjectName, "/") {
|
||||
resource += "/"
|
||||
}
|
||||
|
||||
resource += args.ObjectName
|
||||
}
|
||||
|
||||
if !statement.Resources.Match(resource, args.ConditionValues) {
|
||||
return false
|
||||
}
|
||||
|
||||
return statement.Conditions.Evaluate(args.ConditionValues)
|
||||
}
|
||||
|
||||
return statement.Effect.IsAllowed(check())
|
||||
}
|
||||
|
||||
// isValid - checks whether statement is valid or not.
|
||||
func (statement Statement) isValid() error {
|
||||
if !statement.Effect.IsValid() {
|
||||
return Errorf("invalid Effect %v", statement.Effect)
|
||||
}
|
||||
|
||||
if !statement.Principal.IsValid() {
|
||||
return Errorf("invalid Principal %v", statement.Principal)
|
||||
}
|
||||
|
||||
if len(statement.Actions) == 0 {
|
||||
return Errorf("Action must not be empty")
|
||||
}
|
||||
|
||||
if len(statement.Resources) == 0 {
|
||||
return Errorf("Resource must not be empty")
|
||||
}
|
||||
|
||||
for action := range statement.Actions {
|
||||
if action.isObjectAction() {
|
||||
if !statement.Resources.objectResourceExists() {
|
||||
return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
|
||||
}
|
||||
} else {
|
||||
if !statement.Resources.bucketResourceExists() {
|
||||
return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
|
||||
}
|
||||
}
|
||||
|
||||
keys := statement.Conditions.Keys()
|
||||
keyDiff := keys.Difference(actionConditionKeyMap[action])
|
||||
if !keyDiff.IsEmpty() {
|
||||
return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes JSON data to Statement.
|
||||
func (statement Statement) MarshalJSON() ([]byte, error) {
|
||||
if err := statement.isValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// subtype to avoid recursive call to MarshalJSON()
|
||||
type subStatement Statement
|
||||
ss := subStatement(statement)
|
||||
return json.Marshal(ss)
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to Statement.
|
||||
func (statement *Statement) UnmarshalJSON(data []byte) error {
|
||||
// subtype to avoid recursive call to UnmarshalJSON()
|
||||
type subStatement Statement
|
||||
var ss subStatement
|
||||
|
||||
if err := json.Unmarshal(data, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := Statement(ss)
|
||||
if err := s.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*statement = s
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate - validates Statement is for given bucket or not.
|
||||
func (statement Statement) Validate(bucketName string) error {
|
||||
if err := statement.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return statement.Resources.Validate(bucketName)
|
||||
}
|
||||
|
||||
// Clone clones Statement structure
|
||||
func (statement Statement) Clone() Statement {
|
||||
return NewStatement(statement.Effect, statement.Principal.Clone(),
|
||||
statement.Actions.Clone(), statement.Resources.Clone(), statement.Conditions.Clone())
|
||||
}
|
||||
|
||||
// NewStatement - creates new statement.
|
||||
func NewStatement(effect Effect, principal Principal, actionSet ActionSet, resourceSet ResourceSet, conditions condition.Functions) Statement {
|
||||
return Statement{
|
||||
Effect: effect,
|
||||
Principal: principal,
|
||||
Actions: actionSet,
|
||||
Resources: resourceSet,
|
||||
Conditions: conditions,
|
||||
}
|
||||
}
|
@ -1,572 +0,0 @@
|
||||
// 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 policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
)
|
||||
|
||||
func TestStatementIsAllowed(t *testing.T) {
|
||||
case1Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("*", "")),
|
||||
condition.NewFunctions(),
|
||||
)
|
||||
|
||||
case2Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(),
|
||||
)
|
||||
|
||||
_, IPNet1, err := net.ParseCIDR("192.168.1.0/24")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
func1, err := condition.NewIPAddressFunc(
|
||||
condition.AWSSourceIP,
|
||||
IPNet1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
case3Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(func1),
|
||||
)
|
||||
|
||||
case4Statement := NewStatement(
|
||||
Deny,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(func1),
|
||||
)
|
||||
|
||||
anonGetBucketLocationArgs := Args{
|
||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
||||
Action: GetBucketLocationAction,
|
||||
BucketName: "mybucket",
|
||||
ConditionValues: map[string][]string{},
|
||||
}
|
||||
|
||||
anonPutObjectActionArgs := Args{
|
||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
||||
Action: PutObjectAction,
|
||||
BucketName: "mybucket",
|
||||
ConditionValues: map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/myobject"},
|
||||
"SourceIp": {"192.168.1.10"},
|
||||
},
|
||||
ObjectName: "myobject",
|
||||
}
|
||||
|
||||
anonGetObjectActionArgs := Args{
|
||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
||||
Action: GetObjectAction,
|
||||
BucketName: "mybucket",
|
||||
ConditionValues: map[string][]string{},
|
||||
ObjectName: "myobject",
|
||||
}
|
||||
|
||||
getBucketLocationArgs := Args{
|
||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
||||
Action: GetBucketLocationAction,
|
||||
BucketName: "mybucket",
|
||||
ConditionValues: map[string][]string{},
|
||||
IsOwner: true,
|
||||
}
|
||||
|
||||
putObjectActionArgs := Args{
|
||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
||||
Action: PutObjectAction,
|
||||
BucketName: "mybucket",
|
||||
ConditionValues: map[string][]string{
|
||||
"x-amz-copy-source": {"mybucket/myobject"},
|
||||
"SourceIp": {"192.168.1.10"},
|
||||
},
|
||||
IsOwner: true,
|
||||
ObjectName: "myobject",
|
||||
}
|
||||
|
||||
getObjectActionArgs := Args{
|
||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
||||
Action: GetObjectAction,
|
||||
BucketName: "mybucket",
|
||||
ConditionValues: map[string][]string{},
|
||||
IsOwner: true,
|
||||
ObjectName: "myobject",
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
statement Statement
|
||||
args Args
|
||||
expectedResult bool
|
||||
}{
|
||||
{case1Statement, anonGetBucketLocationArgs, true},
|
||||
{case1Statement, anonPutObjectActionArgs, true},
|
||||
{case1Statement, anonGetObjectActionArgs, false},
|
||||
{case1Statement, getBucketLocationArgs, true},
|
||||
{case1Statement, putObjectActionArgs, true},
|
||||
{case1Statement, getObjectActionArgs, false},
|
||||
|
||||
{case2Statement, anonGetBucketLocationArgs, false},
|
||||
{case2Statement, anonPutObjectActionArgs, true},
|
||||
{case2Statement, anonGetObjectActionArgs, true},
|
||||
{case2Statement, getBucketLocationArgs, false},
|
||||
{case2Statement, putObjectActionArgs, true},
|
||||
{case2Statement, getObjectActionArgs, true},
|
||||
|
||||
{case3Statement, anonGetBucketLocationArgs, false},
|
||||
{case3Statement, anonPutObjectActionArgs, true},
|
||||
{case3Statement, anonGetObjectActionArgs, false},
|
||||
{case3Statement, getBucketLocationArgs, false},
|
||||
{case3Statement, putObjectActionArgs, true},
|
||||
{case3Statement, getObjectActionArgs, false},
|
||||
|
||||
{case4Statement, anonGetBucketLocationArgs, true},
|
||||
{case4Statement, anonPutObjectActionArgs, false},
|
||||
{case4Statement, anonGetObjectActionArgs, true},
|
||||
{case4Statement, getBucketLocationArgs, true},
|
||||
{case4Statement, putObjectActionArgs, false},
|
||||
{case4Statement, getObjectActionArgs, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.statement.IsAllowed(testCase.args)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatementIsValid(t *testing.T) {
|
||||
_, IPNet1, err := net.ParseCIDR("192.168.1.0/24")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
func1, err := condition.NewIPAddressFunc(
|
||||
condition.AWSSourceIP,
|
||||
IPNet1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
func2, err := condition.NewStringEqualsFunc(
|
||||
condition.S3XAmzCopySource,
|
||||
"mybucket/myobject",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
statement Statement
|
||||
expectErr bool
|
||||
}{
|
||||
// Invalid effect error.
|
||||
{NewStatement(
|
||||
Effect("foo"),
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("*", "")),
|
||||
condition.NewFunctions(),
|
||||
), true},
|
||||
// Invalid principal error.
|
||||
{NewStatement(
|
||||
Allow,
|
||||
NewPrincipal(),
|
||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("*", "")),
|
||||
condition.NewFunctions(),
|
||||
), true},
|
||||
// Empty actions error.
|
||||
{NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(),
|
||||
NewResourceSet(NewResource("*", "")),
|
||||
condition.NewFunctions(),
|
||||
), true},
|
||||
// Empty resources error.
|
||||
{NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
||||
NewResourceSet(),
|
||||
condition.NewFunctions(),
|
||||
), true},
|
||||
// Unsupported resource found for object action.
|
||||
{NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "")),
|
||||
condition.NewFunctions(),
|
||||
), true},
|
||||
// Unsupported resource found for bucket action.
|
||||
{NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
||||
condition.NewFunctions(),
|
||||
), true},
|
||||
// Unsupported condition key for action.
|
||||
{NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
||||
condition.NewFunctions(func1, func2),
|
||||
), true},
|
||||
{NewStatement(
|
||||
Deny,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
||||
condition.NewFunctions(func1),
|
||||
), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.statement.isValid()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatementMarshalJSON(t *testing.T) {
|
||||
case1Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(),
|
||||
)
|
||||
case1Statement.SID = "SomeId1"
|
||||
case1Data := []byte(`{"Sid":"SomeId1","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}`)
|
||||
|
||||
func1, err := condition.NewNullFunc(
|
||||
condition.S3XAmzCopySource,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
case2Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(func1),
|
||||
)
|
||||
case2Data := []byte(`{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-copy-source":[true]}}}`)
|
||||
|
||||
func2, err := condition.NewNullFunc(
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
case3Statement := NewStatement(
|
||||
Deny,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(func2),
|
||||
)
|
||||
case3Data := []byte(`{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-server-side-encryption":[false]}}}`)
|
||||
|
||||
case4Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
||||
condition.NewFunctions(func1, func2),
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
statement Statement
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{case1Statement, case1Data, false},
|
||||
{case2Statement, case2Data, false},
|
||||
{case3Statement, case3Data, false},
|
||||
// Invalid statement error.
|
||||
{case4Statement, nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.statement)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatementUnmarshalJSON(t *testing.T) {
|
||||
case1Data := []byte(`{
|
||||
"Sid": "SomeId1",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
||||
}`)
|
||||
case1Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(),
|
||||
)
|
||||
case1Statement.SID = "SomeId1"
|
||||
|
||||
case2Data := []byte(`{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
||||
"Condition": {
|
||||
"Null": {
|
||||
"s3:x-amz-copy-source": true
|
||||
}
|
||||
}
|
||||
}`)
|
||||
func1, err := condition.NewNullFunc(
|
||||
condition.S3XAmzCopySource,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
case2Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(func1),
|
||||
)
|
||||
|
||||
case3Data := []byte(`{
|
||||
"Effect": "Deny",
|
||||
"Principal": {
|
||||
"AWS": "*"
|
||||
},
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
||||
"Condition": {
|
||||
"Null": {
|
||||
"s3:x-amz-server-side-encryption": "false"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
func2, err := condition.NewNullFunc(
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
case3Statement := NewStatement(
|
||||
Deny,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction, GetObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(func2),
|
||||
)
|
||||
|
||||
case4Data := []byte(`{
|
||||
"Effect": "Allow",
|
||||
"Principal": "Q3AM3UQ867SPQQA43P2F",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
||||
}`)
|
||||
|
||||
case5Data := []byte(`{
|
||||
"Principal": "*",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
||||
}`)
|
||||
|
||||
case6Data := []byte(`{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
||||
}`)
|
||||
|
||||
case7Data := []byte(`{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
||||
}`)
|
||||
|
||||
case8Data := []byte(`{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:PutObject"
|
||||
}`)
|
||||
|
||||
case9Data := []byte(`{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
||||
"Condition": {
|
||||
}
|
||||
}`)
|
||||
|
||||
case10Data := []byte(`{
|
||||
"Effect": "Deny",
|
||||
"Principal": {
|
||||
"AWS": "*"
|
||||
},
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"s3:x-amz-copy-source": "yourbucket/myobject*"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult Statement
|
||||
expectErr bool
|
||||
}{
|
||||
{case1Data, case1Statement, false},
|
||||
{case2Data, case2Statement, false},
|
||||
{case3Data, case3Statement, false},
|
||||
// JSON unmarshaling error.
|
||||
{case4Data, Statement{}, true},
|
||||
// Invalid effect error.
|
||||
{case5Data, Statement{}, true},
|
||||
// empty principal error.
|
||||
{case6Data, Statement{}, true},
|
||||
// Empty action error.
|
||||
{case7Data, Statement{}, true},
|
||||
// Empty resource error.
|
||||
{case8Data, Statement{}, true},
|
||||
// Empty condition error.
|
||||
{case9Data, Statement{}, true},
|
||||
// Unsupported condition key error.
|
||||
{case10Data, Statement{}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var result Statement
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatementValidate(t *testing.T) {
|
||||
case1Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
||||
condition.NewFunctions(),
|
||||
)
|
||||
|
||||
func1, err := condition.NewNullFunc(
|
||||
condition.S3XAmzCopySource,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
func2, err := condition.NewNullFunc(
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error. %v\n", err)
|
||||
}
|
||||
case2Statement := NewStatement(
|
||||
Allow,
|
||||
NewPrincipal("*"),
|
||||
NewActionSet(GetObjectAction, PutObjectAction),
|
||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
||||
condition.NewFunctions(func1, func2),
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
statement Statement
|
||||
bucketName string
|
||||
expectErr bool
|
||||
}{
|
||||
{case1Statement, "mybucket", false},
|
||||
{case2Statement, "mybucket", true},
|
||||
{case1Statement, "yourbucket", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.statement.Validate(testCase.bucketName)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,439 +0,0 @@
|
||||
// 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 iampolicy
|
||||
|
||||
import (
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
// Action - policy action.
|
||||
// Refer https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazons3.html
|
||||
// for more information about available actions.
|
||||
type Action string
|
||||
|
||||
const (
|
||||
// AbortMultipartUploadAction - AbortMultipartUpload Rest API action.
|
||||
AbortMultipartUploadAction Action = "s3:AbortMultipartUpload"
|
||||
|
||||
// CreateBucketAction - CreateBucket Rest API action.
|
||||
CreateBucketAction = "s3:CreateBucket"
|
||||
|
||||
// DeleteBucketAction - DeleteBucket Rest API action.
|
||||
DeleteBucketAction = "s3:DeleteBucket"
|
||||
|
||||
// ForceDeleteBucketAction - DeleteBucket Rest API action when x-minio-force-delete flag
|
||||
// is specified.
|
||||
ForceDeleteBucketAction = "s3:ForceDeleteBucket"
|
||||
|
||||
// DeleteBucketPolicyAction - DeleteBucketPolicy Rest API action.
|
||||
DeleteBucketPolicyAction = "s3:DeleteBucketPolicy"
|
||||
|
||||
// DeleteObjectAction - DeleteObject Rest API action.
|
||||
DeleteObjectAction = "s3:DeleteObject"
|
||||
|
||||
// GetBucketLocationAction - GetBucketLocation Rest API action.
|
||||
GetBucketLocationAction = "s3:GetBucketLocation"
|
||||
|
||||
// GetBucketNotificationAction - GetBucketNotification Rest API action.
|
||||
GetBucketNotificationAction = "s3:GetBucketNotification"
|
||||
|
||||
// GetBucketPolicyAction - GetBucketPolicy Rest API action.
|
||||
GetBucketPolicyAction = "s3:GetBucketPolicy"
|
||||
|
||||
// GetObjectAction - GetObject Rest API action.
|
||||
GetObjectAction = "s3:GetObject"
|
||||
|
||||
// HeadBucketAction - HeadBucket Rest API action. This action is unused in minio.
|
||||
HeadBucketAction = "s3:HeadBucket"
|
||||
|
||||
// ListAllMyBucketsAction - ListAllMyBuckets (List buckets) Rest API action.
|
||||
ListAllMyBucketsAction = "s3:ListAllMyBuckets"
|
||||
|
||||
// ListBucketAction - ListBucket Rest API action.
|
||||
ListBucketAction = "s3:ListBucket"
|
||||
|
||||
// GetBucketPolicyStatusAction - Retrieves the policy status for a bucket.
|
||||
GetBucketPolicyStatusAction = "s3:GetBucketPolicyStatus"
|
||||
|
||||
// ListBucketVersionsAction - ListBucketVersions Rest API action.
|
||||
ListBucketVersionsAction = "s3:ListBucketVersions"
|
||||
|
||||
// ListBucketMultipartUploadsAction - ListMultipartUploads Rest API action.
|
||||
ListBucketMultipartUploadsAction = "s3:ListBucketMultipartUploads"
|
||||
|
||||
// ListenNotificationAction - ListenNotification Rest API action.
|
||||
// This is MinIO extension.
|
||||
ListenNotificationAction = "s3:ListenNotification"
|
||||
|
||||
// ListenBucketNotificationAction - ListenBucketNotification Rest API action.
|
||||
// This is MinIO extension.
|
||||
ListenBucketNotificationAction = "s3:ListenBucketNotification"
|
||||
|
||||
// ListMultipartUploadPartsAction - ListParts Rest API action.
|
||||
ListMultipartUploadPartsAction = "s3:ListMultipartUploadParts"
|
||||
|
||||
// PutBucketLifecycleAction - PutBucketLifecycle Rest API action.
|
||||
PutBucketLifecycleAction = "s3:PutLifecycleConfiguration"
|
||||
|
||||
// GetBucketLifecycleAction - GetBucketLifecycle Rest API action.
|
||||
GetBucketLifecycleAction = "s3:GetLifecycleConfiguration"
|
||||
|
||||
// PutBucketNotificationAction - PutObjectNotification Rest API action.
|
||||
PutBucketNotificationAction = "s3:PutBucketNotification"
|
||||
|
||||
// PutBucketPolicyAction - PutBucketPolicy Rest API action.
|
||||
PutBucketPolicyAction = "s3:PutBucketPolicy"
|
||||
|
||||
// PutObjectAction - PutObject Rest API action.
|
||||
PutObjectAction = "s3:PutObject"
|
||||
|
||||
// DeleteObjectVersionAction - DeleteObjectVersion Rest API action.
|
||||
DeleteObjectVersionAction = "s3:DeleteObjectVersion"
|
||||
|
||||
// DeleteObjectVersionTaggingAction - DeleteObjectVersionTagging Rest API action.
|
||||
DeleteObjectVersionTaggingAction = "s3:DeleteObjectVersionTagging"
|
||||
|
||||
// GetObjectVersionAction - GetObjectVersionAction Rest API action.
|
||||
GetObjectVersionAction = "s3:GetObjectVersion"
|
||||
|
||||
// GetObjectVersionTaggingAction - GetObjectVersionTagging Rest API action.
|
||||
GetObjectVersionTaggingAction = "s3:GetObjectVersionTagging"
|
||||
|
||||
// PutObjectVersionTaggingAction - PutObjectVersionTagging Rest API action.
|
||||
PutObjectVersionTaggingAction = "s3:PutObjectVersionTagging"
|
||||
|
||||
// BypassGovernanceRetentionAction - bypass governance retention for PutObjectRetention, PutObject and DeleteObject Rest API action.
|
||||
BypassGovernanceRetentionAction = "s3:BypassGovernanceRetention"
|
||||
|
||||
// PutObjectRetentionAction - PutObjectRetention Rest API action.
|
||||
PutObjectRetentionAction = "s3:PutObjectRetention"
|
||||
|
||||
// GetObjectRetentionAction - GetObjectRetention, GetObject, HeadObject Rest API action.
|
||||
GetObjectRetentionAction = "s3:GetObjectRetention"
|
||||
|
||||
// GetObjectLegalHoldAction - GetObjectLegalHold, GetObject Rest API action.
|
||||
GetObjectLegalHoldAction = "s3:GetObjectLegalHold"
|
||||
|
||||
// PutObjectLegalHoldAction - PutObjectLegalHold, PutObject Rest API action.
|
||||
PutObjectLegalHoldAction = "s3:PutObjectLegalHold"
|
||||
|
||||
// GetBucketObjectLockConfigurationAction - GetBucketObjectLockConfiguration Rest API action
|
||||
GetBucketObjectLockConfigurationAction = "s3:GetBucketObjectLockConfiguration"
|
||||
|
||||
// PutBucketObjectLockConfigurationAction - PutBucketObjectLockConfiguration Rest API action
|
||||
PutBucketObjectLockConfigurationAction = "s3:PutBucketObjectLockConfiguration"
|
||||
|
||||
// GetBucketTaggingAction - GetBucketTagging Rest API action
|
||||
GetBucketTaggingAction = "s3:GetBucketTagging"
|
||||
|
||||
// PutBucketTaggingAction - PutBucketTagging Rest API action
|
||||
PutBucketTaggingAction = "s3:PutBucketTagging"
|
||||
|
||||
// GetObjectTaggingAction - Get Object Tags API action
|
||||
GetObjectTaggingAction = "s3:GetObjectTagging"
|
||||
|
||||
// PutObjectTaggingAction - Put Object Tags API action
|
||||
PutObjectTaggingAction = "s3:PutObjectTagging"
|
||||
|
||||
// DeleteObjectTaggingAction - Delete Object Tags API action
|
||||
DeleteObjectTaggingAction = "s3:DeleteObjectTagging"
|
||||
|
||||
// PutBucketEncryptionAction - PutBucketEncryption REST API action
|
||||
PutBucketEncryptionAction = "s3:PutEncryptionConfiguration"
|
||||
|
||||
// GetBucketEncryptionAction - GetBucketEncryption REST API action
|
||||
GetBucketEncryptionAction = "s3:GetEncryptionConfiguration"
|
||||
|
||||
// PutBucketVersioningAction - PutBucketVersioning REST API action
|
||||
PutBucketVersioningAction = "s3:PutBucketVersioning"
|
||||
|
||||
// GetBucketVersioningAction - GetBucketVersioning REST API action
|
||||
GetBucketVersioningAction = "s3:GetBucketVersioning"
|
||||
// GetReplicationConfigurationAction - GetReplicationConfiguration REST API action
|
||||
GetReplicationConfigurationAction = "s3:GetReplicationConfiguration"
|
||||
// PutReplicationConfigurationAction - PutReplicationConfiguration REST API action
|
||||
PutReplicationConfigurationAction = "s3:PutReplicationConfiguration"
|
||||
|
||||
// ReplicateObjectAction - ReplicateObject REST API action
|
||||
ReplicateObjectAction = "s3:ReplicateObject"
|
||||
|
||||
// ReplicateDeleteAction - ReplicateDelete REST API action
|
||||
ReplicateDeleteAction = "s3:ReplicateDelete"
|
||||
|
||||
// ReplicateTagsAction - ReplicateTags REST API action
|
||||
ReplicateTagsAction = "s3:ReplicateTags"
|
||||
|
||||
// GetObjectVersionForReplicationAction - GetObjectVersionForReplication REST API action
|
||||
GetObjectVersionForReplicationAction = "s3:GetObjectVersionForReplication"
|
||||
|
||||
// AllActions - all API actions
|
||||
AllActions = "s3:*"
|
||||
)
|
||||
|
||||
// List of all supported actions.
|
||||
var supportedActions = map[Action]struct{}{
|
||||
AbortMultipartUploadAction: {},
|
||||
CreateBucketAction: {},
|
||||
DeleteBucketAction: {},
|
||||
ForceDeleteBucketAction: {},
|
||||
DeleteBucketPolicyAction: {},
|
||||
DeleteObjectAction: {},
|
||||
GetBucketLocationAction: {},
|
||||
GetBucketNotificationAction: {},
|
||||
GetBucketPolicyAction: {},
|
||||
GetObjectAction: {},
|
||||
HeadBucketAction: {},
|
||||
ListAllMyBucketsAction: {},
|
||||
ListBucketAction: {},
|
||||
GetBucketPolicyStatusAction: {},
|
||||
ListBucketVersionsAction: {},
|
||||
ListBucketMultipartUploadsAction: {},
|
||||
ListenNotificationAction: {},
|
||||
ListenBucketNotificationAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutBucketLifecycleAction: {},
|
||||
GetBucketLifecycleAction: {},
|
||||
PutBucketNotificationAction: {},
|
||||
PutBucketPolicyAction: {},
|
||||
PutObjectAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
PutObjectRetentionAction: {},
|
||||
GetObjectRetentionAction: {},
|
||||
GetObjectLegalHoldAction: {},
|
||||
PutObjectLegalHoldAction: {},
|
||||
GetBucketObjectLockConfigurationAction: {},
|
||||
PutBucketObjectLockConfigurationAction: {},
|
||||
GetBucketTaggingAction: {},
|
||||
PutBucketTaggingAction: {},
|
||||
GetObjectVersionAction: {},
|
||||
GetObjectVersionTaggingAction: {},
|
||||
DeleteObjectVersionAction: {},
|
||||
DeleteObjectVersionTaggingAction: {},
|
||||
PutObjectVersionTaggingAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
DeleteObjectTaggingAction: {},
|
||||
PutBucketEncryptionAction: {},
|
||||
GetBucketEncryptionAction: {},
|
||||
PutBucketVersioningAction: {},
|
||||
GetBucketVersioningAction: {},
|
||||
GetReplicationConfigurationAction: {},
|
||||
PutReplicationConfigurationAction: {},
|
||||
ReplicateObjectAction: {},
|
||||
ReplicateDeleteAction: {},
|
||||
ReplicateTagsAction: {},
|
||||
GetObjectVersionForReplicationAction: {},
|
||||
AllActions: {},
|
||||
}
|
||||
|
||||
// List of all supported object actions.
|
||||
var supportedObjectActions = map[Action]struct{}{
|
||||
AllActions: {},
|
||||
AbortMultipartUploadAction: {},
|
||||
DeleteObjectAction: {},
|
||||
GetObjectAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutObjectAction: {},
|
||||
BypassGovernanceRetentionAction: {},
|
||||
PutObjectRetentionAction: {},
|
||||
GetObjectRetentionAction: {},
|
||||
PutObjectLegalHoldAction: {},
|
||||
GetObjectLegalHoldAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
DeleteObjectTaggingAction: {},
|
||||
GetObjectVersionAction: {},
|
||||
GetObjectVersionTaggingAction: {},
|
||||
DeleteObjectVersionAction: {},
|
||||
DeleteObjectVersionTaggingAction: {},
|
||||
PutObjectVersionTaggingAction: {},
|
||||
ReplicateObjectAction: {},
|
||||
ReplicateDeleteAction: {},
|
||||
ReplicateTagsAction: {},
|
||||
GetObjectVersionForReplicationAction: {},
|
||||
}
|
||||
|
||||
// isObjectAction - returns whether action is object type or not.
|
||||
func (action Action) isObjectAction() bool {
|
||||
for supAction := range supportedObjectActions {
|
||||
if action.Match(supAction) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Match - matches action name with action patter.
|
||||
func (action Action) Match(a Action) bool {
|
||||
return wildcard.Match(string(action), string(a))
|
||||
}
|
||||
|
||||
// IsValid - checks if action is valid or not.
|
||||
func (action Action) IsValid() bool {
|
||||
for supAction := range supportedActions {
|
||||
if action.Match(supAction) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type actionConditionKeyMap map[Action]condition.KeySet
|
||||
|
||||
func (a actionConditionKeyMap) Lookup(action Action) condition.KeySet {
|
||||
var ckeysMerged = condition.NewKeySet(condition.CommonKeys...)
|
||||
for act, ckey := range a {
|
||||
if action.Match(act) {
|
||||
ckeysMerged.Merge(ckey)
|
||||
}
|
||||
}
|
||||
return ckeysMerged
|
||||
}
|
||||
|
||||
// iamActionConditionKeyMap - holds mapping of supported condition key for an action.
|
||||
var iamActionConditionKeyMap = actionConditionKeyMap{
|
||||
AllActions: condition.NewKeySet(condition.AllSupportedKeys...),
|
||||
|
||||
GetObjectAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
ListBucketAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3Prefix,
|
||||
condition.S3Delimiter,
|
||||
condition.S3MaxKeys,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
ListBucketVersionsAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3Prefix,
|
||||
condition.S3Delimiter,
|
||||
condition.S3MaxKeys,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
DeleteObjectAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
PutObjectAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzCopySource,
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3XAmzMetadataDirective,
|
||||
condition.S3XAmzStorageClass,
|
||||
condition.S3VersionID,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
||||
// LockLegalHold is not supported with PutObjectRetentionAction
|
||||
PutObjectRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
GetObjectRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
PutObjectLegalHoldAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3XAmzServerSideEncryption,
|
||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
||||
BypassGovernanceRetentionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
condition.S3ObjectLockRemainingRetentionDays,
|
||||
condition.S3ObjectLockRetainUntilDate,
|
||||
condition.S3ObjectLockMode,
|
||||
condition.S3ObjectLockLegalHold,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
PutObjectTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
DeleteObjectTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
|
||||
PutObjectVersionTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectVersionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectVersionTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
DeleteObjectVersionAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
DeleteObjectVersionTaggingAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
ReplicateObjectAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
ReplicateDeleteAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
ReplicateTagsAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
GetObjectVersionForReplicationAction: condition.NewKeySet(
|
||||
append([]condition.Key{
|
||||
condition.S3VersionID,
|
||||
}, condition.CommonKeys...)...),
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// 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 iampolicy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestActionIsObjectAction(t *testing.T) {
|
||||
testCases := []struct {
|
||||
action Action
|
||||
expectedResult bool
|
||||
}{
|
||||
{AbortMultipartUploadAction, true},
|
||||
{DeleteObjectAction, true},
|
||||
{GetObjectAction, true},
|
||||
{ListMultipartUploadPartsAction, true},
|
||||
{PutObjectAction, true},
|
||||
{CreateBucketAction, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.action.isObjectAction()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionIsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
action Action
|
||||
expectedResult bool
|
||||
}{
|
||||
{PutObjectAction, true},
|
||||
{AbortMultipartUploadAction, true},
|
||||
{Action("foo"), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.action.IsValid()
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
// 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 iampolicy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
// ActionSet - set of actions.
|
||||
type ActionSet map[Action]struct{}
|
||||
|
||||
// Clone clones ActionSet structure
|
||||
func (actionSet ActionSet) Clone() ActionSet {
|
||||
return NewActionSet(actionSet.ToSlice()...)
|
||||
}
|
||||
|
||||
// Add - add action to the set.
|
||||
func (actionSet ActionSet) Add(action Action) {
|
||||
actionSet[action] = struct{}{}
|
||||
}
|
||||
|
||||
// IsEmpty - returns if the current action set is empty
|
||||
func (actionSet ActionSet) IsEmpty() bool {
|
||||
return len(actionSet) == 0
|
||||
}
|
||||
|
||||
// Match - matches object name with anyone of action pattern in action set.
|
||||
func (actionSet ActionSet) Match(action Action) bool {
|
||||
for r := range actionSet {
|
||||
if r.Match(action) {
|
||||
return true
|
||||
}
|
||||
|
||||
// This is a special case where GetObjectVersion
|
||||
// means GetObject is enabled implicitly.
|
||||
switch r {
|
||||
case GetObjectVersionAction:
|
||||
if action == GetObjectAction {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals - checks whether given action set is equal to current action set or not.
|
||||
func (actionSet ActionSet) Equals(sactionSet ActionSet) bool {
|
||||
// If length of set is not equal to length of given set, the
|
||||
// set is not equal to given set.
|
||||
if len(actionSet) != len(sactionSet) {
|
||||
return false
|
||||
}
|
||||
|
||||
// As both sets are equal in length, check each elements are equal.
|
||||
for k := range actionSet {
|
||||
if _, ok := sactionSet[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersection - returns actions available in both ActionSet.
|
||||
func (actionSet ActionSet) Intersection(sset ActionSet) ActionSet {
|
||||
nset := NewActionSet()
|
||||
for k := range actionSet {
|
||||
if _, ok := sset[k]; ok {
|
||||
nset.Add(k)
|
||||
}
|
||||
}
|
||||
|
||||
return nset
|
||||
}
|
||||
|
||||
// MarshalJSON - encodes ActionSet to JSON data.
|
||||
func (actionSet ActionSet) MarshalJSON() ([]byte, error) {
|
||||
if len(actionSet) == 0 {
|
||||
return nil, Errorf("empty action set")
|
||||
}
|
||||
|
||||
return json.Marshal(actionSet.ToSlice())
|
||||
}
|
||||
|
||||
func (actionSet ActionSet) String() string {
|
||||
actions := []string{}
|
||||
for action := range actionSet {
|
||||
actions = append(actions, string(action))
|
||||
}
|
||||
sort.Strings(actions)
|
||||
|
||||
return fmt.Sprintf("%v", actions)
|
||||
}
|
||||
|
||||
// ToSlice - returns slice of actions from the action set.
|
||||
func (actionSet ActionSet) ToSlice() []Action {
|
||||
actions := []Action{}
|
||||
for action := range actionSet {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// ToAdminSlice - returns slice of admin actions from the action set.
|
||||
func (actionSet ActionSet) ToAdminSlice() []AdminAction {
|
||||
actions := []AdminAction{}
|
||||
for action := range actionSet {
|
||||
actions = append(actions, AdminAction(action))
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// UnmarshalJSON - decodes JSON data to ActionSet.
|
||||
func (actionSet *ActionSet) UnmarshalJSON(data []byte) error {
|
||||
var sset set.StringSet
|
||||
if err := json.Unmarshal(data, &sset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sset) == 0 {
|
||||
return Errorf("empty action set")
|
||||
}
|
||||
|
||||
*actionSet = make(ActionSet)
|
||||
for _, s := range sset.ToSlice() {
|
||||
actionSet.Add(Action(s))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateAdmin checks if all actions are valid Admin actions
|
||||
func (actionSet ActionSet) ValidateAdmin() error {
|
||||
for _, action := range actionSet.ToAdminSlice() {
|
||||
if !action.IsValid() {
|
||||
return Errorf("unsupported admin action '%v'", action)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks if all actions are valid
|
||||
func (actionSet ActionSet) Validate() error {
|
||||
for _, action := range actionSet.ToSlice() {
|
||||
if !action.IsValid() {
|
||||
return Errorf("unsupported action '%v'", action)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewActionSet - creates new action set.
|
||||
func NewActionSet(actions ...Action) ActionSet {
|
||||
actionSet := make(ActionSet)
|
||||
for _, action := range actions {
|
||||
actionSet.Add(action)
|
||||
}
|
||||
|
||||
return actionSet
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
// 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 iampolicy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestActionSetAdd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ActionSet
|
||||
action Action
|
||||
expectedResult ActionSet
|
||||
}{
|
||||
{NewActionSet(), PutObjectAction, NewActionSet(PutObjectAction)},
|
||||
{NewActionSet(PutObjectAction), PutObjectAction, NewActionSet(PutObjectAction)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
testCase.set.Add(testCase.action)
|
||||
|
||||
if !reflect.DeepEqual(testCase.expectedResult, testCase.set) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetMatches(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ActionSet
|
||||
action Action
|
||||
expectedResult bool
|
||||
}{
|
||||
{NewActionSet(AllActions), AbortMultipartUploadAction, true},
|
||||
{NewActionSet(PutObjectAction), PutObjectAction, true},
|
||||
{NewActionSet(PutObjectAction, GetObjectAction), PutObjectAction, true},
|
||||
{NewActionSet(PutObjectAction, GetObjectAction), AbortMultipartUploadAction, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.Match(testCase.action)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetIntersection(t *testing.T) {
|
||||
testCases := []struct {
|
||||
set ActionSet
|
||||
setToIntersect ActionSet
|
||||
expectedResult ActionSet
|
||||
}{
|
||||
{NewActionSet(), NewActionSet(PutObjectAction), NewActionSet()},
|
||||
{NewActionSet(PutObjectAction), NewActionSet(), NewActionSet()},
|
||||
{NewActionSet(PutObjectAction), NewActionSet(PutObjectAction, GetObjectAction), NewActionSet(PutObjectAction)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.set.Intersection(testCase.setToIntersect)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
actionSet ActionSet
|
||||
expectedResult []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{NewActionSet(PutObjectAction), []byte(`["s3:PutObject"]`), false},
|
||||
{NewActionSet(), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result, err := json.Marshal(testCase.actionSet)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetToSlice(t *testing.T) {
|
||||
testCases := []struct {
|
||||
actionSet ActionSet
|
||||
expectedResult []Action
|
||||
}{
|
||||
{NewActionSet(PutObjectAction), []Action{PutObjectAction}},
|
||||
{NewActionSet(), []Action{}},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.actionSet.ToSlice()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSetUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult ActionSet
|
||||
expectUnmarshalErr bool
|
||||
expectValidateErr bool
|
||||
}{
|
||||
{[]byte(`"s3:PutObject"`), NewActionSet(PutObjectAction), false, false},
|
||||
{[]byte(`["s3:PutObject"]`), NewActionSet(PutObjectAction), false, false},
|
||||
{[]byte(`["s3:PutObject", "s3:GetObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false, false},
|
||||
{[]byte(`["s3:PutObject", "s3:GetObject", "s3:PutObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false, false},
|
||||
{[]byte(`[]`), NewActionSet(), true, false}, // Empty array.
|
||||
{[]byte(`"foo"`), nil, false, true}, // Invalid action.
|
||||
{[]byte(`["s3:PutObject", "foo"]`), nil, false, true}, // Invalid action.
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := make(ActionSet)
|
||||
err := json.Unmarshal(testCase.data, &result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectUnmarshalErr {
|
||||
t.Fatalf("case %v: error during unmarshal: expected: %v, got: %v\n", i+1, testCase.expectUnmarshalErr, expectErr)
|
||||
}
|
||||
|
||||
err = result.Validate()
|
||||
expectErr = (err != nil)
|
||||
if expectErr != testCase.expectValidateErr {
|
||||
t.Fatalf("case %v: error during validation: expected: %v, got: %v\n", i+1, testCase.expectValidateErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectUnmarshalErr && !testCase.expectValidateErr {
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user