Skip to content

Commit

Permalink
Merge pull request #3609 from kobergj/SpaceManagementPermissions
Browse files Browse the repository at this point in the history
Space Management Permissions
  • Loading branch information
kobergj authored Jan 25, 2023
2 parents 0cde0a3 + 23542ed commit 2d125a5
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 293 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/space-management-permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Space Management Permissions

Added new permissions to manage spaces: `manage space properties` and `disable spaces`

https://github.com/cs3org/reva/pull/3609
52 changes: 19 additions & 33 deletions pkg/storage/utils/decomposedfs/decomposedfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import (
"syscall"
"time"

cs3permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/appctx"
Expand All @@ -62,22 +61,11 @@ import (
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/go-micro/plugins/v4/events/natsjs"
"github.com/pkg/errors"
"google.golang.org/grpc"
)

// name is the Tracer name used to identify this instrumentation library.
const tracerName = "decomposedfs"

// PermissionsChecker defines an interface for checking permissions on a Node
type PermissionsChecker interface {
AssemblePermissions(ctx context.Context, n *node.Node) (ap provider.ResourcePermissions, err error)
}

// CS3PermissionsClient defines an interface for checking permissions against the CS3 permissions service
type CS3PermissionsClient interface {
CheckPermission(ctx context.Context, in *cs3permissions.CheckPermissionRequest, opts ...grpc.CallOption) (*cs3permissions.CheckPermissionResponse, error)
}

// Tree is used to manage a tree hierarchy
type Tree interface {
Setup() error
Expand All @@ -102,14 +90,13 @@ type Tree interface {

// Decomposedfs provides the base for decomposed filesystem implementations
type Decomposedfs struct {
lu *lookup.Lookup
tp Tree
o *options.Options
p PermissionsChecker
chunkHandler *chunking.ChunkHandler
permissionsClient CS3PermissionsClient
stream events.Stream
cache cache.StatCache
lu *lookup.Lookup
tp Tree
o *options.Options
p Permissions
chunkHandler *chunking.ChunkHandler
stream events.Stream
cache cache.StatCache
}

// NewDefault returns an instance with default components
Expand All @@ -120,8 +107,6 @@ func NewDefault(m map[string]interface{}, bs tree.Blobstore) (storage.FS, error)
}

lu := &lookup.Lookup{}
p := node.NewPermissions(lu)

lu.Options = o

tp := tree.New(o.Root, o.TreeTimeAccounting, o.TreeSizeAccounting, lu, bs)
Expand All @@ -131,6 +116,8 @@ func NewDefault(m map[string]interface{}, bs tree.Blobstore) (storage.FS, error)
return nil, err
}

permissions := NewPermissions(node.NewPermissions(lu), permissionsClient)

var es events.Stream
if o.Events.NatsAddress != "" {
evtsCfg := o.Events
Expand Down Expand Up @@ -169,12 +156,12 @@ func NewDefault(m map[string]interface{}, bs tree.Blobstore) (storage.FS, error)
}
}

return New(o, lu, p, tp, permissionsClient, es)
return New(o, lu, permissions, tp, es)
}

// New returns an implementation of the storage.FS interface that talks to
// a local filesystem.
func New(o *options.Options, lu *lookup.Lookup, p PermissionsChecker, tp Tree, permissionsClient CS3PermissionsClient, es events.Stream) (storage.FS, error) {
func New(o *options.Options, lu *lookup.Lookup, p Permissions, tp Tree, es events.Stream) (storage.FS, error) {
log := logger.New()
err := tp.Setup()
if err != nil {
Expand All @@ -191,14 +178,13 @@ func New(o *options.Options, lu *lookup.Lookup, p PermissionsChecker, tp Tree, p
}

fs := &Decomposedfs{
tp: tp,
lu: lu,
o: o,
p: p,
chunkHandler: chunking.NewChunkHandler(filepath.Join(o.Root, "uploads")),
permissionsClient: permissionsClient,
stream: es,
cache: cache.GetStatCache(o.StatCache.CacheStore, o.StatCache.CacheNodes, o.StatCache.CacheDatabase, "stat", 0),
tp: tp,
lu: lu,
o: o,
p: p,
chunkHandler: chunking.NewChunkHandler(filepath.Join(o.Root, "uploads")),
stream: es,
cache: cache.GetStatCache(o.StatCache.CacheStore, o.StatCache.CacheNodes, o.StatCache.CacheDatabase, "stat", 0),
}

if o.AsyncFileUploads {
Expand Down Expand Up @@ -448,7 +434,7 @@ func (fs *Decomposedfs) GetQuota(ctx context.Context, ref *provider.Reference) (
switch {
case err != nil:
return 0, 0, 0, errtypes.InternalError(err.Error())
case !rp.GetQuota && !fs.canListAllSpaces(ctx):
case !rp.GetQuota && !fs.p.ListAllSpaces(ctx):
f, _ := storagespace.FormatReference(ref)
if rp.Stat {
return 0, 0, 0, errtypes.PermissionDenied(f)
Expand Down
129 changes: 129 additions & 0 deletions pkg/storage/utils/decomposedfs/spacepermissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package decomposedfs

import (
"context"

cs3permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
"google.golang.org/grpc"
)

// PermissionsChecker defines an interface for checking permissions on a Node
type PermissionsChecker interface {
AssemblePermissions(ctx context.Context, n *node.Node) (ap provider.ResourcePermissions, err error)
}

// CS3PermissionsClient defines an interface for checking permissions against the CS3 permissions service
type CS3PermissionsClient interface {
CheckPermission(ctx context.Context, in *cs3permissions.CheckPermissionRequest, opts ...grpc.CallOption) (*cs3permissions.CheckPermissionResponse, error)
}

// Permissions manages permissions
type Permissions struct {
item PermissionsChecker // handles item permissions
space CS3PermissionsClient // handlers space permissions
}

// NewPermissions returns a new Permissions instance
func NewPermissions(item PermissionsChecker, space CS3PermissionsClient) Permissions {
return Permissions{item: item, space: space}
}

// AssemblePermissions is used to assemble file permissions
func (p Permissions) AssemblePermissions(ctx context.Context, n *node.Node) (provider.ResourcePermissions, error) {
return p.item.AssemblePermissions(ctx, n)
}

// CreateSpace returns true when the user is allowed to create the space
func (p Permissions) CreateSpace(ctx context.Context, spaceid string) bool {
return p.checkPermission(ctx, "create-space", spaceRef(spaceid))
}

// SetSpaceQuota returns true when the user is allowed to change the spaces quota
func (p Permissions) SetSpaceQuota(ctx context.Context, spaceid string) bool {
return p.checkPermission(ctx, "set-space-quota", spaceRef(spaceid))
}

// ManageSpaceProperties returns true when the user is allowed to change space properties (name/subtitle)
func (p Permissions) ManageSpaceProperties(ctx context.Context, spaceid string) bool {
return p.checkPermission(ctx, "Drive.ReadWrite", spaceRef(spaceid))
}

// SpaceAbility returns true when the user is allowed to enable/disable the space
func (p Permissions) SpaceAbility(ctx context.Context, spaceid string) bool {
return p.checkPermission(ctx, "Drive.ReadWriteEnabled", spaceRef(spaceid))
}

// ListAllSpaces returns true when the user is allowed to list all spaces
func (p Permissions) ListAllSpaces(ctx context.Context) bool {
return p.checkPermission(ctx, "list-all-spaces", nil)
}

// ListSpacesOfUser returns true when the user is allowed to list the spaces of the given user
func (p Permissions) ListSpacesOfUser(ctx context.Context, userid string) bool {
switch userid {
case userIDAny:
// there is no filter
return true // TODO: is `true` actually correct here? Shouldn't we check for ListAllSpaces too?
case ctxpkg.ContextMustGetUser(ctx).GetId().GetOpaqueId():
return true
default:
return p.ListAllSpaces(ctx)
}
}

// DeleteAllSpaces returns true when the user is allowed to delete all spaces
func (p Permissions) DeleteAllSpaces(ctx context.Context) bool {
return p.checkPermission(ctx, "delete-all-spaces", nil)
}

// DeleteAllHomeSpaces returns true when the user is allowed to delete all home spaces
func (p Permissions) DeleteAllHomeSpaces(ctx context.Context) bool {
return p.checkPermission(ctx, "delete-all-home-spaces", nil)
}

// checkPermission is used to check a users space permissions
func (p Permissions) checkPermission(ctx context.Context, perm string, ref *provider.Reference) bool {
user := ctxpkg.ContextMustGetUser(ctx)
checkRes, err := p.space.CheckPermission(ctx, &cs3permissions.CheckPermissionRequest{
Permission: perm,
SubjectRef: &cs3permissions.SubjectReference{
Spec: &cs3permissions.SubjectReference_UserId{
UserId: user.Id,
},
},
Ref: ref,
})
if err != nil {
return false
}

return checkRes.Status.Code == v1beta11.Code_CODE_OK
}

// IsManager returns true if the given resource permissions evaluate the user as "manager"
func IsManager(rp provider.ResourcePermissions) bool {
return rp.RemoveGrant
}

// IsEditor returns true if the given resource permissions evaluate the user as "editor"
func IsEditor(rp provider.ResourcePermissions) bool {
return rp.InitiateFileUpload
}

// IsViewer returns true if the given resource permissions evaluate the user as "viewer"
func IsViewer(rp provider.ResourcePermissions) bool {
return rp.Stat
}

func spaceRef(spaceid string) *provider.Reference {
return &provider.Reference{
ResourceId: &provider.ResourceId{
StorageId: spaceid,
// OpaqueId is the same, no need to transfer it
},
}
}
Loading

0 comments on commit 2d125a5

Please sign in to comment.