diff --git a/internal/grpc/services/publicshareprovider/publicshareprovider.go b/internal/grpc/services/publicshareprovider/publicshareprovider.go index c993f30f610..8b605083b83 100644 --- a/internal/grpc/services/publicshareprovider/publicshareprovider.go +++ b/internal/grpc/services/publicshareprovider/publicshareprovider.go @@ -26,6 +26,7 @@ import ( link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/v2/pkg/appctx" + "github.com/cs3org/reva/v2/pkg/conversions" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/publicshare" @@ -140,6 +141,12 @@ func (s *service) CreatePublicShare(ctx context.Context, req *link.CreatePublicS log := appctx.GetLogger(ctx) log.Info().Str("publicshareprovider", "create").Msg("create public share") + if !conversions.SufficientCS3Permissions(req.GetResourceInfo().GetPermissionSet(), req.GetGrant().GetPermissions().GetPermissions()) { + return &link.CreatePublicShareResponse{ + Status: status.NewInvalid(ctx, "insufficient permissions to create that kind of share"), + }, nil + } + if !s.isPathAllowed(req.ResourceInfo.Path) { return &link.CreatePublicShareResponse{ Status: status.NewInvalid(ctx, "share creation is not allowed for the specified path"), diff --git a/pkg/conversions/role.go b/pkg/conversions/role.go index 6971957c8fa..7abd56343aa 100644 --- a/pkg/conversions/role.go +++ b/pkg/conversions/role.go @@ -21,6 +21,7 @@ package conversions import ( "fmt" + "reflect" "strings" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" @@ -508,3 +509,28 @@ func RoleFromResourcePermissions(rp *provider.ResourcePermissions, islink bool) // TODO what about even more granular cs3 permissions?, eg. only stat return r } + +// SufficientCS3Permissions returns true if the `existing` permissions contain the `requested` permissions +func SufficientCS3Permissions(existing, requested *provider.ResourcePermissions) bool { + // empty permissions represent a denial + if grants.PermissionsEqual(requested, &provider.ResourcePermissions{}) { + return existing.DenyGrant + } + requestedPermissionsType := reflect.TypeOf(provider.ResourcePermissions{}) + numFields := requestedPermissionsType.NumField() + reqestedPermissionsValues := reflect.ValueOf(requested) + existingPermissionsValues := reflect.ValueOf(existing) + + for i := 0; i < numFields; i++ { + permissionName := requestedPermissionsType.Field(i).Name + if strings.Contains(permissionName, "XXX") { + continue + } + existingPermission := reflect.Indirect(existingPermissionsValues).FieldByName(permissionName).Bool() + requestedPermission := reqestedPermissionsValues.Elem().Field(i).Bool() + if requestedPermission == true && existingPermission == false { + return false + } + } + return true +} diff --git a/pkg/conversions/role_test.go b/pkg/conversions/role_test.go new file mode 100644 index 00000000000..3ee9248fa8f --- /dev/null +++ b/pkg/conversions/role_test.go @@ -0,0 +1,92 @@ +package conversions + +import ( + "testing" + + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/stretchr/testify/assert" +) + +func TestSufficientPermissions(t *testing.T) { + type testData struct { + Existing *providerv1beta1.ResourcePermissions + Requested *providerv1beta1.ResourcePermissions + Sufficient bool + } + table := []testData{ + { + Existing: RoleFromName("editor", true).CS3ResourcePermissions(), + Requested: RoleFromName("viewer", true).CS3ResourcePermissions(), + Sufficient: true, + }, + { + Existing: RoleFromName("viewer", true).CS3ResourcePermissions(), + Requested: RoleFromName("editor", true).CS3ResourcePermissions(), + Sufficient: false, + }, + { + Existing: RoleFromName("spaceviewer", true).CS3ResourcePermissions(), + Requested: RoleFromName("spaceeditor", true).CS3ResourcePermissions(), + Sufficient: false, + }, + { + Existing: RoleFromName("manager", true).CS3ResourcePermissions(), + Requested: RoleFromName("spaceeditor", true).CS3ResourcePermissions(), + Sufficient: true, + }, + { + Existing: RoleFromName("manager", true).CS3ResourcePermissions(), + Requested: RoleFromName("spaceviewer", true).CS3ResourcePermissions(), + Sufficient: true, + }, + { + Existing: RoleFromName("manager", true).CS3ResourcePermissions(), + Requested: RoleFromName("manager", true).CS3ResourcePermissions(), + Sufficient: true, + }, + { + Existing: RoleFromName("manager", true).CS3ResourcePermissions(), + Requested: RoleFromName("denied", true).CS3ResourcePermissions(), + Sufficient: true, + }, + { + Existing: RoleFromName("spaceeditor", true).CS3ResourcePermissions(), + Requested: RoleFromName("denied", true).CS3ResourcePermissions(), + Sufficient: false, + }, + { + Existing: RoleFromName("editor", true).CS3ResourcePermissions(), + Requested: RoleFromName("denied", true).CS3ResourcePermissions(), + Sufficient: false, + }, + { + Existing: &providerv1beta1.ResourcePermissions{ + // all permissions, used for personal space owners + AddGrant: true, + CreateContainer: true, + Delete: true, + GetPath: true, + GetQuota: true, + InitiateFileDownload: true, + InitiateFileUpload: true, + ListContainer: true, + ListFileVersions: true, + ListGrants: true, + ListRecycle: true, + Move: true, + PurgeRecycle: true, + RemoveGrant: true, + RestoreFileVersion: true, + RestoreRecycleItem: true, + Stat: true, + UpdateGrant: true, + DenyGrant: true, + }, + Requested: RoleFromName("denied", true).CS3ResourcePermissions(), + Sufficient: true, + }, + } + for _, test := range table { + assert.Equal(t, test.Sufficient, SufficientCS3Permissions(test.Existing, test.Requested)) + } +}