mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
move to iam, bucket policy from minio/pkg (#12400)
This commit is contained in:
parent
3350dbc50d
commit
fdc2020b10
@ -25,7 +25,7 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"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
|
// Data types used for returning dummy access control
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,7 +37,7 @@ import (
|
|||||||
"github.com/minio/minio/cmd/config/storageclass"
|
"github.com/minio/minio/cmd/config/storageclass"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"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) {
|
func validateAdminReqConfigKV(ctx context.Context, w http.ResponseWriter, r *http.Request) (auth.Credentials, ObjectLayer) {
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
"github.com/minio/minio/cmd/config/dns"
|
"github.com/minio/minio/cmd/config/dns"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"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) {
|
func validateAdminUsersReq(ctx context.Context, w http.ResponseWriter, r *http.Request, action iampolicy.AdminAction) (ObjectLayer, auth.Credentials) {
|
||||||
|
@ -46,9 +46,9 @@ import (
|
|||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/dsync"
|
"github.com/minio/minio/pkg/dsync"
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
|
||||||
"github.com/minio/minio/pkg/kms"
|
"github.com/minio/minio/pkg/kms"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -38,10 +38,10 @@ import (
|
|||||||
"github.com/minio/minio/pkg/bucket/replication"
|
"github.com/minio/minio/pkg/bucket/replication"
|
||||||
|
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/bucket/versioning"
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/hash"
|
"github.com/minio/minio/pkg/hash"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIError structure
|
// APIError structure
|
||||||
|
@ -37,10 +37,10 @@ import (
|
|||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/etag"
|
||||||
"github.com/minio/minio/pkg/hash"
|
"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.
|
// Verify if request has JWT.
|
||||||
|
@ -29,7 +29,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/auth"
|
"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.
|
// Test get request auth type.
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -44,14 +44,14 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/replication"
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
"github.com/minio/minio/pkg/hash"
|
"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/kms"
|
||||||
"github.com/minio/minio/pkg/sync/errgroup"
|
"github.com/minio/minio/pkg/sync/errgroup"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -26,8 +26,8 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/sync/errgroup"
|
"github.com/minio/minio/pkg/sync/errgroup"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func concurrentDecryptETag(ctx context.Context, objects []ObjectInfo) {
|
func concurrentDecryptETag(ctx context.Context, objects []ObjectInfo) {
|
||||||
|
@ -30,12 +30,12 @@ import (
|
|||||||
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/replication"
|
||||||
"github.com/minio/minio/pkg/bucket/versioning"
|
"github.com/minio/minio/pkg/bucket/versioning"
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/kms"
|
"github.com/minio/minio/pkg/kms"
|
||||||
"github.com/minio/minio/pkg/sync/errgroup"
|
"github.com/minio/minio/pkg/sync/errgroup"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BucketMetadataSys captures all bucket metadata for a given cluster.
|
// BucketMetadataSys captures all bucket metadata for a given cluster.
|
||||||
|
@ -36,12 +36,12 @@ import (
|
|||||||
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/replication"
|
||||||
"github.com/minio/minio/pkg/bucket/versioning"
|
"github.com/minio/minio/pkg/bucket/versioning"
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/fips"
|
"github.com/minio/minio/pkg/fips"
|
||||||
"github.com/minio/minio/pkg/kms"
|
"github.com/minio/minio/pkg/kms"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
"github.com/minio/sio"
|
"github.com/minio/sio"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -26,8 +26,8 @@ import (
|
|||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/replication"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BucketObjectLockSys - map of bucket and retention configuration.
|
// BucketObjectLockSys - map of bucket and retention configuration.
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -29,8 +29,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
"github.com/minio/pkg/bucket/policy/condition"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAnonReadOnlyBucketPolicy(bucketName string) *policy.Policy {
|
func getAnonReadOnlyBucketPolicy(bucketName string) *policy.Policy {
|
||||||
|
@ -29,8 +29,8 @@ import (
|
|||||||
miniogopolicy "github.com/minio/minio-go/v7/pkg/policy"
|
miniogopolicy "github.com/minio/minio-go/v7/pkg/policy"
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PolicySys - policy subsystem.
|
// PolicySys - policy subsystem.
|
||||||
|
@ -38,7 +38,7 @@ import (
|
|||||||
"github.com/minio/minio/pkg/bucket/bandwidth"
|
"github.com/minio/minio/pkg/bucket/bandwidth"
|
||||||
"github.com/minio/minio/pkg/bucket/replication"
|
"github.com/minio/minio/pkg/bucket/replication"
|
||||||
"github.com/minio/minio/pkg/event"
|
"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.
|
// gets replication config associated to a given bucket name.
|
||||||
|
@ -25,8 +25,8 @@ import (
|
|||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/bucket/versioning"
|
"github.com/minio/minio/pkg/bucket/versioning"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -32,9 +32,9 @@ import (
|
|||||||
jwtgo "github.com/dgrijalva/jwt-go"
|
jwtgo "github.com/dgrijalva/jwt-go"
|
||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
"github.com/minio/pkg/env"
|
"github.com/minio/pkg/env"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config - OpenID Config
|
// Config - OpenID Config
|
||||||
|
@ -25,9 +25,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
"github.com/minio/pkg/env"
|
"github.com/minio/pkg/env"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Env IAM OPA URL
|
// Env IAM OPA URL
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"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.
|
// Data types used for returning dummy tagging XML.
|
||||||
|
@ -40,11 +40,11 @@ import (
|
|||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/color"
|
"github.com/minio/minio/pkg/color"
|
||||||
xioutil "github.com/minio/minio/pkg/ioutil"
|
xioutil "github.com/minio/minio/pkg/ioutil"
|
||||||
"github.com/minio/minio/pkg/lock"
|
"github.com/minio/minio/pkg/lock"
|
||||||
"github.com/minio/minio/pkg/mountinfo"
|
"github.com/minio/minio/pkg/mountinfo"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
"github.com/minio/pkg/mimedb"
|
"github.com/minio/pkg/mimedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ import (
|
|||||||
"github.com/minio/minio-go/v7/pkg/tags"
|
"github.com/minio/minio-go/v7/pkg/tags"
|
||||||
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/bucket/versioning"
|
"github.com/minio/minio/pkg/bucket/versioning"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
|
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
)
|
)
|
||||||
|
@ -45,8 +45,8 @@ import (
|
|||||||
minio "github.com/minio/minio/cmd"
|
minio "github.com/minio/minio/cmd"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
"github.com/minio/pkg/bucket/policy/condition"
|
||||||
"github.com/minio/pkg/env"
|
"github.com/minio/pkg/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ import (
|
|||||||
minio "github.com/minio/minio/cmd"
|
minio "github.com/minio/minio/cmd"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
"github.com/minio/pkg/bucket/policy/condition"
|
||||||
"github.com/minio/pkg/env"
|
"github.com/minio/pkg/env"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
"google.golang.org/api/iterator"
|
"google.golang.org/api/iterator"
|
||||||
|
@ -37,7 +37,7 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -33,8 +33,8 @@ import (
|
|||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
|
||||||
"github.com/minio/minio/pkg/kms"
|
"github.com/minio/minio/pkg/kms"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||||
etcd "go.etcd.io/etcd/client/v3"
|
etcd "go.etcd.io/etcd/client/v3"
|
||||||
)
|
)
|
||||||
|
@ -29,8 +29,8 @@ import (
|
|||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
|
||||||
"github.com/minio/minio/pkg/kms"
|
"github.com/minio/minio/pkg/kms"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IAMObjectStore implements IAMStorageAPI
|
// IAMObjectStore implements IAMStorageAPI
|
||||||
|
@ -34,7 +34,7 @@ import (
|
|||||||
"github.com/minio/minio-go/v7/pkg/set"
|
"github.com/minio/minio-go/v7/pkg/set"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"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
|
// UsersSysType - defines the type of users and groups system that is
|
||||||
|
@ -24,8 +24,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
policy "github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
|
policy "github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api objectAPIHandlers) ListenNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListenNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"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"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
@ -40,10 +40,10 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
bucketBandwidth "github.com/minio/minio/pkg/bucket/bandwidth"
|
bucketBandwidth "github.com/minio/minio/pkg/bucket/bandwidth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
"github.com/minio/minio/pkg/sync/errgroup"
|
"github.com/minio/minio/pkg/sync/errgroup"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotificationSys - notification system.
|
// NotificationSys - notification system.
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
"github.com/minio/minio-go/v7/pkg/encrypt"
|
"github.com/minio/minio-go/v7/pkg/encrypt"
|
||||||
"github.com/minio/minio-go/v7/pkg/tags"
|
"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.
|
// CheckPreconditionFn returns true if precondition check failed.
|
||||||
|
@ -47,18 +47,18 @@ import (
|
|||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
"github.com/minio/minio/pkg/bucket/lifecycle"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/replication"
|
||||||
"github.com/minio/minio/pkg/etag"
|
"github.com/minio/minio/pkg/etag"
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/fips"
|
"github.com/minio/minio/pkg/fips"
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
"github.com/minio/minio/pkg/hash"
|
"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/ioutil"
|
||||||
"github.com/minio/minio/pkg/kms"
|
"github.com/minio/minio/pkg/kms"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
"github.com/minio/minio/pkg/s3select"
|
"github.com/minio/minio/pkg/s3select"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
"github.com/minio/sio"
|
"github.com/minio/sio"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ import (
|
|||||||
|
|
||||||
miniogopolicy "github.com/minio/minio-go/v7/pkg/policy"
|
miniogopolicy "github.com/minio/minio-go/v7/pkg/policy"
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
"github.com/minio/minio-go/v7/pkg/set"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/pkg/bucket/policy"
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
"github.com/minio/pkg/bucket/policy/condition"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPolicySysIsAllowed(t *testing.T) {
|
func TestPolicySysIsAllowed(t *testing.T) {
|
||||||
|
@ -35,7 +35,7 @@ import (
|
|||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
"github.com/minio/minio-go/v7/pkg/set"
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
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.
|
// API suite container common to both FS and Erasure.
|
||||||
|
@ -31,7 +31,7 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"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"
|
"github.com/minio/pkg/wildcard"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ import (
|
|||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/cmd/rest"
|
"github.com/minio/minio/cmd/rest"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/hash"
|
"github.com/minio/minio/pkg/hash"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestMain to set up global env.
|
// TestMain to set up global env.
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -48,14 +48,14 @@ import (
|
|||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
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/replication"
|
||||||
"github.com/minio/minio/pkg/etag"
|
"github.com/minio/minio/pkg/etag"
|
||||||
"github.com/minio/minio/pkg/event"
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
"github.com/minio/minio/pkg/hash"
|
"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/ioutil"
|
||||||
|
"github.com/minio/pkg/bucket/policy"
|
||||||
|
iampolicy "github.com/minio/pkg/iam/policy"
|
||||||
"github.com/minio/rpc/json2"
|
"github.com/minio/rpc/json2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -47,7 +47,7 @@ require (
|
|||||||
github.com/minio/madmin-go v1.0.2
|
github.com/minio/madmin-go v1.0.2
|
||||||
github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78
|
github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78
|
||||||
github.com/minio/parquet-go v1.0.0
|
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/rpc v1.0.0
|
||||||
github.com/minio/selfupdate v0.3.1
|
github.com/minio/selfupdate v0.3.1
|
||||||
github.com/minio/sha256-simd v1.0.0
|
github.com/minio/sha256-simd v1.0.0
|
||||||
|
4
go.sum
4
go.sum
@ -504,8 +504,8 @@ github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78 h1:v7OMbUnWky
|
|||||||
github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78/go.mod h1:mTh2uJuAbEqdhMVl6CMIIZLUeiMiWtJR4JB8/5g2skw=
|
github.com/minio/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 h1:fcWsEvub04Nsl/4hiRBDWlbqd6jhacQieV07a+nhiIk=
|
||||||
github.com/minio/parquet-go v1.0.0/go.mod h1:aQlkSOfOq2AtQKkuou3mosNVMwNokd+faTacxxk/oHA=
|
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.3 h1:tUhM6lG/BdNB0+5f2RbE4ifCAYwMs6cRJnZ/AY0WIeQ=
|
||||||
github.com/minio/pkg v1.0.2/go.mod h1:e9WOU0bav8jd8AzloFjCTSiXSNqnXqxbzGwlH+2rQnI=
|
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 h1:tJCHyLfQF6k6HlMQFpKy2FO/7lc2WP8gLDGMZp18E70=
|
||||||
github.com/minio/rpc v1.0.0/go.mod h1:b9xqF7J0xeMXr0cM4pnBlP7Te7PDsG5JrRxl5dG6Ldk=
|
github.com/minio/rpc v1.0.0/go.mod h1:b9xqF7J0xeMXr0cM4pnBlP7Te7PDsG5JrRxl5dG6Ldk=
|
||||||
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
||||||
|
@ -1,426 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Action - policy action.
|
|
||||||
// Refer https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazons3.html
|
|
||||||
// for more information about available actions.
|
|
||||||
type Action string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// AbortMultipartUploadAction - AbortMultipartUpload Rest API action.
|
|
||||||
AbortMultipartUploadAction Action = "s3:AbortMultipartUpload"
|
|
||||||
|
|
||||||
// CreateBucketAction - CreateBucket Rest API action.
|
|
||||||
CreateBucketAction = "s3:CreateBucket"
|
|
||||||
|
|
||||||
// DeleteBucketAction - DeleteBucket Rest API action.
|
|
||||||
DeleteBucketAction = "s3:DeleteBucket"
|
|
||||||
|
|
||||||
// ForceDeleteBucketAction - DeleteBucket Rest API action when x-minio-force-delete flag
|
|
||||||
// is specified.
|
|
||||||
ForceDeleteBucketAction = "s3:ForceDeleteBucket"
|
|
||||||
|
|
||||||
// DeleteBucketPolicyAction - DeleteBucketPolicy Rest API action.
|
|
||||||
DeleteBucketPolicyAction = "s3:DeleteBucketPolicy"
|
|
||||||
|
|
||||||
// DeleteObjectAction - DeleteObject Rest API action.
|
|
||||||
DeleteObjectAction = "s3:DeleteObject"
|
|
||||||
|
|
||||||
// GetBucketLocationAction - GetBucketLocation Rest API action.
|
|
||||||
GetBucketLocationAction = "s3:GetBucketLocation"
|
|
||||||
|
|
||||||
// GetBucketNotificationAction - GetBucketNotification Rest API action.
|
|
||||||
GetBucketNotificationAction = "s3:GetBucketNotification"
|
|
||||||
|
|
||||||
// GetBucketPolicyAction - GetBucketPolicy Rest API action.
|
|
||||||
GetBucketPolicyAction = "s3:GetBucketPolicy"
|
|
||||||
|
|
||||||
// GetObjectAction - GetObject Rest API action.
|
|
||||||
GetObjectAction = "s3:GetObject"
|
|
||||||
|
|
||||||
// HeadBucketAction - HeadBucket Rest API action. This action is unused in minio.
|
|
||||||
HeadBucketAction = "s3:HeadBucket"
|
|
||||||
|
|
||||||
// ListAllMyBucketsAction - ListAllMyBuckets (List buckets) Rest API action.
|
|
||||||
ListAllMyBucketsAction = "s3:ListAllMyBuckets"
|
|
||||||
|
|
||||||
// ListBucketAction - ListBucket Rest API action.
|
|
||||||
ListBucketAction = "s3:ListBucket"
|
|
||||||
|
|
||||||
// GetBucketPolicyStatusAction - Retrieves the policy status for a bucket.
|
|
||||||
GetBucketPolicyStatusAction = "s3:GetBucketPolicyStatus"
|
|
||||||
|
|
||||||
// ListBucketMultipartUploadsAction - ListMultipartUploads Rest API action.
|
|
||||||
ListBucketMultipartUploadsAction = "s3:ListBucketMultipartUploads"
|
|
||||||
|
|
||||||
// ListBucketVersionsAction - ListBucket versions Rest API action.
|
|
||||||
ListBucketVersionsAction = "s3:ListBucketVersions"
|
|
||||||
|
|
||||||
// ListenNotificationAction - ListenNotification Rest API action.
|
|
||||||
// This is MinIO extension.
|
|
||||||
ListenNotificationAction = "s3:ListenNotification"
|
|
||||||
|
|
||||||
// ListenBucketNotificationAction - ListenBucketNotification Rest API action.
|
|
||||||
// This is MinIO extension.
|
|
||||||
ListenBucketNotificationAction = "s3:ListenBucketNotification"
|
|
||||||
|
|
||||||
// ListMultipartUploadPartsAction - ListParts Rest API action.
|
|
||||||
ListMultipartUploadPartsAction = "s3:ListMultipartUploadParts"
|
|
||||||
|
|
||||||
// PutBucketNotificationAction - PutObjectNotification Rest API action.
|
|
||||||
PutBucketNotificationAction = "s3:PutBucketNotification"
|
|
||||||
|
|
||||||
// PutBucketPolicyAction - PutBucketPolicy Rest API action.
|
|
||||||
PutBucketPolicyAction = "s3:PutBucketPolicy"
|
|
||||||
|
|
||||||
// PutObjectAction - PutObject Rest API action.
|
|
||||||
PutObjectAction = "s3:PutObject"
|
|
||||||
|
|
||||||
// PutBucketLifecycleAction - PutBucketLifecycle Rest API action.
|
|
||||||
PutBucketLifecycleAction = "s3:PutLifecycleConfiguration"
|
|
||||||
|
|
||||||
// GetBucketLifecycleAction - GetBucketLifecycle Rest API action.
|
|
||||||
GetBucketLifecycleAction = "s3:GetLifecycleConfiguration"
|
|
||||||
|
|
||||||
// BypassGovernanceRetentionAction - bypass governance retention for PutObjectRetention, PutObject and DeleteObject Rest API action.
|
|
||||||
BypassGovernanceRetentionAction = "s3:BypassGovernanceRetention"
|
|
||||||
// PutObjectRetentionAction - PutObjectRetention Rest API action.
|
|
||||||
PutObjectRetentionAction = "s3:PutObjectRetention"
|
|
||||||
|
|
||||||
// GetObjectRetentionAction - GetObjectRetention, GetObject, HeadObject Rest API action.
|
|
||||||
GetObjectRetentionAction = "s3:GetObjectRetention"
|
|
||||||
// GetObjectLegalHoldAction - GetObjectLegalHold, GetObject Rest API action.
|
|
||||||
GetObjectLegalHoldAction = "s3:GetObjectLegalHold"
|
|
||||||
// PutObjectLegalHoldAction - PutObjectLegalHold, PutObject Rest API action.
|
|
||||||
PutObjectLegalHoldAction = "s3:PutObjectLegalHold"
|
|
||||||
// GetBucketObjectLockConfigurationAction - GetObjectLockConfiguration Rest API action
|
|
||||||
GetBucketObjectLockConfigurationAction = "s3:GetBucketObjectLockConfiguration"
|
|
||||||
// PutBucketObjectLockConfigurationAction - PutObjectLockConfiguration Rest API action
|
|
||||||
PutBucketObjectLockConfigurationAction = "s3:PutBucketObjectLockConfiguration"
|
|
||||||
|
|
||||||
// GetBucketTaggingAction - GetTagging Rest API action
|
|
||||||
GetBucketTaggingAction = "s3:GetBucketTagging"
|
|
||||||
// PutBucketTaggingAction - PutTagging Rest API action
|
|
||||||
PutBucketTaggingAction = "s3:PutBucketTagging"
|
|
||||||
|
|
||||||
// GetObjectTaggingAction - Get Object Tags API action
|
|
||||||
GetObjectTaggingAction = "s3:GetObjectTagging"
|
|
||||||
// PutObjectTaggingAction - Put Object Tags API action
|
|
||||||
PutObjectTaggingAction = "s3:PutObjectTagging"
|
|
||||||
// DeleteObjectTaggingAction - Delete Object Tags API action
|
|
||||||
DeleteObjectTaggingAction = "s3:DeleteObjectTagging"
|
|
||||||
|
|
||||||
// PutBucketEncryptionAction - PutBucketEncryption REST API action
|
|
||||||
PutBucketEncryptionAction = "s3:PutEncryptionConfiguration"
|
|
||||||
// GetBucketEncryptionAction - GetBucketEncryption REST API action
|
|
||||||
GetBucketEncryptionAction = "s3:GetEncryptionConfiguration"
|
|
||||||
|
|
||||||
// PutBucketVersioningAction - PutBucketVersioning REST API action
|
|
||||||
PutBucketVersioningAction = "s3:PutBucketVersioning"
|
|
||||||
// GetBucketVersioningAction - GetBucketVersioning REST API action
|
|
||||||
GetBucketVersioningAction = "s3:GetBucketVersioning"
|
|
||||||
|
|
||||||
// DeleteObjectVersionAction - DeleteObjectVersion Rest API action.
|
|
||||||
DeleteObjectVersionAction = "s3:DeleteObjectVersion"
|
|
||||||
|
|
||||||
// DeleteObjectVersionTaggingAction - DeleteObjectVersionTagging Rest API action.
|
|
||||||
DeleteObjectVersionTaggingAction = "s3:DeleteObjectVersionTagging"
|
|
||||||
|
|
||||||
// GetObjectVersionAction - GetObjectVersionAction Rest API action.
|
|
||||||
GetObjectVersionAction = "s3:GetObjectVersion"
|
|
||||||
|
|
||||||
// GetObjectVersionTaggingAction - GetObjectVersionTagging Rest API action.
|
|
||||||
GetObjectVersionTaggingAction = "s3:GetObjectVersionTagging"
|
|
||||||
|
|
||||||
// PutObjectVersionTaggingAction - PutObjectVersionTagging Rest API action.
|
|
||||||
PutObjectVersionTaggingAction = "s3:PutObjectVersionTagging"
|
|
||||||
|
|
||||||
// GetReplicationConfigurationAction - GetReplicationConfiguration REST API action
|
|
||||||
GetReplicationConfigurationAction = "s3:GetReplicationConfiguration"
|
|
||||||
// PutReplicationConfigurationAction - PutReplicationConfiguration REST API action
|
|
||||||
PutReplicationConfigurationAction = "s3:PutReplicationConfiguration"
|
|
||||||
|
|
||||||
// ReplicateObjectAction - ReplicateObject REST API action
|
|
||||||
ReplicateObjectAction = "s3:ReplicateObject"
|
|
||||||
|
|
||||||
// ReplicateDeleteAction - ReplicateDelete REST API action
|
|
||||||
ReplicateDeleteAction = "s3:ReplicateDelete"
|
|
||||||
|
|
||||||
// ReplicateTagsAction - ReplicateTags REST API action
|
|
||||||
ReplicateTagsAction = "s3:ReplicateTags"
|
|
||||||
|
|
||||||
// GetObjectVersionForReplicationAction - GetObjectVersionForReplication REST API action
|
|
||||||
GetObjectVersionForReplicationAction = "s3:GetObjectVersionForReplication"
|
|
||||||
|
|
||||||
// RestoreObjectAction - RestoreObject REST API action
|
|
||||||
RestoreObjectAction = "s3:RestoreObject"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List of all supported object actions.
|
|
||||||
var supportedObjectActions = map[Action]struct{}{
|
|
||||||
AbortMultipartUploadAction: {},
|
|
||||||
DeleteObjectAction: {},
|
|
||||||
GetObjectAction: {},
|
|
||||||
ListMultipartUploadPartsAction: {},
|
|
||||||
PutObjectAction: {},
|
|
||||||
BypassGovernanceRetentionAction: {},
|
|
||||||
PutObjectRetentionAction: {},
|
|
||||||
GetObjectRetentionAction: {},
|
|
||||||
PutObjectLegalHoldAction: {},
|
|
||||||
GetObjectLegalHoldAction: {},
|
|
||||||
GetObjectTaggingAction: {},
|
|
||||||
PutObjectTaggingAction: {},
|
|
||||||
DeleteObjectTaggingAction: {},
|
|
||||||
GetObjectVersionAction: {},
|
|
||||||
GetObjectVersionTaggingAction: {},
|
|
||||||
DeleteObjectVersionAction: {},
|
|
||||||
DeleteObjectVersionTaggingAction: {},
|
|
||||||
PutObjectVersionTaggingAction: {},
|
|
||||||
ReplicateObjectAction: {},
|
|
||||||
ReplicateDeleteAction: {},
|
|
||||||
ReplicateTagsAction: {},
|
|
||||||
GetObjectVersionForReplicationAction: {},
|
|
||||||
RestoreObjectAction: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// isObjectAction - returns whether action is object type or not.
|
|
||||||
func (action Action) isObjectAction() bool {
|
|
||||||
_, ok := supportedObjectActions[action]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of all supported actions.
|
|
||||||
var supportedActions = map[Action]struct{}{
|
|
||||||
AbortMultipartUploadAction: {},
|
|
||||||
CreateBucketAction: {},
|
|
||||||
DeleteBucketAction: {},
|
|
||||||
ForceDeleteBucketAction: {},
|
|
||||||
DeleteBucketPolicyAction: {},
|
|
||||||
DeleteObjectAction: {},
|
|
||||||
GetBucketLocationAction: {},
|
|
||||||
GetBucketNotificationAction: {},
|
|
||||||
GetBucketPolicyAction: {},
|
|
||||||
GetObjectAction: {},
|
|
||||||
HeadBucketAction: {},
|
|
||||||
ListAllMyBucketsAction: {},
|
|
||||||
ListBucketAction: {},
|
|
||||||
GetBucketPolicyStatusAction: {},
|
|
||||||
ListBucketVersionsAction: {},
|
|
||||||
ListBucketMultipartUploadsAction: {},
|
|
||||||
ListenNotificationAction: {},
|
|
||||||
ListenBucketNotificationAction: {},
|
|
||||||
ListMultipartUploadPartsAction: {},
|
|
||||||
PutBucketNotificationAction: {},
|
|
||||||
PutBucketPolicyAction: {},
|
|
||||||
PutObjectAction: {},
|
|
||||||
GetBucketLifecycleAction: {},
|
|
||||||
PutBucketLifecycleAction: {},
|
|
||||||
PutObjectRetentionAction: {},
|
|
||||||
GetObjectRetentionAction: {},
|
|
||||||
GetObjectLegalHoldAction: {},
|
|
||||||
PutObjectLegalHoldAction: {},
|
|
||||||
PutBucketObjectLockConfigurationAction: {},
|
|
||||||
GetBucketObjectLockConfigurationAction: {},
|
|
||||||
PutBucketTaggingAction: {},
|
|
||||||
GetBucketTaggingAction: {},
|
|
||||||
GetObjectVersionAction: {},
|
|
||||||
GetObjectVersionTaggingAction: {},
|
|
||||||
DeleteObjectVersionAction: {},
|
|
||||||
DeleteObjectVersionTaggingAction: {},
|
|
||||||
PutObjectVersionTaggingAction: {},
|
|
||||||
BypassGovernanceRetentionAction: {},
|
|
||||||
GetObjectTaggingAction: {},
|
|
||||||
PutObjectTaggingAction: {},
|
|
||||||
DeleteObjectTaggingAction: {},
|
|
||||||
PutBucketEncryptionAction: {},
|
|
||||||
GetBucketEncryptionAction: {},
|
|
||||||
PutBucketVersioningAction: {},
|
|
||||||
GetBucketVersioningAction: {},
|
|
||||||
GetReplicationConfigurationAction: {},
|
|
||||||
PutReplicationConfigurationAction: {},
|
|
||||||
ReplicateObjectAction: {},
|
|
||||||
ReplicateDeleteAction: {},
|
|
||||||
ReplicateTagsAction: {},
|
|
||||||
GetObjectVersionForReplicationAction: {},
|
|
||||||
RestoreObjectAction: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks if action is valid or not.
|
|
||||||
func (action Action) IsValid() bool {
|
|
||||||
_, ok := supportedActions[action]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Action to JSON data.
|
|
||||||
func (action Action) MarshalJSON() ([]byte, error) {
|
|
||||||
if action.IsValid() {
|
|
||||||
return json.Marshal(string(action))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, Errorf("invalid action '%v'", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Action.
|
|
||||||
func (action *Action) UnmarshalJSON(data []byte) error {
|
|
||||||
var s string
|
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := Action(s)
|
|
||||||
if !a.IsValid() {
|
|
||||||
return Errorf("invalid action '%v'", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
*action = a
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAction(s string) (Action, error) {
|
|
||||||
action := Action(s)
|
|
||||||
|
|
||||||
if action.IsValid() {
|
|
||||||
return action, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return action, Errorf("unsupported action '%v'", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// actionConditionKeyMap - holds mapping of supported condition key for an action.
|
|
||||||
var actionConditionKeyMap = map[Action]condition.KeySet{
|
|
||||||
AbortMultipartUploadAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
CreateBucketAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
DeleteObjectAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
GetBucketLocationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
GetBucketPolicyStatusAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
GetObjectAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
HeadBucketAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
ListAllMyBucketsAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
ListBucketAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3Prefix,
|
|
||||||
condition.S3Delimiter,
|
|
||||||
condition.S3MaxKeys,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
ListBucketVersionsAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3Prefix,
|
|
||||||
condition.S3Delimiter,
|
|
||||||
condition.S3MaxKeys,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
ListBucketMultipartUploadsAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
ListenNotificationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
ListenBucketNotificationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
ListMultipartUploadPartsAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
PutObjectAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzCopySource,
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
condition.S3XAmzMetadataDirective,
|
|
||||||
condition.S3XAmzStorageClass,
|
|
||||||
condition.S3ObjectLockRetainUntilDate,
|
|
||||||
condition.S3ObjectLockMode,
|
|
||||||
condition.S3ObjectLockLegalHold,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
|
||||||
// LockLegalHold is not supported with PutObjectRetentionAction
|
|
||||||
PutObjectRetentionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3ObjectLockRemainingRetentionDays,
|
|
||||||
condition.S3ObjectLockRetainUntilDate,
|
|
||||||
condition.S3ObjectLockMode,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
GetObjectRetentionAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
PutObjectLegalHoldAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3ObjectLockLegalHold,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
|
||||||
BypassGovernanceRetentionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3ObjectLockRemainingRetentionDays,
|
|
||||||
condition.S3ObjectLockRetainUntilDate,
|
|
||||||
condition.S3ObjectLockMode,
|
|
||||||
condition.S3ObjectLockLegalHold,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
GetBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
PutBucketObjectLockConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
GetBucketTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
PutBucketTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
PutObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
GetObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
DeleteObjectTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
PutObjectVersionTaggingAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
GetObjectVersionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectVersionTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
DeleteObjectVersionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
DeleteObjectVersionTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetReplicationConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
PutReplicationConfigurationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
ReplicateObjectAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
ReplicateDeleteAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
ReplicateTagsAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
GetObjectVersionForReplicationAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
RestoreObjectAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActionIsObjectAction(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
action Action
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{AbortMultipartUploadAction, true},
|
|
||||||
{DeleteObjectAction, true},
|
|
||||||
{GetObjectAction, true},
|
|
||||||
{ListMultipartUploadPartsAction, true},
|
|
||||||
{PutObjectAction, true},
|
|
||||||
{CreateBucketAction, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.action.isObjectAction()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
action Action
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{AbortMultipartUploadAction, true},
|
|
||||||
{Action("foo"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.action.IsValid()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
action Action
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{PutObjectAction, []byte(`"s3:PutObject"`), false},
|
|
||||||
{Action("foo"), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.action)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult Action
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"s3:PutObject"`), PutObjectAction, false},
|
|
||||||
{[]byte(`"foo"`), Action(""), true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Action
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ActionSet - set of actions.
|
|
||||||
type ActionSet map[Action]struct{}
|
|
||||||
|
|
||||||
// Add - add action to the set.
|
|
||||||
func (actionSet ActionSet) Add(action Action) {
|
|
||||||
actionSet[action] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains - checks given action exists in the action set.
|
|
||||||
func (actionSet ActionSet) Contains(action Action) bool {
|
|
||||||
_, found := actionSet[action]
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals - checks whether given action set is equal to current action set or not.
|
|
||||||
func (actionSet ActionSet) Equals(sactionSet ActionSet) bool {
|
|
||||||
// If length of set is not equal to length of given set, the
|
|
||||||
// set is not equal to given set.
|
|
||||||
if len(actionSet) != len(sactionSet) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// As both sets are equal in length, check each elements are equal.
|
|
||||||
for k := range actionSet {
|
|
||||||
if _, ok := sactionSet[k]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersection - returns actions available in both ActionSet.
|
|
||||||
func (actionSet ActionSet) Intersection(sset ActionSet) ActionSet {
|
|
||||||
nset := NewActionSet()
|
|
||||||
for k := range actionSet {
|
|
||||||
if _, ok := sset[k]; ok {
|
|
||||||
nset.Add(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nset
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes ActionSet to JSON data.
|
|
||||||
func (actionSet ActionSet) MarshalJSON() ([]byte, error) {
|
|
||||||
if len(actionSet) == 0 {
|
|
||||||
return nil, Errorf("empty actions not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(actionSet.ToSlice())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (actionSet ActionSet) String() string {
|
|
||||||
actions := []string{}
|
|
||||||
for action := range actionSet {
|
|
||||||
actions = append(actions, string(action))
|
|
||||||
}
|
|
||||||
sort.Strings(actions)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v", actions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSlice - returns slice of actions from the action set.
|
|
||||||
func (actionSet ActionSet) ToSlice() []Action {
|
|
||||||
actions := []Action{}
|
|
||||||
for action := range actionSet {
|
|
||||||
actions = append(actions, action)
|
|
||||||
}
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone clones ActionSet structure
|
|
||||||
func (actionSet ActionSet) Clone() ActionSet {
|
|
||||||
return NewActionSet(actionSet.ToSlice()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to ActionSet.
|
|
||||||
func (actionSet *ActionSet) UnmarshalJSON(data []byte) error {
|
|
||||||
var sset set.StringSet
|
|
||||||
if err := json.Unmarshal(data, &sset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sset) == 0 {
|
|
||||||
return Errorf("empty actions not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
*actionSet = make(ActionSet)
|
|
||||||
for _, s := range sset.ToSlice() {
|
|
||||||
action, err := parseAction(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
actionSet.Add(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewActionSet - creates new action set.
|
|
||||||
func NewActionSet(actions ...Action) ActionSet {
|
|
||||||
actionSet := make(ActionSet)
|
|
||||||
for _, action := range actions {
|
|
||||||
actionSet.Add(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
return actionSet
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActionSetAdd(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ActionSet
|
|
||||||
action Action
|
|
||||||
expectedResult ActionSet
|
|
||||||
}{
|
|
||||||
{NewActionSet(), PutObjectAction, NewActionSet(PutObjectAction)},
|
|
||||||
{NewActionSet(PutObjectAction), PutObjectAction, NewActionSet(PutObjectAction)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
testCase.set.Add(testCase.action)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(testCase.expectedResult, testCase.set) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetContains(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ActionSet
|
|
||||||
action Action
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewActionSet(PutObjectAction), PutObjectAction, true},
|
|
||||||
{NewActionSet(PutObjectAction, GetObjectAction), PutObjectAction, true},
|
|
||||||
{NewActionSet(PutObjectAction, GetObjectAction), AbortMultipartUploadAction, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.Contains(testCase.action)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetIntersection(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ActionSet
|
|
||||||
setToIntersect ActionSet
|
|
||||||
expectedResult ActionSet
|
|
||||||
}{
|
|
||||||
{NewActionSet(), NewActionSet(PutObjectAction), NewActionSet()},
|
|
||||||
{NewActionSet(PutObjectAction), NewActionSet(), NewActionSet()},
|
|
||||||
{NewActionSet(PutObjectAction), NewActionSet(PutObjectAction, GetObjectAction), NewActionSet(PutObjectAction)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.Intersection(testCase.setToIntersect)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
actionSet ActionSet
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewActionSet(PutObjectAction), []byte(`["s3:PutObject"]`), false},
|
|
||||||
{NewActionSet(), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.actionSet)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetToSlice(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
actionSet ActionSet
|
|
||||||
expectedResult []Action
|
|
||||||
}{
|
|
||||||
{NewActionSet(PutObjectAction), []Action{PutObjectAction}},
|
|
||||||
{NewActionSet(), []Action{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.actionSet.ToSlice()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult ActionSet
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"s3:PutObject"`), NewActionSet(PutObjectAction), false},
|
|
||||||
{[]byte(`["s3:PutObject"]`), NewActionSet(PutObjectAction), false},
|
|
||||||
{[]byte(`["s3:PutObject", "s3:GetObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false},
|
|
||||||
{[]byte(`["s3:PutObject", "s3:GetObject", "s3:PutObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false},
|
|
||||||
{[]byte(`[]`), NewActionSet(), true}, // Empty array.
|
|
||||||
{[]byte(`"foo"`), nil, true}, // Invalid action.
|
|
||||||
{[]byte(`["s3:PutObject", "foo"]`), nil, true}, // Invalid action.
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := make(ActionSet)
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toBinaryEqualsFuncString(n name, key Key, values set.StringSet) string {
|
|
||||||
valueStrings := values.ToSlice()
|
|
||||||
sort.Strings(valueStrings)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// binaryEqualsFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type binaryEqualsFunc struct {
|
|
||||||
k Key
|
|
||||||
values set.StringSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f binaryEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
|
||||||
return !fvalues.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f binaryEqualsFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "BinaryEquals" condition name.
|
|
||||||
func (f binaryEqualsFunc) name() name {
|
|
||||||
return binaryEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f binaryEqualsFunc) String() string {
|
|
||||||
return toBinaryEqualsFuncString(binaryEquals, f.k, f.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f binaryEqualsFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
for _, value := range f.values.ToSlice() {
|
|
||||||
values.Add(NewStringValue(base64.StdEncoding.EncodeToString([]byte(value))))
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateBinaryEqualsValues(n name, key Key, values set.StringSet) error {
|
|
||||||
vslice := values.ToSlice()
|
|
||||||
for _, s := range vslice {
|
|
||||||
sbytes, err := base64.StdEncoding.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
values.Remove(s)
|
|
||||||
s = string(sbytes)
|
|
||||||
switch key {
|
|
||||||
case S3XAmzCopySource:
|
|
||||||
bucket, object := path2BucketAndObject(s)
|
|
||||||
if object == "" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzCopySource, n)
|
|
||||||
}
|
|
||||||
if err = s3utils.CheckValidBucketName(bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case S3XAmzServerSideEncryption, S3XAmzServerSideEncryptionCustomerAlgorithm:
|
|
||||||
if s != "AES256" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzServerSideEncryption, n)
|
|
||||||
}
|
|
||||||
case S3XAmzMetadataDirective:
|
|
||||||
if s != "COPY" && s != "REPLACE" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzMetadataDirective, n)
|
|
||||||
}
|
|
||||||
case S3XAmzContentSha256:
|
|
||||||
if s == "" {
|
|
||||||
return fmt.Errorf("invalid empty value for '%v' for %v condition", S3XAmzContentSha256, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values.Add(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBinaryEqualsFunc - returns new BinaryEquals function.
|
|
||||||
func newBinaryEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(binaryEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewBinaryEqualsFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBinaryEqualsFunc - returns new BinaryEquals function.
|
|
||||||
func NewBinaryEqualsFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateBinaryEqualsValues(binaryEquals, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &binaryEqualsFunc{key, sset}, nil
|
|
||||||
}
|
|
@ -1,383 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBinaryEqualsFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{}, false},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
|
||||||
{case2Function, map[string][]string{}, false},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
|
||||||
{case3Function, map[string][]string{}, false},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
|
||||||
{case4Function, map[string][]string{}, false},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBinaryEqualsFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBinaryEqualsFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&binaryEqualsFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewBinaryEqualsFunc(t *testing.T) {
|
|
||||||
case1Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBinaryEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newBinaryEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newBinaryEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newBinaryEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject")))), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("yourbucket/myobject"))),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256")))), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE")))), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("COPY"))),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1")))), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))),
|
|
||||||
NewStringValue(base64.StdEncoding.EncodeToString([]byte("us-west-1"))),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket/myobject"))), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("AES256"))), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("REPLACE"))), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("eu-west-1"))), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("mybucket")))), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("SSE-C")))), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue(base64.StdEncoding.EncodeToString([]byte("DUPLICATE")))), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newBinaryEqualsFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// booleanFunc - Bool condition function. It checks whether Key is true or false.
|
|
||||||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_Boolean
|
|
||||||
type booleanFunc struct {
|
|
||||||
k Key
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether Key is present in given values or not.
|
|
||||||
// Depending on condition boolean value, this function returns true or false.
|
|
||||||
func (f booleanFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.value == requestValue[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f booleanFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "Bool" condition name.
|
|
||||||
func (f booleanFunc) name() name {
|
|
||||||
return boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f booleanFunc) String() string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", boolean, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f booleanFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: NewValueSet(NewStringValue(f.value)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBooleanFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
if key != AWSSecureTransport {
|
|
||||||
return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSecureTransport, boolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) != 1 {
|
|
||||||
return nil, fmt.Errorf("only one value is allowed for boolean condition")
|
|
||||||
}
|
|
||||||
|
|
||||||
var value Value
|
|
||||||
for v := range values {
|
|
||||||
value = v
|
|
||||||
switch v.GetType() {
|
|
||||||
case reflect.Bool:
|
|
||||||
if _, err := v.GetBool(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
s, err := v.GetString()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err = strconv.ParseBool(s); err != nil {
|
|
||||||
return nil, fmt.Errorf("value must be a boolean string for boolean condition")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("value must be a boolean for boolean condition")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &booleanFunc{key, value.String()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBoolFunc - returns new Bool function.
|
|
||||||
func NewBoolFunc(key Key, value string) (Function, error) {
|
|
||||||
return &booleanFunc{key, value}, nil
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBooleanFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"SecureTransport": {"true"}}, true},
|
|
||||||
{case2Function, map[string][]string{"SecureTransport": {"false"}}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBooleanFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, AWSSecureTransport},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBooleanFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
AWSSecureTransport: NewValueSet(NewStringValue("true")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
AWSSecureTransport: NewValueSet(NewStringValue("false")),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewBooleanFunc(t *testing.T) {
|
|
||||||
case1Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newBooleanFunc(AWSSecureTransport, NewValueSet(NewBoolValue(false)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{AWSSecureTransport, NewValueSet(NewBoolValue(true)), case1Function, false},
|
|
||||||
{AWSSecureTransport, NewValueSet(NewStringValue("false")), case2Function, false},
|
|
||||||
// Multiple values error.
|
|
||||||
{AWSSecureTransport, NewValueSet(NewStringValue("true"), NewStringValue("false")), nil, true},
|
|
||||||
// Invalid boolean string error.
|
|
||||||
{AWSSecureTransport, NewValueSet(NewStringValue("foo")), nil, true},
|
|
||||||
// Invalid value error.
|
|
||||||
{AWSSecureTransport, NewValueSet(NewIntValue(7)), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newBooleanFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toDateEqualsFuncString(n name, key Key, value time.Time) string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, value.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateEqualsFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type dateEqualsFunc struct {
|
|
||||||
k Key
|
|
||||||
value time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f dateEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.value.Equal(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f dateEqualsFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "DateEquals" condition name.
|
|
||||||
func (f dateEqualsFunc) name() name {
|
|
||||||
return dateEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f dateEqualsFunc) String() string {
|
|
||||||
return toDateEqualsFuncString(dateEquals, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f dateEqualsFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
values.Add(NewStringValue(f.value.Format(time.RFC3339)))
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateNotEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type dateNotEqualsFunc struct {
|
|
||||||
dateEqualsFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f dateNotEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
return !f.dateEqualsFunc.evaluate(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "DateNotEquals" condition name.
|
|
||||||
func (f dateNotEqualsFunc) name() name {
|
|
||||||
return dateNotEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f dateNotEqualsFunc) String() string {
|
|
||||||
return toDateEqualsFuncString(dateNotEquals, f.dateEqualsFunc.k, f.dateEqualsFunc.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func valueToTime(n name, values ValueSet) (v time.Time, err error) {
|
|
||||||
if len(values) != 1 {
|
|
||||||
return v, fmt.Errorf("only one value is allowed for %s condition", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
for vs := range values {
|
|
||||||
switch vs.GetType() {
|
|
||||||
case reflect.String:
|
|
||||||
s, err := vs.GetString()
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
if v, err = time.Parse(time.RFC3339, s); err != nil {
|
|
||||||
return v, fmt.Errorf("value %s must be a time.Time string for %s condition: %w", vs, n, err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return v, fmt.Errorf("value %s must be a time.Time for %s condition", vs, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDateEqualsFunc - returns new DateEquals function.
|
|
||||||
func newDateEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToTime(dateEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewDateEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateEqualsFunc - returns new DateEquals function.
|
|
||||||
func NewDateEqualsFunc(key Key, value time.Time) (Function, error) {
|
|
||||||
return &dateEqualsFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDateNotEqualsFunc - returns new DateNotEquals function.
|
|
||||||
func newDateNotEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToTime(dateNotEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewDateNotEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateNotEqualsFunc - returns new DateNotEquals function.
|
|
||||||
func NewDateNotEqualsFunc(key Key, value time.Time) (Function, error) {
|
|
||||||
return &dateNotEqualsFunc{dateEqualsFunc{key, value}}, nil
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toDateGreaterThanFuncString(n name, key Key, value time.Time) string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, value.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateGreaterThanFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type dateGreaterThanFunc struct {
|
|
||||||
k Key
|
|
||||||
value time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f dateGreaterThanFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.After(f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f dateGreaterThanFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "DateGreaterThan" condition name.
|
|
||||||
func (f dateGreaterThanFunc) name() name {
|
|
||||||
return dateGreaterThan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f dateGreaterThanFunc) String() string {
|
|
||||||
return toDateGreaterThanFuncString(dateGreaterThan, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f dateGreaterThanFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
values.Add(NewStringValue(f.value.Format(time.RFC3339)))
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateNotEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type dateGreaterThanEqualsFunc struct {
|
|
||||||
dateGreaterThanFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f dateGreaterThanEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.After(f.value) || t.Equal(f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "DateNotEquals" condition name.
|
|
||||||
func (f dateGreaterThanEqualsFunc) name() name {
|
|
||||||
return dateGreaterThanEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f dateGreaterThanEqualsFunc) String() string {
|
|
||||||
return toDateGreaterThanFuncString(dateNotEquals, f.dateGreaterThanFunc.k, f.dateGreaterThanFunc.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDateGreaterThanFunc - returns new DateGreaterThan function.
|
|
||||||
func newDateGreaterThanFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToTime(dateGreaterThan, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewDateGreaterThanFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateGreaterThanFunc - returns new DateGreaterThan function.
|
|
||||||
func NewDateGreaterThanFunc(key Key, value time.Time) (Function, error) {
|
|
||||||
return &dateGreaterThanFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDateNotEqualsFunc - returns new DateNotEquals function.
|
|
||||||
func newDateGreaterThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToTime(dateNotEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewDateGreaterThanEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateGreaterThanEqualsFunc - returns new DateNotEquals function.
|
|
||||||
func NewDateGreaterThanEqualsFunc(key Key, value time.Time) (Function, error) {
|
|
||||||
return &dateGreaterThanEqualsFunc{dateGreaterThanFunc{key, value}}, nil
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toDateLessThanFuncString(n name, key Key, value time.Time) string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, value.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateLessThanFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type dateLessThanFunc struct {
|
|
||||||
k Key
|
|
||||||
value time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f dateLessThanFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.Before(f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f dateLessThanFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "DateLessThan" condition name.
|
|
||||||
func (f dateLessThanFunc) name() name {
|
|
||||||
return dateLessThan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f dateLessThanFunc) String() string {
|
|
||||||
return toDateLessThanFuncString(dateLessThan, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f dateLessThanFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
values.Add(NewStringValue(f.value.Format(time.RFC3339)))
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateNotEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type dateLessThanEqualsFunc struct {
|
|
||||||
dateLessThanFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f dateLessThanEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.Before(f.value) || t.Equal(f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "DateNotEquals" condition name.
|
|
||||||
func (f dateLessThanEqualsFunc) name() name {
|
|
||||||
return dateLessThanEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f dateLessThanEqualsFunc) String() string {
|
|
||||||
return toDateLessThanFuncString(dateNotEquals, f.dateLessThanFunc.k, f.dateLessThanFunc.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDateLessThanFunc - returns new DateLessThan function.
|
|
||||||
func newDateLessThanFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToTime(dateLessThan, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewDateLessThanFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateLessThanFunc - returns new DateLessThan function.
|
|
||||||
func NewDateLessThanFunc(key Key, value time.Time) (Function, error) {
|
|
||||||
return &dateLessThanFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDateNotEqualsFunc - returns new DateNotEquals function.
|
|
||||||
func newDateLessThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToTime(dateNotEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewDateLessThanEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateLessThanEqualsFunc - returns new DateNotEquals function.
|
|
||||||
func NewDateLessThanEqualsFunc(key Key, value time.Time) (Function, error) {
|
|
||||||
return &dateLessThanEqualsFunc{dateLessThanFunc{key, value}}, nil
|
|
||||||
}
|
|
@ -1,224 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Function - condition function interface.
|
|
||||||
type Function interface {
|
|
||||||
// evaluate() - evaluates this condition function with given values.
|
|
||||||
evaluate(values map[string][]string) bool
|
|
||||||
|
|
||||||
// key() - returns condition key used in this function.
|
|
||||||
key() Key
|
|
||||||
|
|
||||||
// name() - returns condition name of this function.
|
|
||||||
name() name
|
|
||||||
|
|
||||||
// String() - returns string representation of function.
|
|
||||||
String() string
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
toMap() map[Key]ValueSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functions - list of functions.
|
|
||||||
type Functions []Function
|
|
||||||
|
|
||||||
// Evaluate - evaluates all functions with given values map. Each function is evaluated
|
|
||||||
// sequencely and next function is called only if current function succeeds.
|
|
||||||
func (functions Functions) Evaluate(values map[string][]string) bool {
|
|
||||||
for _, f := range functions {
|
|
||||||
if !f.evaluate(values) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys - returns list of keys used in all functions.
|
|
||||||
func (functions Functions) Keys() KeySet {
|
|
||||||
keySet := NewKeySet()
|
|
||||||
|
|
||||||
for _, f := range functions {
|
|
||||||
keySet.Add(f.key())
|
|
||||||
}
|
|
||||||
|
|
||||||
return keySet
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone clones Functions structure
|
|
||||||
func (functions Functions) Clone() Functions {
|
|
||||||
funcs := []Function{}
|
|
||||||
|
|
||||||
for _, f := range functions {
|
|
||||||
vfn := conditionFuncMap[f.name()]
|
|
||||||
for key, values := range f.toMap() {
|
|
||||||
function, _ := vfn(key, values.Clone())
|
|
||||||
funcs = append(funcs, function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return funcs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns true if two Functions structures are equal
|
|
||||||
func (functions Functions) Equals(funcs Functions) bool {
|
|
||||||
if len(functions) != len(funcs) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, fi := range functions {
|
|
||||||
fistr := fi.String()
|
|
||||||
found := false
|
|
||||||
for _, fj := range funcs {
|
|
||||||
if fistr == fj.String() {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Functions to JSON data.
|
|
||||||
func (functions Functions) MarshalJSON() ([]byte, error) {
|
|
||||||
nm := make(map[name]map[Key]ValueSet)
|
|
||||||
|
|
||||||
for _, f := range functions {
|
|
||||||
if _, ok := nm[f.name()]; ok {
|
|
||||||
for k, v := range f.toMap() {
|
|
||||||
nm[f.name()][k] = v
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nm[f.name()] = f.toMap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(nm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functions Functions) String() string {
|
|
||||||
funcStrings := []string{}
|
|
||||||
for _, f := range functions {
|
|
||||||
s := fmt.Sprintf("%v", f)
|
|
||||||
funcStrings = append(funcStrings, s)
|
|
||||||
}
|
|
||||||
sort.Strings(funcStrings)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v", funcStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
var conditionFuncMap = map[name]func(Key, ValueSet) (Function, error){
|
|
||||||
stringEquals: newStringEqualsFunc,
|
|
||||||
stringNotEquals: newStringNotEqualsFunc,
|
|
||||||
stringEqualsIgnoreCase: newStringEqualsIgnoreCaseFunc,
|
|
||||||
stringNotEqualsIgnoreCase: newStringNotEqualsIgnoreCaseFunc,
|
|
||||||
binaryEquals: newBinaryEqualsFunc,
|
|
||||||
stringLike: newStringLikeFunc,
|
|
||||||
stringNotLike: newStringNotLikeFunc,
|
|
||||||
ipAddress: newIPAddressFunc,
|
|
||||||
notIPAddress: newNotIPAddressFunc,
|
|
||||||
null: newNullFunc,
|
|
||||||
boolean: newBooleanFunc,
|
|
||||||
numericEquals: newNumericEqualsFunc,
|
|
||||||
numericNotEquals: newNumericNotEqualsFunc,
|
|
||||||
numericLessThan: newNumericLessThanFunc,
|
|
||||||
numericLessThanEquals: newNumericLessThanEqualsFunc,
|
|
||||||
numericGreaterThan: newNumericGreaterThanFunc,
|
|
||||||
numericGreaterThanEquals: newNumericGreaterThanEqualsFunc,
|
|
||||||
dateEquals: newDateEqualsFunc,
|
|
||||||
dateNotEquals: newDateNotEqualsFunc,
|
|
||||||
dateLessThan: newDateLessThanFunc,
|
|
||||||
dateLessThanEquals: newDateLessThanEqualsFunc,
|
|
||||||
dateGreaterThan: newDateGreaterThanFunc,
|
|
||||||
dateGreaterThanEquals: newDateGreaterThanEqualsFunc,
|
|
||||||
// Add new conditions here.
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Functions.
|
|
||||||
func (functions *Functions) UnmarshalJSON(data []byte) error {
|
|
||||||
// As string kind, int kind then json.Unmarshaler is checked at
|
|
||||||
// https://github.com/golang/go/blob/master/src/encoding/json/decode.go#L618
|
|
||||||
// UnmarshalJSON() is not called for types extending string
|
|
||||||
// see https://play.golang.org/p/HrSsKksHvrS, better way to do is
|
|
||||||
// https://play.golang.org/p/y9ElWpBgVAB
|
|
||||||
//
|
|
||||||
// Due to this issue, name and Key types cannot be used as map keys below.
|
|
||||||
nm := make(map[string]map[string]ValueSet)
|
|
||||||
if err := json.Unmarshal(data, &nm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nm) == 0 {
|
|
||||||
return fmt.Errorf("condition must not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
funcs := []Function{}
|
|
||||||
for nameString, args := range nm {
|
|
||||||
n, err := parseName(nameString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for keyString, values := range args {
|
|
||||||
key, err := parseKey(keyString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
vfn, ok := conditionFuncMap[n]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("condition %v is not handled", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := vfn(key, values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
funcs = append(funcs, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*functions = funcs
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GobEncode - encodes Functions to gob data.
|
|
||||||
func (functions Functions) GobEncode() ([]byte, error) {
|
|
||||||
return functions.MarshalJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GobDecode - decodes gob data to Functions.
|
|
||||||
func (functions *Functions) GobDecode(data []byte) error {
|
|
||||||
return functions.UnmarshalJSON(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFunctions - returns new Functions with given function list.
|
|
||||||
func NewFunctions(functions ...Function) Functions {
|
|
||||||
return Functions(functions)
|
|
||||||
}
|
|
@ -1,354 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFunctionsEvaluate(t *testing.T) {
|
|
||||||
func1, err := newNullFunc(S3XAmzCopySource, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func3, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func4, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Function := NewFunctions(func1, func2, func3, func4)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
functions Functions
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/myobject"},
|
|
||||||
"SourceIp": {"192.168.1.10"},
|
|
||||||
}, false},
|
|
||||||
{case1Function, map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/myobject"},
|
|
||||||
"SourceIp": {"192.168.1.10"},
|
|
||||||
"Refer": {"http://example.org/"},
|
|
||||||
}, false},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{"SourceIp": {"192.168.1.10"}}, false},
|
|
||||||
{case1Function, map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/yourobject"},
|
|
||||||
"SourceIp": {"192.168.1.10"},
|
|
||||||
}, false},
|
|
||||||
{case1Function, map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/myobject"},
|
|
||||||
"SourceIp": {"192.168.2.10"},
|
|
||||||
}, false},
|
|
||||||
{case1Function, map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/myobject"},
|
|
||||||
"Refer": {"http://example.org/"},
|
|
||||||
}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.functions.Evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFunctionsKeys(t *testing.T) {
|
|
||||||
func1, err := newNullFunc(S3XAmzCopySource, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func3, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func4, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
functions Functions
|
|
||||||
expectedResult KeySet
|
|
||||||
}{
|
|
||||||
{NewFunctions(func1, func2, func3, func4), NewKeySet(S3XAmzCopySource, AWSSourceIP)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.functions.Keys()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFunctionsMarshalJSON(t *testing.T) {
|
|
||||||
func1, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func3, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func4, err := newNotIPAddressFunc(AWSSourceIP,
|
|
||||||
NewValueSet(NewStringValue("10.1.10.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func5, err := newStringNotLikeFunc(S3XAmzStorageClass, NewValueSet(NewStringValue("STANDARD")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func6, err := newNullFunc(S3XAmzServerSideEncryptionCustomerAlgorithm, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func7, err := newIPAddressFunc(AWSSourceIP,
|
|
||||||
NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := []byte(`{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]},"NotIpAddress":{"aws:SourceIp":["10.1.10.0/24"]},"Null":{"s3:x-amz-server-side-encryption-customer-algorithm":[true]},"StringEquals":{"s3:x-amz-copy-source":["mybucket/myobject"]},"StringLike":{"s3:x-amz-metadata-directive":["REPL*"]},"StringNotEquals":{"s3:x-amz-server-side-encryption":["AES256"]},"StringNotLike":{"s3:x-amz-storage-class":["STANDARD"]}}`)
|
|
||||||
|
|
||||||
case2Result := []byte(`{"Null":{"s3:x-amz-server-side-encryption-customer-algorithm":[true]}}`)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
functions Functions
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewFunctions(func1, func2, func3, func4, func5, func6, func7), case1Result, false},
|
|
||||||
{NewFunctions(func6), case2Result, false},
|
|
||||||
{NewFunctions(), []byte(`{}`), false},
|
|
||||||
{nil, []byte(`{}`), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.functions)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFunctionsUnmarshalJSON(t *testing.T) {
|
|
||||||
case1Data := []byte(`{
|
|
||||||
"StringLike": {
|
|
||||||
"s3:x-amz-metadata-directive": "REPL*"
|
|
||||||
},
|
|
||||||
"StringEquals": {
|
|
||||||
"s3:x-amz-copy-source": "mybucket/myobject"
|
|
||||||
},
|
|
||||||
"StringNotEquals": {
|
|
||||||
"s3:x-amz-server-side-encryption": "AES256"
|
|
||||||
},
|
|
||||||
"NotIpAddress": {
|
|
||||||
"aws:SourceIp": [
|
|
||||||
"10.1.10.0/24",
|
|
||||||
"10.10.1.0/24"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"StringNotLike": {
|
|
||||||
"s3:x-amz-storage-class": "STANDARD"
|
|
||||||
},
|
|
||||||
"Null": {
|
|
||||||
"s3:x-amz-server-side-encryption-customer-algorithm": true
|
|
||||||
},
|
|
||||||
"IpAddress": {
|
|
||||||
"aws:SourceIp": [
|
|
||||||
"192.168.1.0/24",
|
|
||||||
"192.168.2.0/24"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
func1, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func3, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func4, err := newNotIPAddressFunc(AWSSourceIP,
|
|
||||||
NewValueSet(NewStringValue("10.1.10.0/24"), NewStringValue("10.10.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func5, err := newStringNotLikeFunc(S3XAmzStorageClass, NewValueSet(NewStringValue("STANDARD")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func6, err := newNullFunc(S3XAmzServerSideEncryptionCustomerAlgorithm, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func7, err := newIPAddressFunc(AWSSourceIP,
|
|
||||||
NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("192.168.2.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Data := []byte(`{
|
|
||||||
"Null": {
|
|
||||||
"s3:x-amz-server-side-encryption-customer-algorithm": true
|
|
||||||
},
|
|
||||||
"Null": {
|
|
||||||
"s3:x-amz-server-side-encryption-customer-algorithm": "true"
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case3Data := []byte(`{}`)
|
|
||||||
|
|
||||||
case4Data := []byte(`{
|
|
||||||
"StringLike": {
|
|
||||||
"s3:x-amz-metadata-directive": "REPL*"
|
|
||||||
},
|
|
||||||
"StringEquals": {
|
|
||||||
"s3:x-amz-copy-source": "mybucket/myobject",
|
|
||||||
"s3:prefix": [
|
|
||||||
"",
|
|
||||||
"home/"
|
|
||||||
],
|
|
||||||
"s3:delimiter": [
|
|
||||||
"/"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"StringNotEquals": {
|
|
||||||
"s3:x-amz-server-side-encryption": "AES256"
|
|
||||||
},
|
|
||||||
"NotIpAddress": {
|
|
||||||
"aws:SourceIp": [
|
|
||||||
"10.1.10.0/24",
|
|
||||||
"10.10.1.0/24"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"StringNotLike": {
|
|
||||||
"s3:x-amz-storage-class": "STANDARD"
|
|
||||||
},
|
|
||||||
"Null": {
|
|
||||||
"s3:x-amz-server-side-encryption-customer-algorithm": true
|
|
||||||
},
|
|
||||||
"IpAddress": {
|
|
||||||
"aws:SourceIp": [
|
|
||||||
"192.168.1.0/24",
|
|
||||||
"192.168.2.0/24"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
func2_1, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2_2, err := newStringEqualsFunc(S3Prefix, NewValueSet(NewStringValue(""), NewStringValue("home/")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2_3, err := newStringEqualsFunc(S3Delimiter, NewValueSet(NewStringValue("/")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult Functions
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
// Success case, basic conditions.
|
|
||||||
{case1Data, NewFunctions(func1, func2, func3, func4, func5, func6, func7), false},
|
|
||||||
// Duplicate conditions, success case only one value is preserved.
|
|
||||||
{case2Data, NewFunctions(func6), false},
|
|
||||||
// empty condition error.
|
|
||||||
{case3Data, nil, true},
|
|
||||||
// Success case multiple keys, same condition.
|
|
||||||
{case4Data, NewFunctions(func1, func2_1, func2_2, func2_3, func3, func4, func5, func6, func7), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := new(Functions)
|
|
||||||
err := json.Unmarshal(testCase.data, result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if (*result).String() != testCase.expectedResult.String() {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, *result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,187 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toIPAddressFuncString(n name, key Key, values []*net.IPNet) string {
|
|
||||||
valueStrings := []string{}
|
|
||||||
for _, value := range values {
|
|
||||||
valueStrings = append(valueStrings, value.String())
|
|
||||||
}
|
|
||||||
sort.Strings(valueStrings)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ipAddressFunc - IP address function. It checks whether value by Key in given
|
|
||||||
// values is in IP network. Here Key must be AWSSourceIP.
|
|
||||||
// For example,
|
|
||||||
// - if values = [192.168.1.0/24], at evaluate() it returns whether IP address
|
|
||||||
// in value map for AWSSourceIP falls in the network 192.168.1.10/24.
|
|
||||||
type ipAddressFunc struct {
|
|
||||||
k Key
|
|
||||||
values []*net.IPNet
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether IP address in values map for AWSSourceIP
|
|
||||||
// falls in one of network or not.
|
|
||||||
func (f ipAddressFunc) evaluate(values map[string][]string) bool {
|
|
||||||
IPs := []net.IP{}
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range requestValue {
|
|
||||||
IP := net.ParseIP(s)
|
|
||||||
if IP == nil {
|
|
||||||
panic(fmt.Errorf("invalid IP address '%v'", s))
|
|
||||||
}
|
|
||||||
|
|
||||||
IPs = append(IPs, IP)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, IP := range IPs {
|
|
||||||
for _, IPNet := range f.values {
|
|
||||||
if IPNet.Contains(IP) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
// Key is always AWSSourceIP.
|
|
||||||
func (f ipAddressFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "IpAddress" condition name.
|
|
||||||
func (f ipAddressFunc) name() name {
|
|
||||||
return ipAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f ipAddressFunc) String() string {
|
|
||||||
return toIPAddressFuncString(ipAddress, f.k, f.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f ipAddressFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
for _, value := range f.values {
|
|
||||||
values.Add(NewStringValue(value.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notIPAddressFunc - Not IP address function. It checks whether value by Key in given
|
|
||||||
// values is NOT in IP network. Here Key must be AWSSourceIP.
|
|
||||||
// For example,
|
|
||||||
// - if values = [192.168.1.0/24], at evaluate() it returns whether IP address
|
|
||||||
// in value map for AWSSourceIP does not fall in the network 192.168.1.10/24.
|
|
||||||
type notIPAddressFunc struct {
|
|
||||||
ipAddressFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether IP address in values map for AWSSourceIP
|
|
||||||
// does not fall in one of network.
|
|
||||||
func (f notIPAddressFunc) evaluate(values map[string][]string) bool {
|
|
||||||
return !f.ipAddressFunc.evaluate(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NotIpAddress" condition name.
|
|
||||||
func (f notIPAddressFunc) name() name {
|
|
||||||
return notIPAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f notIPAddressFunc) String() string {
|
|
||||||
return toIPAddressFuncString(notIPAddress, f.ipAddressFunc.k, f.ipAddressFunc.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
func valuesToIPNets(n name, values ValueSet) ([]*net.IPNet, error) {
|
|
||||||
IPNets := []*net.IPNet{}
|
|
||||||
for v := range values {
|
|
||||||
s, err := v.GetString()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("value %v must be string representation of CIDR for %v condition", v, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
var IPNet *net.IPNet
|
|
||||||
_, IPNet, err = net.ParseCIDR(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("value %v must be CIDR string for %v condition", s, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
IPNets = append(IPNets, IPNet)
|
|
||||||
}
|
|
||||||
|
|
||||||
return IPNets, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newIPAddressFunc - returns new IP address function.
|
|
||||||
func newIPAddressFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
IPNets, err := valuesToIPNets(ipAddress, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewIPAddressFunc(key, IPNets...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIPAddressFunc - returns new IP address function.
|
|
||||||
func NewIPAddressFunc(key Key, IPNets ...*net.IPNet) (Function, error) {
|
|
||||||
if key != AWSSourceIP {
|
|
||||||
return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSourceIP, ipAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ipAddressFunc{key, IPNets}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNotIPAddressFunc - returns new Not IP address function.
|
|
||||||
func newNotIPAddressFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
IPNets, err := valuesToIPNets(notIPAddress, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNotIPAddressFunc(key, IPNets...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotIPAddressFunc - returns new Not IP address function.
|
|
||||||
func NewNotIPAddressFunc(key Key, IPNets ...*net.IPNet) (Function, error) {
|
|
||||||
if key != AWSSourceIP {
|
|
||||||
return nil, fmt.Errorf("only %v key is allowed for %v condition", AWSSourceIP, notIPAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ¬IPAddressFunc{ipAddressFunc{key, IPNets}}, nil
|
|
||||||
}
|
|
@ -1,279 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIPAddressFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"SourceIp": {"192.168.1.10"}}, true},
|
|
||||||
{case1Function, map[string][]string{"SourceIp": {"192.168.2.10"}}, false},
|
|
||||||
{case1Function, map[string][]string{}, false},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPAddressFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, AWSSourceIP},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPAddressFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{&ipAddressFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotIPAddressFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"SourceIp": {"192.168.2.10"}}, true},
|
|
||||||
{case1Function, map[string][]string{}, true},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
{case1Function, map[string][]string{"SourceIp": {"192.168.1.10"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotIPAddressFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, AWSSourceIP},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotIPAddressFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
AWSSourceIP: NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{¬IPAddressFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewIPAddressFunc(t *testing.T) {
|
|
||||||
case1Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")), case1Function, false},
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")), case2Function, false},
|
|
||||||
// Unsupported key error.
|
|
||||||
{S3Prefix, NewValueSet(NewStringValue("192.168.1.0/24")), nil, true},
|
|
||||||
// Invalid value error.
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("node1.example.org")), nil, true},
|
|
||||||
// Invalid CIDR format error.
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0.0/24")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newIPAddressFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if result.String() != testCase.expectedResult.String() {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewNotIPAddressFunc(t *testing.T) {
|
|
||||||
case1Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newNotIPAddressFunc(AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24")), case1Function, false},
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0/24"), NewStringValue("10.1.10.1/32")), case2Function, false},
|
|
||||||
// Unsupported key error.
|
|
||||||
{S3Prefix, NewValueSet(NewStringValue("192.168.1.0/24")), nil, true},
|
|
||||||
// Invalid value error.
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("node1.example.org")), nil, true},
|
|
||||||
// Invalid CIDR format error.
|
|
||||||
{AWSSourceIP, NewValueSet(NewStringValue("192.168.1.0.0/24")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newNotIPAddressFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if result.String() != testCase.expectedResult.String() {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
// JWT claims supported substitutions.
|
|
||||||
// https://www.iana.org/assignments/jwt/jwt.xhtml#claims
|
|
||||||
const (
|
|
||||||
// JWTSub - JWT subject claim substitution.
|
|
||||||
JWTSub Key = "jwt:sub"
|
|
||||||
|
|
||||||
// JWTIss issuer claim substitution.
|
|
||||||
JWTIss Key = "jwt:iss"
|
|
||||||
|
|
||||||
// JWTAud audience claim substitution.
|
|
||||||
JWTAud Key = "jwt:aud"
|
|
||||||
|
|
||||||
// JWTJti JWT unique identifier claim substitution.
|
|
||||||
JWTJti Key = "jwt:jti"
|
|
||||||
|
|
||||||
JWTUpn Key = "jwt:upn"
|
|
||||||
JWTName Key = "jwt:name"
|
|
||||||
JWTGroups Key = "jwt:groups"
|
|
||||||
JWTGivenName Key = "jwt:given_name"
|
|
||||||
JWTFamilyName Key = "jwt:family_name"
|
|
||||||
JWTMiddleName Key = "jwt:middle_name"
|
|
||||||
JWTNickName Key = "jwt:nickname"
|
|
||||||
JWTPrefUsername Key = "jwt:preferred_username"
|
|
||||||
JWTProfile Key = "jwt:profile"
|
|
||||||
JWTPicture Key = "jwt:picture"
|
|
||||||
JWTWebsite Key = "jwt:website"
|
|
||||||
JWTEmail Key = "jwt:email"
|
|
||||||
JWTGender Key = "jwt:gender"
|
|
||||||
JWTBirthdate Key = "jwt:birthdate"
|
|
||||||
JWTPhoneNumber Key = "jwt:phone_number"
|
|
||||||
JWTAddress Key = "jwt:address"
|
|
||||||
JWTScope Key = "jwt:scope"
|
|
||||||
JWTClientID Key = "jwt:client_id"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JWTKeys - Supported JWT keys, non-exhaustive list please
|
|
||||||
// expand as new claims are standardized.
|
|
||||||
var JWTKeys = []Key{
|
|
||||||
JWTSub,
|
|
||||||
JWTIss,
|
|
||||||
JWTAud,
|
|
||||||
JWTJti,
|
|
||||||
JWTName,
|
|
||||||
JWTUpn,
|
|
||||||
JWTGroups,
|
|
||||||
JWTGivenName,
|
|
||||||
JWTFamilyName,
|
|
||||||
JWTMiddleName,
|
|
||||||
JWTNickName,
|
|
||||||
JWTPrefUsername,
|
|
||||||
JWTProfile,
|
|
||||||
JWTPicture,
|
|
||||||
JWTWebsite,
|
|
||||||
JWTEmail,
|
|
||||||
JWTGender,
|
|
||||||
JWTBirthdate,
|
|
||||||
JWTPhoneNumber,
|
|
||||||
JWTAddress,
|
|
||||||
JWTScope,
|
|
||||||
JWTClientID,
|
|
||||||
}
|
|
@ -1,322 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Key - conditional key which is used to fetch values for any condition.
|
|
||||||
// Refer https://docs.aws.amazon.com/IAM/latest/UserGuide/list_s3.html
|
|
||||||
// for more information about available condition keys.
|
|
||||||
type Key string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// S3XAmzCopySource - key representing x-amz-copy-source HTTP header applicable to PutObject API only.
|
|
||||||
S3XAmzCopySource Key = "s3:x-amz-copy-source"
|
|
||||||
|
|
||||||
// S3XAmzServerSideEncryption - key representing x-amz-server-side-encryption HTTP header applicable
|
|
||||||
// to PutObject API only.
|
|
||||||
S3XAmzServerSideEncryption Key = "s3:x-amz-server-side-encryption"
|
|
||||||
|
|
||||||
// S3XAmzServerSideEncryptionCustomerAlgorithm - key representing
|
|
||||||
// x-amz-server-side-encryption-customer-algorithm HTTP header applicable to PutObject API only.
|
|
||||||
S3XAmzServerSideEncryptionCustomerAlgorithm Key = "s3:x-amz-server-side-encryption-customer-algorithm"
|
|
||||||
|
|
||||||
// S3XAmzMetadataDirective - key representing x-amz-metadata-directive HTTP header applicable to
|
|
||||||
// PutObject API only.
|
|
||||||
S3XAmzMetadataDirective Key = "s3:x-amz-metadata-directive"
|
|
||||||
|
|
||||||
// S3XAmzContentSha256 - set a static content-sha256 for all calls for a given action.
|
|
||||||
S3XAmzContentSha256 = "s3:x-amz-content-sha256"
|
|
||||||
|
|
||||||
// S3XAmzStorageClass - key representing x-amz-storage-class HTTP header applicable to PutObject API
|
|
||||||
// only.
|
|
||||||
S3XAmzStorageClass Key = "s3:x-amz-storage-class"
|
|
||||||
|
|
||||||
// S3LocationConstraint - key representing LocationConstraint XML tag of CreateBucket API only.
|
|
||||||
S3LocationConstraint Key = "s3:LocationConstraint"
|
|
||||||
|
|
||||||
// S3Prefix - key representing prefix query parameter of ListBucket API only.
|
|
||||||
S3Prefix Key = "s3:prefix"
|
|
||||||
|
|
||||||
// S3Delimiter - key representing delimiter query parameter of ListBucket API only.
|
|
||||||
S3Delimiter Key = "s3:delimiter"
|
|
||||||
|
|
||||||
// S3VersionID - Enables you to limit the permission for the
|
|
||||||
// s3:PutObjectVersionTagging action to a specific object version.
|
|
||||||
S3VersionID Key = "s3:versionid"
|
|
||||||
|
|
||||||
// S3MaxKeys - key representing max-keys query parameter of ListBucket API only.
|
|
||||||
S3MaxKeys Key = "s3:max-keys"
|
|
||||||
|
|
||||||
// S3ObjectLockRemainingRetentionDays - key representing object-lock-remaining-retention-days
|
|
||||||
// Enables enforcement of an object relative to the remaining retention days, you can set
|
|
||||||
// minimum and maximum allowable retention periods for a bucket using a bucket policy.
|
|
||||||
// This key are specific for s3:PutObjectRetention API.
|
|
||||||
S3ObjectLockRemainingRetentionDays Key = "s3:object-lock-remaining-retention-days"
|
|
||||||
|
|
||||||
// S3ObjectLockMode - key representing object-lock-mode
|
|
||||||
// Enables enforcement of the specified object retention mode
|
|
||||||
S3ObjectLockMode Key = "s3:object-lock-mode"
|
|
||||||
|
|
||||||
// S3ObjectLockRetainUntilDate - key representing object-lock-retain-util-date
|
|
||||||
// Enables enforcement of a specific retain-until-date
|
|
||||||
S3ObjectLockRetainUntilDate Key = "s3:object-lock-retain-until-date"
|
|
||||||
|
|
||||||
// S3ObjectLockLegalHold - key representing object-local-legal-hold
|
|
||||||
// Enables enforcement of the specified object legal hold status
|
|
||||||
S3ObjectLockLegalHold Key = "s3:object-lock-legal-hold"
|
|
||||||
|
|
||||||
// AWSReferer - key representing Referer header of any API.
|
|
||||||
AWSReferer Key = "aws:Referer"
|
|
||||||
|
|
||||||
// AWSSourceIP - key representing client's IP address (not intermittent proxies) of any API.
|
|
||||||
AWSSourceIP Key = "aws:SourceIp"
|
|
||||||
|
|
||||||
// AWSUserAgent - key representing UserAgent header for any API.
|
|
||||||
AWSUserAgent Key = "aws:UserAgent"
|
|
||||||
|
|
||||||
// AWSSecureTransport - key representing if the clients request is authenticated or not.
|
|
||||||
AWSSecureTransport Key = "aws:SecureTransport"
|
|
||||||
|
|
||||||
// AWSCurrentTime - key representing the current time.
|
|
||||||
AWSCurrentTime Key = "aws:CurrentTime"
|
|
||||||
|
|
||||||
// AWSEpochTime - key representing the current epoch time.
|
|
||||||
AWSEpochTime Key = "aws:EpochTime"
|
|
||||||
|
|
||||||
// AWSPrincipalType - user principal type currently supported values are "User" and "Anonymous".
|
|
||||||
AWSPrincipalType Key = "aws:principaltype"
|
|
||||||
|
|
||||||
// AWSUserID - user unique ID, in MinIO this value is same as your user Access Key.
|
|
||||||
AWSUserID Key = "aws:userid"
|
|
||||||
|
|
||||||
// AWSUsername - user friendly name, in MinIO this value is same as your user Access Key.
|
|
||||||
AWSUsername Key = "aws:username"
|
|
||||||
|
|
||||||
// S3SignatureVersion - identifies the version of AWS Signature that you want to support for authenticated requests.
|
|
||||||
S3SignatureVersion = "s3:signatureversion"
|
|
||||||
|
|
||||||
// S3AuthType - optionally use this condition key to restrict incoming requests to use a specific authentication method.
|
|
||||||
S3AuthType = "s3:authType"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AllSupportedKeys - is list of all all supported keys.
|
|
||||||
var AllSupportedKeys = append([]Key{
|
|
||||||
S3SignatureVersion,
|
|
||||||
S3AuthType,
|
|
||||||
S3XAmzCopySource,
|
|
||||||
S3XAmzServerSideEncryption,
|
|
||||||
S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
S3XAmzMetadataDirective,
|
|
||||||
S3XAmzStorageClass,
|
|
||||||
S3XAmzContentSha256,
|
|
||||||
S3LocationConstraint,
|
|
||||||
S3Prefix,
|
|
||||||
S3Delimiter,
|
|
||||||
S3MaxKeys,
|
|
||||||
S3VersionID,
|
|
||||||
S3ObjectLockRemainingRetentionDays,
|
|
||||||
S3ObjectLockMode,
|
|
||||||
S3ObjectLockLegalHold,
|
|
||||||
S3ObjectLockRetainUntilDate,
|
|
||||||
AWSReferer,
|
|
||||||
AWSSourceIP,
|
|
||||||
AWSUserAgent,
|
|
||||||
AWSSecureTransport,
|
|
||||||
AWSCurrentTime,
|
|
||||||
AWSEpochTime,
|
|
||||||
AWSPrincipalType,
|
|
||||||
AWSUserID,
|
|
||||||
AWSUsername,
|
|
||||||
LDAPUser,
|
|
||||||
LDAPUsername,
|
|
||||||
// Add new supported condition keys.
|
|
||||||
}, JWTKeys...)
|
|
||||||
|
|
||||||
// CommonKeys - is list of all common condition keys.
|
|
||||||
var CommonKeys = append([]Key{
|
|
||||||
S3SignatureVersion,
|
|
||||||
S3AuthType,
|
|
||||||
S3XAmzContentSha256,
|
|
||||||
S3LocationConstraint,
|
|
||||||
AWSReferer,
|
|
||||||
AWSSourceIP,
|
|
||||||
AWSUserAgent,
|
|
||||||
AWSSecureTransport,
|
|
||||||
AWSCurrentTime,
|
|
||||||
AWSEpochTime,
|
|
||||||
AWSPrincipalType,
|
|
||||||
AWSUserID,
|
|
||||||
AWSUsername,
|
|
||||||
LDAPUser,
|
|
||||||
LDAPUsername,
|
|
||||||
}, JWTKeys...)
|
|
||||||
|
|
||||||
func substFuncFromValues(values map[string][]string) func(string) string {
|
|
||||||
return func(v string) string {
|
|
||||||
for _, key := range CommonKeys {
|
|
||||||
// Empty values are not supported for policy variables.
|
|
||||||
if rvalues, ok := values[key.Name()]; ok && rvalues[0] != "" {
|
|
||||||
v = strings.Replace(v, key.VarName(), rvalues[0], -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks if key is valid or not.
|
|
||||||
func (key Key) IsValid() bool {
|
|
||||||
for _, supKey := range AllSupportedKeys {
|
|
||||||
if supKey == key {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Key to JSON data.
|
|
||||||
func (key Key) MarshalJSON() ([]byte, error) {
|
|
||||||
if !key.IsValid() {
|
|
||||||
return nil, fmt.Errorf("unknown key %v", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(string(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// VarName - returns variable key name, such as "${aws:username}"
|
|
||||||
func (key Key) VarName() string {
|
|
||||||
return fmt.Sprintf("${%s}", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name - returns key name which is stripped value of prefixes "aws:" and "s3:"
|
|
||||||
func (key Key) Name() string {
|
|
||||||
keyString := string(key)
|
|
||||||
|
|
||||||
if strings.HasPrefix(keyString, "aws:") {
|
|
||||||
return strings.TrimPrefix(keyString, "aws:")
|
|
||||||
} else if strings.HasPrefix(keyString, "jwt:") {
|
|
||||||
return strings.TrimPrefix(keyString, "jwt:")
|
|
||||||
} else if strings.HasPrefix(keyString, "ldap:") {
|
|
||||||
return strings.TrimPrefix(keyString, "ldap:")
|
|
||||||
}
|
|
||||||
return strings.TrimPrefix(keyString, "s3:")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Key.
|
|
||||||
func (key *Key) UnmarshalJSON(data []byte) error {
|
|
||||||
var s string
|
|
||||||
if err := json.Unmarshal(data, &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedKey, err := parseKey(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*key = parsedKey
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseKey(s string) (Key, error) {
|
|
||||||
key := Key(s)
|
|
||||||
|
|
||||||
if key.IsValid() {
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, fmt.Errorf("invalid condition key '%v'", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeySet - set representation of slice of keys.
|
|
||||||
type KeySet map[Key]struct{}
|
|
||||||
|
|
||||||
// Add - add a key to key set.
|
|
||||||
func (set KeySet) Add(key Key) {
|
|
||||||
set[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges two key sets, duplicates are overwritten
|
|
||||||
func (set KeySet) Merge(mset KeySet) {
|
|
||||||
for k, v := range mset {
|
|
||||||
set[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Difference - returns a key set contains difference of two keys.
|
|
||||||
// Example:
|
|
||||||
// keySet1 := ["one", "two", "three"]
|
|
||||||
// keySet2 := ["two", "four", "three"]
|
|
||||||
// keySet1.Difference(keySet2) == ["one"]
|
|
||||||
func (set KeySet) Difference(sset KeySet) KeySet {
|
|
||||||
nset := make(KeySet)
|
|
||||||
|
|
||||||
for k := range set {
|
|
||||||
if _, ok := sset[k]; !ok {
|
|
||||||
nset.Add(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nset
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty - returns whether key set is empty or not.
|
|
||||||
func (set KeySet) IsEmpty() bool {
|
|
||||||
return len(set) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (set KeySet) String() string {
|
|
||||||
return fmt.Sprintf("%v", set.ToSlice())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSlice - returns slice of keys.
|
|
||||||
func (set KeySet) ToSlice() []Key {
|
|
||||||
keys := []Key{}
|
|
||||||
|
|
||||||
for key := range set {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeySet - returns new KeySet contains given keys.
|
|
||||||
func NewKeySet(keys ...Key) KeySet {
|
|
||||||
set := make(KeySet)
|
|
||||||
for _, key := range keys {
|
|
||||||
set.Add(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return set
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllSupportedAdminKeys - is list of all admin supported keys.
|
|
||||||
var AllSupportedAdminKeys = []Key{
|
|
||||||
AWSReferer,
|
|
||||||
AWSSourceIP,
|
|
||||||
AWSUserAgent,
|
|
||||||
AWSSecureTransport,
|
|
||||||
AWSCurrentTime,
|
|
||||||
AWSEpochTime,
|
|
||||||
// Add new supported condition keys.
|
|
||||||
}
|
|
@ -1,215 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestKeyIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, true},
|
|
||||||
{S3XAmzServerSideEncryption, true},
|
|
||||||
{S3XAmzServerSideEncryptionCustomerAlgorithm, true},
|
|
||||||
{S3XAmzMetadataDirective, true},
|
|
||||||
{S3XAmzStorageClass, true},
|
|
||||||
{S3LocationConstraint, true},
|
|
||||||
{S3Prefix, true},
|
|
||||||
{S3Delimiter, true},
|
|
||||||
{S3MaxKeys, true},
|
|
||||||
{AWSReferer, true},
|
|
||||||
{AWSSourceIP, true},
|
|
||||||
{Key("foo"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.key.IsValid()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, []byte(`"s3:x-amz-copy-source"`), false},
|
|
||||||
{Key("foo"), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.key)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: key: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyName(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
expectedResult string
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, "x-amz-copy-source"},
|
|
||||||
{AWSReferer, "Referer"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.key.Name()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedKey Key
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"s3:x-amz-copy-source"`), S3XAmzCopySource, false},
|
|
||||||
{[]byte(`"foo"`), Key(""), true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var key Key
|
|
||||||
err := json.Unmarshal(testCase.data, &key)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if testCase.expectedKey != key {
|
|
||||||
t.Fatalf("case %v: key: expected: %v, got: %v\n", i+1, testCase.expectedKey, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeySetAdd(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set KeySet
|
|
||||||
key Key
|
|
||||||
expectedResult KeySet
|
|
||||||
}{
|
|
||||||
{NewKeySet(), S3XAmzCopySource, NewKeySet(S3XAmzCopySource)},
|
|
||||||
{NewKeySet(S3XAmzCopySource), S3XAmzCopySource, NewKeySet(S3XAmzCopySource)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
testCase.set.Add(testCase.key)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(testCase.expectedResult, testCase.set) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeySetDifference(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set KeySet
|
|
||||||
setToDiff KeySet
|
|
||||||
expectedResult KeySet
|
|
||||||
}{
|
|
||||||
{NewKeySet(), NewKeySet(S3XAmzCopySource), NewKeySet()},
|
|
||||||
{NewKeySet(S3Prefix, S3Delimiter, S3MaxKeys), NewKeySet(S3Delimiter, S3MaxKeys), NewKeySet(S3Prefix)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.Difference(testCase.setToDiff)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(testCase.expectedResult, result) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeySetIsEmpty(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set KeySet
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewKeySet(), true},
|
|
||||||
{NewKeySet(S3Delimiter), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.IsEmpty()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeySetString(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set KeySet
|
|
||||||
expectedResult string
|
|
||||||
}{
|
|
||||||
{NewKeySet(), `[]`},
|
|
||||||
{NewKeySet(S3Delimiter), `[s3:delimiter]`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.String()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeySetToSlice(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set KeySet
|
|
||||||
expectedResult []Key
|
|
||||||
}{
|
|
||||||
{NewKeySet(), []Key{}},
|
|
||||||
{NewKeySet(S3Delimiter), []Key{S3Delimiter}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.ToSlice()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(testCase.expectedResult, result) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
const (
|
|
||||||
// LDAPUser - LDAP user DN, in MinIO this value is equal to user DN of the authenticated user.
|
|
||||||
LDAPUser Key = "ldap:user"
|
|
||||||
|
|
||||||
// LDAPUsername - LDAP username, in MinIO is the authenticated simply user.
|
|
||||||
LDAPUsername Key = "ldap:username"
|
|
||||||
)
|
|
@ -1,124 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type name string
|
|
||||||
|
|
||||||
const (
|
|
||||||
stringEquals name = "StringEquals"
|
|
||||||
stringNotEquals = "StringNotEquals"
|
|
||||||
stringEqualsIgnoreCase = "StringEqualsIgnoreCase"
|
|
||||||
stringNotEqualsIgnoreCase = "StringNotEqualsIgnoreCase"
|
|
||||||
stringLike = "StringLike"
|
|
||||||
stringNotLike = "StringNotLike"
|
|
||||||
binaryEquals = "BinaryEquals"
|
|
||||||
ipAddress = "IpAddress"
|
|
||||||
notIPAddress = "NotIpAddress"
|
|
||||||
null = "Null"
|
|
||||||
boolean = "Bool"
|
|
||||||
numericEquals = "NumericEquals"
|
|
||||||
numericNotEquals = "NumericNotEquals"
|
|
||||||
numericLessThan = "NumericLessThan"
|
|
||||||
numericLessThanEquals = "NumericLessThanEquals"
|
|
||||||
numericGreaterThan = "NumericGreaterThan"
|
|
||||||
numericGreaterThanEquals = "NumericGreaterThanEquals"
|
|
||||||
dateEquals = "DateEquals"
|
|
||||||
dateNotEquals = "DateNotEquals"
|
|
||||||
dateLessThan = "DateLessThan"
|
|
||||||
dateLessThanEquals = "DateLessThanEquals"
|
|
||||||
dateGreaterThan = "DateGreaterThan"
|
|
||||||
dateGreaterThanEquals = "DateGreaterThanEquals"
|
|
||||||
)
|
|
||||||
|
|
||||||
var supportedConditions = []name{
|
|
||||||
stringEquals,
|
|
||||||
stringNotEquals,
|
|
||||||
stringEqualsIgnoreCase,
|
|
||||||
stringNotEqualsIgnoreCase,
|
|
||||||
binaryEquals,
|
|
||||||
stringLike,
|
|
||||||
stringNotLike,
|
|
||||||
ipAddress,
|
|
||||||
notIPAddress,
|
|
||||||
null,
|
|
||||||
boolean,
|
|
||||||
numericEquals,
|
|
||||||
numericNotEquals,
|
|
||||||
numericLessThan,
|
|
||||||
numericLessThanEquals,
|
|
||||||
numericGreaterThan,
|
|
||||||
numericGreaterThanEquals,
|
|
||||||
dateEquals,
|
|
||||||
dateNotEquals,
|
|
||||||
dateLessThan,
|
|
||||||
dateLessThanEquals,
|
|
||||||
dateGreaterThan,
|
|
||||||
dateGreaterThanEquals,
|
|
||||||
// Add new conditions here.
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks if name is valid or not.
|
|
||||||
func (n name) IsValid() bool {
|
|
||||||
for _, supn := range supportedConditions {
|
|
||||||
if n == supn {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes name to JSON data.
|
|
||||||
func (n name) MarshalJSON() ([]byte, error) {
|
|
||||||
if !n.IsValid() {
|
|
||||||
return nil, fmt.Errorf("invalid name %v", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(string(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to condition name.
|
|
||||||
func (n *name) UnmarshalJSON(data []byte) error {
|
|
||||||
var s string
|
|
||||||
if err := json.Unmarshal(data, &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedName, err := parseName(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*n = parsedName
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseName(s string) (name, error) {
|
|
||||||
n := name(s)
|
|
||||||
|
|
||||||
if n.IsValid() {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, fmt.Errorf("invalid condition name '%v'", s)
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNameIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
n name
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{stringEquals, true},
|
|
||||||
{stringNotEquals, true},
|
|
||||||
{stringLike, true},
|
|
||||||
{stringNotLike, true},
|
|
||||||
{ipAddress, true},
|
|
||||||
{notIPAddress, true},
|
|
||||||
{null, true},
|
|
||||||
{name("foo"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.n.IsValid()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNameMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
n name
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{stringEquals, []byte(`"StringEquals"`), false},
|
|
||||||
{stringNotEquals, []byte(`"StringNotEquals"`), false},
|
|
||||||
{stringLike, []byte(`"StringLike"`), false},
|
|
||||||
{stringNotLike, []byte(`"StringNotLike"`), false},
|
|
||||||
{ipAddress, []byte(`"IpAddress"`), false},
|
|
||||||
{notIPAddress, []byte(`"NotIpAddress"`), false},
|
|
||||||
{null, []byte(`"Null"`), false},
|
|
||||||
{name("foo"), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.n)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNameUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult name
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"StringEquals"`), stringEquals, false},
|
|
||||||
{[]byte(`"foo"`), name(""), true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result name
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if testCase.expectErr != expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nullFunc - Null condition function. It checks whether Key is not present in given
|
|
||||||
// values or not.
|
|
||||||
// For example,
|
|
||||||
// 1. if Key = S3XAmzCopySource and Value = true, at evaluate() it returns whether
|
|
||||||
// S3XAmzCopySource is NOT in given value map or not.
|
|
||||||
// 2. if Key = S3XAmzCopySource and Value = false, at evaluate() it returns whether
|
|
||||||
// S3XAmzCopySource is in given value map or not.
|
|
||||||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_Null
|
|
||||||
type nullFunc struct {
|
|
||||||
k Key
|
|
||||||
value bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether Key is present in given values or not.
|
|
||||||
// Depending on condition boolean value, this function returns true or false.
|
|
||||||
func (f nullFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.value {
|
|
||||||
return len(requestValue) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(requestValue) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f nullFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "Null" condition name.
|
|
||||||
func (f nullFunc) name() name {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f nullFunc) String() string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", null, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f nullFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: NewValueSet(NewBoolValue(f.value)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNullFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
if len(values) != 1 {
|
|
||||||
return nil, fmt.Errorf("only one value is allowed for Null condition")
|
|
||||||
}
|
|
||||||
|
|
||||||
var value bool
|
|
||||||
for v := range values {
|
|
||||||
switch v.GetType() {
|
|
||||||
case reflect.Bool:
|
|
||||||
value, _ = v.GetBool()
|
|
||||||
case reflect.String:
|
|
||||||
var err error
|
|
||||||
s, _ := v.GetString()
|
|
||||||
if value, err = strconv.ParseBool(s); err != nil {
|
|
||||||
return nil, fmt.Errorf("value must be a boolean string for Null condition")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("value must be a boolean for Null condition")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &nullFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNullFunc - returns new Null function.
|
|
||||||
func NewNullFunc(key Key, value bool) (Function, error) {
|
|
||||||
return &nullFunc{key, value}, nil
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNullFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(false)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"prefix": {"true"}}, false},
|
|
||||||
{case1Function, map[string][]string{"prefix": {"false"}}, false},
|
|
||||||
{case1Function, map[string][]string{"prefix": {"mybucket/foo"}}, false},
|
|
||||||
{case1Function, map[string][]string{}, true},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
{case2Function, map[string][]string{"prefix": {"true"}}, true},
|
|
||||||
{case2Function, map[string][]string{"prefix": {"false"}}, true},
|
|
||||||
{case2Function, map[string][]string{"prefix": {"mybucket/foo"}}, true},
|
|
||||||
{case2Function, map[string][]string{}, false},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Errorf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNullFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newNullFunc(S3XAmzCopySource, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNullFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3Prefix: NewValueSet(NewBoolValue(true)),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(false)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3Prefix: NewValueSet(NewBoolValue(false)),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{&nullFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewNullFunc(t *testing.T) {
|
|
||||||
case1Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(true)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newNullFunc(S3Prefix, NewValueSet(NewBoolValue(false)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3Prefix, NewValueSet(NewBoolValue(true)), case1Function, false},
|
|
||||||
{S3Prefix, NewValueSet(NewStringValue("false")), case2Function, false},
|
|
||||||
// Multiple values error.
|
|
||||||
{S3Prefix, NewValueSet(NewBoolValue(true), NewBoolValue(false)), nil, true},
|
|
||||||
// Invalid boolean string error.
|
|
||||||
{S3Prefix, NewValueSet(NewStringValue("foo")), nil, true},
|
|
||||||
// Invalid value error.
|
|
||||||
{S3Prefix, NewValueSet(NewIntValue(7)), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newNullFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toNumericEqualsFuncString(n name, key Key, value int) string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// numericEqualsFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type numericEqualsFunc struct {
|
|
||||||
k Key
|
|
||||||
value int
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f numericEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rvInt, err := strconv.Atoi(requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.value == rvInt
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f numericEqualsFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NumericEquals" condition name.
|
|
||||||
func (f numericEqualsFunc) name() name {
|
|
||||||
return numericEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f numericEqualsFunc) String() string {
|
|
||||||
return toNumericEqualsFuncString(numericEquals, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f numericEqualsFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
values.Add(NewIntValue(f.value))
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// numericNotEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type numericNotEqualsFunc struct {
|
|
||||||
numericEqualsFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f numericNotEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
return !f.numericEqualsFunc.evaluate(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NumericNotEquals" condition name.
|
|
||||||
func (f numericNotEqualsFunc) name() name {
|
|
||||||
return numericNotEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f numericNotEqualsFunc) String() string {
|
|
||||||
return toNumericEqualsFuncString(numericNotEquals, f.numericEqualsFunc.k, f.numericEqualsFunc.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func valueToInt(n name, values ValueSet) (v int, err error) {
|
|
||||||
if len(values) != 1 {
|
|
||||||
return -1, fmt.Errorf("only one value is allowed for %s condition", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
for vs := range values {
|
|
||||||
switch vs.GetType() {
|
|
||||||
case reflect.Int:
|
|
||||||
if v, err = vs.GetInt(); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
s, err := vs.GetString()
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
if v, err = strconv.Atoi(s); err != nil {
|
|
||||||
return -1, fmt.Errorf("value %s must be a int for %s condition: %w", vs, n, err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return -1, fmt.Errorf("value %s must be a int for %s condition", vs, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNumericEqualsFunc - returns new NumericEquals function.
|
|
||||||
func newNumericEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToInt(numericEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNumericEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumericEqualsFunc - returns new NumericEquals function.
|
|
||||||
func NewNumericEqualsFunc(key Key, value int) (Function, error) {
|
|
||||||
return &numericEqualsFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNumericNotEqualsFunc - returns new NumericNotEquals function.
|
|
||||||
func newNumericNotEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToInt(numericNotEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNumericNotEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumericNotEqualsFunc - returns new NumericNotEquals function.
|
|
||||||
func NewNumericNotEqualsFunc(key Key, value int) (Function, error) {
|
|
||||||
return &numericNotEqualsFunc{numericEqualsFunc{key, value}}, nil
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toNumericGreaterThanFuncString(n name, key Key, value int) string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// numericGreaterThanFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type numericGreaterThanFunc struct {
|
|
||||||
k Key
|
|
||||||
value int
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f numericGreaterThanFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rvInt, err := strconv.Atoi(requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rvInt > f.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f numericGreaterThanFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NumericGreaterThan" condition name.
|
|
||||||
func (f numericGreaterThanFunc) name() name {
|
|
||||||
return numericGreaterThan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f numericGreaterThanFunc) String() string {
|
|
||||||
return toNumericGreaterThanFuncString(numericGreaterThan, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f numericGreaterThanFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
values.Add(NewIntValue(f.value))
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// numericGreaterThanEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type numericGreaterThanEqualsFunc struct {
|
|
||||||
numericGreaterThanFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f numericGreaterThanEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rvInt, err := strconv.Atoi(requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rvInt >= f.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NumericGreaterThanEquals" condition name.
|
|
||||||
func (f numericGreaterThanEqualsFunc) name() name {
|
|
||||||
return numericGreaterThanEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f numericGreaterThanEqualsFunc) String() string {
|
|
||||||
return toNumericGreaterThanFuncString(numericGreaterThanEquals, f.numericGreaterThanFunc.k, f.numericGreaterThanFunc.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNumericGreaterThanFunc - returns new NumericGreaterThan function.
|
|
||||||
func newNumericGreaterThanFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToInt(numericGreaterThan, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNumericGreaterThanFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumericGreaterThanFunc - returns new NumericGreaterThan function.
|
|
||||||
func NewNumericGreaterThanFunc(key Key, value int) (Function, error) {
|
|
||||||
return &numericGreaterThanFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNumericGreaterThanEqualsFunc - returns new NumericGreaterThanEquals function.
|
|
||||||
func newNumericGreaterThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToInt(numericGreaterThanEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNumericGreaterThanEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumericGreaterThanEqualsFunc - returns new NumericGreaterThanEquals function.
|
|
||||||
func NewNumericGreaterThanEqualsFunc(key Key, value int) (Function, error) {
|
|
||||||
return &numericGreaterThanEqualsFunc{numericGreaterThanFunc{key, value}}, nil
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toNumericLessThanFuncString(n name, key Key, value int) string {
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// numericLessThanFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type numericLessThanFunc struct {
|
|
||||||
k Key
|
|
||||||
value int
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f numericLessThanFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rvInt, err := strconv.Atoi(requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rvInt < f.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f numericLessThanFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NumericLessThan" condition name.
|
|
||||||
func (f numericLessThanFunc) name() name {
|
|
||||||
return numericLessThan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f numericLessThanFunc) String() string {
|
|
||||||
return toNumericLessThanFuncString(numericLessThan, f.k, f.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f numericLessThanFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
values.Add(NewIntValue(f.value))
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// numericLessThanEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type numericLessThanEqualsFunc struct {
|
|
||||||
numericLessThanFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f numericLessThanEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestValue) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rvInt, err := strconv.Atoi(requestValue[0])
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return rvInt <= f.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "NumericLessThanEquals" condition name.
|
|
||||||
func (f numericLessThanEqualsFunc) name() name {
|
|
||||||
return numericLessThanEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f numericLessThanEqualsFunc) String() string {
|
|
||||||
return toNumericLessThanFuncString(numericLessThanEquals, f.numericLessThanFunc.k, f.numericLessThanFunc.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNumericLessThanFunc - returns new NumericLessThan function.
|
|
||||||
func newNumericLessThanFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToInt(numericLessThan, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNumericLessThanFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumericLessThanFunc - returns new NumericLessThan function.
|
|
||||||
func NewNumericLessThanFunc(key Key, value int) (Function, error) {
|
|
||||||
return &numericLessThanFunc{key, value}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNumericLessThanEqualsFunc - returns new NumericLessThanEquals function.
|
|
||||||
func newNumericLessThanEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
v, err := valueToInt(numericLessThanEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewNumericLessThanEqualsFunc(key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumericLessThanEqualsFunc - returns new NumericLessThanEquals function.
|
|
||||||
func NewNumericLessThanEqualsFunc(key Key, value int) (Function, error) {
|
|
||||||
return &numericLessThanEqualsFunc{numericLessThanFunc{key, value}}, nil
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toStringEqualsFuncString(n name, key Key, values set.StringSet) string {
|
|
||||||
valueStrings := values.ToSlice()
|
|
||||||
sort.Strings(valueStrings)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringEqualsFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type stringEqualsFunc struct {
|
|
||||||
k Key
|
|
||||||
values set.StringSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values.
|
|
||||||
func (f stringEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
|
||||||
return !fvalues.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f stringEqualsFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "StringEquals" condition name.
|
|
||||||
func (f stringEqualsFunc) name() name {
|
|
||||||
return stringEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f stringEqualsFunc) String() string {
|
|
||||||
return toStringEqualsFuncString(stringEquals, f.k, f.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f stringEqualsFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
for _, value := range f.values.ToSlice() {
|
|
||||||
values.Add(NewStringValue(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringNotEqualsFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type stringNotEqualsFunc struct {
|
|
||||||
stringEqualsFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f stringNotEqualsFunc) evaluate(values map[string][]string) bool {
|
|
||||||
return !f.stringEqualsFunc.evaluate(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "StringNotEquals" condition name.
|
|
||||||
func (f stringNotEqualsFunc) name() name {
|
|
||||||
return stringNotEquals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f stringNotEqualsFunc) String() string {
|
|
||||||
return toStringEqualsFuncString(stringNotEquals, f.stringEqualsFunc.k, f.stringEqualsFunc.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
func valuesToStringSlice(n name, values ValueSet) ([]string, error) {
|
|
||||||
valueStrings := []string{}
|
|
||||||
|
|
||||||
for value := range values {
|
|
||||||
s, err := value.GetString()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("value must be a string for %v condition", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
valueStrings = append(valueStrings, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueStrings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateStringEqualsValues(n name, key Key, values set.StringSet) error {
|
|
||||||
for _, s := range values.ToSlice() {
|
|
||||||
switch key {
|
|
||||||
case S3XAmzCopySource:
|
|
||||||
bucket, object := path2BucketAndObject(s)
|
|
||||||
if object == "" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzCopySource, n)
|
|
||||||
}
|
|
||||||
if err := s3utils.CheckValidBucketName(bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case S3XAmzServerSideEncryption, S3XAmzServerSideEncryptionCustomerAlgorithm:
|
|
||||||
if s != "AES256" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzServerSideEncryption, n)
|
|
||||||
}
|
|
||||||
case S3XAmzMetadataDirective:
|
|
||||||
if s != "COPY" && s != "REPLACE" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzMetadataDirective, n)
|
|
||||||
}
|
|
||||||
case S3XAmzContentSha256:
|
|
||||||
if s == "" {
|
|
||||||
return fmt.Errorf("invalid empty value for '%v' for %v condition", S3XAmzContentSha256, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStringEqualsFunc - returns new StringEquals function.
|
|
||||||
func newStringEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(stringEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewStringEqualsFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringEqualsFunc - returns new StringEquals function.
|
|
||||||
func NewStringEqualsFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateStringEqualsValues(stringEquals, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stringEqualsFunc{key, sset}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStringNotEqualsFunc - returns new StringNotEquals function.
|
|
||||||
func newStringNotEqualsFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(stringNotEquals, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewStringNotEqualsFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringNotEqualsFunc - returns new StringNotEquals function.
|
|
||||||
func NewStringNotEqualsFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateStringEqualsValues(stringNotEquals, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stringNotEqualsFunc{stringEqualsFunc{key, sset}}, nil
|
|
||||||
}
|
|
@ -1,709 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringEqualsFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{}, false},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
|
||||||
{case2Function, map[string][]string{}, false},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
|
||||||
{case3Function, map[string][]string{}, false},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
|
||||||
{case4Function, map[string][]string{}, false},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringEqualsFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringEqualsFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&stringEqualsFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotEqualsFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{}, true},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
|
||||||
{case2Function, map[string][]string{}, true},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
|
||||||
{case3Function, map[string][]string{}, true},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
|
||||||
{case4Function, map[string][]string{}, true},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotEqualsFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotEqualsFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&stringNotEqualsFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStringEqualsFunc(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newStringEqualsFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStringNotEqualsFunc(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotEqualsFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotEqualsFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotEqualsFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newStringNotEqualsFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,161 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toStringEqualsIgnoreCaseFuncString(n name, key Key, values set.StringSet) string {
|
|
||||||
valueStrings := values.ToSlice()
|
|
||||||
sort.Strings(valueStrings)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringEqualsIgnoreCaseFunc - String equals function. It checks whether value by Key in given
|
|
||||||
// values map is in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is in values.
|
|
||||||
type stringEqualsIgnoreCaseFunc struct {
|
|
||||||
k Key
|
|
||||||
values set.StringSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is in
|
|
||||||
// condition values, ignores case.
|
|
||||||
func (f stringEqualsIgnoreCaseFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
|
||||||
|
|
||||||
for _, v := range requestValue {
|
|
||||||
if !fvalues.FuncMatch(strings.EqualFold, v).IsEmpty() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f stringEqualsIgnoreCaseFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "StringEqualsIgnoreCase" condition name.
|
|
||||||
func (f stringEqualsIgnoreCaseFunc) name() name {
|
|
||||||
return stringEqualsIgnoreCase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f stringEqualsIgnoreCaseFunc) String() string {
|
|
||||||
return toStringEqualsIgnoreCaseFuncString(stringEqualsIgnoreCase, f.k, f.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f stringEqualsIgnoreCaseFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
for _, value := range f.values.ToSlice() {
|
|
||||||
values.Add(NewStringValue(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringNotEqualsIgnoreCaseFunc - String not equals function. It checks whether value by Key in
|
|
||||||
// given values is NOT in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT in values.
|
|
||||||
type stringNotEqualsIgnoreCaseFunc struct {
|
|
||||||
stringEqualsIgnoreCaseFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT in
|
|
||||||
// condition values.
|
|
||||||
func (f stringNotEqualsIgnoreCaseFunc) evaluate(values map[string][]string) bool {
|
|
||||||
return !f.stringEqualsIgnoreCaseFunc.evaluate(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "StringNotEqualsIgnoreCase" condition name.
|
|
||||||
func (f stringNotEqualsIgnoreCaseFunc) name() name {
|
|
||||||
return stringNotEqualsIgnoreCase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f stringNotEqualsIgnoreCaseFunc) String() string {
|
|
||||||
return toStringEqualsIgnoreCaseFuncString(stringNotEqualsIgnoreCase, f.stringEqualsIgnoreCaseFunc.k, f.stringEqualsIgnoreCaseFunc.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateStringEqualsIgnoreCaseValues(n name, key Key, values set.StringSet) error {
|
|
||||||
return validateStringEqualsValues(n, key, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStringEqualsIgnoreCaseFunc - returns new StringEqualsIgnoreCase function.
|
|
||||||
func newStringEqualsIgnoreCaseFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(stringEqualsIgnoreCase, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewStringEqualsIgnoreCaseFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringEqualsIgnoreCaseFunc - returns new StringEqualsIgnoreCase function.
|
|
||||||
func NewStringEqualsIgnoreCaseFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateStringEqualsIgnoreCaseValues(stringEqualsIgnoreCase, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stringEqualsIgnoreCaseFunc{key, sset}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStringNotEqualsIgnoreCaseFunc - returns new StringNotEqualsIgnoreCase function.
|
|
||||||
func newStringNotEqualsIgnoreCaseFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(stringNotEqualsIgnoreCase, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewStringNotEqualsIgnoreCaseFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringNotEqualsIgnoreCaseFunc - returns new StringNotEqualsIgnoreCase function.
|
|
||||||
func NewStringNotEqualsIgnoreCaseFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateStringEqualsIgnoreCaseValues(stringNotEqualsIgnoreCase, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stringNotEqualsIgnoreCaseFunc{stringEqualsIgnoreCaseFunc{key, sset}}, nil
|
|
||||||
}
|
|
@ -1,711 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringEqualsIgnoreCaseFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{}, false},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
|
||||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"aes256"}}, true},
|
|
||||||
{case2Function, map[string][]string{}, false},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"replace"}}, true},
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
|
||||||
{case3Function, map[string][]string{}, false},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
|
||||||
{case4Function, map[string][]string{}, false},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringEqualsIgnoreCaseFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringEqualsIgnoreCaseFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&stringEqualsFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotEqualsIgnoreCaseFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{}, true},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
|
||||||
{case2Function, map[string][]string{}, true},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
|
||||||
{case3Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
|
||||||
{case3Function, map[string][]string{}, true},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
|
||||||
{case4Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
|
||||||
{case4Function, map[string][]string{}, true},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotEqualsIgnoreCaseFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotEqualsIgnoreCaseFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/myobject")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES256")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPLACE")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-1")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&stringNotEqualsFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStringEqualsIgnoreCaseFunc(t *testing.T) {
|
|
||||||
case1Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringEqualsIgnoreCaseFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newStringEqualsIgnoreCaseFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStringNotEqualsIgnoreCaseFunc(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotEqualsIgnoreCaseFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotEqualsIgnoreCaseFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/myobject"),
|
|
||||||
NewStringValue("yourbucket/myobject"),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES256"),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPLACE"),
|
|
||||||
NewStringValue("COPY"),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-1"),
|
|
||||||
NewStringValue("us-west-1"),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("SSE-C")), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("DUPLICATE")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newStringNotEqualsIgnoreCaseFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,175 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
"github.com/minio/pkg/wildcard"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toStringLikeFuncString(n name, key Key, values set.StringSet) string {
|
|
||||||
valueStrings := values.ToSlice()
|
|
||||||
sort.Strings(valueStrings)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v:%v:%v", n, key, valueStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringLikeFunc - String like function. It checks whether value by Key in given
|
|
||||||
// values map is widcard matching in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo*"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is wildcard matching in values.
|
|
||||||
type stringLikeFunc struct {
|
|
||||||
k Key
|
|
||||||
values set.StringSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is wildcard
|
|
||||||
// matching in condition values.
|
|
||||||
func (f stringLikeFunc) evaluate(values map[string][]string) bool {
|
|
||||||
requestValue, ok := values[http.CanonicalHeaderKey(f.k.Name())]
|
|
||||||
if !ok {
|
|
||||||
requestValue = values[f.k.Name()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
|
||||||
|
|
||||||
for _, v := range requestValue {
|
|
||||||
if !fvalues.FuncMatch(wildcard.Match, v).IsEmpty() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
|
||||||
func (f stringLikeFunc) key() Key {
|
|
||||||
return f.k
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "StringLike" function name.
|
|
||||||
func (f stringLikeFunc) name() name {
|
|
||||||
return stringLike
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f stringLikeFunc) String() string {
|
|
||||||
return toStringLikeFuncString(stringLike, f.k, f.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toMap - returns map representation of this function.
|
|
||||||
func (f stringLikeFunc) toMap() map[Key]ValueSet {
|
|
||||||
if !f.k.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := NewValueSet()
|
|
||||||
for _, value := range f.values.ToSlice() {
|
|
||||||
values.Add(NewStringValue(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[Key]ValueSet{
|
|
||||||
f.k: values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringNotLikeFunc - String not like function. It checks whether value by Key in given
|
|
||||||
// values map is NOT widcard matching in condition values.
|
|
||||||
// For example,
|
|
||||||
// - if values = ["mybucket/foo*"], at evaluate() it returns whether string
|
|
||||||
// in value map for Key is NOT wildcard matching in values.
|
|
||||||
type stringNotLikeFunc struct {
|
|
||||||
stringLikeFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate() - evaluates to check whether value by Key in given values is NOT wildcard
|
|
||||||
// matching in condition values.
|
|
||||||
func (f stringNotLikeFunc) evaluate(values map[string][]string) bool {
|
|
||||||
return !f.stringLikeFunc.evaluate(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// name() - returns "StringNotLike" function name.
|
|
||||||
func (f stringNotLikeFunc) name() name {
|
|
||||||
return stringNotLike
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f stringNotLikeFunc) String() string {
|
|
||||||
return toStringLikeFuncString(stringNotLike, f.stringLikeFunc.k, f.stringLikeFunc.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateStringLikeValues(n name, key Key, values set.StringSet) error {
|
|
||||||
for _, s := range values.ToSlice() {
|
|
||||||
switch key {
|
|
||||||
case S3XAmzCopySource:
|
|
||||||
bucket, object := path2BucketAndObject(s)
|
|
||||||
if object == "" {
|
|
||||||
return fmt.Errorf("invalid value '%v' for '%v' for %v condition", s, S3XAmzCopySource, n)
|
|
||||||
}
|
|
||||||
if err := s3utils.CheckValidBucketName(bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStringLikeFunc - returns new StringLike function.
|
|
||||||
func newStringLikeFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(stringLike, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewStringLikeFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringLikeFunc - returns new StringLike function.
|
|
||||||
func NewStringLikeFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateStringLikeValues(stringLike, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stringLikeFunc{key, sset}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStringNotLikeFunc - returns new StringNotLike function.
|
|
||||||
func newStringNotLikeFunc(key Key, values ValueSet) (Function, error) {
|
|
||||||
valueStrings, err := valuesToStringSlice(stringNotLike, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewStringNotLikeFunc(key, valueStrings...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringNotLikeFunc - returns new StringNotLike function.
|
|
||||||
func NewStringNotLikeFunc(key Key, values ...string) (Function, error) {
|
|
||||||
sset := set.CreateStringSet(values...)
|
|
||||||
if err := validateStringLikeValues(stringNotLike, key, sset); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stringNotLikeFunc{stringLikeFunc{key, sset}}, nil
|
|
||||||
}
|
|
@ -1,799 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringLikeFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, true},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{}, false},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, true},
|
|
||||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, false},
|
|
||||||
{case2Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, false},
|
|
||||||
{case2Function, map[string][]string{}, false},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
|
||||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, true},
|
|
||||||
{case3Function, map[string][]string{}, false},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, true},
|
|
||||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, false},
|
|
||||||
{case4Function, map[string][]string{}, false},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
|
||||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, true},
|
|
||||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
|
||||||
{case5Function, map[string][]string{}, false},
|
|
||||||
{case5Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, true},
|
|
||||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, false},
|
|
||||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, false},
|
|
||||||
{case6Function, map[string][]string{}, false},
|
|
||||||
{case6Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
|
||||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, true},
|
|
||||||
{case7Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
|
||||||
{case7Function, map[string][]string{}, false},
|
|
||||||
{case7Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
|
|
||||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, true},
|
|
||||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, false},
|
|
||||||
{case8Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, false},
|
|
||||||
{case8Function, map[string][]string{}, false},
|
|
||||||
{case8Function, map[string][]string{"delimiter": {"/"}}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringLikeFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringLikeFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringLikeFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPL*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringLikeFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringLikeFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&stringLikeFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotLikeFuncEvaluate(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
values map[string][]string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, false},
|
|
||||||
{case1Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
|
||||||
{case1Function, map[string][]string{}, true},
|
|
||||||
{case1Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject"}}, false},
|
|
||||||
{case2Function, map[string][]string{"x-amz-copy-source": {"mybucket/myobject.png"}}, true},
|
|
||||||
{case2Function, map[string][]string{"x-amz-copy-source": {"yourbucket/myobject"}}, true},
|
|
||||||
{case2Function, map[string][]string{}, true},
|
|
||||||
{case2Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
|
||||||
{case3Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, false},
|
|
||||||
{case3Function, map[string][]string{}, true},
|
|
||||||
{case3Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES256"}}, false},
|
|
||||||
{case4Function, map[string][]string{"x-amz-server-side-encryption": {"AES512"}}, true},
|
|
||||||
{case4Function, map[string][]string{}, true},
|
|
||||||
{case4Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
|
||||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, false},
|
|
||||||
{case5Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
|
||||||
{case5Function, map[string][]string{}, true},
|
|
||||||
{case5Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE"}}, false},
|
|
||||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"REPLACE/COPY"}}, true},
|
|
||||||
{case6Function, map[string][]string{"x-amz-metadata-directive": {"COPY"}}, true},
|
|
||||||
{case6Function, map[string][]string{}, true},
|
|
||||||
{case6Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
|
||||||
{case7Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, false},
|
|
||||||
{case7Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
|
||||||
{case7Function, map[string][]string{}, true},
|
|
||||||
{case7Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
|
|
||||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-1"}}, false},
|
|
||||||
{case8Function, map[string][]string{"LocationConstraint": {"eu-west-2"}}, true},
|
|
||||||
{case8Function, map[string][]string{"LocationConstraint": {"us-east-1"}}, true},
|
|
||||||
{case8Function, map[string][]string{}, true},
|
|
||||||
{case8Function, map[string][]string{"delimiter": {"/"}}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.evaluate(testCase.values)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotLikeFuncKey(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
function Function
|
|
||||||
expectedResult Key
|
|
||||||
}{
|
|
||||||
{case1Function, S3XAmzCopySource},
|
|
||||||
{case2Function, S3XAmzServerSideEncryption},
|
|
||||||
{case3Function, S3XAmzMetadataDirective},
|
|
||||||
{case4Function, S3LocationConstraint},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.function.key()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNotLikeFuncToMap(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case1Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(NewStringValue("mybucket/*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotLikeFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Result := map[Key]ValueSet{
|
|
||||||
S3XAmzCopySource: NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(NewStringValue("AES*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Result := map[Key]ValueSet{
|
|
||||||
S3XAmzServerSideEncryption: NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(NewStringValue("REPL*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Result := map[Key]ValueSet{
|
|
||||||
S3XAmzMetadataDirective: NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(NewStringValue("eu-west-*")),
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotLikeFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Result := map[Key]ValueSet{
|
|
||||||
S3LocationConstraint: NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
f Function
|
|
||||||
expectedResult map[Key]ValueSet
|
|
||||||
}{
|
|
||||||
{case1Function, case1Result},
|
|
||||||
{case2Function, case2Result},
|
|
||||||
{case3Function, case3Result},
|
|
||||||
{case4Function, case4Result},
|
|
||||||
{case5Function, case5Result},
|
|
||||||
{case6Function, case6Result},
|
|
||||||
{case7Function, case7Result},
|
|
||||||
{case8Function, case8Result},
|
|
||||||
{&stringNotLikeFunc{}, nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.f.toMap()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStringLikeFunc(t *testing.T) {
|
|
||||||
case1Function, err := newStringLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringLikeFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringLikeFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringLikeFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringLikeFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newStringLikeFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStringNotLikeFunc(t *testing.T) {
|
|
||||||
case1Function, err := newStringNotLikeFunc(S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case2Function, err := newStringNotLikeFunc(S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case4Function, err := newStringNotLikeFunc(S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case5Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case6Function, err := newStringNotLikeFunc(S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case7Function, err := newStringNotLikeFunc(S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case8Function, err := newStringNotLikeFunc(S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key Key
|
|
||||||
values ValueSet
|
|
||||||
expectedResult Function
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/*")), case1Function, false},
|
|
||||||
{S3XAmzCopySource,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("mybucket/*"),
|
|
||||||
NewStringValue("yourbucket/myobject*"),
|
|
||||||
), case2Function, false},
|
|
||||||
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES*")), case3Function, false},
|
|
||||||
{S3XAmzServerSideEncryption,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("AES*"),
|
|
||||||
), case4Function, false},
|
|
||||||
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPL*")), case5Function, false},
|
|
||||||
{S3XAmzMetadataDirective,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("REPL*"),
|
|
||||||
NewStringValue("COPY*"),
|
|
||||||
), case6Function, false},
|
|
||||||
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-*")), case7Function, false},
|
|
||||||
{S3LocationConstraint,
|
|
||||||
NewValueSet(
|
|
||||||
NewStringValue("eu-west-*"),
|
|
||||||
NewStringValue("us-west-*"),
|
|
||||||
), case8Function, false},
|
|
||||||
|
|
||||||
// Unsupported value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket/myobject"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzServerSideEncryption, NewValueSet(NewStringValue("AES256"), NewIntValue(7)), nil, true},
|
|
||||||
{S3XAmzMetadataDirective, NewValueSet(NewStringValue("REPLACE"), NewIntValue(7)), nil, true},
|
|
||||||
{S3LocationConstraint, NewValueSet(NewStringValue("eu-west-1"), NewIntValue(7)), nil, true},
|
|
||||||
|
|
||||||
// Invalid value error.
|
|
||||||
{S3XAmzCopySource, NewValueSet(NewStringValue("mybucket")), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := newStringNotLikeFunc(testCase.key, testCase.values)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Splits an incoming path into bucket and object components.
|
|
||||||
func path2BucketAndObject(path string) (bucket, object string) {
|
|
||||||
// Skip the first element if it is '/', split the rest.
|
|
||||||
path = strings.TrimPrefix(path, "/")
|
|
||||||
pathComponents := strings.SplitN(path, "/", 2)
|
|
||||||
|
|
||||||
// Save the bucket and object extracted from path.
|
|
||||||
switch len(pathComponents) {
|
|
||||||
case 1:
|
|
||||||
bucket = pathComponents[0]
|
|
||||||
case 2:
|
|
||||||
bucket = pathComponents[0]
|
|
||||||
object = pathComponents[1]
|
|
||||||
}
|
|
||||||
return bucket, object
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value - is enum type of string, int or bool.
|
|
||||||
type Value struct {
|
|
||||||
t reflect.Kind
|
|
||||||
s string
|
|
||||||
i int
|
|
||||||
b bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBool - gets stored bool value.
|
|
||||||
func (v Value) GetBool() (bool, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if v.t != reflect.Bool {
|
|
||||||
err = fmt.Errorf("not a bool Value")
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.b, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt - gets stored int value.
|
|
||||||
func (v Value) GetInt() (int, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if v.t != reflect.Int {
|
|
||||||
err = fmt.Errorf("not a int Value")
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetString - gets stored string value.
|
|
||||||
func (v Value) GetString() (string, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if v.t != reflect.String {
|
|
||||||
err = fmt.Errorf("not a string Value")
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetType - gets enum type.
|
|
||||||
func (v Value) GetType() reflect.Kind {
|
|
||||||
return v.t
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Value to JSON data.
|
|
||||||
func (v Value) MarshalJSON() ([]byte, error) {
|
|
||||||
switch v.t {
|
|
||||||
case reflect.String:
|
|
||||||
return json.Marshal(v.s)
|
|
||||||
case reflect.Int:
|
|
||||||
return json.Marshal(v.i)
|
|
||||||
case reflect.Bool:
|
|
||||||
return json.Marshal(v.b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("unknown value kind %v", v.t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreBool - stores bool value.
|
|
||||||
func (v *Value) StoreBool(b bool) {
|
|
||||||
*v = Value{t: reflect.Bool, b: b}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreInt - stores int value.
|
|
||||||
func (v *Value) StoreInt(i int) {
|
|
||||||
*v = Value{t: reflect.Int, i: i}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreString - stores string value.
|
|
||||||
func (v *Value) StoreString(s string) {
|
|
||||||
*v = Value{t: reflect.String, s: s}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String - returns string representation of value.
|
|
||||||
func (v Value) String() string {
|
|
||||||
switch v.t {
|
|
||||||
case reflect.String:
|
|
||||||
return v.s
|
|
||||||
case reflect.Int:
|
|
||||||
return strconv.Itoa(v.i)
|
|
||||||
case reflect.Bool:
|
|
||||||
return strconv.FormatBool(v.b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data.
|
|
||||||
func (v *Value) UnmarshalJSON(data []byte) error {
|
|
||||||
var b bool
|
|
||||||
if err := json.Unmarshal(data, &b); err == nil {
|
|
||||||
v.StoreBool(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var i int
|
|
||||||
if err := json.Unmarshal(data, &i); err == nil {
|
|
||||||
v.StoreInt(i)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var s string
|
|
||||||
if err := json.Unmarshal(data, &s); err == nil {
|
|
||||||
v.StoreString(s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("unknown json data '%v'", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBoolValue - returns new bool value.
|
|
||||||
func NewBoolValue(b bool) Value {
|
|
||||||
value := &Value{}
|
|
||||||
value.StoreBool(b)
|
|
||||||
return *value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIntValue - returns new int value.
|
|
||||||
func NewIntValue(i int) Value {
|
|
||||||
value := &Value{}
|
|
||||||
value.StoreInt(i)
|
|
||||||
return *value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringValue - returns new string value.
|
|
||||||
func NewStringValue(s string) Value {
|
|
||||||
value := &Value{}
|
|
||||||
value.StoreString(s)
|
|
||||||
return *value
|
|
||||||
}
|
|
@ -1,261 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValueGetBool(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult bool
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewBoolValue(true), true, false},
|
|
||||||
{NewIntValue(7), false, true},
|
|
||||||
{Value{}, false, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := testCase.value.GetBool()
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueGetInt(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult int
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewIntValue(7), 7, false},
|
|
||||||
{NewBoolValue(true), 0, true},
|
|
||||||
{Value{}, 0, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := testCase.value.GetInt()
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueGetString(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult string
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewStringValue("foo"), "foo", false},
|
|
||||||
{NewBoolValue(true), "", true},
|
|
||||||
{Value{}, "", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := testCase.value.GetString()
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueGetType(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult reflect.Kind
|
|
||||||
}{
|
|
||||||
{NewBoolValue(true), reflect.Bool},
|
|
||||||
{NewIntValue(7), reflect.Int},
|
|
||||||
{NewStringValue("foo"), reflect.String},
|
|
||||||
{Value{}, reflect.Invalid},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.value.GetType()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewBoolValue(true), []byte("true"), false},
|
|
||||||
{NewIntValue(7), []byte("7"), false},
|
|
||||||
{NewStringValue("foo"), []byte(`"foo"`), false},
|
|
||||||
{Value{}, nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.value)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueStoreBool(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value bool
|
|
||||||
expectedResult Value
|
|
||||||
}{
|
|
||||||
{false, NewBoolValue(false)},
|
|
||||||
{true, NewBoolValue(true)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Value
|
|
||||||
result.StoreBool(testCase.value)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueStoreInt(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value int
|
|
||||||
expectedResult Value
|
|
||||||
}{
|
|
||||||
{0, NewIntValue(0)},
|
|
||||||
{7, NewIntValue(7)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Value
|
|
||||||
result.StoreInt(testCase.value)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueStoreString(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value string
|
|
||||||
expectedResult Value
|
|
||||||
}{
|
|
||||||
{"", NewStringValue("")},
|
|
||||||
{"foo", NewStringValue("foo")},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Value
|
|
||||||
result.StoreString(testCase.value)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueString(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult string
|
|
||||||
}{
|
|
||||||
{NewBoolValue(true), "true"},
|
|
||||||
{NewIntValue(7), "7"},
|
|
||||||
{NewStringValue("foo"), "foo"},
|
|
||||||
{Value{}, ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.value.String()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult Value
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte("true"), NewBoolValue(true), false},
|
|
||||||
{[]byte("7"), NewIntValue(7), false},
|
|
||||||
{[]byte(`"foo"`), NewStringValue("foo"), false},
|
|
||||||
{[]byte("True"), Value{}, true},
|
|
||||||
{[]byte("7.1"), Value{}, true},
|
|
||||||
{[]byte(`["foo"]`), Value{}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Value
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ValueSet - unique list of values.
|
|
||||||
type ValueSet map[Value]struct{}
|
|
||||||
|
|
||||||
// Add - adds given value to value set.
|
|
||||||
func (set ValueSet) Add(value Value) {
|
|
||||||
set[value] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSlice converts ValueSet to a slice of Value
|
|
||||||
func (set ValueSet) ToSlice() []Value {
|
|
||||||
var values []Value
|
|
||||||
for k := range set {
|
|
||||||
values = append(values, k)
|
|
||||||
}
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes ValueSet to JSON data.
|
|
||||||
func (set ValueSet) MarshalJSON() ([]byte, error) {
|
|
||||||
var values []Value
|
|
||||||
for k := range set {
|
|
||||||
values = append(values, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) == 0 {
|
|
||||||
return nil, fmt.Errorf("invalid value set %v", set)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data.
|
|
||||||
func (set *ValueSet) UnmarshalJSON(data []byte) error {
|
|
||||||
var v Value
|
|
||||||
if err := json.Unmarshal(data, &v); err == nil {
|
|
||||||
*set = make(ValueSet)
|
|
||||||
set.Add(v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var values []Value
|
|
||||||
if err := json.Unmarshal(data, &values); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) < 1 {
|
|
||||||
return fmt.Errorf("invalid value")
|
|
||||||
}
|
|
||||||
|
|
||||||
*set = make(ValueSet)
|
|
||||||
for _, v = range values {
|
|
||||||
if _, found := (*set)[v]; found {
|
|
||||||
return fmt.Errorf("duplicate value found '%v'", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
set.Add(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone clones ValueSet structure
|
|
||||||
func (set ValueSet) Clone() ValueSet {
|
|
||||||
return NewValueSet(set.ToSlice()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewValueSet - returns new value set containing given values.
|
|
||||||
func NewValueSet(values ...Value) ValueSet {
|
|
||||||
set := make(ValueSet)
|
|
||||||
|
|
||||||
for _, value := range values {
|
|
||||||
set.Add(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return set
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package condition
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValueSetAdd(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
value Value
|
|
||||||
expectedResult ValueSet
|
|
||||||
}{
|
|
||||||
{NewBoolValue(true), NewValueSet(NewBoolValue(true))},
|
|
||||||
{NewIntValue(7), NewValueSet(NewIntValue(7))},
|
|
||||||
{NewStringValue("foo"), NewValueSet(NewStringValue("foo"))},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := NewValueSet()
|
|
||||||
result.Add(testCase.value)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueSetMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ValueSet
|
|
||||||
expectedResult string
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewValueSet(NewBoolValue(true)), `[true]`, false},
|
|
||||||
{NewValueSet(NewIntValue(7)), `[7]`, false},
|
|
||||||
{NewValueSet(NewStringValue("foo")), `["foo"]`, false},
|
|
||||||
{NewValueSet(NewBoolValue(true)), `[true]`, false},
|
|
||||||
{NewValueSet(NewStringValue("7")), `["7"]`, false},
|
|
||||||
{NewValueSet(NewStringValue("foo")), `["foo"]`, false},
|
|
||||||
{make(ValueSet), "", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.set)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if string(result) != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValueSetUnmarshalJSON(t *testing.T) {
|
|
||||||
set1 := NewValueSet(
|
|
||||||
NewBoolValue(true),
|
|
||||||
NewStringValue("false"),
|
|
||||||
NewIntValue(7),
|
|
||||||
NewStringValue("7"),
|
|
||||||
NewStringValue("foo"),
|
|
||||||
NewStringValue("192.168.1.100/24"),
|
|
||||||
)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult ValueSet
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`true`), NewValueSet(NewBoolValue(true)), false},
|
|
||||||
{[]byte(`7`), NewValueSet(NewIntValue(7)), false},
|
|
||||||
{[]byte(`"foo"`), NewValueSet(NewStringValue("foo")), false},
|
|
||||||
{[]byte(`[true]`), NewValueSet(NewBoolValue(true)), false},
|
|
||||||
{[]byte(`[7]`), NewValueSet(NewIntValue(7)), false},
|
|
||||||
{[]byte(`["foo"]`), NewValueSet(NewStringValue("foo")), false},
|
|
||||||
{[]byte(`[true, "false", 7, "7", "foo", "192.168.1.100/24"]`), set1, false},
|
|
||||||
{[]byte(`{}`), nil, true}, // Unsupported data.
|
|
||||||
{[]byte(`[]`), nil, true}, // Empty array.
|
|
||||||
{[]byte(`[7, 7, true]`), nil, true}, // Duplicate value.
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := make(ValueSet)
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
// Effect - policy statement effect Allow or Deny.
|
|
||||||
type Effect string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Allow - allow effect.
|
|
||||||
Allow Effect = "Allow"
|
|
||||||
|
|
||||||
// Deny - deny effect.
|
|
||||||
Deny = "Deny"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsAllowed - returns if given check is allowed or not.
|
|
||||||
func (effect Effect) IsAllowed(b bool) bool {
|
|
||||||
if effect == Allow {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
return !b
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks if Effect is valid or not
|
|
||||||
func (effect Effect) IsValid() bool {
|
|
||||||
switch effect {
|
|
||||||
case Allow, Deny:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEffectIsAllowed(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
effect Effect
|
|
||||||
check bool
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{Allow, false, false},
|
|
||||||
{Allow, true, true},
|
|
||||||
{Deny, false, true},
|
|
||||||
{Deny, true, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.effect.IsAllowed(testCase.check)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEffectIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
effect Effect
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{Allow, true},
|
|
||||||
{Deny, true},
|
|
||||||
{Effect(""), false},
|
|
||||||
{Effect("foo"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.effect.IsValid()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error is the generic type for any error happening during policy
|
|
||||||
// parsing.
|
|
||||||
type Error struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf - formats according to a format specifier and returns
|
|
||||||
// the string as a value that satisfies error of type policy.Error
|
|
||||||
func Errorf(format string, a ...interface{}) error {
|
|
||||||
return Error{err: fmt.Errorf(format, a...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap the internal error.
|
|
||||||
func (e Error) Unwrap() error { return e.err }
|
|
||||||
|
|
||||||
// Error 'error' compatible method.
|
|
||||||
func (e Error) Error() string {
|
|
||||||
if e.err == nil {
|
|
||||||
return "policy: cause <nil>"
|
|
||||||
}
|
|
||||||
return e.err.Error()
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ID - policy ID.
|
|
||||||
type ID string
|
|
||||||
|
|
||||||
// IsValid - checks if ID is valid or not.
|
|
||||||
func (id ID) IsValid() bool {
|
|
||||||
return utf8.ValidString(string(id))
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIDIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
id ID
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{ID("DenyEncryptionSt1"), true},
|
|
||||||
{ID(""), true},
|
|
||||||
{ID("aa\xe2"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.id.IsValid()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Errorf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultVersion - default policy version as per AWS S3 specification.
|
|
||||||
const DefaultVersion = "2012-10-17"
|
|
||||||
|
|
||||||
// Args - arguments to policy to check whether it is allowed
|
|
||||||
type Args struct {
|
|
||||||
AccountName string `json:"account"`
|
|
||||||
Groups []string `json:"groups"`
|
|
||||||
Action Action `json:"action"`
|
|
||||||
BucketName string `json:"bucket"`
|
|
||||||
ConditionValues map[string][]string `json:"conditions"`
|
|
||||||
IsOwner bool `json:"owner"`
|
|
||||||
ObjectName string `json:"object"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Policy - bucket policy.
|
|
||||||
type Policy struct {
|
|
||||||
ID ID `json:"ID,omitempty"`
|
|
||||||
Version string
|
|
||||||
Statements []Statement `json:"Statement"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAllowed - checks given policy args is allowed to continue the Rest API.
|
|
||||||
func (policy Policy) IsAllowed(args Args) bool {
|
|
||||||
// Check all deny statements. If any one statement denies, return false.
|
|
||||||
for _, statement := range policy.Statements {
|
|
||||||
if statement.Effect == Deny {
|
|
||||||
if !statement.IsAllowed(args) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For owner, its allowed by default.
|
|
||||||
if args.IsOwner {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check all allow statements. If any one statement allows, return true.
|
|
||||||
for _, statement := range policy.Statements {
|
|
||||||
if statement.Effect == Allow {
|
|
||||||
if statement.IsAllowed(args) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty - returns whether policy is empty or not.
|
|
||||||
func (policy Policy) IsEmpty() bool {
|
|
||||||
return len(policy.Statements) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValid - checks if Policy is valid or not.
|
|
||||||
func (policy Policy) isValid() error {
|
|
||||||
if policy.Version != DefaultVersion && policy.Version != "" {
|
|
||||||
return Errorf("invalid version '%v'", policy.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, statement := range policy.Statements {
|
|
||||||
if err := statement.isValid(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Policy to JSON data.
|
|
||||||
func (policy Policy) MarshalJSON() ([]byte, error) {
|
|
||||||
if err := policy.isValid(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtype to avoid recursive call to MarshalJSON()
|
|
||||||
type subPolicy Policy
|
|
||||||
return json.Marshal(subPolicy(policy))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges two policies documents and drop
|
|
||||||
// duplicate statements if any.
|
|
||||||
func (policy Policy) Merge(input Policy) Policy {
|
|
||||||
var mergedPolicy Policy
|
|
||||||
if policy.Version != "" {
|
|
||||||
mergedPolicy.Version = policy.Version
|
|
||||||
} else {
|
|
||||||
mergedPolicy.Version = input.Version
|
|
||||||
}
|
|
||||||
for _, st := range policy.Statements {
|
|
||||||
mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone())
|
|
||||||
}
|
|
||||||
for _, st := range input.Statements {
|
|
||||||
mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone())
|
|
||||||
}
|
|
||||||
mergedPolicy.dropDuplicateStatements()
|
|
||||||
return mergedPolicy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (policy *Policy) dropDuplicateStatements() {
|
|
||||||
redo:
|
|
||||||
for i := range policy.Statements {
|
|
||||||
for j, statement := range policy.Statements[i+1:] {
|
|
||||||
if !policy.Statements[i].Equals(statement) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
policy.Statements = append(policy.Statements[:j], policy.Statements[j+1:]...)
|
|
||||||
goto redo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Policy.
|
|
||||||
func (policy *Policy) UnmarshalJSON(data []byte) error {
|
|
||||||
// subtype to avoid recursive call to UnmarshalJSON()
|
|
||||||
type subPolicy Policy
|
|
||||||
var sp subPolicy
|
|
||||||
if err := json.Unmarshal(data, &sp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := Policy(sp)
|
|
||||||
if err := p.isValid(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.dropDuplicateStatements()
|
|
||||||
|
|
||||||
*policy = p
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate - validates all statements are for given bucket or not.
|
|
||||||
func (policy Policy) Validate(bucketName string) error {
|
|
||||||
if err := policy.isValid(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, statement := range policy.Statements {
|
|
||||||
if err := statement.Validate(bucketName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseConfig - parses data in given reader to Policy.
|
|
||||||
func ParseConfig(reader io.Reader, bucketName string) (*Policy, error) {
|
|
||||||
var policy Policy
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(reader)
|
|
||||||
decoder.DisallowUnknownFields()
|
|
||||||
if err := decoder.Decode(&policy); err != nil {
|
|
||||||
return nil, Errorf("%w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := policy.Validate(bucketName)
|
|
||||||
return &policy, err
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,103 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
"github.com/minio/pkg/wildcard"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Principal - policy principal.
|
|
||||||
type Principal struct {
|
|
||||||
AWS set.StringSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks whether Principal is valid or not.
|
|
||||||
func (p Principal) IsValid() bool {
|
|
||||||
return len(p.AWS) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals - returns true if principals are equal.
|
|
||||||
func (p Principal) Equals(pp Principal) bool {
|
|
||||||
return p.AWS.Equals(pp.AWS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersection - returns principals available in both Principal.
|
|
||||||
func (p Principal) Intersection(principal Principal) set.StringSet {
|
|
||||||
return p.AWS.Intersection(principal.AWS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Principal to JSON data.
|
|
||||||
func (p Principal) MarshalJSON() ([]byte, error) {
|
|
||||||
if !p.IsValid() {
|
|
||||||
return nil, Errorf("invalid principal %v", p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtype to avoid recursive call to MarshalJSON()
|
|
||||||
type subPrincipal Principal
|
|
||||||
sp := subPrincipal(p)
|
|
||||||
return json.Marshal(sp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match - matches given principal is wildcard matching with Principal.
|
|
||||||
func (p Principal) Match(principal string) bool {
|
|
||||||
for _, pattern := range p.AWS.ToSlice() {
|
|
||||||
if wildcard.MatchSimple(pattern, principal) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Principal.
|
|
||||||
func (p *Principal) UnmarshalJSON(data []byte) error {
|
|
||||||
// subtype to avoid recursive call to UnmarshalJSON()
|
|
||||||
type subPrincipal Principal
|
|
||||||
var sp subPrincipal
|
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &sp); err != nil {
|
|
||||||
var s string
|
|
||||||
if err = json.Unmarshal(data, &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s != "*" {
|
|
||||||
return Errorf("invalid principal '%v'", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
sp.AWS = set.CreateStringSet("*")
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = Principal(sp)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone clones Principal structure
|
|
||||||
func (p Principal) Clone() Principal {
|
|
||||||
return NewPrincipal(p.AWS.ToSlice()...)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPrincipal - creates new Principal.
|
|
||||||
func NewPrincipal(principals ...string) Principal {
|
|
||||||
return Principal{AWS: set.CreateStringSet(principals...)}
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPrincipalIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
principal Principal
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewPrincipal("*"), true},
|
|
||||||
{NewPrincipal("arn:aws:iam::AccountNumber:root"), true},
|
|
||||||
{NewPrincipal(), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.principal.IsValid()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrincipalIntersection(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
principal Principal
|
|
||||||
principalToIntersect Principal
|
|
||||||
expectedResult set.StringSet
|
|
||||||
}{
|
|
||||||
{NewPrincipal("*"), NewPrincipal("*"), set.CreateStringSet("*")},
|
|
||||||
{NewPrincipal("arn:aws:iam::AccountNumber:root"), NewPrincipal("arn:aws:iam::AccountNumber:myuser"), set.CreateStringSet()},
|
|
||||||
{NewPrincipal(), NewPrincipal("*"), set.CreateStringSet()},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.principal.Intersection(testCase.principalToIntersect)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrincipalMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
principal Principal
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewPrincipal("*"), []byte(`{"AWS":["*"]}`), false},
|
|
||||||
{NewPrincipal("arn:aws:iam::AccountNumber:*"), []byte(`{"AWS":["arn:aws:iam::AccountNumber:*"]}`), false},
|
|
||||||
{NewPrincipal(), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.principal)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrincipalMatch(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
principals Principal
|
|
||||||
principal string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewPrincipal("*"), "AccountNumber", true},
|
|
||||||
{NewPrincipal("arn:aws:iam::*"), "arn:aws:iam::AccountNumber:root", true},
|
|
||||||
{NewPrincipal("arn:aws:iam::AccountNumber:*"), "arn:aws:iam::TestAccountNumber:root", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.principals.Match(testCase.principal)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrincipalUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult Principal
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"*"`), NewPrincipal("*"), false},
|
|
||||||
{[]byte(`{"AWS": "*"}`), NewPrincipal("*"), false},
|
|
||||||
{[]byte(`{"AWS": "arn:aws:iam::AccountNumber:*"}`), NewPrincipal("arn:aws:iam::AccountNumber:*"), false},
|
|
||||||
{[]byte(`"arn:aws:iam::AccountNumber:*"`), NewPrincipal(), true},
|
|
||||||
{[]byte(`["arn:aws:iam::AccountNumber:*", "arn:aws:iam:AnotherAccount:*"]`), NewPrincipal(), true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Principal
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
|
||||||
"github.com/minio/pkg/wildcard"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ResourceARNPrefix - resource ARN prefix as per AWS S3 specification.
|
|
||||||
const ResourceARNPrefix = "arn:aws:s3:::"
|
|
||||||
|
|
||||||
// Resource - resource in policy statement.
|
|
||||||
type Resource struct {
|
|
||||||
BucketName string
|
|
||||||
Pattern string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Resource) isBucketPattern() bool {
|
|
||||||
return !strings.Contains(r.Pattern, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Resource) isObjectPattern() bool {
|
|
||||||
return strings.Contains(r.Pattern, "/") || strings.Contains(r.BucketName, "*")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks whether Resource is valid or not.
|
|
||||||
func (r Resource) IsValid() bool {
|
|
||||||
return r.BucketName != "" && r.Pattern != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match - matches object name with resource pattern.
|
|
||||||
func (r Resource) Match(resource string, conditionValues map[string][]string) bool {
|
|
||||||
pattern := r.Pattern
|
|
||||||
for _, key := range condition.CommonKeys {
|
|
||||||
// Empty values are not supported for policy variables.
|
|
||||||
if rvalues, ok := conditionValues[key.Name()]; ok && rvalues[0] != "" {
|
|
||||||
pattern = strings.Replace(pattern, key.VarName(), rvalues[0], -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return wildcard.Match(pattern, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes Resource to JSON data.
|
|
||||||
func (r Resource) MarshalJSON() ([]byte, error) {
|
|
||||||
if !r.IsValid() {
|
|
||||||
return nil, Errorf("invalid resource %v", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(r.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Resource) String() string {
|
|
||||||
return ResourceARNPrefix + r.Pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Resource.
|
|
||||||
func (r *Resource) UnmarshalJSON(data []byte) error {
|
|
||||||
var s string
|
|
||||||
if err := json.Unmarshal(data, &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedResource, err := parseResource(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = parsedResource
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate - validates Resource is for given bucket or not.
|
|
||||||
func (r Resource) Validate(bucketName string) error {
|
|
||||||
if !r.IsValid() {
|
|
||||||
return Errorf("invalid resource")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !wildcard.Match(r.BucketName, bucketName) {
|
|
||||||
return Errorf("bucket name does not match")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseResource - parses string to Resource.
|
|
||||||
func parseResource(s string) (Resource, error) {
|
|
||||||
if !strings.HasPrefix(s, ResourceARNPrefix) {
|
|
||||||
return Resource{}, Errorf("invalid resource '%v'", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern := strings.TrimPrefix(s, ResourceARNPrefix)
|
|
||||||
tokens := strings.SplitN(pattern, "/", 2)
|
|
||||||
bucketName := tokens[0]
|
|
||||||
if bucketName == "" {
|
|
||||||
return Resource{}, Errorf("invalid resource format '%v'", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Resource{
|
|
||||||
BucketName: bucketName,
|
|
||||||
Pattern: pattern,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResource - creates new resource.
|
|
||||||
func NewResource(bucketName, keyName string) Resource {
|
|
||||||
pattern := bucketName
|
|
||||||
if keyName != "" {
|
|
||||||
if !strings.HasPrefix(keyName, "/") {
|
|
||||||
pattern += "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern += keyName
|
|
||||||
}
|
|
||||||
|
|
||||||
return Resource{
|
|
||||||
BucketName: bucketName,
|
|
||||||
Pattern: pattern,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,222 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResourceIsBucketPattern(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resource Resource
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResource("*", ""), true},
|
|
||||||
{NewResource("mybucket", ""), true},
|
|
||||||
{NewResource("mybucket*", ""), true},
|
|
||||||
{NewResource("mybucket?0", ""), true},
|
|
||||||
{NewResource("", "*"), false},
|
|
||||||
{NewResource("*", "*"), false},
|
|
||||||
{NewResource("mybucket", "*"), false},
|
|
||||||
{NewResource("mybucket*", "/myobject"), false},
|
|
||||||
{NewResource("mybucket?0", "/2010/photos/*"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resource.isBucketPattern()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceIsObjectPattern(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resource Resource
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResource("*", ""), true},
|
|
||||||
{NewResource("mybucket*", ""), true},
|
|
||||||
{NewResource("", "*"), true},
|
|
||||||
{NewResource("*", "*"), true},
|
|
||||||
{NewResource("mybucket", "*"), true},
|
|
||||||
{NewResource("mybucket*", "/myobject"), true},
|
|
||||||
{NewResource("mybucket?0", "/2010/photos/*"), true},
|
|
||||||
{NewResource("mybucket", ""), false},
|
|
||||||
{NewResource("mybucket?0", ""), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resource.isObjectPattern()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resource Resource
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResource("*", ""), true},
|
|
||||||
{NewResource("mybucket*", ""), true},
|
|
||||||
{NewResource("*", "*"), true},
|
|
||||||
{NewResource("mybucket", "*"), true},
|
|
||||||
{NewResource("mybucket*", "/myobject"), true},
|
|
||||||
{NewResource("mybucket?0", "/2010/photos/*"), true},
|
|
||||||
{NewResource("mybucket", ""), true},
|
|
||||||
{NewResource("mybucket?0", ""), true},
|
|
||||||
{NewResource("", ""), false},
|
|
||||||
{NewResource("", "*"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resource.IsValid()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceMatch(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resource Resource
|
|
||||||
objectName string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResource("*", ""), "mybucket", true},
|
|
||||||
{NewResource("*", ""), "mybucket/myobject", true},
|
|
||||||
{NewResource("mybucket*", ""), "mybucket", true},
|
|
||||||
{NewResource("mybucket*", ""), "mybucket/myobject", true},
|
|
||||||
{NewResource("", "*"), "/myobject", true},
|
|
||||||
{NewResource("*", "*"), "mybucket/myobject", true},
|
|
||||||
{NewResource("mybucket", "*"), "mybucket/myobject", true},
|
|
||||||
{NewResource("mybucket*", "/myobject"), "mybucket/myobject", true},
|
|
||||||
{NewResource("mybucket*", "/myobject"), "mybucket100/myobject", true},
|
|
||||||
{NewResource("mybucket?0", "/2010/photos/*"), "mybucket20/2010/photos/1.jpg", true},
|
|
||||||
{NewResource("mybucket", ""), "mybucket", true},
|
|
||||||
{NewResource("mybucket?0", ""), "mybucket30", true},
|
|
||||||
{NewResource("", "*"), "mybucket/myobject", false},
|
|
||||||
{NewResource("*", "*"), "mybucket", false},
|
|
||||||
{NewResource("mybucket", "*"), "mybucket10/myobject", false},
|
|
||||||
{NewResource("mybucket?0", "/2010/photos/*"), "mybucket0/2010/photos/1.jpg", false},
|
|
||||||
{NewResource("mybucket", ""), "mybucket/myobject", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resource.Match(testCase.objectName, nil)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resource Resource
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewResource("*", ""), []byte(`"arn:aws:s3:::*"`), false},
|
|
||||||
{NewResource("mybucket*", ""), []byte(`"arn:aws:s3:::mybucket*"`), false},
|
|
||||||
{NewResource("mybucket", ""), []byte(`"arn:aws:s3:::mybucket"`), false},
|
|
||||||
{NewResource("*", "*"), []byte(`"arn:aws:s3:::*/*"`), false},
|
|
||||||
{NewResource("mybucket", "*"), []byte(`"arn:aws:s3:::mybucket/*"`), false},
|
|
||||||
{NewResource("mybucket*", "myobject"), []byte(`"arn:aws:s3:::mybucket*/myobject"`), false},
|
|
||||||
{NewResource("mybucket?0", "/2010/photos/*"), []byte(`"arn:aws:s3:::mybucket?0/2010/photos/*"`), false},
|
|
||||||
{Resource{}, nil, true},
|
|
||||||
{NewResource("", "*"), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.resource)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult Resource
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"arn:aws:s3:::*"`), NewResource("*", ""), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket*"`), NewResource("mybucket*", ""), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket"`), NewResource("mybucket", ""), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::*/*"`), NewResource("*", "*"), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket/*"`), NewResource("mybucket", "*"), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket*/myobject"`), NewResource("mybucket*", "myobject"), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket?0/2010/photos/*"`), NewResource("mybucket?0", "/2010/photos/*"), false},
|
|
||||||
{[]byte(`"mybucket/myobject*"`), Resource{}, true},
|
|
||||||
{[]byte(`"arn:aws:s3:::/*"`), Resource{}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Resource
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceValidate(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resource Resource
|
|
||||||
bucketName string
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewResource("mybucket", "/myobject*"), "mybucket", false},
|
|
||||||
{NewResource("", "/myobject*"), "yourbucket", true},
|
|
||||||
{NewResource("mybucket", "/myobject*"), "yourbucket", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
err := testCase.resource.Validate(testCase.bucketName)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,181 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ResourceSet - set of resources in policy statement.
|
|
||||||
type ResourceSet map[Resource]struct{}
|
|
||||||
|
|
||||||
// bucketResourceExists - checks if at least one bucket resource exists in the set.
|
|
||||||
func (resourceSet ResourceSet) bucketResourceExists() bool {
|
|
||||||
for resource := range resourceSet {
|
|
||||||
if resource.isBucketPattern() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// objectResourceExists - checks if at least one object resource exists in the set.
|
|
||||||
func (resourceSet ResourceSet) objectResourceExists() bool {
|
|
||||||
for resource := range resourceSet {
|
|
||||||
if resource.isObjectPattern() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add - adds resource to resource set.
|
|
||||||
func (resourceSet ResourceSet) Add(resource Resource) {
|
|
||||||
resourceSet[resource] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals - checks whether given resource set is equal to current resource set or not.
|
|
||||||
func (resourceSet ResourceSet) Equals(sresourceSet ResourceSet) bool {
|
|
||||||
// If length of set is not equal to length of given set, the
|
|
||||||
// set is not equal to given set.
|
|
||||||
if len(resourceSet) != len(sresourceSet) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// As both sets are equal in length, check each elements are equal.
|
|
||||||
for k := range resourceSet {
|
|
||||||
if _, ok := sresourceSet[k]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersection - returns resouces available in both ResourcsSet.
|
|
||||||
func (resourceSet ResourceSet) Intersection(sset ResourceSet) ResourceSet {
|
|
||||||
nset := NewResourceSet()
|
|
||||||
for k := range resourceSet {
|
|
||||||
if _, ok := sset[k]; ok {
|
|
||||||
nset.Add(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nset
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes ResourceSet to JSON data.
|
|
||||||
func (resourceSet ResourceSet) MarshalJSON() ([]byte, error) {
|
|
||||||
if len(resourceSet) == 0 {
|
|
||||||
return nil, Errorf("empty resources not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
resources := []Resource{}
|
|
||||||
for resource := range resourceSet {
|
|
||||||
resources = append(resources, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(resources)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match - matches object name with anyone of resource pattern in resource set.
|
|
||||||
func (resourceSet ResourceSet) Match(resource string, conditionValues map[string][]string) bool {
|
|
||||||
for r := range resourceSet {
|
|
||||||
if r.Match(resource, conditionValues) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (resourceSet ResourceSet) String() string {
|
|
||||||
resources := []string{}
|
|
||||||
for resource := range resourceSet {
|
|
||||||
resources = append(resources, resource.String())
|
|
||||||
}
|
|
||||||
sort.Strings(resources)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v", resources)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to ResourceSet.
|
|
||||||
func (resourceSet *ResourceSet) UnmarshalJSON(data []byte) error {
|
|
||||||
var sset set.StringSet
|
|
||||||
if err := json.Unmarshal(data, &sset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*resourceSet = make(ResourceSet)
|
|
||||||
for _, s := range sset.ToSlice() {
|
|
||||||
resource, err := parseResource(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, found := (*resourceSet)[resource]; found {
|
|
||||||
return Errorf("duplicate resource '%v' found", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceSet.Add(resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate - validates ResourceSet is for given bucket or not.
|
|
||||||
func (resourceSet ResourceSet) Validate(bucketName string) error {
|
|
||||||
for resource := range resourceSet {
|
|
||||||
if err := resource.Validate(bucketName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSlice - returns slice of resources from the resource set.
|
|
||||||
func (resourceSet ResourceSet) ToSlice() []Resource {
|
|
||||||
resources := []Resource{}
|
|
||||||
for resource := range resourceSet {
|
|
||||||
resources = append(resources, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resources
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone clones ResourceSet structure
|
|
||||||
func (resourceSet ResourceSet) Clone() ResourceSet {
|
|
||||||
return NewResourceSet(resourceSet.ToSlice()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResourceSet - creates new resource set.
|
|
||||||
func NewResourceSet(resources ...Resource) ResourceSet {
|
|
||||||
resourceSet := make(ResourceSet)
|
|
||||||
for _, resource := range resources {
|
|
||||||
resourceSet.Add(resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resourceSet
|
|
||||||
}
|
|
@ -1,241 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResourceSetBucketResourceExists(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resourceSet ResourceSet
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResourceSet(NewResource("*", "")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/2010/photos/*"), NewResource("mybucket", "")), true},
|
|
||||||
{NewResourceSet(NewResource("", "*")), false},
|
|
||||||
{NewResourceSet(NewResource("*", "*")), false},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "*")), false},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), false},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*")), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resourceSet.bucketResourceExists()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetObjectResourceExists(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resourceSet ResourceSet
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResourceSet(NewResource("*", "")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "")), true},
|
|
||||||
{NewResourceSet(NewResource("", "*")), true},
|
|
||||||
{NewResourceSet(NewResource("*", "*")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "*")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", ""), NewResource("mybucket", "/2910/photos/*")), true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "")), false},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "")), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resourceSet.objectResourceExists()
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetAdd(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resourceSet ResourceSet
|
|
||||||
resource Resource
|
|
||||||
expectedResult ResourceSet
|
|
||||||
}{
|
|
||||||
{NewResourceSet(), NewResource("mybucket", "/myobject*"),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*"))},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
NewResource("mybucket", "/yourobject*"),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*"),
|
|
||||||
NewResource("mybucket", "/yourobject*"))},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
NewResource("mybucket", "/myobject*"),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*"))},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
testCase.resourceSet.Add(testCase.resource)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(testCase.resourceSet, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, testCase.resourceSet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetIntersection(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ResourceSet
|
|
||||||
setToIntersect ResourceSet
|
|
||||||
expectedResult ResourceSet
|
|
||||||
}{
|
|
||||||
{NewResourceSet(), NewResourceSet(NewResource("mybucket", "/myobject*")), NewResourceSet()},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")), NewResourceSet(), NewResourceSet()},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*"), NewResource("mybucket", "/yourobject*")),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*"))},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.Intersection(testCase.setToIntersect)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resoruceSet ResourceSet
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
[]byte(`["arn:aws:s3:::mybucket/myobject*"]`), false},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/photos/myobject*")),
|
|
||||||
[]byte(`["arn:aws:s3:::mybucket/photos/myobject*"]`), false},
|
|
||||||
{NewResourceSet(), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.resoruceSet)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetMatch(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resourceSet ResourceSet
|
|
||||||
resource string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewResourceSet(NewResource("*", "")), "mybucket", true},
|
|
||||||
{NewResourceSet(NewResource("*", "")), "mybucket/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "")), "mybucket", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "")), "mybucket/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("", "*")), "/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("*", "*")), "mybucket/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "*")), "mybucket/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), "mybucket/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket*", "/myobject")), "mybucket100/myobject", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*")), "mybucket20/2010/photos/1.jpg", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "")), "mybucket", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "")), "mybucket30", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket?0", "/2010/photos/*"),
|
|
||||||
NewResource("mybucket", "/2010/photos/*")), "mybucket/2010/photos/1.jpg", true},
|
|
||||||
{NewResourceSet(NewResource("", "*")), "mybucket/myobject", false},
|
|
||||||
{NewResourceSet(NewResource("*", "*")), "mybucket", false},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "*")), "mybucket10/myobject", false},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "")), "mybucket/myobject", false},
|
|
||||||
{NewResourceSet(), "mybucket/myobject", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.resourceSet.Match(testCase.resource, nil)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult ResourceSet
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket/myobject*"`),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket/photos/myobject*"`),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/photos/myobject*")), false},
|
|
||||||
{[]byte(`"arn:aws:s3:::mybucket"`), NewResourceSet(NewResource("mybucket", "")), false},
|
|
||||||
{[]byte(`"mybucket/myobject*"`), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result ResourceSet
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceSetValidate(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
resourceSet ResourceSet
|
|
||||||
bucketName string
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")), "mybucket", false},
|
|
||||||
{NewResourceSet(NewResource("", "/myobject*")), "yourbucket", true},
|
|
||||||
{NewResourceSet(NewResource("mybucket", "/myobject*")), "yourbucket", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
err := testCase.resourceSet.Validate(testCase.bucketName)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,182 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Statement - policy statement.
|
|
||||||
type Statement struct {
|
|
||||||
SID ID `json:"Sid,omitempty"`
|
|
||||||
Effect Effect `json:"Effect"`
|
|
||||||
Principal Principal `json:"Principal"`
|
|
||||||
Actions ActionSet `json:"Action"`
|
|
||||||
Resources ResourceSet `json:"Resource"`
|
|
||||||
Conditions condition.Functions `json:"Condition,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals checks if two statements are equal
|
|
||||||
func (statement Statement) Equals(st Statement) bool {
|
|
||||||
if statement.Effect != st.Effect {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !statement.Principal.Equals(st.Principal) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !statement.Actions.Equals(st.Actions) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !statement.Resources.Equals(st.Resources) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !statement.Conditions.Equals(st.Conditions) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAllowed - checks given policy args is allowed to continue the Rest API.
|
|
||||||
func (statement Statement) IsAllowed(args Args) bool {
|
|
||||||
check := func() bool {
|
|
||||||
if !statement.Principal.Match(args.AccountName) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !statement.Actions.Contains(args.Action) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
resource := args.BucketName
|
|
||||||
if args.ObjectName != "" {
|
|
||||||
if !strings.HasPrefix(args.ObjectName, "/") {
|
|
||||||
resource += "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource += args.ObjectName
|
|
||||||
}
|
|
||||||
|
|
||||||
if !statement.Resources.Match(resource, args.ConditionValues) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return statement.Conditions.Evaluate(args.ConditionValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
return statement.Effect.IsAllowed(check())
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValid - checks whether statement is valid or not.
|
|
||||||
func (statement Statement) isValid() error {
|
|
||||||
if !statement.Effect.IsValid() {
|
|
||||||
return Errorf("invalid Effect %v", statement.Effect)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !statement.Principal.IsValid() {
|
|
||||||
return Errorf("invalid Principal %v", statement.Principal)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(statement.Actions) == 0 {
|
|
||||||
return Errorf("Action must not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(statement.Resources) == 0 {
|
|
||||||
return Errorf("Resource must not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
for action := range statement.Actions {
|
|
||||||
if action.isObjectAction() {
|
|
||||||
if !statement.Resources.objectResourceExists() {
|
|
||||||
return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !statement.Resources.bucketResourceExists() {
|
|
||||||
return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := statement.Conditions.Keys()
|
|
||||||
keyDiff := keys.Difference(actionConditionKeyMap[action])
|
|
||||||
if !keyDiff.IsEmpty() {
|
|
||||||
return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes JSON data to Statement.
|
|
||||||
func (statement Statement) MarshalJSON() ([]byte, error) {
|
|
||||||
if err := statement.isValid(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtype to avoid recursive call to MarshalJSON()
|
|
||||||
type subStatement Statement
|
|
||||||
ss := subStatement(statement)
|
|
||||||
return json.Marshal(ss)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to Statement.
|
|
||||||
func (statement *Statement) UnmarshalJSON(data []byte) error {
|
|
||||||
// subtype to avoid recursive call to UnmarshalJSON()
|
|
||||||
type subStatement Statement
|
|
||||||
var ss subStatement
|
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &ss); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := Statement(ss)
|
|
||||||
if err := s.isValid(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*statement = s
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate - validates Statement is for given bucket or not.
|
|
||||||
func (statement Statement) Validate(bucketName string) error {
|
|
||||||
if err := statement.isValid(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return statement.Resources.Validate(bucketName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone clones Statement structure
|
|
||||||
func (statement Statement) Clone() Statement {
|
|
||||||
return NewStatement(statement.Effect, statement.Principal.Clone(),
|
|
||||||
statement.Actions.Clone(), statement.Resources.Clone(), statement.Conditions.Clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStatement - creates new statement.
|
|
||||||
func NewStatement(effect Effect, principal Principal, actionSet ActionSet, resourceSet ResourceSet, conditions condition.Functions) Statement {
|
|
||||||
return Statement{
|
|
||||||
Effect: effect,
|
|
||||||
Principal: principal,
|
|
||||||
Actions: actionSet,
|
|
||||||
Resources: resourceSet,
|
|
||||||
Conditions: conditions,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,572 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStatementIsAllowed(t *testing.T) {
|
|
||||||
case1Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("*", "")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
)
|
|
||||||
|
|
||||||
case2Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
)
|
|
||||||
|
|
||||||
_, IPNet1, err := net.ParseCIDR("192.168.1.0/24")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
func1, err := condition.NewIPAddressFunc(
|
|
||||||
condition.AWSSourceIP,
|
|
||||||
IPNet1,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case3Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(func1),
|
|
||||||
)
|
|
||||||
|
|
||||||
case4Statement := NewStatement(
|
|
||||||
Deny,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(func1),
|
|
||||||
)
|
|
||||||
|
|
||||||
anonGetBucketLocationArgs := Args{
|
|
||||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
Action: GetBucketLocationAction,
|
|
||||||
BucketName: "mybucket",
|
|
||||||
ConditionValues: map[string][]string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
anonPutObjectActionArgs := Args{
|
|
||||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
Action: PutObjectAction,
|
|
||||||
BucketName: "mybucket",
|
|
||||||
ConditionValues: map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/myobject"},
|
|
||||||
"SourceIp": {"192.168.1.10"},
|
|
||||||
},
|
|
||||||
ObjectName: "myobject",
|
|
||||||
}
|
|
||||||
|
|
||||||
anonGetObjectActionArgs := Args{
|
|
||||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
Action: GetObjectAction,
|
|
||||||
BucketName: "mybucket",
|
|
||||||
ConditionValues: map[string][]string{},
|
|
||||||
ObjectName: "myobject",
|
|
||||||
}
|
|
||||||
|
|
||||||
getBucketLocationArgs := Args{
|
|
||||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
Action: GetBucketLocationAction,
|
|
||||||
BucketName: "mybucket",
|
|
||||||
ConditionValues: map[string][]string{},
|
|
||||||
IsOwner: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
putObjectActionArgs := Args{
|
|
||||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
Action: PutObjectAction,
|
|
||||||
BucketName: "mybucket",
|
|
||||||
ConditionValues: map[string][]string{
|
|
||||||
"x-amz-copy-source": {"mybucket/myobject"},
|
|
||||||
"SourceIp": {"192.168.1.10"},
|
|
||||||
},
|
|
||||||
IsOwner: true,
|
|
||||||
ObjectName: "myobject",
|
|
||||||
}
|
|
||||||
|
|
||||||
getObjectActionArgs := Args{
|
|
||||||
AccountName: "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
Action: GetObjectAction,
|
|
||||||
BucketName: "mybucket",
|
|
||||||
ConditionValues: map[string][]string{},
|
|
||||||
IsOwner: true,
|
|
||||||
ObjectName: "myobject",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
statement Statement
|
|
||||||
args Args
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{case1Statement, anonGetBucketLocationArgs, true},
|
|
||||||
{case1Statement, anonPutObjectActionArgs, true},
|
|
||||||
{case1Statement, anonGetObjectActionArgs, false},
|
|
||||||
{case1Statement, getBucketLocationArgs, true},
|
|
||||||
{case1Statement, putObjectActionArgs, true},
|
|
||||||
{case1Statement, getObjectActionArgs, false},
|
|
||||||
|
|
||||||
{case2Statement, anonGetBucketLocationArgs, false},
|
|
||||||
{case2Statement, anonPutObjectActionArgs, true},
|
|
||||||
{case2Statement, anonGetObjectActionArgs, true},
|
|
||||||
{case2Statement, getBucketLocationArgs, false},
|
|
||||||
{case2Statement, putObjectActionArgs, true},
|
|
||||||
{case2Statement, getObjectActionArgs, true},
|
|
||||||
|
|
||||||
{case3Statement, anonGetBucketLocationArgs, false},
|
|
||||||
{case3Statement, anonPutObjectActionArgs, true},
|
|
||||||
{case3Statement, anonGetObjectActionArgs, false},
|
|
||||||
{case3Statement, getBucketLocationArgs, false},
|
|
||||||
{case3Statement, putObjectActionArgs, true},
|
|
||||||
{case3Statement, getObjectActionArgs, false},
|
|
||||||
|
|
||||||
{case4Statement, anonGetBucketLocationArgs, true},
|
|
||||||
{case4Statement, anonPutObjectActionArgs, false},
|
|
||||||
{case4Statement, anonGetObjectActionArgs, true},
|
|
||||||
{case4Statement, getBucketLocationArgs, true},
|
|
||||||
{case4Statement, putObjectActionArgs, false},
|
|
||||||
{case4Statement, getObjectActionArgs, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.statement.IsAllowed(testCase.args)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatementIsValid(t *testing.T) {
|
|
||||||
_, IPNet1, err := net.ParseCIDR("192.168.1.0/24")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
func1, err := condition.NewIPAddressFunc(
|
|
||||||
condition.AWSSourceIP,
|
|
||||||
IPNet1,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func2, err := condition.NewStringEqualsFunc(
|
|
||||||
condition.S3XAmzCopySource,
|
|
||||||
"mybucket/myobject",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
statement Statement
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
// Invalid effect error.
|
|
||||||
{NewStatement(
|
|
||||||
Effect("foo"),
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("*", "")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
), true},
|
|
||||||
// Invalid principal error.
|
|
||||||
{NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal(),
|
|
||||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("*", "")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
), true},
|
|
||||||
// Empty actions error.
|
|
||||||
{NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(),
|
|
||||||
NewResourceSet(NewResource("*", "")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
), true},
|
|
||||||
// Empty resources error.
|
|
||||||
{NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
|
||||||
NewResourceSet(),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
), true},
|
|
||||||
// Unsupported resource found for object action.
|
|
||||||
{NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
), true},
|
|
||||||
// Unsupported resource found for bucket action.
|
|
||||||
{NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetBucketLocationAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
), true},
|
|
||||||
// Unsupported condition key for action.
|
|
||||||
{NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
|
||||||
condition.NewFunctions(func1, func2),
|
|
||||||
), true},
|
|
||||||
{NewStatement(
|
|
||||||
Deny,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
|
||||||
condition.NewFunctions(func1),
|
|
||||||
), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
err := testCase.statement.isValid()
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatementMarshalJSON(t *testing.T) {
|
|
||||||
case1Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
)
|
|
||||||
case1Statement.SID = "SomeId1"
|
|
||||||
case1Data := []byte(`{"Sid":"SomeId1","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}`)
|
|
||||||
|
|
||||||
func1, err := condition.NewNullFunc(
|
|
||||||
condition.S3XAmzCopySource,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
case2Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(func1),
|
|
||||||
)
|
|
||||||
case2Data := []byte(`{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-copy-source":[true]}}}`)
|
|
||||||
|
|
||||||
func2, err := condition.NewNullFunc(
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
case3Statement := NewStatement(
|
|
||||||
Deny,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(func2),
|
|
||||||
)
|
|
||||||
case3Data := []byte(`{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-server-side-encryption":[false]}}}`)
|
|
||||||
|
|
||||||
case4Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
|
||||||
condition.NewFunctions(func1, func2),
|
|
||||||
)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
statement Statement
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{case1Statement, case1Data, false},
|
|
||||||
{case2Statement, case2Data, false},
|
|
||||||
{case3Statement, case3Data, false},
|
|
||||||
// Invalid statement error.
|
|
||||||
{case4Statement, nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.statement)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatementUnmarshalJSON(t *testing.T) {
|
|
||||||
case1Data := []byte(`{
|
|
||||||
"Sid": "SomeId1",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:PutObject",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
|
||||||
}`)
|
|
||||||
case1Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
)
|
|
||||||
case1Statement.SID = "SomeId1"
|
|
||||||
|
|
||||||
case2Data := []byte(`{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:PutObject",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
|
||||||
"Condition": {
|
|
||||||
"Null": {
|
|
||||||
"s3:x-amz-copy-source": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
func1, err := condition.NewNullFunc(
|
|
||||||
condition.S3XAmzCopySource,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
case2Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(func1),
|
|
||||||
)
|
|
||||||
|
|
||||||
case3Data := []byte(`{
|
|
||||||
"Effect": "Deny",
|
|
||||||
"Principal": {
|
|
||||||
"AWS": "*"
|
|
||||||
},
|
|
||||||
"Action": [
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:GetObject"
|
|
||||||
],
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
|
||||||
"Condition": {
|
|
||||||
"Null": {
|
|
||||||
"s3:x-amz-server-side-encryption": "false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
func2, err := condition.NewNullFunc(
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
case3Statement := NewStatement(
|
|
||||||
Deny,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction, GetObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(func2),
|
|
||||||
)
|
|
||||||
|
|
||||||
case4Data := []byte(`{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "Q3AM3UQ867SPQQA43P2F",
|
|
||||||
"Action": "s3:PutObject",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case5Data := []byte(`{
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:PutObject",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case6Data := []byte(`{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": "s3:PutObject",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case7Data := []byte(`{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case8Data := []byte(`{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:PutObject"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case9Data := []byte(`{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:PutObject",
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
|
||||||
"Condition": {
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
case10Data := []byte(`{
|
|
||||||
"Effect": "Deny",
|
|
||||||
"Principal": {
|
|
||||||
"AWS": "*"
|
|
||||||
},
|
|
||||||
"Action": [
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:GetObject"
|
|
||||||
],
|
|
||||||
"Resource": "arn:aws:s3:::mybucket/myobject*",
|
|
||||||
"Condition": {
|
|
||||||
"StringEquals": {
|
|
||||||
"s3:x-amz-copy-source": "yourbucket/myobject*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult Statement
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{case1Data, case1Statement, false},
|
|
||||||
{case2Data, case2Statement, false},
|
|
||||||
{case3Data, case3Statement, false},
|
|
||||||
// JSON unmarshaling error.
|
|
||||||
{case4Data, Statement{}, true},
|
|
||||||
// Invalid effect error.
|
|
||||||
{case5Data, Statement{}, true},
|
|
||||||
// empty principal error.
|
|
||||||
{case6Data, Statement{}, true},
|
|
||||||
// Empty action error.
|
|
||||||
{case7Data, Statement{}, true},
|
|
||||||
// Empty resource error.
|
|
||||||
{case8Data, Statement{}, true},
|
|
||||||
// Empty condition error.
|
|
||||||
{case9Data, Statement{}, true},
|
|
||||||
// Unsupported condition key error.
|
|
||||||
{case10Data, Statement{}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
var result Statement
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatementValidate(t *testing.T) {
|
|
||||||
case1Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "/myobject*")),
|
|
||||||
condition.NewFunctions(),
|
|
||||||
)
|
|
||||||
|
|
||||||
func1, err := condition.NewNullFunc(
|
|
||||||
condition.S3XAmzCopySource,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
func2, err := condition.NewNullFunc(
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error. %v\n", err)
|
|
||||||
}
|
|
||||||
case2Statement := NewStatement(
|
|
||||||
Allow,
|
|
||||||
NewPrincipal("*"),
|
|
||||||
NewActionSet(GetObjectAction, PutObjectAction),
|
|
||||||
NewResourceSet(NewResource("mybucket", "myobject*")),
|
|
||||||
condition.NewFunctions(func1, func2),
|
|
||||||
)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
statement Statement
|
|
||||||
bucketName string
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{case1Statement, "mybucket", false},
|
|
||||||
{case2Statement, "mybucket", true},
|
|
||||||
{case1Statement, "yourbucket", true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
err := testCase.statement.Validate(testCase.bucketName)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,439 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package iampolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
|
||||||
"github.com/minio/pkg/wildcard"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Action - policy action.
|
|
||||||
// Refer https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazons3.html
|
|
||||||
// for more information about available actions.
|
|
||||||
type Action string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// AbortMultipartUploadAction - AbortMultipartUpload Rest API action.
|
|
||||||
AbortMultipartUploadAction Action = "s3:AbortMultipartUpload"
|
|
||||||
|
|
||||||
// CreateBucketAction - CreateBucket Rest API action.
|
|
||||||
CreateBucketAction = "s3:CreateBucket"
|
|
||||||
|
|
||||||
// DeleteBucketAction - DeleteBucket Rest API action.
|
|
||||||
DeleteBucketAction = "s3:DeleteBucket"
|
|
||||||
|
|
||||||
// ForceDeleteBucketAction - DeleteBucket Rest API action when x-minio-force-delete flag
|
|
||||||
// is specified.
|
|
||||||
ForceDeleteBucketAction = "s3:ForceDeleteBucket"
|
|
||||||
|
|
||||||
// DeleteBucketPolicyAction - DeleteBucketPolicy Rest API action.
|
|
||||||
DeleteBucketPolicyAction = "s3:DeleteBucketPolicy"
|
|
||||||
|
|
||||||
// DeleteObjectAction - DeleteObject Rest API action.
|
|
||||||
DeleteObjectAction = "s3:DeleteObject"
|
|
||||||
|
|
||||||
// GetBucketLocationAction - GetBucketLocation Rest API action.
|
|
||||||
GetBucketLocationAction = "s3:GetBucketLocation"
|
|
||||||
|
|
||||||
// GetBucketNotificationAction - GetBucketNotification Rest API action.
|
|
||||||
GetBucketNotificationAction = "s3:GetBucketNotification"
|
|
||||||
|
|
||||||
// GetBucketPolicyAction - GetBucketPolicy Rest API action.
|
|
||||||
GetBucketPolicyAction = "s3:GetBucketPolicy"
|
|
||||||
|
|
||||||
// GetObjectAction - GetObject Rest API action.
|
|
||||||
GetObjectAction = "s3:GetObject"
|
|
||||||
|
|
||||||
// HeadBucketAction - HeadBucket Rest API action. This action is unused in minio.
|
|
||||||
HeadBucketAction = "s3:HeadBucket"
|
|
||||||
|
|
||||||
// ListAllMyBucketsAction - ListAllMyBuckets (List buckets) Rest API action.
|
|
||||||
ListAllMyBucketsAction = "s3:ListAllMyBuckets"
|
|
||||||
|
|
||||||
// ListBucketAction - ListBucket Rest API action.
|
|
||||||
ListBucketAction = "s3:ListBucket"
|
|
||||||
|
|
||||||
// GetBucketPolicyStatusAction - Retrieves the policy status for a bucket.
|
|
||||||
GetBucketPolicyStatusAction = "s3:GetBucketPolicyStatus"
|
|
||||||
|
|
||||||
// ListBucketVersionsAction - ListBucketVersions Rest API action.
|
|
||||||
ListBucketVersionsAction = "s3:ListBucketVersions"
|
|
||||||
|
|
||||||
// ListBucketMultipartUploadsAction - ListMultipartUploads Rest API action.
|
|
||||||
ListBucketMultipartUploadsAction = "s3:ListBucketMultipartUploads"
|
|
||||||
|
|
||||||
// ListenNotificationAction - ListenNotification Rest API action.
|
|
||||||
// This is MinIO extension.
|
|
||||||
ListenNotificationAction = "s3:ListenNotification"
|
|
||||||
|
|
||||||
// ListenBucketNotificationAction - ListenBucketNotification Rest API action.
|
|
||||||
// This is MinIO extension.
|
|
||||||
ListenBucketNotificationAction = "s3:ListenBucketNotification"
|
|
||||||
|
|
||||||
// ListMultipartUploadPartsAction - ListParts Rest API action.
|
|
||||||
ListMultipartUploadPartsAction = "s3:ListMultipartUploadParts"
|
|
||||||
|
|
||||||
// PutBucketLifecycleAction - PutBucketLifecycle Rest API action.
|
|
||||||
PutBucketLifecycleAction = "s3:PutLifecycleConfiguration"
|
|
||||||
|
|
||||||
// GetBucketLifecycleAction - GetBucketLifecycle Rest API action.
|
|
||||||
GetBucketLifecycleAction = "s3:GetLifecycleConfiguration"
|
|
||||||
|
|
||||||
// PutBucketNotificationAction - PutObjectNotification Rest API action.
|
|
||||||
PutBucketNotificationAction = "s3:PutBucketNotification"
|
|
||||||
|
|
||||||
// PutBucketPolicyAction - PutBucketPolicy Rest API action.
|
|
||||||
PutBucketPolicyAction = "s3:PutBucketPolicy"
|
|
||||||
|
|
||||||
// PutObjectAction - PutObject Rest API action.
|
|
||||||
PutObjectAction = "s3:PutObject"
|
|
||||||
|
|
||||||
// DeleteObjectVersionAction - DeleteObjectVersion Rest API action.
|
|
||||||
DeleteObjectVersionAction = "s3:DeleteObjectVersion"
|
|
||||||
|
|
||||||
// DeleteObjectVersionTaggingAction - DeleteObjectVersionTagging Rest API action.
|
|
||||||
DeleteObjectVersionTaggingAction = "s3:DeleteObjectVersionTagging"
|
|
||||||
|
|
||||||
// GetObjectVersionAction - GetObjectVersionAction Rest API action.
|
|
||||||
GetObjectVersionAction = "s3:GetObjectVersion"
|
|
||||||
|
|
||||||
// GetObjectVersionTaggingAction - GetObjectVersionTagging Rest API action.
|
|
||||||
GetObjectVersionTaggingAction = "s3:GetObjectVersionTagging"
|
|
||||||
|
|
||||||
// PutObjectVersionTaggingAction - PutObjectVersionTagging Rest API action.
|
|
||||||
PutObjectVersionTaggingAction = "s3:PutObjectVersionTagging"
|
|
||||||
|
|
||||||
// BypassGovernanceRetentionAction - bypass governance retention for PutObjectRetention, PutObject and DeleteObject Rest API action.
|
|
||||||
BypassGovernanceRetentionAction = "s3:BypassGovernanceRetention"
|
|
||||||
|
|
||||||
// PutObjectRetentionAction - PutObjectRetention Rest API action.
|
|
||||||
PutObjectRetentionAction = "s3:PutObjectRetention"
|
|
||||||
|
|
||||||
// GetObjectRetentionAction - GetObjectRetention, GetObject, HeadObject Rest API action.
|
|
||||||
GetObjectRetentionAction = "s3:GetObjectRetention"
|
|
||||||
|
|
||||||
// GetObjectLegalHoldAction - GetObjectLegalHold, GetObject Rest API action.
|
|
||||||
GetObjectLegalHoldAction = "s3:GetObjectLegalHold"
|
|
||||||
|
|
||||||
// PutObjectLegalHoldAction - PutObjectLegalHold, PutObject Rest API action.
|
|
||||||
PutObjectLegalHoldAction = "s3:PutObjectLegalHold"
|
|
||||||
|
|
||||||
// GetBucketObjectLockConfigurationAction - GetBucketObjectLockConfiguration Rest API action
|
|
||||||
GetBucketObjectLockConfigurationAction = "s3:GetBucketObjectLockConfiguration"
|
|
||||||
|
|
||||||
// PutBucketObjectLockConfigurationAction - PutBucketObjectLockConfiguration Rest API action
|
|
||||||
PutBucketObjectLockConfigurationAction = "s3:PutBucketObjectLockConfiguration"
|
|
||||||
|
|
||||||
// GetBucketTaggingAction - GetBucketTagging Rest API action
|
|
||||||
GetBucketTaggingAction = "s3:GetBucketTagging"
|
|
||||||
|
|
||||||
// PutBucketTaggingAction - PutBucketTagging Rest API action
|
|
||||||
PutBucketTaggingAction = "s3:PutBucketTagging"
|
|
||||||
|
|
||||||
// GetObjectTaggingAction - Get Object Tags API action
|
|
||||||
GetObjectTaggingAction = "s3:GetObjectTagging"
|
|
||||||
|
|
||||||
// PutObjectTaggingAction - Put Object Tags API action
|
|
||||||
PutObjectTaggingAction = "s3:PutObjectTagging"
|
|
||||||
|
|
||||||
// DeleteObjectTaggingAction - Delete Object Tags API action
|
|
||||||
DeleteObjectTaggingAction = "s3:DeleteObjectTagging"
|
|
||||||
|
|
||||||
// PutBucketEncryptionAction - PutBucketEncryption REST API action
|
|
||||||
PutBucketEncryptionAction = "s3:PutEncryptionConfiguration"
|
|
||||||
|
|
||||||
// GetBucketEncryptionAction - GetBucketEncryption REST API action
|
|
||||||
GetBucketEncryptionAction = "s3:GetEncryptionConfiguration"
|
|
||||||
|
|
||||||
// PutBucketVersioningAction - PutBucketVersioning REST API action
|
|
||||||
PutBucketVersioningAction = "s3:PutBucketVersioning"
|
|
||||||
|
|
||||||
// GetBucketVersioningAction - GetBucketVersioning REST API action
|
|
||||||
GetBucketVersioningAction = "s3:GetBucketVersioning"
|
|
||||||
// GetReplicationConfigurationAction - GetReplicationConfiguration REST API action
|
|
||||||
GetReplicationConfigurationAction = "s3:GetReplicationConfiguration"
|
|
||||||
// PutReplicationConfigurationAction - PutReplicationConfiguration REST API action
|
|
||||||
PutReplicationConfigurationAction = "s3:PutReplicationConfiguration"
|
|
||||||
|
|
||||||
// ReplicateObjectAction - ReplicateObject REST API action
|
|
||||||
ReplicateObjectAction = "s3:ReplicateObject"
|
|
||||||
|
|
||||||
// ReplicateDeleteAction - ReplicateDelete REST API action
|
|
||||||
ReplicateDeleteAction = "s3:ReplicateDelete"
|
|
||||||
|
|
||||||
// ReplicateTagsAction - ReplicateTags REST API action
|
|
||||||
ReplicateTagsAction = "s3:ReplicateTags"
|
|
||||||
|
|
||||||
// GetObjectVersionForReplicationAction - GetObjectVersionForReplication REST API action
|
|
||||||
GetObjectVersionForReplicationAction = "s3:GetObjectVersionForReplication"
|
|
||||||
|
|
||||||
// AllActions - all API actions
|
|
||||||
AllActions = "s3:*"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List of all supported actions.
|
|
||||||
var supportedActions = map[Action]struct{}{
|
|
||||||
AbortMultipartUploadAction: {},
|
|
||||||
CreateBucketAction: {},
|
|
||||||
DeleteBucketAction: {},
|
|
||||||
ForceDeleteBucketAction: {},
|
|
||||||
DeleteBucketPolicyAction: {},
|
|
||||||
DeleteObjectAction: {},
|
|
||||||
GetBucketLocationAction: {},
|
|
||||||
GetBucketNotificationAction: {},
|
|
||||||
GetBucketPolicyAction: {},
|
|
||||||
GetObjectAction: {},
|
|
||||||
HeadBucketAction: {},
|
|
||||||
ListAllMyBucketsAction: {},
|
|
||||||
ListBucketAction: {},
|
|
||||||
GetBucketPolicyStatusAction: {},
|
|
||||||
ListBucketVersionsAction: {},
|
|
||||||
ListBucketMultipartUploadsAction: {},
|
|
||||||
ListenNotificationAction: {},
|
|
||||||
ListenBucketNotificationAction: {},
|
|
||||||
ListMultipartUploadPartsAction: {},
|
|
||||||
PutBucketLifecycleAction: {},
|
|
||||||
GetBucketLifecycleAction: {},
|
|
||||||
PutBucketNotificationAction: {},
|
|
||||||
PutBucketPolicyAction: {},
|
|
||||||
PutObjectAction: {},
|
|
||||||
BypassGovernanceRetentionAction: {},
|
|
||||||
PutObjectRetentionAction: {},
|
|
||||||
GetObjectRetentionAction: {},
|
|
||||||
GetObjectLegalHoldAction: {},
|
|
||||||
PutObjectLegalHoldAction: {},
|
|
||||||
GetBucketObjectLockConfigurationAction: {},
|
|
||||||
PutBucketObjectLockConfigurationAction: {},
|
|
||||||
GetBucketTaggingAction: {},
|
|
||||||
PutBucketTaggingAction: {},
|
|
||||||
GetObjectVersionAction: {},
|
|
||||||
GetObjectVersionTaggingAction: {},
|
|
||||||
DeleteObjectVersionAction: {},
|
|
||||||
DeleteObjectVersionTaggingAction: {},
|
|
||||||
PutObjectVersionTaggingAction: {},
|
|
||||||
GetObjectTaggingAction: {},
|
|
||||||
PutObjectTaggingAction: {},
|
|
||||||
DeleteObjectTaggingAction: {},
|
|
||||||
PutBucketEncryptionAction: {},
|
|
||||||
GetBucketEncryptionAction: {},
|
|
||||||
PutBucketVersioningAction: {},
|
|
||||||
GetBucketVersioningAction: {},
|
|
||||||
GetReplicationConfigurationAction: {},
|
|
||||||
PutReplicationConfigurationAction: {},
|
|
||||||
ReplicateObjectAction: {},
|
|
||||||
ReplicateDeleteAction: {},
|
|
||||||
ReplicateTagsAction: {},
|
|
||||||
GetObjectVersionForReplicationAction: {},
|
|
||||||
AllActions: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of all supported object actions.
|
|
||||||
var supportedObjectActions = map[Action]struct{}{
|
|
||||||
AllActions: {},
|
|
||||||
AbortMultipartUploadAction: {},
|
|
||||||
DeleteObjectAction: {},
|
|
||||||
GetObjectAction: {},
|
|
||||||
ListMultipartUploadPartsAction: {},
|
|
||||||
PutObjectAction: {},
|
|
||||||
BypassGovernanceRetentionAction: {},
|
|
||||||
PutObjectRetentionAction: {},
|
|
||||||
GetObjectRetentionAction: {},
|
|
||||||
PutObjectLegalHoldAction: {},
|
|
||||||
GetObjectLegalHoldAction: {},
|
|
||||||
GetObjectTaggingAction: {},
|
|
||||||
PutObjectTaggingAction: {},
|
|
||||||
DeleteObjectTaggingAction: {},
|
|
||||||
GetObjectVersionAction: {},
|
|
||||||
GetObjectVersionTaggingAction: {},
|
|
||||||
DeleteObjectVersionAction: {},
|
|
||||||
DeleteObjectVersionTaggingAction: {},
|
|
||||||
PutObjectVersionTaggingAction: {},
|
|
||||||
ReplicateObjectAction: {},
|
|
||||||
ReplicateDeleteAction: {},
|
|
||||||
ReplicateTagsAction: {},
|
|
||||||
GetObjectVersionForReplicationAction: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// isObjectAction - returns whether action is object type or not.
|
|
||||||
func (action Action) isObjectAction() bool {
|
|
||||||
for supAction := range supportedObjectActions {
|
|
||||||
if action.Match(supAction) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match - matches action name with action patter.
|
|
||||||
func (action Action) Match(a Action) bool {
|
|
||||||
return wildcard.Match(string(action), string(a))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid - checks if action is valid or not.
|
|
||||||
func (action Action) IsValid() bool {
|
|
||||||
for supAction := range supportedActions {
|
|
||||||
if action.Match(supAction) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type actionConditionKeyMap map[Action]condition.KeySet
|
|
||||||
|
|
||||||
func (a actionConditionKeyMap) Lookup(action Action) condition.KeySet {
|
|
||||||
var ckeysMerged = condition.NewKeySet(condition.CommonKeys...)
|
|
||||||
for act, ckey := range a {
|
|
||||||
if action.Match(act) {
|
|
||||||
ckeysMerged.Merge(ckey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ckeysMerged
|
|
||||||
}
|
|
||||||
|
|
||||||
// iamActionConditionKeyMap - holds mapping of supported condition key for an action.
|
|
||||||
var iamActionConditionKeyMap = actionConditionKeyMap{
|
|
||||||
AllActions: condition.NewKeySet(condition.AllSupportedKeys...),
|
|
||||||
|
|
||||||
GetObjectAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
ListBucketAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3Prefix,
|
|
||||||
condition.S3Delimiter,
|
|
||||||
condition.S3MaxKeys,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
ListBucketVersionsAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3Prefix,
|
|
||||||
condition.S3Delimiter,
|
|
||||||
condition.S3MaxKeys,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
DeleteObjectAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
PutObjectAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzCopySource,
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
condition.S3XAmzMetadataDirective,
|
|
||||||
condition.S3XAmzStorageClass,
|
|
||||||
condition.S3VersionID,
|
|
||||||
condition.S3ObjectLockRetainUntilDate,
|
|
||||||
condition.S3ObjectLockMode,
|
|
||||||
condition.S3ObjectLockLegalHold,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
|
||||||
// LockLegalHold is not supported with PutObjectRetentionAction
|
|
||||||
PutObjectRetentionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
condition.S3ObjectLockRemainingRetentionDays,
|
|
||||||
condition.S3ObjectLockRetainUntilDate,
|
|
||||||
condition.S3ObjectLockMode,
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
GetObjectRetentionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
PutObjectLegalHoldAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3XAmzServerSideEncryption,
|
|
||||||
condition.S3XAmzServerSideEncryptionCustomerAlgorithm,
|
|
||||||
condition.S3ObjectLockLegalHold,
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectLegalHoldAction: condition.NewKeySet(condition.CommonKeys...),
|
|
||||||
|
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/list_amazons3.html
|
|
||||||
BypassGovernanceRetentionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
condition.S3ObjectLockRemainingRetentionDays,
|
|
||||||
condition.S3ObjectLockRetainUntilDate,
|
|
||||||
condition.S3ObjectLockMode,
|
|
||||||
condition.S3ObjectLockLegalHold,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
PutObjectTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
DeleteObjectTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
|
|
||||||
PutObjectVersionTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectVersionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectVersionTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
DeleteObjectVersionAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
DeleteObjectVersionTaggingAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
ReplicateObjectAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
ReplicateDeleteAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
ReplicateTagsAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
GetObjectVersionForReplicationAction: condition.NewKeySet(
|
|
||||||
append([]condition.Key{
|
|
||||||
condition.S3VersionID,
|
|
||||||
}, condition.CommonKeys...)...),
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package iampolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActionIsObjectAction(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
action Action
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{AbortMultipartUploadAction, true},
|
|
||||||
{DeleteObjectAction, true},
|
|
||||||
{GetObjectAction, true},
|
|
||||||
{ListMultipartUploadPartsAction, true},
|
|
||||||
{PutObjectAction, true},
|
|
||||||
{CreateBucketAction, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.action.isObjectAction()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionIsValid(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
action Action
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{PutObjectAction, true},
|
|
||||||
{AbortMultipartUploadAction, true},
|
|
||||||
{Action("foo"), false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.action.IsValid()
|
|
||||||
|
|
||||||
if testCase.expectedResult != result {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,182 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package iampolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ActionSet - set of actions.
|
|
||||||
type ActionSet map[Action]struct{}
|
|
||||||
|
|
||||||
// Clone clones ActionSet structure
|
|
||||||
func (actionSet ActionSet) Clone() ActionSet {
|
|
||||||
return NewActionSet(actionSet.ToSlice()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add - add action to the set.
|
|
||||||
func (actionSet ActionSet) Add(action Action) {
|
|
||||||
actionSet[action] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty - returns if the current action set is empty
|
|
||||||
func (actionSet ActionSet) IsEmpty() bool {
|
|
||||||
return len(actionSet) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match - matches object name with anyone of action pattern in action set.
|
|
||||||
func (actionSet ActionSet) Match(action Action) bool {
|
|
||||||
for r := range actionSet {
|
|
||||||
if r.Match(action) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a special case where GetObjectVersion
|
|
||||||
// means GetObject is enabled implicitly.
|
|
||||||
switch r {
|
|
||||||
case GetObjectVersionAction:
|
|
||||||
if action == GetObjectAction {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals - checks whether given action set is equal to current action set or not.
|
|
||||||
func (actionSet ActionSet) Equals(sactionSet ActionSet) bool {
|
|
||||||
// If length of set is not equal to length of given set, the
|
|
||||||
// set is not equal to given set.
|
|
||||||
if len(actionSet) != len(sactionSet) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// As both sets are equal in length, check each elements are equal.
|
|
||||||
for k := range actionSet {
|
|
||||||
if _, ok := sactionSet[k]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersection - returns actions available in both ActionSet.
|
|
||||||
func (actionSet ActionSet) Intersection(sset ActionSet) ActionSet {
|
|
||||||
nset := NewActionSet()
|
|
||||||
for k := range actionSet {
|
|
||||||
if _, ok := sset[k]; ok {
|
|
||||||
nset.Add(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nset
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON - encodes ActionSet to JSON data.
|
|
||||||
func (actionSet ActionSet) MarshalJSON() ([]byte, error) {
|
|
||||||
if len(actionSet) == 0 {
|
|
||||||
return nil, Errorf("empty action set")
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(actionSet.ToSlice())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (actionSet ActionSet) String() string {
|
|
||||||
actions := []string{}
|
|
||||||
for action := range actionSet {
|
|
||||||
actions = append(actions, string(action))
|
|
||||||
}
|
|
||||||
sort.Strings(actions)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v", actions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSlice - returns slice of actions from the action set.
|
|
||||||
func (actionSet ActionSet) ToSlice() []Action {
|
|
||||||
actions := []Action{}
|
|
||||||
for action := range actionSet {
|
|
||||||
actions = append(actions, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToAdminSlice - returns slice of admin actions from the action set.
|
|
||||||
func (actionSet ActionSet) ToAdminSlice() []AdminAction {
|
|
||||||
actions := []AdminAction{}
|
|
||||||
for action := range actionSet {
|
|
||||||
actions = append(actions, AdminAction(action))
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON - decodes JSON data to ActionSet.
|
|
||||||
func (actionSet *ActionSet) UnmarshalJSON(data []byte) error {
|
|
||||||
var sset set.StringSet
|
|
||||||
if err := json.Unmarshal(data, &sset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sset) == 0 {
|
|
||||||
return Errorf("empty action set")
|
|
||||||
}
|
|
||||||
|
|
||||||
*actionSet = make(ActionSet)
|
|
||||||
for _, s := range sset.ToSlice() {
|
|
||||||
actionSet.Add(Action(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateAdmin checks if all actions are valid Admin actions
|
|
||||||
func (actionSet ActionSet) ValidateAdmin() error {
|
|
||||||
for _, action := range actionSet.ToAdminSlice() {
|
|
||||||
if !action.IsValid() {
|
|
||||||
return Errorf("unsupported admin action '%v'", action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks if all actions are valid
|
|
||||||
func (actionSet ActionSet) Validate() error {
|
|
||||||
for _, action := range actionSet.ToSlice() {
|
|
||||||
if !action.IsValid() {
|
|
||||||
return Errorf("unsupported action '%v'", action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewActionSet - creates new action set.
|
|
||||||
func NewActionSet(actions ...Action) ActionSet {
|
|
||||||
actionSet := make(ActionSet)
|
|
||||||
for _, action := range actions {
|
|
||||||
actionSet.Add(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
return actionSet
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
// This file is part of MinIO Object Storage stack
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package iampolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActionSetAdd(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ActionSet
|
|
||||||
action Action
|
|
||||||
expectedResult ActionSet
|
|
||||||
}{
|
|
||||||
{NewActionSet(), PutObjectAction, NewActionSet(PutObjectAction)},
|
|
||||||
{NewActionSet(PutObjectAction), PutObjectAction, NewActionSet(PutObjectAction)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
testCase.set.Add(testCase.action)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(testCase.expectedResult, testCase.set) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetMatches(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ActionSet
|
|
||||||
action Action
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{NewActionSet(AllActions), AbortMultipartUploadAction, true},
|
|
||||||
{NewActionSet(PutObjectAction), PutObjectAction, true},
|
|
||||||
{NewActionSet(PutObjectAction, GetObjectAction), PutObjectAction, true},
|
|
||||||
{NewActionSet(PutObjectAction, GetObjectAction), AbortMultipartUploadAction, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.Match(testCase.action)
|
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetIntersection(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
set ActionSet
|
|
||||||
setToIntersect ActionSet
|
|
||||||
expectedResult ActionSet
|
|
||||||
}{
|
|
||||||
{NewActionSet(), NewActionSet(PutObjectAction), NewActionSet()},
|
|
||||||
{NewActionSet(PutObjectAction), NewActionSet(), NewActionSet()},
|
|
||||||
{NewActionSet(PutObjectAction), NewActionSet(PutObjectAction, GetObjectAction), NewActionSet(PutObjectAction)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.set.Intersection(testCase.setToIntersect)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, testCase.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetMarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
actionSet ActionSet
|
|
||||||
expectedResult []byte
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{NewActionSet(PutObjectAction), []byte(`["s3:PutObject"]`), false},
|
|
||||||
{NewActionSet(), nil, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result, err := json.Marshal(testCase.actionSet)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectErr {
|
|
||||||
t.Fatalf("case %v: error: expected: %v, got: %v\n", i+1, testCase.expectErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, string(testCase.expectedResult), string(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetToSlice(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
actionSet ActionSet
|
|
||||||
expectedResult []Action
|
|
||||||
}{
|
|
||||||
{NewActionSet(PutObjectAction), []Action{PutObjectAction}},
|
|
||||||
{NewActionSet(), []Action{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := testCase.actionSet.ToSlice()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionSetUnmarshalJSON(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data []byte
|
|
||||||
expectedResult ActionSet
|
|
||||||
expectUnmarshalErr bool
|
|
||||||
expectValidateErr bool
|
|
||||||
}{
|
|
||||||
{[]byte(`"s3:PutObject"`), NewActionSet(PutObjectAction), false, false},
|
|
||||||
{[]byte(`["s3:PutObject"]`), NewActionSet(PutObjectAction), false, false},
|
|
||||||
{[]byte(`["s3:PutObject", "s3:GetObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false, false},
|
|
||||||
{[]byte(`["s3:PutObject", "s3:GetObject", "s3:PutObject"]`), NewActionSet(PutObjectAction, GetObjectAction), false, false},
|
|
||||||
{[]byte(`[]`), NewActionSet(), true, false}, // Empty array.
|
|
||||||
{[]byte(`"foo"`), nil, false, true}, // Invalid action.
|
|
||||||
{[]byte(`["s3:PutObject", "foo"]`), nil, false, true}, // Invalid action.
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
|
||||||
result := make(ActionSet)
|
|
||||||
err := json.Unmarshal(testCase.data, &result)
|
|
||||||
expectErr := (err != nil)
|
|
||||||
|
|
||||||
if expectErr != testCase.expectUnmarshalErr {
|
|
||||||
t.Fatalf("case %v: error during unmarshal: expected: %v, got: %v\n", i+1, testCase.expectUnmarshalErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = result.Validate()
|
|
||||||
expectErr = (err != nil)
|
|
||||||
if expectErr != testCase.expectValidateErr {
|
|
||||||
t.Fatalf("case %v: error during validation: expected: %v, got: %v\n", i+1, testCase.expectValidateErr, expectErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !testCase.expectUnmarshalErr && !testCase.expectValidateErr {
|
|
||||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
||||||
t.Fatalf("case %v: result: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user