Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

graph: Set roles/actions in sharedByMe response #7703

Merged
merged 5 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 2 additions & 15 deletions services/graph/pkg/service/v0/rolemanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
// GetRoleDefinitions a list of permission roles than can be used when sharing with users or groups
func (g Graph) GetRoleDefinitions(w http.ResponseWriter, r *http.Request) {
render.Status(r, http.StatusOK)
render.JSON(w, r, getRoleDefinitionList(g.config.FilesSharing.EnableResharing))
render.JSON(w, r, unifiedrole.GetBuiltinRoleDefinitionList(g.config.FilesSharing.EnableResharing))
}

// GetRoleDefinition a permission role than can be used when sharing with users or groups
Expand All @@ -37,21 +37,8 @@ func (g Graph) GetRoleDefinition(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, role)
}

func getRoleDefinitionList(resharing bool) []*libregraph.UnifiedRoleDefinition {
return []*libregraph.UnifiedRoleDefinition{
unifiedrole.NewViewerUnifiedRole(resharing),
unifiedrole.NewSpaceViewerUnifiedRole(),
unifiedrole.NewEditorUnifiedRole(resharing),
unifiedrole.NewSpaceEditorUnifiedRole(),
unifiedrole.NewFileEditorUnifiedRole(resharing),
unifiedrole.NewCoownerUnifiedRole(),
unifiedrole.NewUploaderUnifiedRole(),
unifiedrole.NewManagerUnifiedRole(),
}
}

func getRoleDefinition(roleID string, resharing bool) (*libregraph.UnifiedRoleDefinition, error) {
roleList := getRoleDefinitionList(resharing)
roleList := unifiedrole.GetBuiltinRoleDefinitionList(resharing)
for _, role := range roleList {
if role != nil && role.Id != nil && *role.Id == roleID {
return role, nil
Expand Down
14 changes: 13 additions & 1 deletion services/graph/pkg/service/v0/sharedbyme.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/services/graph/pkg/identity"
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
)

type driveItemsByResourceID map[string]libregraph.DriveItem
Expand Down Expand Up @@ -160,7 +161,18 @@ func (g Graph) cs3UserSharesToDriveItems(ctx context.Context, shares []*collabor
if s.GetExpiration() != nil {
perm.SetExpirationDateTime(cs3TimestampToTime(s.GetExpiration()))
}

role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(
*s.GetPermissions().GetPermissions(),
unifiedrole.UnifiedRoleConditionGrantee,
g.config.FilesSharing.EnableResharing,
)
if role != nil {
perm.SetRoles([]string{role.GetId()})
} else {
actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*s.GetPermissions().GetPermissions())
perm.SetLibreGraphPermissionsActions(actions)
perm.SetRoles(nil)
}
perm.SetGrantedToV2(grantedTo)
item.Permissions = append(item.Permissions, perm)
driveItems[resIDStr] = item
Expand Down
22 changes: 22 additions & 0 deletions services/graph/pkg/service/v0/sharedbyme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/conversions"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/storagespace"
Expand All @@ -28,6 +29,7 @@ import (
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
"github.com/stretchr/testify/mock"
"google.golang.org/grpc"
)
Expand All @@ -45,6 +47,8 @@ var _ = Describe("sharedbyme", func() {
rr *httptest.ResponseRecorder
)
expiration := time.Now()

editorResourcePermissions := conversions.NewEditorRole(true).CS3ResourcePermissions()
userShare := collaboration.Share{
Id: &collaboration.ShareId{
OpaqueId: "share-id",
Expand All @@ -62,6 +66,9 @@ var _ = Describe("sharedbyme", func() {
},
},
},
Permissions: &collaboration.SharePermissions{
Permissions: editorResourcePermissions,
},
}
groupShare := collaboration.Share{
Id: &collaboration.ShareId{
Expand All @@ -80,6 +87,9 @@ var _ = Describe("sharedbyme", func() {
},
},
},
Permissions: &collaboration.SharePermissions{
Permissions: editorResourcePermissions,
},
}
userShareWithExpiration := collaboration.Share{
Id: &collaboration.ShareId{
Expand All @@ -98,6 +108,9 @@ var _ = Describe("sharedbyme", func() {
},
},
},
Permissions: &collaboration.SharePermissions{
Permissions: editorResourcePermissions,
},
Expiration: utils.TimeToTS(expiration),
}

Expand Down Expand Up @@ -218,6 +231,7 @@ var _ = Describe("sharedbyme", func() {
cfg.TokenManager.JWTSecret = "loremipsum"
cfg.Commons = &shared.Commons{}
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
cfg.FilesSharing.EnableResharing = true

svc, _ = service.NewService(
service.Config(cfg),
Expand Down Expand Up @@ -323,6 +337,10 @@ var _ = Describe("sharedbyme", func() {
Expect(user.GetId()).To(Equal(userShare.GetGrantee().GetUserId().GetOpaqueId()))
_, ok = perm[0].GetLinkOk()
Expect(ok).To(BeFalse())
roles, ok := perm[0].GetRolesOk()
Expect(ok).To(BeTrue())
Expect(len(roles)).To(Equal(1))
Expect(roles[0]).To(Equal(unifiedrole.UnifiedRoleEditorID))
})

It("returns a proper driveItem, when a single group share is returned", func() {
Expand Down Expand Up @@ -363,6 +381,10 @@ var _ = Describe("sharedbyme", func() {
Expect(group.GetId()).To(Equal(groupShare.GetGrantee().GetGroupId().GetOpaqueId()))
_, ok = perm[0].GetLinkOk()
Expect(ok).To(BeFalse())
roles, ok := perm[0].GetRolesOk()
Expect(ok).To(BeTrue())
Expect(len(roles)).To(Equal(1))
Expect(roles[0]).To(Equal(unifiedrole.UnifiedRoleEditorID))
})

It("returns a single driveItem, when a mulitple shares for the same resource are returned", func() {
Expand Down
191 changes: 137 additions & 54 deletions services/graph/pkg/unifiedrole/unifiedrole.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package unifiedrole
micbar marked this conversation as resolved.
Show resolved Hide resolved

import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/conversions"
libregraph "github.com/owncloud/libre-graph-api-go"
"google.golang.org/protobuf/proto"
Expand All @@ -24,12 +25,32 @@ const (
// UnifiedRoleManagerID Unified role manager id.
UnifiedRoleManagerID = "312c0871-5ef7-4b3a-85b6-0e4074c64049"

// UnifiedRoleConditionSelf TODO defines constraints
UnifiedRoleConditionSelf = "Self: @Subject.objectId == @Resource.objectId"
// UnifiedRoleConditionSelf defines constraint where the principal matches the target resource
UnifiedRoleConditionSelf = "@Subject.objectId == @Resource.objectId"
// UnifiedRoleConditionOwner defines constraints when the principal is the owner of the target resource
UnifiedRoleConditionOwner = "Owner: @Subject.objectId Any_of @Resource.owners"
UnifiedRoleConditionOwner = "@Subject.objectId Any_of @Resource.owners"
// UnifiedRoleConditionGrantee does not exist in MS Graph, but we use it to express permissions on shared resources
UnifiedRoleConditionGrantee = "Grantee: @Subject.objectId Any_of @Resource.grantee"
UnifiedRoleConditionGrantee = "@Subject.objectId Any_of @Resource.grantee"

DriveItemPermissionsCreate = "libre.graph/driveItem/permissions/create"
DriveItemChildrenCreate = "libre.graph/driveItem/children/create"
DriveItemStandardDelete = "libre.graph/driveItem/standard/delete"
DriveItemPathRead = "libre.graph/driveItem/path/read"
DriveItemQuotaRead = "libre.graph/driveItem/quota/read"
DriveItemContentRead = "libre.graph/driveItem/content/read"
DriveItemUploadCreate = "libre.graph/driveItem/upload/create"
DriveItemPermissionsRead = "libre.graph/driveItem/permissions/read"
DriveItemChildrenRead = "libre.graph/driveItem/children/read"
DriveItemVersionsRead = "libre.graph/driveItem/versions/read"
DriveItemDeletedRead = "libre.graph/driveItem/deleted/read"
DriveItemPathUpdate = "libre.graph/driveItem/path/update"
DriveItemPermissionsDelete = "libre.graph/driveItem/permissions/delete"
DriveItemDeletedDelete = "libre.graph/driveItem/deleted/delete"
DriveItemVersionsUpdate = "libre.graph/driveItem/versions/update"
DriveItemDeletedUpdate = "libre.graph/driveItem/deleted/update"
DriveItemBasicRead = "libre.graph/driveItem/basic/read"
DriveItemPermissionsUpdate = "libre.graph/driveItem/permissions/update"
DriveItemPermissionsDeny = "libre.graph/driveItem/permissions/deny"
)

// NewViewerUnifiedRole creates a viewer role. `sharing` indicates if sharing permission should be added
Expand Down Expand Up @@ -168,96 +189,158 @@ func NewManagerUnifiedRole() *libregraph.UnifiedRoleDefinition {
}
}

func displayName(role *conversions.Role) *string {
if role == nil {
return nil
func GetBuiltinRoleDefinitionList(resharing bool) []*libregraph.UnifiedRoleDefinition {
return []*libregraph.UnifiedRoleDefinition{
NewViewerUnifiedRole(resharing),
NewSpaceViewerUnifiedRole(),
NewEditorUnifiedRole(resharing),
NewSpaceEditorUnifiedRole(),
NewFileEditorUnifiedRole(resharing),
NewCoownerUnifiedRole(),
NewUploaderUnifiedRole(),
NewManagerUnifiedRole(),
}
var displayName string
switch role.Name {
case conversions.RoleViewer:
displayName = "Viewer"
case conversions.RoleSpaceViewer:
displayName = "Space Viewer"
case conversions.RoleEditor:
displayName = "Editor"
case conversions.RoleSpaceEditor:
displayName = "Space Editor"
case conversions.RoleFileEditor:
displayName = "File Editor"
case conversions.RoleCoowner:
displayName = "Co Owner"
case conversions.RoleUploader:
displayName = "Uploader"
case conversions.RoleManager:
displayName = "Manager"
default:
return nil
}
return proto.String(displayName)
}

func convert(role *conversions.Role) []string {
actions := make([]string, 0, 8)
if role == nil && role.CS3ResourcePermissions() == nil {
return actions
}
p := role.CS3ResourcePermissions()
// CS3ResourcePermissionsToLibregraphActions converts the provided cs3 ResourcePermissions to a list of
// libregraph actions
func CS3ResourcePermissionsToLibregraphActions(p provider.ResourcePermissions) (actions []string) {
if p.AddGrant {
actions = append(actions, "libre.graph/driveItem/permissions/create")
actions = append(actions, DriveItemPermissionsCreate)
}
if p.CreateContainer {
actions = append(actions, "libre.graph/driveItem/children/create")
actions = append(actions, DriveItemChildrenCreate)
}
if p.Delete {
actions = append(actions, "libre.graph/driveItem/standard/delete")
actions = append(actions, DriveItemStandardDelete)
}
if p.GetPath {
actions = append(actions, "libre.graph/driveItem/path/read")
actions = append(actions, DriveItemPathRead)
}
if p.GetQuota {
actions = append(actions, "libre.graph/driveItem/quota/read")
actions = append(actions, DriveItemQuotaRead)
}
if p.InitiateFileDownload {
actions = append(actions, "libre.graph/driveItem/content/read")
actions = append(actions, DriveItemContentRead)
}
if p.InitiateFileUpload {
actions = append(actions, "libre.graph/driveItem/upload/create")
actions = append(actions, DriveItemUploadCreate)
}
if p.ListGrants {
actions = append(actions, "libre.graph/driveItem/permissions/read")
actions = append(actions, DriveItemPermissionsRead)
}
if p.ListContainer {
actions = append(actions, "libre.graph/driveItem/children/read")
actions = append(actions, DriveItemChildrenRead)
}
if p.ListFileVersions {
actions = append(actions, "libre.graph/driveItem/versions/read")
actions = append(actions, DriveItemVersionsRead)
}
if p.ListRecycle {
actions = append(actions, "libre.graph/driveItem/deleted/read")
actions = append(actions, DriveItemDeletedRead)
}
if p.Move {
actions = append(actions, "libre.graph/driveItem/path/update")
actions = append(actions, DriveItemPathUpdate)
}
if p.RemoveGrant {
actions = append(actions, "libre.graph/driveItem/permissions/delete")
actions = append(actions, DriveItemPermissionsDelete)
}
if p.PurgeRecycle {
actions = append(actions, "libre.graph/driveItem/deleted/delete")
actions = append(actions, DriveItemDeletedDelete)
}
if p.RestoreFileVersion {
actions = append(actions, "libre.graph/driveItem/versions/update")
actions = append(actions, DriveItemVersionsUpdate)
}
if p.RestoreRecycleItem {
actions = append(actions, "libre.graph/driveItem/deleted/update")
actions = append(actions, DriveItemDeletedUpdate)
}
if p.Stat {
actions = append(actions, "libre.graph/driveItem/basic/read")
actions = append(actions, DriveItemBasicRead)
}
if p.UpdateGrant {
actions = append(actions, "libre.graph/driveItem/permissions/update")
actions = append(actions, DriveItemPermissionsUpdate)
}
if p.DenyGrant {
actions = append(actions, "libre.graph/driveItem/permissions/deny")
actions = append(actions, DriveItemPermissionsDeny)
}
return actions
}

// CS3ResourcePermissionsToUnifiedRole tries to find the UnifiedRoleDefinition that matches the supplied
// CS3 ResourcePermissions and constraints.
func CS3ResourcePermissionsToUnifiedRole(p provider.ResourcePermissions, constraints string, resharing bool) *libregraph.UnifiedRoleDefinition {
actionSet := map[string]struct{}{}
for _, action := range CS3ResourcePermissionsToLibregraphActions(p) {
actionSet[action] = struct{}{}
}

var res *libregraph.UnifiedRoleDefinition
for _, uRole := range GetBuiltinRoleDefinitionList(resharing) {
matchFound := false
for _, uPerm := range uRole.GetRolePermissions() {
if uPerm.GetCondition() != constraints {
// the requested constraints don't match, this isn't our role
continue
}

// if the actions converted from the ResourcePermissions equal the action the defined for the role, we have match
if resourceActionsEqual(actionSet, uPerm.GetAllowedResourceActions()) {
matchFound = true
break
}
}
if matchFound {
res = uRole
break
}
}
return res
}

func resourceActionsEqual(targetActionSet map[string]struct{}, actions []string) bool {
if len(targetActionSet) != len(actions) {
return false
}

for _, action := range actions {
if _, ok := targetActionSet[action]; !ok {
return false
}
}
return true
}

func displayName(role *conversions.Role) *string {
if role == nil {
return nil
}
var displayName string
switch role.Name {
case conversions.RoleViewer:
displayName = "Viewer"
case conversions.RoleSpaceViewer:
displayName = "Space Viewer"
case conversions.RoleEditor:
displayName = "Editor"
case conversions.RoleSpaceEditor:
displayName = "Space Editor"
case conversions.RoleFileEditor:
displayName = "File Editor"
case conversions.RoleCoowner:
displayName = "Co Owner"
case conversions.RoleUploader:
displayName = "Uploader"
case conversions.RoleManager:
displayName = "Manager"
default:
return nil
}
return proto.String(displayName)
}

func convert(role *conversions.Role) []string {
actions := make([]string, 0, 8)
if role == nil && role.CS3ResourcePermissions() == nil {
return actions
}
return CS3ResourcePermissionsToLibregraphActions(*role.CS3ResourcePermissions())
}
Loading