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

List OCM permissions as graph drive item permissions #9905

Merged
merged 1 commit into from
Aug 23, 2024
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
6 changes: 6 additions & 0 deletions changelog/unreleased/list-ocm-permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bugfix: List OCM permissions as graph drive item permissions

The libre graph API now returns OCM shares when listing driveItem permissions.

https://github.com/owncloud/ocis/pull/9905
https://github.com/owncloud/ocis/issues/9898
11 changes: 11 additions & 0 deletions services/graph/pkg/service/v0/api_driveitem_permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,17 @@ func (s DriveItemPermissionsService) ListPermissions(ctx context.Context, itemID
if err != nil {
return collectionOfPermissions, err
}
if s.config.IncludeOCMSharees {
driveItems, err = s.listOCMShares(ctx, []*ocm.ListOCMSharesRequest_Filter{
{
Type: ocm.ListOCMSharesRequest_Filter_TYPE_RESOURCE_ID,
Term: &ocm.ListOCMSharesRequest_Filter_ResourceId{ResourceId: itemID},
},
}, driveItems)
if err != nil {
return collectionOfPermissions, err
}
}
}
// finally get public shares, which are possible for spaceroots and "normal" resources
driveItems, err = s.listPublicShares(ctx, []*link.ListPublicSharesRequest_Filter{
Expand Down
147 changes: 147 additions & 0 deletions services/graph/pkg/service/v0/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,34 @@ func (g BaseGraphService) listUserShares(ctx context.Context, filters []*collabo
return driveItems, nil
}

func (g BaseGraphService) listOCMShares(ctx context.Context, filters []*ocm.ListOCMSharesRequest_Filter, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
g.logger.Error().Err(err).Msg("could not select next gateway client")
return driveItems, errorcode.New(errorcode.GeneralException, err.Error())
}

concreteFilters := []*ocm.ListOCMSharesRequest_Filter{}
concreteFilters = append(concreteFilters, filters...)

lsOCMSharesRequest := ocm.ListOCMSharesRequest{
Filters: concreteFilters,
}

lsOCMSharesResponse, err := gatewayClient.ListOCMShares(ctx, &lsOCMSharesRequest)
if err != nil {
return driveItems, errorcode.New(errorcode.GeneralException, err.Error())
}
if statusCode := lsOCMSharesResponse.GetStatus().GetCode(); statusCode != rpc.Code_CODE_OK {
return driveItems, errorcode.New(cs3StatusToErrCode(statusCode), lsOCMSharesResponse.Status.Message)
}
driveItems, err = g.cs3OCMSharesToDriveItems(ctx, lsOCMSharesResponse.Shares, driveItems)
if err != nil {
return driveItems, errorcode.New(errorcode.GeneralException, err.Error())
}
return driveItems, nil
}

func (g BaseGraphService) listPublicShares(ctx context.Context, filters []*link.ListPublicSharesRequest_Filter, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {

gatewayClient, err := g.gatewaySelector.Next()
Expand Down Expand Up @@ -343,6 +371,42 @@ func (g BaseGraphService) cs3UserSharesToDriveItems(ctx context.Context, shares
}
return driveItems, nil
}
func (g BaseGraphService) cs3OCMSharesToDriveItems(ctx context.Context, shares []*ocm.Share, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
for _, s := range shares {
g.logger.Debug().Interface("CS3 OCMShare", s).Msg("Got Share")
resIDStr := storagespace.FormatResourceID(s.ResourceId)
item, ok := driveItems[resIDStr]
if !ok {
itemptr, err := g.getDriveItem(ctx, &storageprovider.Reference{ResourceId: s.ResourceId})
if err != nil {
g.logger.Debug().Err(err).Interface("Share", s.ResourceId).Msg("could not stat ocm share, skipping")
continue
}
item = *itemptr
}

var condition string
switch {
case item.Folder != nil:
condition = unifiedrole.UnifiedRoleConditionFolderFederatedUser
case item.File != nil:
condition = unifiedrole.UnifiedRoleConditionFileFederatedUser
}
perm, err := g.cs3OCMShareToPermission(ctx, s, condition)

var errcode errorcode.Error
switch {
case errors.As(err, &errcode) && errcode.GetCode() == errorcode.ItemNotFound:
// The Grantee couldn't be found (user/group does not exist anymore)
continue
case err != nil:
return driveItems, err
}
item.Permissions = append(item.Permissions, *perm)
driveItems[resIDStr] = item
}
return driveItems, nil
}

func (g BaseGraphService) cs3UserShareToPermission(ctx context.Context, share *collaboration.Share, roleCondition string) (*libregraph.Permission, error) {
perm := libregraph.Permission{}
Expand Down Expand Up @@ -419,6 +483,89 @@ func (g BaseGraphService) cs3UserShareToPermission(ctx context.Context, share *c
}
return &perm, nil
}
func (g BaseGraphService) cs3OCMShareToPermission(ctx context.Context, share *ocm.Share, roleCondition string) (*libregraph.Permission, error) {
perm := libregraph.Permission{}
perm.SetRoles([]string{})
if roleCondition != unifiedrole.UnifiedRoleConditionDrive {
perm.SetId(share.GetId().GetOpaqueId())
}
grantedTo := libregraph.SharePointIdentitySet{}
// hm or use share.GetShareType() to determine the type of share???
switch share.GetGrantee().GetType() {
case storageprovider.GranteeType_GRANTEE_TYPE_USER:
user, err := cs3UserIdToIdentity(ctx, g.identityCache, share.Grantee.GetUserId())
switch {
case errors.Is(err, identity.ErrNotFound):
g.logger.Warn().Str("userid", share.Grantee.GetUserId().GetOpaqueId()).Msg("User not found by id")
// User does not seem to exist anymore, don't add a permission for this
return nil, errorcode.New(errorcode.ItemNotFound, "grantee does not exist")
case err != nil:
return nil, errorcode.New(errorcode.GeneralException, err.Error())
default:
grantedTo.SetUser(user)
if roleCondition == unifiedrole.UnifiedRoleConditionDrive {
perm.SetId("u:" + user.GetId())
}
}
case storageprovider.GranteeType_GRANTEE_TYPE_GROUP:
group, err := groupIdToIdentity(ctx, g.identityCache, share.Grantee.GetGroupId().GetOpaqueId())
switch {
case errors.Is(err, identity.ErrNotFound):
g.logger.Warn().Str("groupid", share.Grantee.GetGroupId().GetOpaqueId()).Msg("Group not found by id")
// Group not seem to exist anymore, don't add a permission for this
return nil, errorcode.New(errorcode.ItemNotFound, "grantee does not exist")
case err != nil:
return nil, errorcode.New(errorcode.GeneralException, err.Error())
default:
grantedTo.SetGroup(group)
if roleCondition == unifiedrole.UnifiedRoleConditionDrive {
perm.SetId("g:" + group.GetId())
}
}
}

// set expiration date
if share.GetExpiration() != nil {
perm.SetExpirationDateTime(cs3TimestampToTime(share.GetExpiration()))
}
// set cTime
if share.GetCtime() != nil {
perm.SetCreatedDateTime(cs3TimestampToTime(share.GetCtime()))
}
var permissions *storageprovider.ResourcePermissions
for _, role := range share.GetAccessMethods() {
if role.GetWebdavOptions().GetPermissions() != nil {
permissions = role.GetWebdavOptions().GetPermissions()
}
}

role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(
permissions,
roleCondition,
)
if role != nil {
perm.SetRoles([]string{role.GetId()})
} else {
actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(permissions)
perm.SetLibreGraphPermissionsActions(actions)
perm.SetRoles(nil)
}
perm.SetGrantedToV2(grantedTo)
if share.GetCreator() != nil {
identity, err := cs3UserIdToIdentity(ctx, g.identityCache, share.GetCreator())
if err != nil {
return nil, errorcode.New(errorcode.GeneralException, err.Error())
}
perm.SetInvitation(
libregraph.SharingInvitation{
InvitedBy: &libregraph.IdentitySet{
User: &identity,
},
},
)
}
return &perm, nil
}

func (g BaseGraphService) cs3PublicSharesToDriveItems(ctx context.Context, shares []*link.PublicShare, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
for _, s := range shares {
Expand Down
19 changes: 19 additions & 0 deletions services/graph/pkg/service/v0/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,32 @@ func userIdToIdentity(ctx context.Context, cache identity.IdentityCache, userID
user, err := cache.GetUser(ctx, userID)
if err == nil {
identity.SetDisplayName(user.GetDisplayName())
identity.SetLibreGraphUserType(user.GetUserType())
}
return identity, err
}

// federatedIdToIdentity looks the user for the supplied id using the cache and returns it
// as a libregraph.Identity
func federatedIdToIdentity(ctx context.Context, cache identity.IdentityCache, userID string) (libregraph.Identity, error) {
identity := libregraph.Identity{
Id: libregraph.PtrString(userID),
LibreGraphUserType: libregraph.PtrString("Federated"),
}
user, err := cache.GetAcceptedUser(ctx, userID)
if err == nil {
identity.SetDisplayName(user.GetDisplayName())
identity.SetLibreGraphUserType(user.GetUserType())
}
return identity, err
}

// cs3UserIdToIdentity looks up the user for the supplied cs3 userid using the cache and returns it
// as a libregraph.Identity. Skips the user lookup if the id type is USER_TYPE_SPACE_OWNER
func cs3UserIdToIdentity(ctx context.Context, cache identity.IdentityCache, cs3UserID *cs3User.UserId) (libregraph.Identity, error) {
if cs3UserID.GetType() == cs3User.UserType_USER_TYPE_FEDERATED {
return federatedIdToIdentity(ctx, cache, cs3UserID.GetOpaqueId())
}
if cs3UserID.GetType() != cs3User.UserType_USER_TYPE_SPACE_OWNER {
return userIdToIdentity(ctx, cache, cs3UserID.GetOpaqueId())
}
Expand Down