diff --git a/cmd/acl-handlers.go b/cmd/acl-handlers.go index 69f327501..5c5810da2 100644 --- a/cmd/acl-handlers.go +++ b/cmd/acl-handlers.go @@ -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 diff --git a/cmd/admin-bucket-handlers.go b/cmd/admin-bucket-handlers.go index 7ebe53b9c..e0f300a96 100644 --- a/cmd/admin-bucket-handlers.go +++ b/cmd/admin-bucket-handlers.go @@ -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 ( diff --git a/cmd/admin-handlers-config-kv.go b/cmd/admin-handlers-config-kv.go index 36af8753a..3d24373d0 100644 --- a/cmd/admin-handlers-config-kv.go +++ b/cmd/admin-handlers-config-kv.go @@ -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) { diff --git a/cmd/admin-handlers-users.go b/cmd/admin-handlers-users.go index 0d615a72d..ee3b6a0a5 100644 --- a/cmd/admin-handlers-users.go +++ b/cmd/admin-handlers-users.go @@ -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) { diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 3fd7038f4..93e16225e 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -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 ( diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 57d839067..e47b2df3d 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -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 diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go index e9721d79f..c311568fb 100644 --- a/cmd/auth-handler.go +++ b/cmd/auth-handler.go @@ -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. diff --git a/cmd/auth-handler_test.go b/cmd/auth-handler_test.go index f8b3a917b..7fb7df765 100644 --- a/cmd/auth-handler_test.go +++ b/cmd/auth-handler_test.go @@ -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. diff --git a/cmd/bucket-encryption-handlers.go b/cmd/bucket-encryption-handlers.go index 5f7fad500..c40a6ea64 100644 --- a/cmd/bucket-encryption-handlers.go +++ b/cmd/bucket-encryption-handlers.go @@ -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 ( diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 2e19ee23e..3f6bc19b4 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -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 ( diff --git a/cmd/bucket-lifecycle-handlers.go b/cmd/bucket-lifecycle-handlers.go index 76888f357..84ca002a2 100644 --- a/cmd/bucket-lifecycle-handlers.go +++ b/cmd/bucket-lifecycle-handlers.go @@ -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 ( diff --git a/cmd/bucket-listobjects-handlers.go b/cmd/bucket-listobjects-handlers.go index f7b69bdff..08907721a 100644 --- a/cmd/bucket-listobjects-handlers.go +++ b/cmd/bucket-listobjects-handlers.go @@ -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) { diff --git a/cmd/bucket-metadata-sys.go b/cmd/bucket-metadata-sys.go index dc5894bcf..dfaa8f9ec 100644 --- a/cmd/bucket-metadata-sys.go +++ b/cmd/bucket-metadata-sys.go @@ -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. diff --git a/cmd/bucket-metadata.go b/cmd/bucket-metadata.go index d6b0cf95d..a69d38a32 100644 --- a/cmd/bucket-metadata.go +++ b/cmd/bucket-metadata.go @@ -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" ) diff --git a/cmd/bucket-notification-handlers.go b/cmd/bucket-notification-handlers.go index e72703cf7..0af293dbe 100644 --- a/cmd/bucket-notification-handlers.go +++ b/cmd/bucket-notification-handlers.go @@ -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 ( diff --git a/cmd/bucket-object-lock.go b/cmd/bucket-object-lock.go index 0bac18aa5..4844f4c49 100644 --- a/cmd/bucket-object-lock.go +++ b/cmd/bucket-object-lock.go @@ -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. diff --git a/cmd/bucket-policy-handlers.go b/cmd/bucket-policy-handlers.go index 20bc34d8b..a81feeb43 100644 --- a/cmd/bucket-policy-handlers.go +++ b/cmd/bucket-policy-handlers.go @@ -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 ( diff --git a/cmd/bucket-policy-handlers_test.go b/cmd/bucket-policy-handlers_test.go index 7716477a7..7470b7d00 100644 --- a/cmd/bucket-policy-handlers_test.go +++ b/cmd/bucket-policy-handlers_test.go @@ -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 { diff --git a/cmd/bucket-policy.go b/cmd/bucket-policy.go index dcc8c87e7..b94c7ba0f 100644 --- a/cmd/bucket-policy.go +++ b/cmd/bucket-policy.go @@ -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. diff --git a/cmd/bucket-replication.go b/cmd/bucket-replication.go index b0f308a2c..b9363ca1d 100644 --- a/cmd/bucket-replication.go +++ b/cmd/bucket-replication.go @@ -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. diff --git a/cmd/bucket-versioning-handler.go b/cmd/bucket-versioning-handler.go index c1ca8e449..60b919c90 100644 --- a/cmd/bucket-versioning-handler.go +++ b/cmd/bucket-versioning-handler.go @@ -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 ( diff --git a/cmd/config/identity/openid/jwt.go b/cmd/config/identity/openid/jwt.go index 03773e3f6..64aaa72b4 100644 --- a/cmd/config/identity/openid/jwt.go +++ b/cmd/config/identity/openid/jwt.go @@ -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 diff --git a/cmd/config/policy/opa/config.go b/cmd/config/policy/opa/config.go index cf478a161..8b86847ca 100644 --- a/cmd/config/policy/opa/config.go +++ b/cmd/config/policy/opa/config.go @@ -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 diff --git a/cmd/dummy-handlers.go b/cmd/dummy-handlers.go index 4b30ce22e..d0d2828d5 100644 --- a/cmd/dummy-handlers.go +++ b/cmd/dummy-handlers.go @@ -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. diff --git a/cmd/fs-v1.go b/cmd/fs-v1.go index 91f374e49..5ebe26722 100644 --- a/cmd/fs-v1.go +++ b/cmd/fs-v1.go @@ -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" ) diff --git a/cmd/gateway-unsupported.go b/cmd/gateway-unsupported.go index bc2d57b83..e9b4f6956 100644 --- a/cmd/gateway-unsupported.go +++ b/cmd/gateway-unsupported.go @@ -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" ) diff --git a/cmd/gateway/azure/gateway-azure.go b/cmd/gateway/azure/gateway-azure.go index 3901aa85c..9d612e5e1 100644 --- a/cmd/gateway/azure/gateway-azure.go +++ b/cmd/gateway/azure/gateway-azure.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" ) diff --git a/cmd/gateway/gcs/gateway-gcs.go b/cmd/gateway/gcs/gateway-gcs.go index 724af2750..1aa5f34f4 100644 --- a/cmd/gateway/gcs/gateway-gcs.go +++ b/cmd/gateway/gcs/gateway-gcs.go @@ -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" diff --git a/cmd/gateway/s3/gateway-s3.go b/cmd/gateway/s3/gateway-s3.go index e076e32d4..51266b6b4 100644 --- a/cmd/gateway/s3/gateway-s3.go +++ b/cmd/gateway/s3/gateway-s3.go @@ -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() { diff --git a/cmd/iam-etcd-store.go b/cmd/iam-etcd-store.go index 97b38b2a8..f6a77470a 100644 --- a/cmd/iam-etcd-store.go +++ b/cmd/iam-etcd-store.go @@ -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" ) diff --git a/cmd/iam-object-store.go b/cmd/iam-object-store.go index 00152b46a..d01be1e19 100644 --- a/cmd/iam-object-store.go +++ b/cmd/iam-object-store.go @@ -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 diff --git a/cmd/iam.go b/cmd/iam.go index b427e1578..ba2e15799 100644 --- a/cmd/iam.go +++ b/cmd/iam.go @@ -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 diff --git a/cmd/listen-notification-handlers.go b/cmd/listen-notification-handlers.go index ce46a8eee..a262d8d9d 100644 --- a/cmd/listen-notification-handlers.go +++ b/cmd/listen-notification-handlers.go @@ -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) { diff --git a/cmd/metrics.go b/cmd/metrics.go index d3850420b..fdbb12b08 100644 --- a/cmd/metrics.go +++ b/cmd/metrics.go @@ -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" ) diff --git a/cmd/notification.go b/cmd/notification.go index 9f371201b..d09c9c82b 100644 --- a/cmd/notification.go +++ b/cmd/notification.go @@ -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. diff --git a/cmd/object-api-interface.go b/cmd/object-api-interface.go index 18cdc622c..44ef1d323 100644 --- a/cmd/object-api-interface.go +++ b/cmd/object-api-interface.go @@ -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. diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 775a9f104..e3611cf48 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -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" ) diff --git a/cmd/policy_test.go b/cmd/policy_test.go index c8157538a..fae883976 100644 --- a/cmd/policy_test.go +++ b/cmd/policy_test.go @@ -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) { diff --git a/cmd/server_test.go b/cmd/server_test.go index a72852faa..e8eeaf752 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -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. diff --git a/cmd/sts-handlers.go b/cmd/sts-handlers.go index bb11c172f..323ab508e 100644 --- a/cmd/sts-handlers.go +++ b/cmd/sts-handlers.go @@ -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" ) diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 5d9ce13d0..5a3b48965 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -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. diff --git a/cmd/tier-handlers.go b/cmd/tier-handlers.go index 7a5289b2f..17c9aba54 100644 --- a/cmd/tier-handlers.go +++ b/cmd/tier-handlers.go @@ -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 ( diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index 76853f169..7868b125b 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -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" ) diff --git a/go.mod b/go.mod index d06e4f257..c7d46e762 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index a4f1c7f70..2f2a19860 100644 --- a/go.sum +++ b/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= diff --git a/pkg/bucket/policy/action.go b/pkg/bucket/policy/action.go deleted file mode 100644 index 94ab27a94..000000000 --- a/pkg/bucket/policy/action.go +++ /dev/null @@ -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 . - -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...), -} diff --git a/pkg/bucket/policy/action_test.go b/pkg/bucket/policy/action_test.go deleted file mode 100644 index 1784432f0..000000000 --- a/pkg/bucket/policy/action_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/actionset.go b/pkg/bucket/policy/actionset.go deleted file mode 100644 index 73ed286e6..000000000 --- a/pkg/bucket/policy/actionset.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/actionset_test.go b/pkg/bucket/policy/actionset_test.go deleted file mode 100644 index 8c6ef7d10..000000000 --- a/pkg/bucket/policy/actionset_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/binaryequalsfunc.go b/pkg/bucket/policy/condition/binaryequalsfunc.go deleted file mode 100644 index 27991e5dc..000000000 --- a/pkg/bucket/policy/condition/binaryequalsfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/binaryequalsfunc_test.go b/pkg/bucket/policy/condition/binaryequalsfunc_test.go deleted file mode 100644 index 47e927efd..000000000 --- a/pkg/bucket/policy/condition/binaryequalsfunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/boolfunc.go b/pkg/bucket/policy/condition/boolfunc.go deleted file mode 100644 index b0e49e513..000000000 --- a/pkg/bucket/policy/condition/boolfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/boolfunc_test.go b/pkg/bucket/policy/condition/boolfunc_test.go deleted file mode 100644 index 49a6ef8b7..000000000 --- a/pkg/bucket/policy/condition/boolfunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/dateequalsfunc.go b/pkg/bucket/policy/condition/dateequalsfunc.go deleted file mode 100644 index b9a0b2e1a..000000000 --- a/pkg/bucket/policy/condition/dateequalsfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/dategreaterthanfunc.go b/pkg/bucket/policy/condition/dategreaterthanfunc.go deleted file mode 100644 index ca18c4c6e..000000000 --- a/pkg/bucket/policy/condition/dategreaterthanfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/datelessthanfunc.go b/pkg/bucket/policy/condition/datelessthanfunc.go deleted file mode 100644 index 0151cfa2c..000000000 --- a/pkg/bucket/policy/condition/datelessthanfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/func.go b/pkg/bucket/policy/condition/func.go deleted file mode 100644 index e6c0a3c22..000000000 --- a/pkg/bucket/policy/condition/func.go +++ /dev/null @@ -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 . - -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) -} diff --git a/pkg/bucket/policy/condition/func_test.go b/pkg/bucket/policy/condition/func_test.go deleted file mode 100644 index 4bfdc92f5..000000000 --- a/pkg/bucket/policy/condition/func_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/ipaddressfunc.go b/pkg/bucket/policy/condition/ipaddressfunc.go deleted file mode 100644 index 717c574ba..000000000 --- a/pkg/bucket/policy/condition/ipaddressfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/ipaddressfunc_test.go b/pkg/bucket/policy/condition/ipaddressfunc_test.go deleted file mode 100644 index 5c0618304..000000000 --- a/pkg/bucket/policy/condition/ipaddressfunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/jwt.go b/pkg/bucket/policy/condition/jwt.go deleted file mode 100644 index 3c8d223be..000000000 --- a/pkg/bucket/policy/condition/jwt.go +++ /dev/null @@ -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 . - -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, -} diff --git a/pkg/bucket/policy/condition/key.go b/pkg/bucket/policy/condition/key.go deleted file mode 100644 index 6550a74b4..000000000 --- a/pkg/bucket/policy/condition/key.go +++ /dev/null @@ -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 . - -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. -} diff --git a/pkg/bucket/policy/condition/key_test.go b/pkg/bucket/policy/condition/key_test.go deleted file mode 100644 index 7461532c8..000000000 --- a/pkg/bucket/policy/condition/key_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/bucket/policy/condition/ldap.go b/pkg/bucket/policy/condition/ldap.go deleted file mode 100644 index 43f6eb30d..000000000 --- a/pkg/bucket/policy/condition/ldap.go +++ /dev/null @@ -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 . - -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" -) diff --git a/pkg/bucket/policy/condition/name.go b/pkg/bucket/policy/condition/name.go deleted file mode 100644 index 7ec24f25c..000000000 --- a/pkg/bucket/policy/condition/name.go +++ /dev/null @@ -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 . - -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) -} diff --git a/pkg/bucket/policy/condition/name_test.go b/pkg/bucket/policy/condition/name_test.go deleted file mode 100644 index f42af0218..000000000 --- a/pkg/bucket/policy/condition/name_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/nullfunc.go b/pkg/bucket/policy/condition/nullfunc.go deleted file mode 100644 index ecc789a9b..000000000 --- a/pkg/bucket/policy/condition/nullfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/nullfunc_test.go b/pkg/bucket/policy/condition/nullfunc_test.go deleted file mode 100644 index 1bca10a39..000000000 --- a/pkg/bucket/policy/condition/nullfunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/numericequalsfunc.go b/pkg/bucket/policy/condition/numericequalsfunc.go deleted file mode 100644 index 7b43bc15f..000000000 --- a/pkg/bucket/policy/condition/numericequalsfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/numericgreaterfunc.go b/pkg/bucket/policy/condition/numericgreaterfunc.go deleted file mode 100644 index 9c3b558de..000000000 --- a/pkg/bucket/policy/condition/numericgreaterfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/numericlessfunc.go b/pkg/bucket/policy/condition/numericlessfunc.go deleted file mode 100644 index 805d9e33a..000000000 --- a/pkg/bucket/policy/condition/numericlessfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/stringequalsfunc.go b/pkg/bucket/policy/condition/stringequalsfunc.go deleted file mode 100644 index e0f20d239..000000000 --- a/pkg/bucket/policy/condition/stringequalsfunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/stringequalsfunc_test.go b/pkg/bucket/policy/condition/stringequalsfunc_test.go deleted file mode 100644 index d0a92bea1..000000000 --- a/pkg/bucket/policy/condition/stringequalsfunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/stringequalsignorecasefunc.go b/pkg/bucket/policy/condition/stringequalsignorecasefunc.go deleted file mode 100644 index ecee11157..000000000 --- a/pkg/bucket/policy/condition/stringequalsignorecasefunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/stringequalsignorecasefunc_test.go b/pkg/bucket/policy/condition/stringequalsignorecasefunc_test.go deleted file mode 100644 index dde5d3cd6..000000000 --- a/pkg/bucket/policy/condition/stringequalsignorecasefunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/stringlikefunc.go b/pkg/bucket/policy/condition/stringlikefunc.go deleted file mode 100644 index 4d950c953..000000000 --- a/pkg/bucket/policy/condition/stringlikefunc.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/stringlikefunc_test.go b/pkg/bucket/policy/condition/stringlikefunc_test.go deleted file mode 100644 index e11413be3..000000000 --- a/pkg/bucket/policy/condition/stringlikefunc_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/value.go b/pkg/bucket/policy/condition/value.go deleted file mode 100644 index 80b4922d4..000000000 --- a/pkg/bucket/policy/condition/value.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/value_test.go b/pkg/bucket/policy/condition/value_test.go deleted file mode 100644 index eac97321a..000000000 --- a/pkg/bucket/policy/condition/value_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/condition/valueset.go b/pkg/bucket/policy/condition/valueset.go deleted file mode 100644 index f97bdd68a..000000000 --- a/pkg/bucket/policy/condition/valueset.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/condition/valueset_test.go b/pkg/bucket/policy/condition/valueset_test.go deleted file mode 100644 index f727edd3c..000000000 --- a/pkg/bucket/policy/condition/valueset_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/effect.go b/pkg/bucket/policy/effect.go deleted file mode 100644 index e6f6c3ad6..000000000 --- a/pkg/bucket/policy/effect.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/effect_test.go b/pkg/bucket/policy/effect_test.go deleted file mode 100644 index 86eb7c20d..000000000 --- a/pkg/bucket/policy/effect_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/bucket/policy/error.go b/pkg/bucket/policy/error.go deleted file mode 100644 index e0b8c6d01..000000000 --- a/pkg/bucket/policy/error.go +++ /dev/null @@ -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 . - -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 " - } - return e.err.Error() -} diff --git a/pkg/bucket/policy/id.go b/pkg/bucket/policy/id.go deleted file mode 100644 index ad7f966c9..000000000 --- a/pkg/bucket/policy/id.go +++ /dev/null @@ -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 . - -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)) -} diff --git a/pkg/bucket/policy/id_test.go b/pkg/bucket/policy/id_test.go deleted file mode 100644 index 42f969306..000000000 --- a/pkg/bucket/policy/id_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/bucket/policy/policy.go b/pkg/bucket/policy/policy.go deleted file mode 100644 index 25c578aa0..000000000 --- a/pkg/bucket/policy/policy.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/policy_test.go b/pkg/bucket/policy/policy_test.go deleted file mode 100644 index 257675425..000000000 --- a/pkg/bucket/policy/policy_test.go +++ /dev/null @@ -1,1241 +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 . - -package policy - -import ( - "encoding/json" - "net" - "reflect" - "testing" - - "github.com/minio/minio/pkg/bucket/policy/condition" -) - -func TestPolicyIsAllowed(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - )}, - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - )}, - } - - _, IPNet, err := net.ParseCIDR("192.168.1.0/24") - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - func1, err := condition.NewIPAddressFunc( - condition.AWSSourceIP, - IPNet, - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - - case3Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - )}, - } - - case4Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - 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 { - policy Policy - args Args - expectedResult bool - }{ - {case1Policy, anonGetBucketLocationArgs, true}, - {case1Policy, anonPutObjectActionArgs, true}, - {case1Policy, anonGetObjectActionArgs, false}, - {case1Policy, getBucketLocationArgs, true}, - {case1Policy, putObjectActionArgs, true}, - {case1Policy, getObjectActionArgs, true}, - - {case2Policy, anonGetBucketLocationArgs, false}, - {case2Policy, anonPutObjectActionArgs, true}, - {case2Policy, anonGetObjectActionArgs, true}, - {case2Policy, getBucketLocationArgs, true}, - {case2Policy, putObjectActionArgs, true}, - {case2Policy, getObjectActionArgs, true}, - - {case3Policy, anonGetBucketLocationArgs, false}, - {case3Policy, anonPutObjectActionArgs, true}, - {case3Policy, anonGetObjectActionArgs, false}, - {case3Policy, getBucketLocationArgs, true}, - {case3Policy, putObjectActionArgs, true}, - {case3Policy, getObjectActionArgs, true}, - - {case4Policy, anonGetBucketLocationArgs, false}, - {case4Policy, anonPutObjectActionArgs, false}, - {case4Policy, anonGetObjectActionArgs, false}, - {case4Policy, getBucketLocationArgs, true}, - {case4Policy, putObjectActionArgs, false}, - {case4Policy, getObjectActionArgs, true}, - } - - for i, testCase := range testCases { - result := testCase.policy.IsAllowed(testCase.args) - - if result != testCase.expectedResult { - t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) - } - } -} - -func TestPolicyIsEmpty(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case2Policy := Policy{ - ID: "MyPolicyForMyBucket", - Version: DefaultVersion, - } - - testCases := []struct { - policy Policy - expectedResult bool - }{ - {case1Policy, false}, - {case2Policy, true}, - } - - for i, testCase := range testCases { - result := testCase.policy.IsEmpty() - - if result != testCase.expectedResult { - t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) - } - } -} - -func TestPolicyIsValid(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case3Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - 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) - } - - case4Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func2), - ), - }, - } - - case5Policy := Policy{ - Version: "17-10-2012", - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case6Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2), - ), - }, - } - - case7Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case8Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - testCases := []struct { - policy Policy - expectErr bool - }{ - {case1Policy, false}, - // allowed duplicate principal. - {case2Policy, false}, - // allowed duplicate principal and action. - {case3Policy, false}, - // allowed duplicate principal, action and resource. - {case4Policy, false}, - // Invalid version error. - {case5Policy, true}, - // Invalid statement error. - {case6Policy, true}, - // Duplicate statement success different effects. - {case7Policy, false}, - // Duplicate statement success, duplicate statement dropped. - {case8Policy, false}, - } - - for i, testCase := range testCases { - err := testCase.policy.isValid() - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -} - -func TestPolicyMarshalJSON(t *testing.T) { - case1Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - case1Policy.Statements[0].SID = "SomeId1" - case1Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Sid":"SomeId1","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`) - - _, 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) - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - condition.NewFunctions(func1), - ), - }, - } - case2Data := []byte(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/yourobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]}}}]}`) - - case3Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("Q3AM3UQ867SPQQA43P2F"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - case3Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["Q3AM3UQ867SPQQA43P2F"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`) - - case4Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - case4Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`) - - case5Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - condition.NewFunctions(), - ), - }, - } - case5Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/yourobject*"]}]}`) - - _, IPNet2, err := net.ParseCIDR("192.168.2.0/24") - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - func2, err := condition.NewIPAddressFunc( - condition.AWSSourceIP, - IPNet2, - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - - case6Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func2), - ), - }, - } - case6Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]}}},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.2.0/24"]}}}]}`) - - case7Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetBucketLocationAction), - NewResourceSet(NewResource("mybucket", "")), - condition.NewFunctions(), - ), - }, - } - case7Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::mybucket"]}]}`) - - case8Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetBucketLocationAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - ), - }, - } - case8Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::*"]}]}`) - - func3, err := condition.NewNullFunc( - condition.S3XAmzCopySource, - true, - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - case9Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2, func3), - ), - }, - } - - testCases := []struct { - policy Policy - expectedResult []byte - expectErr bool - }{ - {case1Policy, case1Data, false}, - {case2Policy, case2Data, false}, - {case3Policy, case3Data, false}, - {case4Policy, case4Data, false}, - {case5Policy, case5Data, false}, - {case6Policy, case6Data, false}, - {case7Policy, case7Data, false}, - {case8Policy, case8Data, false}, - {case9Policy, nil, true}, - } - - for i, testCase := range testCases { - result, err := json.Marshal(testCase.policy) - 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 TestPolicyUnmarshalJSON(t *testing.T) { - case1Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "SomeId1", - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case1Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - case1Policy.Statements[0].SID = "SomeId1" - - case2Data := []byte(`{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Deny", - "Principal": "*", - "Action": "s3:GetObject", - "Resource": "arn:aws:s3:::mybucket/yourobject*", - "Condition": { - "IpAddress": { - "aws:SourceIp": "192.168.1.0/24" - } - } - } - ] -}`) - _, 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) - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - condition.NewFunctions(func1), - ), - }, - } - - case3Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "AWS": [ - "Q3AM3UQ867SPQQA43P2F" - ] - }, - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case3Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("Q3AM3UQ867SPQQA43P2F"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case4Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:GetObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case4Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case5Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/yourobject*" - } - ] -}`) - case5Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - condition.NewFunctions(), - ), - }, - } - - case6Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*", - "Condition": { - "IpAddress": { - "aws:SourceIp": "192.168.1.0/24" - } - } - }, - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*", - "Condition": { - "IpAddress": { - "aws:SourceIp": "192.168.2.0/24" - } - } - } - ] -}`) - _, IPNet2, err := net.ParseCIDR("192.168.2.0/24") - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - func2, err := condition.NewIPAddressFunc( - condition.AWSSourceIP, - IPNet2, - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - - case6Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ), - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func2), - ), - }, - } - - case7Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:GetBucketLocation", - "Resource": "arn:aws:s3:::mybucket" - } - ] -}`) - - case7Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetBucketLocationAction), - NewResourceSet(NewResource("mybucket", "")), - condition.NewFunctions(), - ), - }, - } - - case8Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:GetBucketLocation", - "Resource": "arn:aws:s3:::*" - } - ] -}`) - - case8Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetBucketLocationAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - ), - }, - } - - case9Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "17-10-2012", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - - case10Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - - case10Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - }, - } - - case11Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Deny", - "Principal": "*", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - - case11Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - NewStatement( - Deny, - NewPrincipal("*"), - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - }, - } - - testCases := []struct { - data []byte - expectedResult Policy - expectErr bool - }{ - {case1Data, case1Policy, false}, - {case2Data, case2Policy, false}, - {case3Data, case3Policy, false}, - {case4Data, case4Policy, false}, - {case5Data, case5Policy, false}, - {case6Data, case6Policy, false}, - {case7Data, case7Policy, false}, - {case8Data, case8Policy, false}, - // Invalid version error. - {case9Data, Policy{}, true}, - // Duplicate statement success, duplicate statement removed. - {case10Data, case10Policy, false}, - // Duplicate statement success (Effect differs). - {case11Data, case11Policy, false}, - } - - for i, testCase := range testCases { - var result Policy - err := json.Unmarshal(testCase.data, &result) - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Errorf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - - if !testCase.expectErr { - if !reflect.DeepEqual(result, testCase.expectedResult) { - t.Errorf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result) - } - } - } -} - -func TestPolicyValidate(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - 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) - } - case2Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - Allow, - NewPrincipal("*"), - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2), - ), - }, - } - - testCases := []struct { - policy Policy - bucketName string - expectErr bool - }{ - {case1Policy, "mybucket", false}, - {case2Policy, "yourbucket", true}, - {case1Policy, "yourbucket", true}, - } - - for i, testCase := range testCases { - err := testCase.policy.Validate(testCase.bucketName) - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -} - -func TestPolicyMerge(t *testing.T) { - testCases := []struct { - policy string - }{ - {`{ - "Version": "2012-10-17", - "Id": "S3PolicyId1", - "Statement": [ - { - "Sid": "statement1", - "Effect": "Deny", - "Principal": "*", - "Action":["s3:GetObject", "s3:PutObject"], - "Resource": "arn:aws:s3:::awsexamplebucket1/*" - } - ] -}`}, - {`{ - "Version": "2012-10-17", - "Id": "S3PolicyId1", - "Statement": [ - { - "Sid": "statement1", - "Effect": "Allow", - "Principal": "*", - "Action":"s3:GetObject", - "Resource": "arn:aws:s3:::awsexamplebucket1/*", - "Condition" : { - "IpAddress" : { - "aws:SourceIp": "192.0.2.0/24" - }, - "NotIpAddress" : { - "aws:SourceIp": "192.0.2.188/32" - } - } - } - ] -}`}, - {`{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "cross-account permission to user in your own account", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::123456789012:user/Dave" - }, - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::awsexamplebucket1/*" - }, - { - "Sid": "Deny your user permission to upload object if copy source is not /bucket/folder", - "Effect": "Deny", - "Principal": { - "AWS": "arn:aws:iam::123456789012:user/Dave" - }, - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::awsexamplebucket1/*", - "Condition": { - "StringNotLike": { - "s3:x-amz-copy-source": "awsexamplebucket1/public/*" - } - } - } - ] -}`}, - } - - for i, testCase := range testCases { - var p Policy - err := json.Unmarshal([]byte(testCase.policy), &p) - if err != nil { - t.Fatalf("case %v: unexpected error: %v", i+1, err) - } - - var clonedPolicy Policy - clonedPolicy = clonedPolicy.Merge(p) - - j, err := json.Marshal(clonedPolicy) - if err != nil { - t.Fatalf("case %v: unexpected error: %v", i+1, err) - } - - err = json.Unmarshal(j, &clonedPolicy) - if err != nil { - t.Fatalf("case %v: unexpected error: %v", i+1, err) - } - - if !clonedPolicy.Statements[0].Equals(p.Statements[0]) { - t.Fatalf("case %v: different policy outcome", i+1) - } - } -} diff --git a/pkg/bucket/policy/principal.go b/pkg/bucket/policy/principal.go deleted file mode 100644 index 732df7433..000000000 --- a/pkg/bucket/policy/principal.go +++ /dev/null @@ -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 . - -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...)} -} diff --git a/pkg/bucket/policy/principal_test.go b/pkg/bucket/policy/principal_test.go deleted file mode 100644 index eb76db635..000000000 --- a/pkg/bucket/policy/principal_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/bucket/policy/resource.go b/pkg/bucket/policy/resource.go deleted file mode 100644 index 07df24276..000000000 --- a/pkg/bucket/policy/resource.go +++ /dev/null @@ -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 . - -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, - } -} diff --git a/pkg/bucket/policy/resource_test.go b/pkg/bucket/policy/resource_test.go deleted file mode 100644 index a780bf4bf..000000000 --- a/pkg/bucket/policy/resource_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/bucket/policy/resourceset.go b/pkg/bucket/policy/resourceset.go deleted file mode 100644 index 1c37c7d79..000000000 --- a/pkg/bucket/policy/resourceset.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/bucket/policy/resourceset_test.go b/pkg/bucket/policy/resourceset_test.go deleted file mode 100644 index 8e74f805c..000000000 --- a/pkg/bucket/policy/resourceset_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/bucket/policy/statement.go b/pkg/bucket/policy/statement.go deleted file mode 100644 index 7614fcabc..000000000 --- a/pkg/bucket/policy/statement.go +++ /dev/null @@ -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 . - -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, - } -} diff --git a/pkg/bucket/policy/statement_test.go b/pkg/bucket/policy/statement_test.go deleted file mode 100644 index 92b584b0a..000000000 --- a/pkg/bucket/policy/statement_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/iam/policy/action.go b/pkg/iam/policy/action.go deleted file mode 100644 index af2652565..000000000 --- a/pkg/iam/policy/action.go +++ /dev/null @@ -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 . - -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...)...), -} diff --git a/pkg/iam/policy/action_test.go b/pkg/iam/policy/action_test.go deleted file mode 100644 index f1e940910..000000000 --- a/pkg/iam/policy/action_test.go +++ /dev/null @@ -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 . - -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) - } - } -} diff --git a/pkg/iam/policy/actionset.go b/pkg/iam/policy/actionset.go deleted file mode 100644 index d8f1be3c0..000000000 --- a/pkg/iam/policy/actionset.go +++ /dev/null @@ -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 . - -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 -} diff --git a/pkg/iam/policy/actionset_test.go b/pkg/iam/policy/actionset_test.go deleted file mode 100644 index 57c58edf3..000000000 --- a/pkg/iam/policy/actionset_test.go +++ /dev/null @@ -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 . - -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) - } - } - } -} diff --git a/pkg/iam/policy/admin-action.go b/pkg/iam/policy/admin-action.go deleted file mode 100644 index 241b41bc1..000000000 --- a/pkg/iam/policy/admin-action.go +++ /dev/null @@ -1,246 +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 . - -package iampolicy - -import ( - "github.com/minio/minio/pkg/bucket/policy/condition" -) - -// AdminAction - admin policy action. -type AdminAction string - -const ( - // HealAdminAction - allows heal command - HealAdminAction = "admin:Heal" - - // Service Actions - - // StorageInfoAdminAction - allow listing server info - StorageInfoAdminAction = "admin:StorageInfo" - // PrometheusAdminAction - prometheus info action - PrometheusAdminAction = "admin:Prometheus" - // DataUsageInfoAdminAction - allow listing data usage info - DataUsageInfoAdminAction = "admin:DataUsageInfo" - // ForceUnlockAdminAction - allow force unlocking locks - ForceUnlockAdminAction = "admin:ForceUnlock" - // TopLocksAdminAction - allow listing top locks - TopLocksAdminAction = "admin:TopLocksInfo" - // ProfilingAdminAction - allow profiling - ProfilingAdminAction = "admin:Profiling" - // TraceAdminAction - allow listing server trace - TraceAdminAction = "admin:ServerTrace" - // ConsoleLogAdminAction - allow listing console logs on terminal - ConsoleLogAdminAction = "admin:ConsoleLog" - // KMSCreateKeyAdminAction - allow creating a new KMS master key - KMSCreateKeyAdminAction = "admin:KMSCreateKey" - // KMSKeyStatusAdminAction - allow getting KMS key status - KMSKeyStatusAdminAction = "admin:KMSKeyStatus" - // ServerInfoAdminAction - allow listing server info - ServerInfoAdminAction = "admin:ServerInfo" - // HealthInfoAdminAction - allow obtaining cluster health information - HealthInfoAdminAction = "admin:OBDInfo" - // BandwidthMonitorAction - allow monitoring bandwidth usage - BandwidthMonitorAction = "admin:BandwidthMonitor" - - // ServerUpdateAdminAction - allow MinIO binary update - ServerUpdateAdminAction = "admin:ServerUpdate" - // ServiceRestartAdminAction - allow restart of MinIO service. - ServiceRestartAdminAction = "admin:ServiceRestart" - // ServiceStopAdminAction - allow stopping MinIO service. - ServiceStopAdminAction = "admin:ServiceStop" - - // ConfigUpdateAdminAction - allow MinIO config management - ConfigUpdateAdminAction = "admin:ConfigUpdate" - - // CreateUserAdminAction - allow creating MinIO user - CreateUserAdminAction = "admin:CreateUser" - // DeleteUserAdminAction - allow deleting MinIO user - DeleteUserAdminAction = "admin:DeleteUser" - // ListUsersAdminAction - allow list users permission - ListUsersAdminAction = "admin:ListUsers" - // EnableUserAdminAction - allow enable user permission - EnableUserAdminAction = "admin:EnableUser" - // DisableUserAdminAction - allow disable user permission - DisableUserAdminAction = "admin:DisableUser" - // GetUserAdminAction - allows GET permission on user info - GetUserAdminAction = "admin:GetUser" - - // Service account Actions - - // CreateServiceAccountAdminAction - allow create a service account for a user - CreateServiceAccountAdminAction = "admin:CreateServiceAccount" - // UpdateServiceAccountAdminAction - allow updating a service account - UpdateServiceAccountAdminAction = "admin:UpdateServiceAccount" - // RemoveServiceAccountAdminAction - allow removing a service account - RemoveServiceAccountAdminAction = "admin:RemoveServiceAccount" - // ListServiceAccountsAdminAction - allow listing service accounts - ListServiceAccountsAdminAction = "admin:ListServiceAccounts" - - // Group Actions - - // AddUserToGroupAdminAction - allow adding user to group permission - AddUserToGroupAdminAction = "admin:AddUserToGroup" - // RemoveUserFromGroupAdminAction - allow removing user to group permission - RemoveUserFromGroupAdminAction = "admin:RemoveUserFromGroup" - // GetGroupAdminAction - allow getting group info - GetGroupAdminAction = "admin:GetGroup" - // ListGroupsAdminAction - allow list groups permission - ListGroupsAdminAction = "admin:ListGroups" - // EnableGroupAdminAction - allow enable group permission - EnableGroupAdminAction = "admin:EnableGroup" - // DisableGroupAdminAction - allow disable group permission - DisableGroupAdminAction = "admin:DisableGroup" - - // Policy Actions - - // CreatePolicyAdminAction - allow create policy permission - CreatePolicyAdminAction = "admin:CreatePolicy" - // DeletePolicyAdminAction - allow delete policy permission - DeletePolicyAdminAction = "admin:DeletePolicy" - // GetPolicyAdminAction - allow get policy permission - GetPolicyAdminAction = "admin:GetPolicy" - // AttachPolicyAdminAction - allows attaching a policy to a user/group - AttachPolicyAdminAction = "admin:AttachUserOrGroupPolicy" - // ListUserPoliciesAdminAction - allows listing user policies - ListUserPoliciesAdminAction = "admin:ListUserPolicies" - - // Bucket quota Actions - - // SetBucketQuotaAdminAction - allow setting bucket quota - SetBucketQuotaAdminAction = "admin:SetBucketQuota" - // GetBucketQuotaAdminAction - allow getting bucket quota - GetBucketQuotaAdminAction = "admin:GetBucketQuota" - - // Bucket Target admin Actions - - // SetBucketTargetAction - allow setting bucket target - SetBucketTargetAction = "admin:SetBucketTarget" - // GetBucketTargetAction - allow getting bucket targets - GetBucketTargetAction = "admin:GetBucketTarget" - - // Remote Tier admin Actions - - // SetTierAction - allow adding/editing a remote tier - SetTierAction = "admin:SetTier" - // ListTierAction - allow listing remote tiers - ListTierAction = "admin:ListTier" - - // AllAdminActions - provides all admin permissions - AllAdminActions = "admin:*" -) - -// List of all supported admin actions. -var supportedAdminActions = map[AdminAction]struct{}{ - HealAdminAction: {}, - StorageInfoAdminAction: {}, - DataUsageInfoAdminAction: {}, - TopLocksAdminAction: {}, - ProfilingAdminAction: {}, - PrometheusAdminAction: {}, - TraceAdminAction: {}, - ConsoleLogAdminAction: {}, - KMSKeyStatusAdminAction: {}, - ServerInfoAdminAction: {}, - HealthInfoAdminAction: {}, - BandwidthMonitorAction: {}, - ServerUpdateAdminAction: {}, - ServiceRestartAdminAction: {}, - ServiceStopAdminAction: {}, - ConfigUpdateAdminAction: {}, - CreateUserAdminAction: {}, - DeleteUserAdminAction: {}, - ListUsersAdminAction: {}, - EnableUserAdminAction: {}, - DisableUserAdminAction: {}, - GetUserAdminAction: {}, - AddUserToGroupAdminAction: {}, - RemoveUserFromGroupAdminAction: {}, - GetGroupAdminAction: {}, - ListGroupsAdminAction: {}, - EnableGroupAdminAction: {}, - DisableGroupAdminAction: {}, - CreateServiceAccountAdminAction: {}, - UpdateServiceAccountAdminAction: {}, - RemoveServiceAccountAdminAction: {}, - ListServiceAccountsAdminAction: {}, - CreatePolicyAdminAction: {}, - DeletePolicyAdminAction: {}, - GetPolicyAdminAction: {}, - AttachPolicyAdminAction: {}, - ListUserPoliciesAdminAction: {}, - SetBucketQuotaAdminAction: {}, - GetBucketQuotaAdminAction: {}, - SetBucketTargetAction: {}, - GetBucketTargetAction: {}, - SetTierAction: {}, - ListTierAction: {}, - AllAdminActions: {}, -} - -// IsValid - checks if action is valid or not. -func (action AdminAction) IsValid() bool { - _, ok := supportedAdminActions[action] - return ok -} - -// adminActionConditionKeyMap - holds mapping of supported condition key for an action. -var adminActionConditionKeyMap = map[Action]condition.KeySet{ - AllAdminActions: condition.NewKeySet(condition.AllSupportedAdminKeys...), - HealAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - StorageInfoAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ServerInfoAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - DataUsageInfoAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - HealthInfoAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - BandwidthMonitorAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - TopLocksAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ProfilingAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - TraceAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ConsoleLogAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - KMSKeyStatusAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ServerUpdateAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ServiceRestartAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ServiceStopAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ConfigUpdateAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - CreateUserAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - DeleteUserAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ListUsersAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - EnableUserAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - DisableUserAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - GetUserAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - AddUserToGroupAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - RemoveUserFromGroupAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ListGroupsAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - EnableGroupAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - DisableGroupAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - CreateServiceAccountAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - UpdateServiceAccountAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - RemoveServiceAccountAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ListServiceAccountsAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - - CreatePolicyAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - DeletePolicyAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - GetPolicyAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - AttachPolicyAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ListUserPoliciesAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - SetBucketQuotaAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - GetBucketQuotaAdminAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - SetBucketTargetAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - GetBucketTargetAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - SetTierAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), - ListTierAction: condition.NewKeySet(condition.AllSupportedAdminKeys...), -} diff --git a/pkg/iam/policy/constants.go b/pkg/iam/policy/constants.go deleted file mode 100644 index e1aa9de52..000000000 --- a/pkg/iam/policy/constants.go +++ /dev/null @@ -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 . - -package iampolicy - -import ( - "github.com/minio/minio/pkg/bucket/policy" - "github.com/minio/minio/pkg/bucket/policy/condition" -) - -// Policy claim constants -const ( - PolicyName = "policy" - SessionPolicyName = "sessionPolicy" -) - -// ReadWrite - provides full access to all buckets and all objects -var ReadWrite = Policy{ - Version: DefaultVersion, - Statements: []Statement{ - { - SID: policy.ID(""), - Effect: policy.Allow, - Actions: NewActionSet(AllActions), - Resources: NewResourceSet(NewResource("*", "")), - }, - }, -} - -// ReadOnly - read only. -var ReadOnly = Policy{ - Version: DefaultVersion, - Statements: []Statement{ - { - SID: policy.ID(""), - Effect: policy.Allow, - Actions: NewActionSet(GetBucketLocationAction, GetObjectAction), - Resources: NewResourceSet(NewResource("*", "")), - }, - }, -} - -// WriteOnly - provides write access. -var WriteOnly = Policy{ - Version: DefaultVersion, - Statements: []Statement{ - { - SID: policy.ID(""), - Effect: policy.Allow, - Actions: NewActionSet(PutObjectAction), - Resources: NewResourceSet(NewResource("*", "")), - }, - }, -} - -// AdminDiagnostics - provides admin diagnostics access. -var AdminDiagnostics = Policy{ - Version: DefaultVersion, - Statements: []Statement{ - { - SID: policy.ID(""), - Effect: policy.Allow, - Actions: NewActionSet(ProfilingAdminAction, - TraceAdminAction, ConsoleLogAdminAction, - ServerInfoAdminAction, TopLocksAdminAction, - HealthInfoAdminAction, BandwidthMonitorAction, - PrometheusAdminAction, - ), - Resources: NewResourceSet(NewResource("*", "")), - }, - }, -} - -// Admin - provides admin all-access canned policy -var Admin = Policy{ - Version: DefaultVersion, - Statements: []Statement{ - { - SID: policy.ID(""), - Effect: policy.Allow, - Actions: NewActionSet(AllAdminActions), - Resources: NewResourceSet(), - Conditions: condition.NewFunctions(), - }, - { - SID: policy.ID(""), - Effect: policy.Allow, - Actions: NewActionSet(AllActions), - Resources: NewResourceSet(NewResource("*", "")), - Conditions: condition.NewFunctions(), - }, - }, -} diff --git a/pkg/iam/policy/error.go b/pkg/iam/policy/error.go deleted file mode 100644 index 8aa2cf3c5..000000000 --- a/pkg/iam/policy/error.go +++ /dev/null @@ -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 . - -package iampolicy - -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 "iam: cause " - } - return e.err.Error() -} diff --git a/pkg/iam/policy/policy.go b/pkg/iam/policy/policy.go deleted file mode 100644 index de8b86acb..000000000 --- a/pkg/iam/policy/policy.go +++ /dev/null @@ -1,229 +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 . - -package iampolicy - -import ( - "encoding/json" - "io" - "strings" - - "github.com/minio/minio-go/v7/pkg/set" - "github.com/minio/minio/pkg/bucket/policy" -) - -// 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"` - Claims map[string]interface{} `json:"claims"` - DenyOnly bool `json:"denyOnly"` // only applies deny -} - -// GetPoliciesFromClaims returns the list of policies to be applied for this -// incoming request, extracting the information from input JWT claims. -func GetPoliciesFromClaims(claims map[string]interface{}, policyClaimName string) (set.StringSet, bool) { - s := set.NewStringSet() - pname, ok := claims[policyClaimName] - if !ok { - return s, false - } - pnames, ok := pname.([]interface{}) - if !ok { - pnameStr, ok := pname.(string) - if ok { - for _, pname := range strings.Split(pnameStr, ",") { - pname = strings.TrimSpace(pname) - if pname == "" { - // ignore any empty strings, considerate - // towards some user errors. - continue - } - s.Add(pname) - } - return s, true - } - return s, false - } - for _, pname := range pnames { - pnameStr, ok := pname.(string) - if ok { - for _, pnameStr := range strings.Split(pnameStr, ",") { - pnameStr = strings.TrimSpace(pnameStr) - if pnameStr == "" { - // ignore any empty strings, considerate - // towards some user errors. - continue - } - s.Add(pnameStr) - } - } - } - return s, true -} - -// GetPolicies returns the list of policies to be applied for this -// incoming request, extracting the information from JWT claims. -func (a Args) GetPolicies(policyClaimName string) (set.StringSet, bool) { - return GetPoliciesFromClaims(a.Claims, policyClaimName) -} - -// Policy - iam bucket iamp. -type Policy struct { - ID policy.ID `json:"ID,omitempty"` - Version string - Statements []Statement `json:"Statement"` -} - -// MatchResource matches resource with match resource patterns -func (iamp Policy) MatchResource(resource string) bool { - for _, statement := range iamp.Statements { - if statement.Resources.MatchResource(resource) { - return true - } - } - return false -} - -// IsAllowed - checks given policy args is allowed to continue the Rest API. -func (iamp Policy) IsAllowed(args Args) bool { - // Check all deny statements. If any one statement denies, return false. - for _, statement := range iamp.Statements { - if statement.Effect == policy.Deny { - if !statement.IsAllowed(args) { - return false - } - } - } - - // Applied any 'Deny' only policies, if we have - // reached here it means that there were no 'Deny' - // policies - this function mainly used for - // specific scenarios where we only want to validate - // 'Deny' only policies. - if args.DenyOnly { - return true - } - - // 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 iamp.Statements { - if statement.Effect == policy.Allow { - if statement.IsAllowed(args) { - return true - } - } - } - - return false -} - -// IsEmpty - returns whether policy is empty or not. -func (iamp Policy) IsEmpty() bool { - return len(iamp.Statements) == 0 -} - -// isValid - checks if Policy is valid or not. -func (iamp Policy) isValid() error { - if iamp.Version != DefaultVersion && iamp.Version != "" { - return Errorf("invalid version '%v'", iamp.Version) - } - - for _, statement := range iamp.Statements { - if err := statement.isValid(); err != nil { - return err - } - } - return nil -} - -// Merge merges two policies documents and drop -// duplicate statements if any. -func (iamp Policy) Merge(input Policy) Policy { - var mergedPolicy Policy - if iamp.Version != "" { - mergedPolicy.Version = iamp.Version - } else { - mergedPolicy.Version = input.Version - } - for _, st := range iamp.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 (iamp *Policy) dropDuplicateStatements() { -redo: - for i := range iamp.Statements { - for j, statement := range iamp.Statements[i+1:] { - if !iamp.Statements[i].Equals(statement) { - continue - } - iamp.Statements = append(iamp.Statements[:j], iamp.Statements[j+1:]...) - goto redo - } - } -} - -// UnmarshalJSON - decodes JSON data to Iamp. -func (iamp *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) - p.dropDuplicateStatements() - *iamp = p - return nil -} - -// Validate - validates all statements are for given bucket or not. -func (iamp Policy) Validate() error { - return iamp.isValid() -} - -// ParseConfig - parses data in given reader to Iamp. -func ParseConfig(reader io.Reader) (*Policy, error) { - var iamp Policy - - decoder := json.NewDecoder(reader) - decoder.DisallowUnknownFields() - if err := decoder.Decode(&iamp); err != nil { - return nil, Errorf("%w", err) - } - - return &iamp, iamp.Validate() -} diff --git a/pkg/iam/policy/policy_test.go b/pkg/iam/policy/policy_test.go deleted file mode 100644 index 3aa9e220d..000000000 --- a/pkg/iam/policy/policy_test.go +++ /dev/null @@ -1,1173 +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 . - -package iampolicy - -import ( - "encoding/json" - "net" - "reflect" - "strings" - "testing" - "time" - - "github.com/minio/minio-go/v7/pkg/set" - "github.com/minio/minio/pkg/bucket/policy" - "github.com/minio/minio/pkg/bucket/policy/condition" -) - -func TestGetPoliciesFromClaims(t *testing.T) { - attributesArray := `{ - "exp": 1594690452, - "iat": 1594689552, - "auth_time": 1594689552, - "jti": "18ed05c9-2c69-45d5-a33f-8c94aca99ad5", - "iss": "http://localhost:8080/auth/realms/minio", - "aud": "account", - "sub": "7e5e2f30-1c97-4616-8623-2eae14dee9b1", - "typ": "ID", - "azp": "account", - "nonce": "66ZoLzwJbjdkiedI", - "session_state": "3df7b526-5310-4038-9f35-50ecd295a31d", - "acr": "1", - "upn": "harsha", - "address": {}, - "email_verified": false, - "groups": [ - "offline_access" - ], - "preferred_username": "harsha", - "policy": [ - "readwrite", - "readwrite,readonly", - " readonly", - "" - ]}` - var m = make(map[string]interface{}) - if err := json.Unmarshal([]byte(attributesArray), &m); err != nil { - t.Fatal(err) - } - var expectedSet = set.CreateStringSet("readwrite", "readonly") - gotSet, ok := GetPoliciesFromClaims(m, "policy") - if !ok { - t.Fatal("no policy claim was found") - } - if gotSet.IsEmpty() { - t.Fatal("no policies were found in policy claim") - } - if !gotSet.Equals(expectedSet) { - t.Fatalf("Expected %v got %v", expectedSet, gotSet) - } -} - -func TestPolicyIsAllowed(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - )}, - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - )}, - } - - _, IPNet, err := net.ParseCIDR("192.168.1.0/24") - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - func1, err := condition.NewIPAddressFunc( - condition.AWSSourceIP, - IPNet, - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - - case3Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - )}, - } - - case4Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Deny, - 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{}, - } - - putObjectActionArgs := Args{ - AccountName: "Q3AM3UQ867SPQQA43P2F", - Action: PutObjectAction, - BucketName: "mybucket", - ConditionValues: map[string][]string{ - "x-amz-copy-source": {"mybucket/myobject"}, - "SourceIp": {"192.168.1.10"}, - }, - ObjectName: "myobject", - } - - getObjectActionArgs := Args{ - AccountName: "Q3AM3UQ867SPQQA43P2F", - Action: GetObjectAction, - BucketName: "mybucket", - ConditionValues: map[string][]string{}, - ObjectName: "myobject", - } - - testCases := []struct { - policy Policy - args Args - expectedResult bool - }{ - {case1Policy, anonGetBucketLocationArgs, true}, - {case1Policy, anonPutObjectActionArgs, true}, - {case1Policy, anonGetObjectActionArgs, false}, - {case1Policy, getBucketLocationArgs, true}, - {case1Policy, putObjectActionArgs, true}, - {case1Policy, getObjectActionArgs, false}, - - {case2Policy, anonGetBucketLocationArgs, false}, - {case2Policy, anonPutObjectActionArgs, true}, - {case2Policy, anonGetObjectActionArgs, true}, - {case2Policy, getBucketLocationArgs, false}, - {case2Policy, putObjectActionArgs, true}, - {case2Policy, getObjectActionArgs, true}, - - {case3Policy, anonGetBucketLocationArgs, false}, - {case3Policy, anonPutObjectActionArgs, true}, - {case3Policy, anonGetObjectActionArgs, false}, - {case3Policy, getBucketLocationArgs, false}, - {case3Policy, putObjectActionArgs, true}, - {case3Policy, getObjectActionArgs, false}, - - {case4Policy, anonGetBucketLocationArgs, false}, - {case4Policy, anonPutObjectActionArgs, false}, - {case4Policy, anonGetObjectActionArgs, false}, - {case4Policy, getBucketLocationArgs, false}, - {case4Policy, putObjectActionArgs, false}, - {case4Policy, getObjectActionArgs, false}, - } - - for i, testCase := range testCases { - result := testCase.policy.IsAllowed(testCase.args) - - if result != testCase.expectedResult { - t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) - } - } -} - -func TestPolicyIsEmpty(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case2Policy := Policy{ - ID: "MyPolicyForMyBucket", - Version: DefaultVersion, - } - - testCases := []struct { - policy Policy - expectedResult bool - }{ - {case1Policy, false}, - {case2Policy, true}, - } - - for i, testCase := range testCases { - result := testCase.policy.IsEmpty() - - if result != testCase.expectedResult { - t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) - } - } -} - -func TestPolicyIsValid(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Deny, - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case3Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Deny, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - 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) - } - - case4Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ), - NewStatement( - policy.Deny, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func2), - ), - }, - } - - case5Policy := Policy{ - Version: "17-10-2012", - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case6Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2), - ), - }, - } - - case7Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Deny, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case8Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - testCases := []struct { - policy Policy - expectErr bool - }{ - {case1Policy, false}, - // allowed duplicate principal. - {case2Policy, false}, - // allowed duplicate principal and action. - {case3Policy, false}, - // allowed duplicate principal, action and resource. - {case4Policy, false}, - // Invalid version error. - {case5Policy, true}, - // Invalid statement error. - {case6Policy, true}, - // Duplicate statement different Effects. - {case7Policy, false}, - // Duplicate statement same Effects, duplicate effect will be removed. - {case8Policy, false}, - } - - for i, testCase := range testCases { - err := testCase.policy.isValid() - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -} - -// Parse config with location constraints -func TestPolicyParseConfig(t *testing.T) { - policy1LocationConstraint := `{ - "Version":"2012-10-17", - "Statement":[ - { - "Sid":"statement1", - "Effect":"Allow", - "Action": "s3:CreateBucket", - "Resource": "arn:aws:s3:::*", - "Condition": { - "StringLike": { - "s3:LocationConstraint": "us-east-1" - } - } - }, - { - "Sid":"statement2", - "Effect":"Deny", - "Action": "s3:CreateBucket", - "Resource": "arn:aws:s3:::*", - "Condition": { - "StringNotLike": { - "s3:LocationConstraint": "us-east-1" - } - } - } - ] -}` - policy2Condition := `{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "statement1", - "Effect": "Allow", - "Action": "s3:GetObjectVersion", - "Resource": "arn:aws:s3:::test/HappyFace.jpg" - }, - { - "Sid": "statement2", - "Effect": "Deny", - "Action": "s3:GetObjectVersion", - "Resource": "arn:aws:s3:::test/HappyFace.jpg", - "Condition": { - "StringNotEquals": { - "s3:versionid": "AaaHbAQitwiL_h47_44lRO2DDfLlBO5e" - } - } - } - ] -}` - - policy3ConditionActionRegex := `{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "statement2", - "Effect": "Allow", - "Action": "s3:Get*", - "Resource": "arn:aws:s3:::test/HappyFace.jpg", - "Condition": { - "StringEquals": { - "s3:versionid": "AaaHbAQitwiL_h47_44lRO2DDfLlBO5e" - } - } - } - ] -}` - - policy4ConditionAction := `{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "statement2", - "Effect": "Allow", - "Action": "s3:GetObject", - "Resource": "arn:aws:s3:::test/HappyFace.jpg", - "Condition": { - "StringEquals": { - "s3:versionid": "AaaHbAQitwiL_h47_44lRO2DDfLlBO5e" - } - } - } - ] -}` - - policy5ConditionCurrenTime := `{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:Get*", - "s3:Put*" - ], - "Resource": [ - "arn:aws:s3:::test/*" - ], - "Condition": { - "DateGreaterThan": { - "aws:CurrentTime": [ - "2017-02-28T00:00:00Z" - ] - } - } - } - ] -}` - - policy5ConditionCurrenTimeLesser := `{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:Get*", - "s3:Put*" - ], - "Resource": [ - "arn:aws:s3:::test/*" - ], - "Condition": { - "DateLessThan": { - "aws:CurrentTime": [ - "2017-02-28T00:00:00Z" - ] - } - } - } - ] -}` - - tests := []struct { - p string - args Args - allowed bool - }{ - { - p: policy1LocationConstraint, - allowed: true, - args: Args{ - AccountName: "allowed", - Action: CreateBucketAction, - BucketName: "test", - ConditionValues: map[string][]string{"LocationConstraint": {"us-east-1"}}, - }, - }, - { - p: policy1LocationConstraint, - allowed: false, - args: Args{ - AccountName: "disallowed", - Action: CreateBucketAction, - BucketName: "test", - ConditionValues: map[string][]string{"LocationConstraint": {"us-east-2"}}, - }, - }, - { - p: policy2Condition, - allowed: true, - args: Args{ - AccountName: "allowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{"versionid": {"AaaHbAQitwiL_h47_44lRO2DDfLlBO5e"}}, - }, - }, - { - p: policy2Condition, - allowed: false, - args: Args{ - AccountName: "disallowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{"versionid": {"AaaHbAQitwiL_h47_44lRO2DDfLlBO5f"}}, - }, - }, - { - p: policy3ConditionActionRegex, - allowed: true, - args: Args{ - AccountName: "allowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{"versionid": {"AaaHbAQitwiL_h47_44lRO2DDfLlBO5e"}}, - }, - }, - { - p: policy3ConditionActionRegex, - allowed: false, - args: Args{ - AccountName: "disallowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{"versionid": {"AaaHbAQitwiL_h47_44lRO2DDfLlBO5f"}}, - }, - }, - { - p: policy4ConditionAction, - allowed: true, - args: Args{ - AccountName: "allowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{"versionid": {"AaaHbAQitwiL_h47_44lRO2DDfLlBO5e"}}, - }, - }, - { - p: policy5ConditionCurrenTime, - allowed: true, - args: Args{ - AccountName: "allowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{ - "CurrentTime": {time.Now().Format(time.RFC3339)}, - }, - }, - }, - { - p: policy5ConditionCurrenTimeLesser, - allowed: false, - args: Args{ - AccountName: "disallowed", - Action: GetObjectAction, - BucketName: "test", - ObjectName: "HappyFace.jpg", - ConditionValues: map[string][]string{ - "CurrentTime": {time.Now().Format(time.RFC3339)}, - }, - }, - }, - } - for _, test := range tests { - test := test - t.Run(test.args.AccountName, func(t *testing.T) { - ip, err := ParseConfig(strings.NewReader(test.p)) - if err != nil { - t.Error(err) - } - if got := ip.IsAllowed(test.args); got != test.allowed { - t.Errorf("Expected %t, got %t", test.allowed, got) - } - }) - } -} - -func TestPolicyUnmarshalJSONAndValidate(t *testing.T) { - case1Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "SomeId1", - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case1Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - case1Policy.Statements[0].SID = "SomeId1" - - case2Data := []byte(`{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Deny", - "Action": "s3:GetObject", - "Resource": "arn:aws:s3:::mybucket/yourobject*", - "Condition": { - "IpAddress": { - "aws:SourceIp": "192.168.1.0/24" - } - } - } - ] -}`) - _, 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) - } - - case2Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Deny, - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - condition.NewFunctions(func1), - ), - }, - } - - case3Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:GetObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case3Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case4Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Action": "s3:GetObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case4Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - }, - } - - case5Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/yourobject*" - } - ] -}`) - case5Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/yourobject*")), - condition.NewFunctions(), - ), - }, - } - - case6Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*", - "Condition": { - "IpAddress": { - "aws:SourceIp": "192.168.1.0/24" - } - } - }, - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*", - "Condition": { - "IpAddress": { - "aws:SourceIp": "192.168.2.0/24" - } - } - } - ] -}`) - _, IPNet2, err := net.ParseCIDR("192.168.2.0/24") - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - func2, err := condition.NewIPAddressFunc( - condition.AWSSourceIP, - IPNet2, - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - - case6Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ), - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func2), - ), - }, - } - - case7Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:GetBucketLocation", - "Resource": "arn:aws:s3:::mybucket" - } - ] -}`) - - case7Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction), - NewResourceSet(NewResource("mybucket", "")), - condition.NewFunctions(), - ), - }, - } - - case8Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:GetBucketLocation", - "Resource": "arn:aws:s3:::*" - } - ] -}`) - - case8Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - ), - }, - } - - case9Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "17-10-2012", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - - case10Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - case10Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - }, - } - - case11Data := []byte(`{ - "ID": "MyPolicyForMyBucket1", - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - }, - { - "Effect": "Deny", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" - } - ] -}`) - - case11Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - NewStatement( - policy.Deny, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - }, - } - - testCases := []struct { - data []byte - expectedResult Policy - expectUnmarshalErr bool - expectValidationErr bool - }{ - {case1Data, case1Policy, false, false}, - {case2Data, case2Policy, false, false}, - {case3Data, case3Policy, false, false}, - {case4Data, case4Policy, false, false}, - {case5Data, case5Policy, false, false}, - {case6Data, case6Policy, false, false}, - {case7Data, case7Policy, false, false}, - {case8Data, case8Policy, false, false}, - // Invalid version error. - {case9Data, Policy{}, false, true}, - // Duplicate statement success, duplicate statement is removed. - {case10Data, case10Policy, false, false}, - // Duplicate statement success (Effect differs). - {case11Data, case11Policy, false, false}, - } - - for i, testCase := range testCases { - var result Policy - err := json.Unmarshal(testCase.data, &result) - expectErr := (err != nil) - - if expectErr != testCase.expectUnmarshalErr { - t.Errorf("case %v: error during unmarshal: expected: %v, got: %v", i+1, testCase.expectUnmarshalErr, expectErr) - } - - err = result.Validate() - expectErr = (err != nil) - - if expectErr != testCase.expectValidationErr { - t.Errorf("case %v: error during validation: expected: %v, got: %v", i+1, testCase.expectValidationErr, expectErr) - } - - if !testCase.expectUnmarshalErr && !testCase.expectValidationErr { - if !reflect.DeepEqual(result, testCase.expectedResult) { - t.Errorf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result) - } - } - } -} - -func TestPolicyValidate(t *testing.T) { - case1Policy := Policy{ - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("", "")), - 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) - } - case2Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2), - ), - }, - } - - case3Policy := Policy{ - ID: "MyPolicyForMyBucket1", - Version: DefaultVersion, - Statements: []Statement{ - NewStatement( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), - }, - } - - testCases := []struct { - policy Policy - expectErr bool - }{ - {case1Policy, true}, - {case2Policy, true}, - {case3Policy, false}, - } - - for i, testCase := range testCases { - err := testCase.policy.Validate() - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -} diff --git a/pkg/iam/policy/resource.go b/pkg/iam/policy/resource.go deleted file mode 100644 index 8d8ea337f..000000000 --- a/pkg/iam/policy/resource.go +++ /dev/null @@ -1,143 +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 . - -package iampolicy - -import ( - "encoding/json" - "path" - "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, "/") || r.Pattern == "*" -} - -func (r Resource) isObjectPattern() bool { - return strings.Contains(r.Pattern, "/") || strings.Contains(r.BucketName, "*") || r.Pattern == "*/*" -} - -// IsValid - checks whether Resource is valid or not. -func (r Resource) IsValid() bool { - return r.Pattern != "" -} - -// MatchResource matches object name with resource pattern only. -func (r Resource) MatchResource(resource string) bool { - return r.Match(resource, nil) -} - -// Match - matches object name with resource pattern, including specific conditionals. -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) - } - } - if cp := path.Clean(resource); cp != "." && cp == pattern { - return true - } - 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() error { - if !r.IsValid() { - return Errorf("invalid resource") - } - 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, - } -} diff --git a/pkg/iam/policy/resource_test.go b/pkg/iam/policy/resource_test.go deleted file mode 100644 index a383084e8..000000000 --- a/pkg/iam/policy/resource_test.go +++ /dev/null @@ -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 . - -package iampolicy - -import ( - "encoding/json" - "fmt" - "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("", "*"), true}, - {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 { - testCase := testCase - t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { - result := testCase.resource.Match(testCase.objectName, nil) - if result != testCase.expectedResult { - t.Errorf("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("", "*"), []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}, - } - - 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 - expectErr bool - }{ - {NewResource("mybucket", "/myobject*"), false}, - {NewResource("", "/myobject*"), false}, - {NewResource("", ""), true}, - } - - for i, testCase := range testCases { - err := testCase.resource.Validate() - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -} diff --git a/pkg/iam/policy/resourceset.go b/pkg/iam/policy/resourceset.go deleted file mode 100644 index 3b16568ca..000000000 --- a/pkg/iam/policy/resourceset.go +++ /dev/null @@ -1,191 +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 . - -package iampolicy - -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 resources available in both ResourceSet. -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 resource set") - } - - resources := []Resource{} - for resource := range resourceSet { - resources = append(resources, resource) - } - - return json.Marshal(resources) -} - -// MatchResource matches object name with resource patterns only. -func (resourceSet ResourceSet) MatchResource(resource string) bool { - for r := range resourceSet { - if r.MatchResource(resource) { - return true - } - } - return false -} - -// 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. -func (resourceSet ResourceSet) Validate() error { - for resource := range resourceSet { - if err := resource.Validate(); 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 -} diff --git a/pkg/iam/policy/resourceset_test.go b/pkg/iam/policy/resourceset_test.go deleted file mode 100644 index 5cf4f6692..000000000 --- a/pkg/iam/policy/resourceset_test.go +++ /dev/null @@ -1,243 +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 . - -package iampolicy - -import ( - "encoding/json" - "fmt" - "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 { - testCase := testCase - t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { - result := testCase.resourceSet.Match(testCase.resource, nil) - if result != testCase.expectedResult { - t.Errorf("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 - expectErr bool - }{ - {NewResourceSet(NewResource("mybucket", "/myobject*")), false}, - {NewResourceSet(NewResource("", "/myobject*")), false}, - {NewResourceSet(NewResource("", "")), true}, - } - - for i, testCase := range testCases { - err := testCase.resourceSet.Validate() - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -} diff --git a/pkg/iam/policy/statement.go b/pkg/iam/policy/statement.go deleted file mode 100644 index 455e63878..000000000 --- a/pkg/iam/policy/statement.go +++ /dev/null @@ -1,164 +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 . - -package iampolicy - -import ( - "strings" - - "github.com/minio/minio/pkg/bucket/policy" - "github.com/minio/minio/pkg/bucket/policy/condition" -) - -// Statement - iam policy statement. -type Statement struct { - SID policy.ID `json:"Sid,omitempty"` - Effect policy.Effect `json:"Effect"` - Actions ActionSet `json:"Action"` - Resources ResourceSet `json:"Resource,omitempty"` - Conditions condition.Functions `json:"Condition,omitempty"` -} - -// IsAllowed - checks given policy args is allowed to continue the Rest API. -func (statement Statement) IsAllowed(args Args) bool { - check := func() bool { - if !statement.Actions.Match(args.Action) { - return false - } - - resource := args.BucketName - if args.ObjectName != "" { - if !strings.HasPrefix(args.ObjectName, "/") { - resource += "/" - } - - resource += args.ObjectName - } else { - resource += "/" - } - - // For admin statements, resource match can be ignored. - if !statement.Resources.Match(resource, args.ConditionValues) && !statement.isAdmin() { - return false - } - - return statement.Conditions.Evaluate(args.ConditionValues) - } - - return statement.Effect.IsAllowed(check()) -} -func (statement Statement) isAdmin() bool { - for action := range statement.Actions { - if AdminAction(action).IsValid() { - return true - } - } - return false -} - -// 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 len(statement.Actions) == 0 { - return Errorf("Action must not be empty") - } - - if statement.isAdmin() { - if err := statement.Actions.ValidateAdmin(); err != nil { - return err - } - for action := range statement.Actions { - keys := statement.Conditions.Keys() - keyDiff := keys.Difference(adminActionConditionKeyMap[action]) - if !keyDiff.IsEmpty() { - return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action) - } - } - return nil - } - - if !statement.SID.IsValid() { - return Errorf("invalid SID %v", statement.SID) - } - - if len(statement.Resources) == 0 { - return Errorf("Resource must not be empty") - } - - if err := statement.Resources.Validate(); err != nil { - return err - } - - if err := statement.Actions.Validate(); err != nil { - return err - } - - for action := range statement.Actions { - if !statement.Resources.objectResourceExists() && !statement.Resources.bucketResourceExists() { - return Errorf("unsupported Resource found %v for action %v", statement.Resources, action) - } - - keys := statement.Conditions.Keys() - keyDiff := keys.Difference(iamActionConditionKeyMap.Lookup(action)) - if !keyDiff.IsEmpty() { - return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action) - } - } - - return nil -} - -// Validate - validates Statement is for given bucket or not. -func (statement Statement) Validate() error { - return statement.isValid() -} - -// Equals checks if two statements are equal -func (statement Statement) Equals(st Statement) bool { - if statement.Effect != st.Effect { - 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 -} - -// Clone clones Statement structure -func (statement Statement) Clone() Statement { - return NewStatement(statement.Effect, statement.Actions.Clone(), - statement.Resources.Clone(), statement.Conditions.Clone()) -} - -// NewStatement - creates new statement. -func NewStatement(effect policy.Effect, actionSet ActionSet, resourceSet ResourceSet, conditions condition.Functions) Statement { - return Statement{ - Effect: effect, - Actions: actionSet, - Resources: resourceSet, - Conditions: conditions, - } -} diff --git a/pkg/iam/policy/statement_test.go b/pkg/iam/policy/statement_test.go deleted file mode 100644 index 629d99afe..000000000 --- a/pkg/iam/policy/statement_test.go +++ /dev/null @@ -1,467 +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 . - -package iampolicy - -import ( - "encoding/json" - "net" - "reflect" - "testing" - - "github.com/minio/minio/pkg/bucket/policy" - "github.com/minio/minio/pkg/bucket/policy/condition" -) - -func TestStatementIsAllowed(t *testing.T) { - case1Statement := NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - ) - - case2Statement := NewStatement( - policy.Allow, - 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( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ) - - case4Statement := NewStatement( - policy.Deny, - 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{}, - } - - putObjectActionArgs := Args{ - AccountName: "Q3AM3UQ867SPQQA43P2F", - Action: PutObjectAction, - BucketName: "mybucket", - ConditionValues: map[string][]string{ - "x-amz-copy-source": {"mybucket/myobject"}, - "SourceIp": {"192.168.1.10"}, - }, - ObjectName: "myobject", - } - - getObjectActionArgs := Args{ - AccountName: "Q3AM3UQ867SPQQA43P2F", - Action: GetObjectAction, - BucketName: "mybucket", - ConditionValues: map[string][]string{}, - 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) - } - - func3, err := condition.NewStringEqualsFunc( - condition.AWSUserAgent, - "NSPlayer", - ) - if err != nil { - t.Fatalf("unexpected error. %v\n", err) - } - - testCases := []struct { - statement Statement - expectErr bool - }{ - // Invalid effect error. - {NewStatement( - policy.Effect("foo"), - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - ), true}, - // Empty actions error. - {NewStatement( - policy.Allow, - NewActionSet(), - NewResourceSet(NewResource("*", "")), - condition.NewFunctions(), - ), true}, - // Empty resources error. - {NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(), - condition.NewFunctions(), - ), true}, - // Unsupported conditions for GetObject - {NewStatement( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2), - ), true}, - {NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(), - ), false}, - {NewStatement( - policy.Allow, - NewActionSet(GetBucketLocationAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "")), - condition.NewFunctions(), - ), false}, - {NewStatement( - policy.Deny, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1), - ), false}, - {NewStatement( - policy.Allow, - NewActionSet(CreateUserAdminAction, DeleteUserAdminAction), - nil, - condition.NewFunctions(func2, func3), - ), true}, - {NewStatement( - policy.Allow, - NewActionSet(CreateUserAdminAction, DeleteUserAdminAction), - nil, - condition.NewFunctions(), - ), 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 TestStatementUnmarshalJSONAndValidate(t *testing.T) { - case1Data := []byte(`{ - "Sid": "SomeId1", - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" -}`) - case1Statement := NewStatement( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(), - ) - case1Statement.SID = "SomeId1" - - case2Data := []byte(`{ - "Effect": "Allow", - "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( - policy.Allow, - NewActionSet(PutObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func1), - ) - - case3Data := []byte(`{ - "Effect": "Deny", - "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( - policy.Deny, - NewActionSet(PutObjectAction, GetObjectAction), - NewResourceSet(NewResource("mybucket", "/myobject*")), - condition.NewFunctions(func2), - ) - - case4Data := []byte(`{ - "Effect": "Allow", - "Action": "s3:PutObjec, - "Resource": "arn:aws:s3:::mybucket/myobject*" -}`) - - case5Data := []byte(`{ - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*" -}`) - - case7Data := []byte(`{ - "Effect": "Allow", - "Resource": "arn:aws:s3:::mybucket/myobject*" -}`) - - case8Data := []byte(`{ - "Effect": "Allow", - "Action": "s3:PutObject" -}`) - - case9Data := []byte(`{ - "Effect": "Allow", - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::mybucket/myobject*", - "Condition": { - } -}`) - - case10Data := []byte(`{ - "Effect": "Deny", - "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 - expectUnmarshalErr bool - expectValidationErr bool - }{ - {case1Data, case1Statement, false, false}, - {case2Data, case2Statement, false, false}, - {case3Data, case3Statement, false, false}, - // JSON unmarshaling error. - {case4Data, Statement{}, true, true}, - // Invalid effect error. - {case5Data, Statement{}, false, true}, - // Empty action error. - {case7Data, Statement{}, false, true}, - // Empty resource error. - {case8Data, Statement{}, false, true}, - // Empty condition error. - {case9Data, Statement{}, true, false}, - // Unsupported condition key error. - {case10Data, Statement{}, false, true}, - } - - for i, testCase := range testCases { - var result Statement - expectErr := (json.Unmarshal(testCase.data, &result) != nil) - - if expectErr != testCase.expectUnmarshalErr { - t.Fatalf("case %v: error during unmarshal: expected: %v, got: %v", i+1, testCase.expectUnmarshalErr, expectErr) - } - - expectErr = (result.Validate() != nil) - if expectErr != testCase.expectValidationErr { - t.Fatalf("case %v: error during validation: expected: %v, got: %v", i+1, testCase.expectValidationErr, expectErr) - } - - if !testCase.expectUnmarshalErr && !testCase.expectValidationErr { - 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( - policy.Allow, - 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( - policy.Allow, - NewActionSet(GetObjectAction, PutObjectAction), - NewResourceSet(NewResource("mybucket", "myobject*")), - condition.NewFunctions(func1, func2), - ) - - testCases := []struct { - statement Statement - expectErr bool - }{ - {case1Statement, false}, - {case2Statement, true}, - } - - for i, testCase := range testCases { - err := testCase.statement.Validate() - expectErr := (err != nil) - - if expectErr != testCase.expectErr { - t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) - } - } -}