mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Support policy variable replacement (#7085)
This PR supports iam and bucket policies to have policy variable replacements in resource and condition key values. For example - ${aws:username} - ${aws:userid}
This commit is contained in:
parent
3265112d04
commit
5353edcc38
@ -283,7 +283,7 @@ func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Ac
|
|||||||
AccountName: cred.AccessKey,
|
AccountName: cred.AccessKey,
|
||||||
Action: action,
|
Action: action,
|
||||||
BucketName: bucketName,
|
BucketName: bucketName,
|
||||||
ConditionValues: getConditionValues(r, locationConstraint),
|
ConditionValues: getConditionValues(r, locationConstraint, ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
}) {
|
}) {
|
||||||
@ -296,7 +296,7 @@ func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Ac
|
|||||||
AccountName: cred.AccessKey,
|
AccountName: cred.AccessKey,
|
||||||
Action: iampolicy.Action(action),
|
Action: iampolicy.Action(action),
|
||||||
BucketName: bucketName,
|
BucketName: bucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", cred.AccessKey),
|
||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
Claims: claims,
|
Claims: claims,
|
||||||
@ -448,7 +448,7 @@ func isPutAllowed(atype authType, bucketName, objectName string, r *http.Request
|
|||||||
AccountName: cred.AccessKey,
|
AccountName: cred.AccessKey,
|
||||||
Action: policy.PutObjectAction,
|
Action: policy.PutObjectAction,
|
||||||
BucketName: bucketName,
|
BucketName: bucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
}) {
|
}) {
|
||||||
@ -461,7 +461,7 @@ func isPutAllowed(atype authType, bucketName, objectName string, r *http.Request
|
|||||||
AccountName: cred.AccessKey,
|
AccountName: cred.AccessKey,
|
||||||
Action: policy.PutObjectAction,
|
Action: policy.PutObjectAction,
|
||||||
BucketName: bucketName,
|
BucketName: bucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", cred.AccessKey),
|
||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
Claims: claims,
|
Claims: claims,
|
||||||
|
@ -131,7 +131,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
|||||||
if globalPolicySys.IsAllowed(policy.Args{
|
if globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.ListBucketAction,
|
Action: policy.ListBucketAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
}) {
|
}) {
|
||||||
_, err = getObjectInfo(ctx, bucket, object, opts)
|
_, err = getObjectInfo(ctx, bucket, object, opts)
|
||||||
@ -281,7 +281,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
if globalPolicySys.IsAllowed(policy.Args{
|
if globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.ListBucketAction,
|
Action: policy.ListBucketAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
}) {
|
}) {
|
||||||
getObjectInfo := objectAPI.GetObjectInfo
|
getObjectInfo := objectAPI.GetObjectInfo
|
||||||
@ -463,7 +463,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
if globalPolicySys.IsAllowed(policy.Args{
|
if globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.ListBucketAction,
|
Action: policy.ListBucketAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
}) {
|
}) {
|
||||||
_, err = getObjectInfo(ctx, bucket, object, opts)
|
_, err = getObjectInfo(ctx, bucket, object, opts)
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
miniogopolicy "github.com/minio/minio-go/pkg/policy"
|
miniogopolicy "github.com/minio/minio-go/pkg/policy"
|
||||||
"github.com/minio/minio-go/pkg/set"
|
"github.com/minio/minio-go/pkg/set"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
"github.com/minio/minio/pkg/event"
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
"github.com/minio/minio/pkg/policy"
|
"github.com/minio/minio/pkg/policy"
|
||||||
)
|
)
|
||||||
@ -183,12 +184,24 @@ func NewPolicySys() *PolicySys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConditionValues(request *http.Request, locationConstraint string) map[string][]string {
|
func getConditionValues(request *http.Request, locationConstraint string, username string) map[string][]string {
|
||||||
|
currTime := UTCNow()
|
||||||
|
principalType := func() string {
|
||||||
|
if username != "" {
|
||||||
|
return "User"
|
||||||
|
}
|
||||||
|
return "Anonymous"
|
||||||
|
}()
|
||||||
args := map[string][]string{
|
args := map[string][]string{
|
||||||
"SourceIp": {handlers.GetSourceIP(request)},
|
"CurrenTime": {currTime.Format(event.AMZTimeFormat)},
|
||||||
|
"EpochTime": {fmt.Sprintf("%d", currTime.Unix())},
|
||||||
|
"principaltype": {principalType},
|
||||||
"SecureTransport": {fmt.Sprintf("%t", request.TLS != nil)},
|
"SecureTransport": {fmt.Sprintf("%t", request.TLS != nil)},
|
||||||
|
"SourceIp": {handlers.GetSourceIP(request)},
|
||||||
"UserAgent": {request.UserAgent()},
|
"UserAgent": {request.UserAgent()},
|
||||||
"Referer": {request.Referer()},
|
"Referer": {request.Referer()},
|
||||||
|
"userid": {username},
|
||||||
|
"username": {username},
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, values := range request.Header {
|
for key, values := range request.Header {
|
||||||
|
@ -306,7 +306,7 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.ListBucketAction,
|
Action: iampolicy.ListBucketAction,
|
||||||
BucketName: bucketName,
|
BucketName: bucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: "",
|
ObjectName: "",
|
||||||
}) {
|
}) {
|
||||||
@ -326,7 +326,7 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.ListBucketAction,
|
Action: iampolicy.ListBucketAction,
|
||||||
BucketName: bucket.Name,
|
BucketName: bucket.Name,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: "",
|
ObjectName: "",
|
||||||
}) {
|
}) {
|
||||||
@ -432,7 +432,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||||||
readable := globalPolicySys.IsAllowed(policy.Args{
|
readable := globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.ListBucketAction,
|
Action: policy.ListBucketAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -440,7 +440,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||||||
writable := globalPolicySys.IsAllowed(policy.Args{
|
writable := globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.PutObjectAction,
|
Action: policy.PutObjectAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
ObjectName: args.Prefix + "/",
|
ObjectName: args.Prefix + "/",
|
||||||
})
|
})
|
||||||
@ -471,7 +471,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.ListBucketAction,
|
Action: iampolicy.ListBucketAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -479,7 +479,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.PutObjectAction,
|
Action: iampolicy.PutObjectAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: args.Prefix + "/",
|
ObjectName: args.Prefix + "/",
|
||||||
})
|
})
|
||||||
@ -611,7 +611,7 @@ next:
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.DeleteObjectAction,
|
Action: iampolicy.DeleteObjectAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
}) {
|
}) {
|
||||||
@ -628,7 +628,7 @@ next:
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.DeleteObjectAction,
|
Action: iampolicy.DeleteObjectAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
}) {
|
}) {
|
||||||
@ -853,7 +853,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !globalPolicySys.IsAllowed(policy.Args{
|
if !globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.PutObjectAction,
|
Action: policy.PutObjectAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
ObjectName: object,
|
ObjectName: object,
|
||||||
}) {
|
}) {
|
||||||
@ -872,7 +872,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.PutObjectAction,
|
Action: iampolicy.PutObjectAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: object,
|
ObjectName: object,
|
||||||
}) {
|
}) {
|
||||||
@ -1040,7 +1040,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !globalPolicySys.IsAllowed(policy.Args{
|
if !globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.GetObjectAction,
|
Action: policy.GetObjectAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
ObjectName: object,
|
ObjectName: object,
|
||||||
}) {
|
}) {
|
||||||
@ -1059,7 +1059,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.GetObjectAction,
|
Action: iampolicy.GetObjectAction,
|
||||||
BucketName: bucket,
|
BucketName: bucket,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: object,
|
ObjectName: object,
|
||||||
}) {
|
}) {
|
||||||
@ -1195,7 +1195,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !globalPolicySys.IsAllowed(policy.Args{
|
if !globalPolicySys.IsAllowed(policy.Args{
|
||||||
Action: policy.GetObjectAction,
|
Action: policy.GetObjectAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", ""),
|
||||||
IsOwner: false,
|
IsOwner: false,
|
||||||
ObjectName: pathJoin(args.Prefix, object),
|
ObjectName: pathJoin(args.Prefix, object),
|
||||||
}) {
|
}) {
|
||||||
@ -1216,7 +1216,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
AccountName: claims.Subject,
|
AccountName: claims.Subject,
|
||||||
Action: iampolicy.GetObjectAction,
|
Action: iampolicy.GetObjectAction,
|
||||||
BucketName: args.BucketName,
|
BucketName: args.BucketName,
|
||||||
ConditionValues: getConditionValues(r, ""),
|
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||||
IsOwner: owner,
|
IsOwner: owner,
|
||||||
ObjectName: pathJoin(args.Prefix, object),
|
ObjectName: pathJoin(args.Prefix, object),
|
||||||
}) {
|
}) {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/minio/minio/pkg/policy/condition"
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,11 +48,18 @@ func (r Resource) IsValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match - matches object name with resource pattern.
|
// Match - matches object name with resource pattern.
|
||||||
func (r Resource) Match(resource string) bool {
|
func (r Resource) Match(resource string, conditionValues map[string][]string) bool {
|
||||||
if strings.HasPrefix(resource, r.Pattern) {
|
pattern := r.Pattern
|
||||||
|
for _, key := range condition.CommonKeys {
|
||||||
|
// Empty values are not supported for policy variables.
|
||||||
|
if rvalues, ok := conditionValues[key.Name()]; ok && rvalues[0] != "" {
|
||||||
|
pattern = strings.Replace(pattern, key.VarName(), rvalues[0], -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(resource, pattern) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return wildcard.Match(r.Pattern, resource)
|
return wildcard.Match(pattern, resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON - encodes Resource to JSON data.
|
// MarshalJSON - encodes Resource to JSON data.
|
||||||
|
@ -124,7 +124,7 @@ func TestResourceMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
result := testCase.resource.Match(testCase.objectName)
|
result := testCase.resource.Match(testCase.objectName, nil)
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
if result != testCase.expectedResult {
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||||
|
@ -81,9 +81,9 @@ func (resourceSet ResourceSet) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match - matches object name with anyone of resource pattern in resource set.
|
// Match - matches object name with anyone of resource pattern in resource set.
|
||||||
func (resourceSet ResourceSet) Match(resource string) bool {
|
func (resourceSet ResourceSet) Match(resource string, conditionValues map[string][]string) bool {
|
||||||
for r := range resourceSet {
|
for r := range resourceSet {
|
||||||
if r.Match(resource) {
|
if r.Match(resource, conditionValues) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ func TestResourceSetMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
result := testCase.resourceSet.Match(testCase.resource)
|
result := testCase.resourceSet.Match(testCase.resource, nil)
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
if result != testCase.expectedResult {
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||||
|
@ -52,7 +52,7 @@ func (statement Statement) IsAllowed(args Args) bool {
|
|||||||
resource += "/"
|
resource += "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
if !statement.Resources.Match(resource) {
|
if !statement.Resources.Match(resource, args.ConditionValues) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,8 @@ func (f binaryEqualsFunc) evaluate(values map[string][]string) bool {
|
|||||||
requestValue = values[f.k.Name()]
|
requestValue = values[f.k.Name()]
|
||||||
}
|
}
|
||||||
|
|
||||||
return !f.values.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||||
|
return !fvalues.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
// key() - returns condition key which is used by this condition function.
|
||||||
|
@ -33,49 +33,58 @@ const (
|
|||||||
|
|
||||||
// S3XAmzServerSideEncryption - key representing x-amz-server-side-encryption HTTP header applicable
|
// S3XAmzServerSideEncryption - key representing x-amz-server-side-encryption HTTP header applicable
|
||||||
// to PutObject API only.
|
// to PutObject API only.
|
||||||
S3XAmzServerSideEncryption = "s3:x-amz-server-side-encryption"
|
S3XAmzServerSideEncryption Key = "s3:x-amz-server-side-encryption"
|
||||||
|
|
||||||
// S3XAmzServerSideEncryptionCustomerAlgorithm - key representing
|
// S3XAmzServerSideEncryptionCustomerAlgorithm - key representing
|
||||||
// x-amz-server-side-encryption-customer-algorithm HTTP header applicable to PutObject API only.
|
// x-amz-server-side-encryption-customer-algorithm HTTP header applicable to PutObject API only.
|
||||||
S3XAmzServerSideEncryptionCustomerAlgorithm = "s3:x-amz-server-side-encryption-customer-algorithm"
|
S3XAmzServerSideEncryptionCustomerAlgorithm Key = "s3:x-amz-server-side-encryption-customer-algorithm"
|
||||||
|
|
||||||
// S3XAmzMetadataDirective - key representing x-amz-metadata-directive HTTP header applicable to
|
// S3XAmzMetadataDirective - key representing x-amz-metadata-directive HTTP header applicable to
|
||||||
// PutObject API only.
|
// PutObject API only.
|
||||||
S3XAmzMetadataDirective = "s3:x-amz-metadata-directive"
|
S3XAmzMetadataDirective Key = "s3:x-amz-metadata-directive"
|
||||||
|
|
||||||
// S3XAmzStorageClass - key representing x-amz-storage-class HTTP header applicable to PutObject API
|
// S3XAmzStorageClass - key representing x-amz-storage-class HTTP header applicable to PutObject API
|
||||||
// only.
|
// only.
|
||||||
S3XAmzStorageClass = "s3:x-amz-storage-class"
|
S3XAmzStorageClass Key = "s3:x-amz-storage-class"
|
||||||
|
|
||||||
// S3LocationConstraint - key representing LocationConstraint XML tag of CreateBucket API only.
|
// S3LocationConstraint - key representing LocationConstraint XML tag of CreateBucket API only.
|
||||||
S3LocationConstraint = "s3:LocationConstraint"
|
S3LocationConstraint Key = "s3:LocationConstraint"
|
||||||
|
|
||||||
// S3Prefix - key representing prefix query parameter of ListBucket API only.
|
// S3Prefix - key representing prefix query parameter of ListBucket API only.
|
||||||
S3Prefix = "s3:prefix"
|
S3Prefix Key = "s3:prefix"
|
||||||
|
|
||||||
// S3Delimiter - key representing delimiter query parameter of ListBucket API only.
|
// S3Delimiter - key representing delimiter query parameter of ListBucket API only.
|
||||||
S3Delimiter = "s3:delimiter"
|
S3Delimiter Key = "s3:delimiter"
|
||||||
|
|
||||||
// S3MaxKeys - key representing max-keys query parameter of ListBucket API only.
|
// S3MaxKeys - key representing max-keys query parameter of ListBucket API only.
|
||||||
S3MaxKeys = "s3:max-keys"
|
S3MaxKeys Key = "s3:max-keys"
|
||||||
|
|
||||||
// AWSReferer - key representing Referer header of any API.
|
// AWSReferer - key representing Referer header of any API.
|
||||||
AWSReferer = "aws:Referer"
|
AWSReferer Key = "aws:Referer"
|
||||||
|
|
||||||
// AWSSourceIP - key representing client's IP address (not intermittent proxies) of any API.
|
// AWSSourceIP - key representing client's IP address (not intermittent proxies) of any API.
|
||||||
AWSSourceIP = "aws:SourceIp"
|
AWSSourceIP Key = "aws:SourceIp"
|
||||||
|
|
||||||
// AWSUserAgent - key representing UserAgent header for any API.
|
// AWSUserAgent - key representing UserAgent header for any API.
|
||||||
AWSUserAgent = "aws:UserAgent"
|
AWSUserAgent Key = "aws:UserAgent"
|
||||||
|
|
||||||
// AWSSecureTransport - key representing if the clients request is authenticated or not.
|
// AWSSecureTransport - key representing if the clients request is authenticated or not.
|
||||||
AWSSecureTransport = "aws:SecureTransport"
|
AWSSecureTransport Key = "aws:SecureTransport"
|
||||||
|
|
||||||
// AWSCurrentTime - key representing the current time.
|
// AWSCurrentTime - key representing the current time.
|
||||||
AWSCurrentTime = "aws:CurrentTime"
|
AWSCurrentTime Key = "aws:CurrentTime"
|
||||||
|
|
||||||
// AWSEpochTime - key representing the current epoch time.
|
// AWSEpochTime - key representing the current epoch time.
|
||||||
AWSEpochTime = "aws:EpochTime"
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AllSupportedKeys - is list of all all supported keys.
|
// AllSupportedKeys - is list of all all supported keys.
|
||||||
@ -95,6 +104,9 @@ var AllSupportedKeys = []Key{
|
|||||||
AWSSecureTransport,
|
AWSSecureTransport,
|
||||||
AWSCurrentTime,
|
AWSCurrentTime,
|
||||||
AWSEpochTime,
|
AWSEpochTime,
|
||||||
|
AWSPrincipalType,
|
||||||
|
AWSUserID,
|
||||||
|
AWSUsername,
|
||||||
// Add new supported condition keys.
|
// Add new supported condition keys.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +118,21 @@ var CommonKeys = []Key{
|
|||||||
AWSSecureTransport,
|
AWSSecureTransport,
|
||||||
AWSCurrentTime,
|
AWSCurrentTime,
|
||||||
AWSEpochTime,
|
AWSEpochTime,
|
||||||
|
AWSPrincipalType,
|
||||||
|
AWSUserID,
|
||||||
|
AWSUsername,
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// IsValid - checks if key is valid or not.
|
||||||
@ -128,6 +155,11 @@ func (key Key) MarshalJSON() ([]byte, error) {
|
|||||||
return json.Marshal(string(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:"
|
// Name - returns key name which is stripped value of prefixes "aws:" and "s3:"
|
||||||
func (key Key) Name() string {
|
func (key Key) Name() string {
|
||||||
keyString := string(key)
|
keyString := string(key)
|
||||||
|
@ -50,7 +50,8 @@ func (f stringEqualsFunc) evaluate(values map[string][]string) bool {
|
|||||||
requestValue = values[f.k.Name()]
|
requestValue = values[f.k.Name()]
|
||||||
}
|
}
|
||||||
|
|
||||||
return !f.values.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||||
|
return !fvalues.Intersection(set.CreateStringSet(requestValue...)).IsEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// key() - returns condition key which is used by this condition function.
|
// key() - returns condition key which is used by this condition function.
|
||||||
|
@ -50,11 +50,14 @@ func (f stringEqualsIgnoreCaseFunc) evaluate(values map[string][]string) bool {
|
|||||||
requestValue = values[f.k.Name()]
|
requestValue = values[f.k.Name()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||||
|
|
||||||
for _, v := range requestValue {
|
for _, v := range requestValue {
|
||||||
if !f.values.FuncMatch(strings.EqualFold, v).IsEmpty() {
|
if !fvalues.FuncMatch(strings.EqualFold, v).IsEmpty() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +51,10 @@ func (f stringLikeFunc) evaluate(values map[string][]string) bool {
|
|||||||
requestValue = values[f.k.Name()]
|
requestValue = values[f.k.Name()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fvalues := f.values.ApplyFunc(substFuncFromValues(values))
|
||||||
|
|
||||||
for _, v := range requestValue {
|
for _, v := range requestValue {
|
||||||
if !f.values.FuncMatch(wildcard.Match, v).IsEmpty() {
|
if !fvalues.FuncMatch(wildcard.Match, v).IsEmpty() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/minio/minio/pkg/policy/condition"
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,8 +48,16 @@ func (r Resource) IsValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match - matches object name with resource pattern.
|
// Match - matches object name with resource pattern.
|
||||||
func (r Resource) Match(resource string) bool {
|
func (r Resource) Match(resource string, conditionValues map[string][]string) bool {
|
||||||
return wildcard.Match(r.Pattern, resource)
|
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.
|
// MarshalJSON - encodes Resource to JSON data.
|
||||||
|
@ -124,7 +124,7 @@ func TestResourceMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
result := testCase.resource.Match(testCase.objectName)
|
result := testCase.resource.Match(testCase.objectName, nil)
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
if result != testCase.expectedResult {
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||||
|
@ -81,9 +81,9 @@ func (resourceSet ResourceSet) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match - matches object name with anyone of resource pattern in resource set.
|
// Match - matches object name with anyone of resource pattern in resource set.
|
||||||
func (resourceSet ResourceSet) Match(resource string) bool {
|
func (resourceSet ResourceSet) Match(resource string, conditionValues map[string][]string) bool {
|
||||||
for r := range resourceSet {
|
for r := range resourceSet {
|
||||||
if r.Match(resource) {
|
if r.Match(resource, conditionValues) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ func TestResourceSetMatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
result := testCase.resourceSet.Match(testCase.resource)
|
result := testCase.resourceSet.Match(testCase.resource, nil)
|
||||||
|
|
||||||
if result != testCase.expectedResult {
|
if result != testCase.expectedResult {
|
||||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||||
|
@ -54,7 +54,7 @@ func (statement Statement) IsAllowed(args Args) bool {
|
|||||||
resource += args.ObjectName
|
resource += args.ObjectName
|
||||||
}
|
}
|
||||||
|
|
||||||
if !statement.Resources.Match(resource) {
|
if !statement.Resources.Match(resource, args.ConditionValues) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user