wildcard prefix to restrict access to directories in s3 bucket

https://github.com/chrislusf/seaweedfs/discussions/2551
This commit is contained in:
chrislu 2022-01-03 15:39:36 -08:00
parent 5799a20f71
commit a7887166cf
4 changed files with 58 additions and 10 deletions

View file

@ -247,9 +247,9 @@ func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action)
glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action) glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action)
bucket, _ := xhttp.GetBucketAndObject(r) bucket, object := xhttp.GetBucketAndObject(r)
if !identity.canDo(action, bucket) { if !identity.canDo(action, bucket, object) {
return identity, s3err.ErrAccessDenied return identity, s3err.ErrAccessDenied
} }
@ -307,7 +307,7 @@ func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err
return identity, s3err.ErrNone return identity, s3err.ErrNone
} }
func (identity *Identity) canDo(action Action, bucket string) bool { func (identity *Identity) canDo(action Action, bucket string, objectKey string) bool {
if identity.isAdmin() { if identity.isAdmin() {
return true return true
} }
@ -319,15 +319,13 @@ func (identity *Identity) canDo(action Action, bucket string) bool {
if bucket == "" { if bucket == "" {
return false return false
} }
target := string(action) + ":" + bucket + "/" + objectKey
limitedByBucket := string(action) + ":" + bucket limitedByBucket := string(action) + ":" + bucket
adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
for _, a := range identity.Actions { for _, a := range identity.Actions {
act := string(a) act := string(a)
if strings.HasSuffix(act, "*") { if strings.HasSuffix(act, "*") {
if strings.HasPrefix(limitedByBucket, act[:len(act)-1]) { if strings.HasPrefix(target, act[:len(act)-1]) {
return true
}
if strings.HasPrefix(adminLimitedByBucket, act[:len(act)-1]) {
return true return true
} }
} else { } else {

View file

@ -2,6 +2,7 @@ package s3api
import ( import (
. "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants" . "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
"github.com/stretchr/testify/assert"
"testing" "testing"
"github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/jsonpb"
@ -67,3 +68,51 @@ func TestIdentityListFileFormat(t *testing.T) {
println(text) println(text)
} }
func TestCanDo(t *testing.T) {
ident1 := &Identity{
Name: "anything",
Actions: []Action{
"Write:bucket1/a/b/c/*",
"Write:bucket1/a/b/other",
},
}
// object specific
assert.Equal(t, true, ident1.canDo(ACTION_WRITE, "bucket1", "a/b/c/d.txt"))
assert.Equal(t, false, ident1.canDo(ACTION_WRITE, "bucket1", "a/b/other/some"), "action without *")
// bucket specific
ident2 := &Identity{
Name: "anything",
Actions: []Action{
"Read:bucket1",
"Write:bucket1/*",
},
}
assert.Equal(t, true, ident2.canDo(ACTION_READ, "bucket1", "a/b/c/d.txt"))
assert.Equal(t, true, ident2.canDo(ACTION_WRITE, "bucket1", "a/b/c/d.txt"))
assert.Equal(t, false, ident2.canDo(ACTION_LIST, "bucket1", "a/b/c/d.txt"))
// across buckets
ident3 := &Identity{
Name: "anything",
Actions: []Action{
"Read",
"Write",
},
}
assert.Equal(t, true, ident3.canDo(ACTION_READ, "bucket1", "a/b/c/d.txt"))
assert.Equal(t, true, ident3.canDo(ACTION_WRITE, "bucket1", "a/b/c/d.txt"))
assert.Equal(t, false, ident3.canDo(ACTION_LIST, "bucket1", "a/b/other/some"))
// partial buckets
ident4 := &Identity{
Name: "anything",
Actions: []Action{
"Read:special_*",
},
}
assert.Equal(t, true, ident4.canDo(ACTION_READ, "special_bucket", "a/b/c/d.txt"))
assert.Equal(t, false, ident4.canDo(ACTION_READ, "bucket1", "a/b/c/d.txt"))
}

View file

@ -25,6 +25,7 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http" xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
"github.com/chrislusf/seaweedfs/weed/s3api/s3err" "github.com/chrislusf/seaweedfs/weed/s3api/s3err"
"hash" "hash"
"io" "io"
@ -91,8 +92,8 @@ func (iam *IdentityAccessManagement) calculateSeedSignature(r *http.Request) (cr
return nil, "", "", time.Time{}, s3err.ErrInvalidAccessKeyID return nil, "", "", time.Time{}, s3err.ErrInvalidAccessKeyID
} }
bucket, _ := xhttp.GetBucketAndObject(r) bucket, object := xhttp.GetBucketAndObject(r)
if !identity.canDo("Write", bucket) { if !identity.canDo(s3_constants.ACTION_WRITE, bucket, object) {
errCode = s3err.ErrAccessDenied errCode = s3err.ErrAccessDenied
return return
} }

View file

@ -55,7 +55,7 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques
var buckets []*s3.Bucket var buckets []*s3.Bucket
for _, entry := range entries { for _, entry := range entries {
if entry.IsDirectory { if entry.IsDirectory {
if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name) { if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name, "") {
continue continue
} }
buckets = append(buckets, &s3.Bucket{ buckets = append(buckets, &s3.Bucket{