github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/permissions/spacepermissions.go (about)

     1  package permissions
     2  
     3  import (
     4  	"context"
     5  
     6  	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
     7  	cs3permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
     8  	v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
     9  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    10  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    11  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    12  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    13  	"github.com/cs3org/reva/v2/pkg/utils"
    14  	"go.opentelemetry.io/otel"
    15  	"go.opentelemetry.io/otel/trace"
    16  	"google.golang.org/grpc"
    17  )
    18  
    19  var (
    20  	tracer trace.Tracer
    21  )
    22  
    23  func init() {
    24  	tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs/permissions")
    25  }
    26  
    27  const (
    28  	_spaceTypePersonal = "personal"
    29  	_spaceTypeProject  = "project"
    30  )
    31  
    32  // PermissionsChecker defines an interface for checking permissions on a Node
    33  type PermissionsChecker interface {
    34  	AssemblePermissions(ctx context.Context, n *node.Node) (ap *provider.ResourcePermissions, err error)
    35  	AssembleTrashPermissions(ctx context.Context, n *node.Node) (ap *provider.ResourcePermissions, err error)
    36  }
    37  
    38  // CS3PermissionsClient defines an interface for checking permissions against the CS3 permissions service
    39  type CS3PermissionsClient interface {
    40  	CheckPermission(ctx context.Context, in *cs3permissions.CheckPermissionRequest, opts ...grpc.CallOption) (*cs3permissions.CheckPermissionResponse, error)
    41  }
    42  
    43  // Permissions manages permissions
    44  type Permissions struct {
    45  	item                PermissionsChecker                                   // handles item permissions
    46  	permissionsSelector pool.Selectable[cs3permissions.PermissionsAPIClient] // handlers space permissions
    47  }
    48  
    49  // NewPermissions returns a new Permissions instance
    50  func NewPermissions(item PermissionsChecker, permissionsSelector pool.Selectable[cs3permissions.PermissionsAPIClient]) Permissions {
    51  	return Permissions{item: item, permissionsSelector: permissionsSelector}
    52  }
    53  
    54  // AssemblePermissions is used to assemble file permissions
    55  func (p Permissions) AssemblePermissions(ctx context.Context, n *node.Node) (*provider.ResourcePermissions, error) {
    56  	ctx, span := tracer.Start(ctx, "AssemblePermissions")
    57  	defer span.End()
    58  	return p.item.AssemblePermissions(ctx, n)
    59  }
    60  
    61  // AssembleTrashPermissions is used to assemble file permissions
    62  func (p Permissions) AssembleTrashPermissions(ctx context.Context, n *node.Node) (*provider.ResourcePermissions, error) {
    63  	_, span := tracer.Start(ctx, "AssembleTrashPermissions")
    64  	defer span.End()
    65  	return p.item.AssembleTrashPermissions(ctx, n)
    66  }
    67  
    68  // CreateSpace returns true when the user is allowed to create the space
    69  func (p Permissions) CreateSpace(ctx context.Context, spaceid string) bool {
    70  	return p.checkPermission(ctx, "Drives.Create", spaceRef(spaceid))
    71  }
    72  
    73  // SetSpaceQuota returns true when the user is allowed to change the spaces quota
    74  func (p Permissions) SetSpaceQuota(ctx context.Context, spaceid string, spaceType string) bool {
    75  	switch spaceType {
    76  	default:
    77  		return false // only quotas of personal and project space may be changed
    78  	case _spaceTypePersonal:
    79  		return p.checkPermission(ctx, "Drives.ReadWritePersonalQuota", spaceRef(spaceid))
    80  	case _spaceTypeProject:
    81  		return p.checkPermission(ctx, "Drives.ReadWriteProjectQuota", spaceRef(spaceid))
    82  	}
    83  }
    84  
    85  // ManageSpaceProperties returns true when the user is allowed to change space properties (name/subtitle)
    86  func (p Permissions) ManageSpaceProperties(ctx context.Context, spaceid string) bool {
    87  	return p.checkPermission(ctx, "Drives.ReadWrite", spaceRef(spaceid))
    88  }
    89  
    90  // SpaceAbility returns true when the user is allowed to enable/disable the space
    91  func (p Permissions) SpaceAbility(ctx context.Context, spaceid string) bool {
    92  	return p.checkPermission(ctx, "Drives.ReadWriteEnabled", spaceRef(spaceid))
    93  }
    94  
    95  // ListAllSpaces returns true when the user is allowed to list all spaces
    96  func (p Permissions) ListAllSpaces(ctx context.Context) bool {
    97  	return p.checkPermission(ctx, "Drives.List", nil)
    98  }
    99  
   100  // ListSpacesOfUser returns true when the user is allowed to list the spaces of the given user
   101  func (p Permissions) ListSpacesOfUser(ctx context.Context, userid *userv1beta1.UserId) bool {
   102  	switch {
   103  	case userid == nil:
   104  		// there is no filter
   105  		return true // TODO: is `true` actually correct here? Shouldn't we check for ListAllSpaces too?
   106  	case utils.UserIDEqual(ctxpkg.ContextMustGetUser(ctx).GetId(), userid):
   107  		return true
   108  	default:
   109  		return p.ListAllSpaces(ctx)
   110  	}
   111  }
   112  
   113  // DeleteAllSpaces returns true when the user is allowed to delete all spaces
   114  func (p Permissions) DeleteAllSpaces(ctx context.Context) bool {
   115  	return p.checkPermission(ctx, "Drives.DeleteProject", nil)
   116  }
   117  
   118  // DeleteAllHomeSpaces returns true when the user is allowed to delete all home spaces
   119  func (p Permissions) DeleteAllHomeSpaces(ctx context.Context) bool {
   120  	return p.checkPermission(ctx, "Drives.DeletePersonal", nil)
   121  }
   122  
   123  // checkPermission is used to check a users space permissions
   124  func (p Permissions) checkPermission(ctx context.Context, perm string, ref *provider.Reference) bool {
   125  	permissionsClient, err := p.permissionsSelector.Next()
   126  	if err != nil {
   127  		return false
   128  	}
   129  
   130  	user := ctxpkg.ContextMustGetUser(ctx)
   131  	checkRes, err := permissionsClient.CheckPermission(ctx, &cs3permissions.CheckPermissionRequest{
   132  		Permission: perm,
   133  		SubjectRef: &cs3permissions.SubjectReference{
   134  			Spec: &cs3permissions.SubjectReference_UserId{
   135  				UserId: user.Id,
   136  			},
   137  		},
   138  		Ref: ref,
   139  	})
   140  	if err != nil {
   141  		return false
   142  	}
   143  
   144  	return checkRes.Status.Code == v1beta11.Code_CODE_OK
   145  }
   146  
   147  // IsManager returns true if the given resource permissions evaluate the user as "manager"
   148  func IsManager(rp *provider.ResourcePermissions) bool {
   149  	return rp.RemoveGrant
   150  }
   151  
   152  // IsEditor returns true if the given resource permissions evaluate the user as "editor"
   153  func IsEditor(rp *provider.ResourcePermissions) bool {
   154  	return rp.InitiateFileUpload
   155  }
   156  
   157  // IsViewer returns true if the given resource permissions evaluate the user as "viewer"
   158  func IsViewer(rp *provider.ResourcePermissions) bool {
   159  	return rp.Stat
   160  }
   161  
   162  func spaceRef(spaceid string) *provider.Reference {
   163  	return &provider.Reference{
   164  		ResourceId: &provider.ResourceId{
   165  			StorageId: spaceid,
   166  			// OpaqueId is the same, no need to transfer it
   167  		},
   168  	}
   169  }