mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-02 00:44:53 -05:00
enhancement(sharing): Return newly created driveItem
When accepting a share via 'POST /v1beta1/drives/{driveId}/root/children'
return the newly created driveItem. This driveItem wraps the accepted
remoteItem representing the shared resource (similar to the
'sharedWithMe' response.
This also refactors some of the helpers for user lookup and CS3 share to
driveItem conversion so they can be more easily shared.
This commit is contained in:
committed by
Ralf Haferkamp
parent
0a24f23164
commit
9d321bf379
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
@@ -14,10 +15,10 @@ import (
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/identity"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,15 +34,19 @@ type DrivesDriveItemProvider interface {
|
||||
|
||||
// DrivesDriveItemService contains the production business logic for everything that relates to drives
|
||||
type DrivesDriveItemService struct {
|
||||
logger log.Logger
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
logger log.Logger
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
identityCache identity.IdentityCache
|
||||
resharingEnabled bool
|
||||
}
|
||||
|
||||
// NewDrivesDriveItemService creates a new DrivesDriveItemService
|
||||
func NewDrivesDriveItemService(logger log.Logger, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (DrivesDriveItemService, error) {
|
||||
func NewDrivesDriveItemService(logger log.Logger, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], identityCache identity.IdentityCache, resharing bool) (DrivesDriveItemService, error) {
|
||||
return DrivesDriveItemService{
|
||||
logger: log.Logger{Logger: logger.With().Str("graph api", "DrivesDriveItemService").Logger()},
|
||||
gatewaySelector: gatewaySelector,
|
||||
logger: log.Logger{Logger: logger.With().Str("graph api", "DrivesDriveItemService").Logger()},
|
||||
gatewaySelector: gatewaySelector,
|
||||
identityCache: identityCache,
|
||||
resharingEnabled: resharing,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -96,11 +101,17 @@ func (s DrivesDriveItemService) UnmountShare(ctx context.Context, resourceID sto
|
||||
|
||||
// MountShare mounts a share
|
||||
func (s DrivesDriveItemService) MountShare(ctx context.Context, resourceID storageprovider.ResourceId, name string) (libregraph.DriveItem, error) {
|
||||
if filepath.IsAbs(name) {
|
||||
return libregraph.DriveItem{}, errorcode.New(errorcode.InvalidRequest, "name cannot be an absolute path")
|
||||
}
|
||||
name = filepath.Clean(name)
|
||||
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return libregraph.DriveItem{}, err
|
||||
}
|
||||
|
||||
// Get all shares that the user has received for this resource. There might be multiple
|
||||
receivedSharesResponse, err := gatewayClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{
|
||||
Filters: []*collaboration.Filter{
|
||||
{
|
||||
@@ -129,6 +140,10 @@ func (s DrivesDriveItemService) MountShare(ctx context.Context, resourceID stora
|
||||
|
||||
var errs []error
|
||||
|
||||
var acceptedShares []*collaboration.ReceivedShare
|
||||
|
||||
// try to accept all of the received shares for this resource. So that the stat is in sync across all
|
||||
// shares
|
||||
for _, receivedShare := range receivedSharesResponse.GetShares() {
|
||||
updateMask := &fieldmaskpb.FieldMask{Paths: []string{_fieldMaskPathState}}
|
||||
receivedShare.State = collaboration.ShareState_SHARE_STATE_ACCEPTED
|
||||
@@ -140,9 +155,8 @@ func (s DrivesDriveItemService) MountShare(ctx context.Context, resourceID stora
|
||||
mountPoint = &storageprovider.Reference{}
|
||||
}
|
||||
|
||||
newPath := utils.MakeRelativePath(name)
|
||||
if mountPoint.GetPath() != newPath {
|
||||
mountPoint.Path = newPath
|
||||
if filepath.Clean(mountPoint.GetPath()) != name {
|
||||
mountPoint.Path = name
|
||||
receivedShare.MountPoint = mountPoint
|
||||
updateMask.Paths = append(updateMask.Paths, _fieldMaskPathMountPoint)
|
||||
}
|
||||
@@ -154,17 +168,35 @@ func (s DrivesDriveItemService) MountShare(ctx context.Context, resourceID stora
|
||||
}
|
||||
|
||||
updateReceivedShareResponse, err := gatewayClient.UpdateReceivedShare(ctx, updateReceivedShareRequest)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
switch errCode := errorcode.FromCS3Status(updateReceivedShareResponse.GetStatus(), err); {
|
||||
case errCode == nil:
|
||||
acceptedShares = append(acceptedShares, updateReceivedShareResponse.GetShare())
|
||||
default:
|
||||
// Just log at debug level here. If a single accept for any of the received shares failed this
|
||||
// is not a critical problem. We mainly need to handle the case where all accepts fail. (Outside
|
||||
// the loop)
|
||||
s.logger.Debug().Err(errCode).
|
||||
Str("shareid", receivedShare.GetShare().GetId().String()).
|
||||
Str("resourceid", receivedShare.GetShare().GetResourceId().String()).
|
||||
Msg("failed to accept share")
|
||||
errs = append(errs, errCode)
|
||||
}
|
||||
|
||||
// fixMe: send to nirvana, wait for toDriverItem func
|
||||
_ = updateReceivedShareResponse
|
||||
}
|
||||
|
||||
// fixMe: return a concrete driveItem
|
||||
return libregraph.DriveItem{}, errors.Join(errs...)
|
||||
if len(receivedSharesResponse.GetShares()) == len(errs) {
|
||||
// none of the received shares could be accepted. This is an error. Return it.
|
||||
return libregraph.DriveItem{}, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// As the accepted shares are all for the same resource they should collapse to a single driveitem
|
||||
items, err := cs3ReceivedSharesToDriveItems(ctx, &s.logger, gatewayClient, s.identityCache, s.resharingEnabled, acceptedShares)
|
||||
switch {
|
||||
case err != nil:
|
||||
return libregraph.DriveItem{}, nil
|
||||
case len(items) != 1:
|
||||
return libregraph.DriveItem{}, errorcode.New(errorcode.GeneralException, "failed to convert accepted shares into driveitem")
|
||||
}
|
||||
return items[0], nil
|
||||
}
|
||||
|
||||
// DrivesDriveItemApi is the api that registers the http endpoints which expose needed operation to the graph api.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
@@ -20,10 +21,12 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/graph/mocks"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/identity"
|
||||
svc "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
)
|
||||
|
||||
@@ -41,7 +44,9 @@ var _ = Describe("DrivesDriveItemService", func() {
|
||||
gatewaySelector = mocks.NewSelectable[gateway.GatewayAPIClient](GinkgoT())
|
||||
gatewaySelector.On("Next").Return(gatewayClient, nil)
|
||||
|
||||
service, err := svc.NewDrivesDriveItemService(logger, gatewaySelector)
|
||||
cache := identity.NewIdentityCache(identity.IdentityCacheWithGatewaySelector(gatewaySelector))
|
||||
|
||||
service, err := svc.NewDrivesDriveItemService(logger, gatewaySelector, cache, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
drivesDriveItemService = service
|
||||
})
|
||||
@@ -111,7 +116,12 @@ var _ = Describe("DrivesDriveItemService", func() {
|
||||
Describe("gateway client share update", func() {
|
||||
It("updates the share state to be accepted", func() {
|
||||
expectedShareID := collaborationv1beta1.ShareId{
|
||||
OpaqueId: "1$2!3",
|
||||
OpaqueId: "1:2:3",
|
||||
}
|
||||
expectedResourceID := storageprovider.ResourceId{
|
||||
StorageId: "1",
|
||||
SpaceId: "2",
|
||||
OpaqueId: "3",
|
||||
}
|
||||
|
||||
gatewayClient.
|
||||
@@ -135,14 +145,42 @@ var _ = Describe("DrivesDriveItemService", func() {
|
||||
Expect(in.GetUpdateMask().GetPaths()).To(Equal([]string{"state"}))
|
||||
Expect(in.GetShare().GetState()).To(Equal(collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED))
|
||||
Expect(in.GetShare().GetShare().GetId().GetOpaqueId()).To(Equal(expectedShareID.GetOpaqueId()))
|
||||
return &collaborationv1beta1.UpdateReceivedShareResponse{}, nil
|
||||
return &collaborationv1beta1.UpdateReceivedShareResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Share: &collaborationv1beta1.ReceivedShare{
|
||||
State: collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED,
|
||||
Share: &collaborationv1beta1.Share{
|
||||
Id: &expectedShareID,
|
||||
ResourceId: &expectedResourceID,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
gatewayClient.
|
||||
On("Stat", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *storageprovider.StatRequest, opts ...grpc.CallOption) (*storageprovider.StatResponse, error) {
|
||||
return &storageprovider.StatResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Info: &storageprovider.ResourceInfo{
|
||||
Id: &expectedResourceID,
|
||||
Name: "name",
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
||||
_, err := drivesDriveItemService.MountShare(context.Background(), storageprovider.ResourceId{}, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("updates the mountPoint", func() {
|
||||
expectedShareID := collaborationv1beta1.ShareId{
|
||||
OpaqueId: "1:2:3",
|
||||
}
|
||||
expectedResourceID := storageprovider.ResourceId{
|
||||
StorageId: "1",
|
||||
SpaceId: "2",
|
||||
OpaqueId: "3",
|
||||
}
|
||||
|
||||
gatewayClient.
|
||||
On("ListReceivedShares", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *collaborationv1beta1.ListReceivedSharesRequest, opts ...grpc.CallOption) (*collaborationv1beta1.ListReceivedSharesResponse, error) {
|
||||
@@ -158,15 +196,45 @@ var _ = Describe("DrivesDriveItemService", func() {
|
||||
Return(func(ctx context.Context, in *collaborationv1beta1.UpdateReceivedShareRequest, opts ...grpc.CallOption) (*collaborationv1beta1.UpdateReceivedShareResponse, error) {
|
||||
Expect(in.GetUpdateMask().GetPaths()).To(HaveLen(2))
|
||||
Expect(in.GetUpdateMask().GetPaths()).To(ContainElements("mount_point"))
|
||||
Expect(in.GetShare().GetMountPoint().GetPath()).To(Equal("./new name"))
|
||||
return &collaborationv1beta1.UpdateReceivedShareResponse{}, nil
|
||||
Expect(in.GetShare().GetMountPoint().GetPath()).To(Equal("new name"))
|
||||
return &collaborationv1beta1.UpdateReceivedShareResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Share: &collaborationv1beta1.ReceivedShare{
|
||||
State: collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED,
|
||||
Share: &collaborationv1beta1.Share{
|
||||
Id: &expectedShareID,
|
||||
ResourceId: &expectedResourceID,
|
||||
},
|
||||
MountPoint: &storageprovider.Reference{
|
||||
Path: "new name",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
gatewayClient.
|
||||
On("Stat", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *storageprovider.StatRequest, opts ...grpc.CallOption) (*storageprovider.StatResponse, error) {
|
||||
return &storageprovider.StatResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Info: &storageprovider.ResourceInfo{
|
||||
Id: &expectedResourceID,
|
||||
Name: "name",
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
||||
_, err := drivesDriveItemService.MountShare(context.Background(), storageprovider.ResourceId{}, "new name")
|
||||
di, err := drivesDriveItemService.MountShare(context.Background(), storageprovider.ResourceId{}, "new name")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(di.GetName()).To(Equal("new name"))
|
||||
})
|
||||
|
||||
It("bubbles errors and continues", func() {
|
||||
It("succeeds when any of the shares was accepted", func() {
|
||||
expectedResourceID := storageprovider.ResourceId{
|
||||
StorageId: "1",
|
||||
SpaceId: "2",
|
||||
OpaqueId: "3",
|
||||
}
|
||||
|
||||
gatewayClient.
|
||||
On("ListReceivedShares", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *collaborationv1beta1.ListReceivedSharesRequest, opts ...grpc.CallOption) (*collaborationv1beta1.ListReceivedSharesResponse, error) {
|
||||
@@ -190,11 +258,61 @@ var _ = Describe("DrivesDriveItemService", func() {
|
||||
return nil, fmt.Errorf("error %d", calls)
|
||||
}
|
||||
|
||||
return &collaborationv1beta1.UpdateReceivedShareResponse{}, nil
|
||||
return &collaborationv1beta1.UpdateReceivedShareResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Share: &collaborationv1beta1.ReceivedShare{
|
||||
State: collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED,
|
||||
Share: &collaborationv1beta1.Share{
|
||||
Id: &collaborationv1beta1.ShareId{
|
||||
OpaqueId: strconv.Itoa(calls),
|
||||
},
|
||||
ResourceId: &expectedResourceID,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
gatewayClient.
|
||||
On("Stat", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *storageprovider.StatRequest, opts ...grpc.CallOption) (*storageprovider.StatResponse, error) {
|
||||
return &storageprovider.StatResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Info: &storageprovider.ResourceInfo{
|
||||
Id: &expectedResourceID,
|
||||
Name: "name",
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
||||
di, err := drivesDriveItemService.MountShare(context.Background(), storageprovider.ResourceId{}, "new name")
|
||||
Expect(err).To(BeNil())
|
||||
Expect(di.GetId()).ToNot(BeEmpty())
|
||||
})
|
||||
It("errors when none of the shares can be accepted", func() {
|
||||
gatewayClient.
|
||||
On("ListReceivedShares", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *collaborationv1beta1.ListReceivedSharesRequest, opts ...grpc.CallOption) (*collaborationv1beta1.ListReceivedSharesResponse, error) {
|
||||
return &collaborationv1beta1.ListReceivedSharesResponse{
|
||||
Shares: []*collaborationv1beta1.ReceivedShare{
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
|
||||
var calls int
|
||||
gatewayClient.
|
||||
On("UpdateReceivedShare", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(func(ctx context.Context, in *collaborationv1beta1.UpdateReceivedShareRequest, opts ...grpc.CallOption) (*collaborationv1beta1.UpdateReceivedShareResponse, error) {
|
||||
calls++
|
||||
Expect(calls).To(BeNumerically("<=", 3))
|
||||
return nil, fmt.Errorf("error %d", calls)
|
||||
})
|
||||
|
||||
_, err := drivesDriveItemService.MountShare(context.Background(), storageprovider.ResourceId{}, "new name")
|
||||
Expect(fmt.Sprint(err)).To(Equal("error 1\nerror 2"))
|
||||
Expect(fmt.Sprint(err)).To(ContainSubstring("error 1"))
|
||||
Expect(fmt.Sprint(err)).To(ContainSubstring("error 2"))
|
||||
Expect(fmt.Sprint(err)).To(ContainSubstring("error 3"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -932,17 +932,17 @@ func (g Graph) cs3PermissionsToLibreGraph(ctx context.Context, space *storagepro
|
||||
tmp := id
|
||||
var identitySet libregraph.IdentitySet
|
||||
if _, ok := groupsMap[id]; ok {
|
||||
group, err := g.identityCache.GetGroup(ctx, tmp)
|
||||
identity, err := groupIdToIdentity(ctx, g.identityCache, tmp)
|
||||
if err != nil {
|
||||
g.logger.Warn().Str("groupid", tmp).Msg("Group not found by id")
|
||||
}
|
||||
identitySet = libregraph.IdentitySet{Group: &libregraph.Identity{Id: &tmp, DisplayName: group.GetDisplayName()}}
|
||||
identitySet = libregraph.IdentitySet{Group: &identity}
|
||||
} else {
|
||||
user, err := g.identityCache.GetUser(ctx, tmp)
|
||||
identity, err := userIdToIdentity(ctx, g.identityCache, tmp)
|
||||
if err != nil {
|
||||
g.logger.Warn().Str("userid", tmp).Msg("User not found by id")
|
||||
}
|
||||
identitySet = libregraph.IdentitySet{User: &libregraph.Identity{Id: &tmp, DisplayName: user.GetDisplayName()}}
|
||||
identitySet = libregraph.IdentitySet{User: &identity}
|
||||
}
|
||||
|
||||
p := libregraph.Permission{
|
||||
|
||||
@@ -204,7 +204,7 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
requireAdmin = options.RequireAdminMiddleware
|
||||
}
|
||||
|
||||
drivesDriveItemService, err := NewDrivesDriveItemService(options.Logger, options.GatewaySelector)
|
||||
drivesDriveItemService, err := NewDrivesDriveItemService(options.Logger, options.GatewaySelector, identityCache, options.Config.FilesSharing.EnableResharing)
|
||||
if err != nil {
|
||||
return svc, err
|
||||
}
|
||||
|
||||
@@ -145,10 +145,9 @@ func (g Graph) cs3UserShareToPermission(ctx context.Context, share *collaboratio
|
||||
perm.SetRoles([]string{})
|
||||
perm.SetId(share.Id.OpaqueId)
|
||||
grantedTo := libregraph.SharePointIdentitySet{}
|
||||
var li libregraph.Identity
|
||||
switch share.GetGrantee().GetType() {
|
||||
case storageprovider.GranteeType_GRANTEE_TYPE_USER:
|
||||
user, err := g.identityCache.GetUser(ctx, share.Grantee.GetUserId().GetOpaqueId())
|
||||
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")
|
||||
@@ -157,12 +156,10 @@ func (g Graph) cs3UserShareToPermission(ctx context.Context, share *collaboratio
|
||||
case err != nil:
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
default:
|
||||
li.SetDisplayName(user.GetDisplayName())
|
||||
li.SetId(user.GetId())
|
||||
grantedTo.SetUser(li)
|
||||
grantedTo.SetUser(user)
|
||||
}
|
||||
case storageprovider.GranteeType_GRANTEE_TYPE_GROUP:
|
||||
group, err := g.identityCache.GetGroup(ctx, share.Grantee.GetGroupId().GetOpaqueId())
|
||||
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")
|
||||
@@ -171,9 +168,7 @@ func (g Graph) cs3UserShareToPermission(ctx context.Context, share *collaboratio
|
||||
case err != nil:
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
default:
|
||||
li.SetDisplayName(group.GetDisplayName())
|
||||
li.SetId(group.GetId())
|
||||
grantedTo.SetGroup(li)
|
||||
grantedTo.SetGroup(group)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,20 +3,12 @@ package svc
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
cs3User "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
|
||||
)
|
||||
|
||||
// ListSharedWithMe lists the files shared with the current user.
|
||||
@@ -47,334 +39,5 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er
|
||||
return nil, *errCode
|
||||
}
|
||||
|
||||
return g.cs3ReceivedSharesToDriveItems(ctx, listReceivedSharesResponse.GetShares())
|
||||
}
|
||||
|
||||
func (g Graph) cs3ReceivedSharesToDriveItems(ctx context.Context, receivedShares []*collaboration.ReceivedShare) ([]libregraph.DriveItem, error) {
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not select next gateway client")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// doStat is a helper function that stat a resource.
|
||||
doStat := func(resourceId *storageprovider.ResourceId) (*storageprovider.StatResponse, error) {
|
||||
shareStat, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{
|
||||
Ref: &storageprovider.Reference{ResourceId: resourceId},
|
||||
})
|
||||
switch errCode := errorcode.FromCS3Status(shareStat.GetStatus(), err); {
|
||||
case errCode == nil:
|
||||
break
|
||||
// skip ItemNotFound shares, they might have been deleted in the meantime or orphans.
|
||||
case errCode.GetCode() == errorcode.ItemNotFound:
|
||||
return nil, nil
|
||||
default:
|
||||
g.logger.Error().Err(errCode).Msg("could not stat")
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
return shareStat, nil
|
||||
}
|
||||
|
||||
ch := make(chan libregraph.DriveItem)
|
||||
group := new(errgroup.Group)
|
||||
// Set max concurrency
|
||||
group.SetLimit(10)
|
||||
|
||||
receivedSharesByResourceID := make(map[string][]*collaboration.ReceivedShare, len(receivedShares))
|
||||
for _, receivedShare := range receivedShares {
|
||||
rIDStr := storagespace.FormatResourceID(*receivedShare.GetShare().GetResourceId())
|
||||
receivedSharesByResourceID[rIDStr] = append(receivedSharesByResourceID[rIDStr], receivedShare)
|
||||
}
|
||||
|
||||
for _, receivedSharesForResource := range receivedSharesByResourceID {
|
||||
receivedShares := receivedSharesForResource
|
||||
|
||||
group.Go(func() error {
|
||||
var err error // redeclare
|
||||
shareStat, err := doStat(receivedShares[0].GetShare().GetResourceId())
|
||||
if shareStat == nil || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
driveItem := libregraph.NewDriveItem()
|
||||
|
||||
permissions := make([]libregraph.Permission, 0, len(receivedShares))
|
||||
|
||||
var oldestReceivedShare *collaboration.ReceivedShare
|
||||
for _, receivedShare := range receivedShares {
|
||||
switch {
|
||||
case oldestReceivedShare == nil:
|
||||
fallthrough
|
||||
case utils.TSToTime(receivedShare.GetShare().GetCtime()).Before(utils.TSToTime(oldestReceivedShare.GetShare().GetCtime())):
|
||||
oldestReceivedShare = receivedShare
|
||||
}
|
||||
|
||||
permission, err := g.cs3ReceivedShareToLibreGraphPermissions(ctx, receivedShare)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If at least one of the shares was accepted, we consider the driveItem's synchronized
|
||||
// flag enabled.
|
||||
// Also we use the Mountpoint name of the first accepted mountpoint as the name of
|
||||
// of the driveItem
|
||||
if receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED {
|
||||
driveItem.SetClientSynchronize(true)
|
||||
if name := receivedShare.GetMountPoint().GetPath(); name != "" && driveItem.GetName() == "" {
|
||||
driveItem.SetName(receivedShare.GetMountPoint().GetPath())
|
||||
}
|
||||
}
|
||||
|
||||
// if at least one share is marked as hidden, consider the whole driveItem to be hidden
|
||||
if receivedShare.GetHidden() {
|
||||
driveItem.SetUIHidden(true)
|
||||
}
|
||||
|
||||
if userID := receivedShare.GetShare().GetCreator(); userID != nil {
|
||||
identity, err := g.cs3UserIdToIdentity(ctx, userID)
|
||||
if err != nil {
|
||||
g.logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get creator of the share")
|
||||
}
|
||||
|
||||
permission.SetInvitation(
|
||||
libregraph.SharingInvitation{
|
||||
InvitedBy: &libregraph.IdentitySet{
|
||||
User: &identity,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
permissions = append(permissions, *permission)
|
||||
|
||||
}
|
||||
|
||||
// To stay compatible with the usershareprovider and the webdav
|
||||
// service the id of the driveItem is composed of the StorageID and
|
||||
// SpaceID of the sharestorage appended with the opaque ID of
|
||||
// the oldest share for the resource:
|
||||
// '<sharestorageid>$<sharespaceid>!<share-opaque-id>
|
||||
// Note: This means that the driveitem ID will change when the oldest
|
||||
// shared is removed. It would be good to have are more stable ID here (e.g.
|
||||
// derived from the shared resource's ID. But as we need to use the same
|
||||
// ID across all services this means we needed to make similar adjustments
|
||||
// to the sharejail (usershareprovider, webdav). Which we can't currently do
|
||||
// as some clients rely on the IDs used there having a special format.
|
||||
driveItem.SetId(storagespace.FormatResourceID(storageprovider.ResourceId{
|
||||
StorageId: utils.ShareStorageProviderID,
|
||||
OpaqueId: oldestReceivedShare.GetShare().GetId().GetOpaqueId(),
|
||||
SpaceId: utils.ShareStorageSpaceID,
|
||||
}))
|
||||
|
||||
if !driveItem.HasUIHidden() {
|
||||
driveItem.SetUIHidden(false)
|
||||
}
|
||||
if !driveItem.HasClientSynchronize() {
|
||||
driveItem.SetClientSynchronize(false)
|
||||
if name := shareStat.GetInfo().GetName(); name != "" {
|
||||
driveItem.SetName(name)
|
||||
}
|
||||
}
|
||||
|
||||
remoteItem := libregraph.NewRemoteItem()
|
||||
{
|
||||
if id := shareStat.GetInfo().GetId(); id != nil {
|
||||
remoteItem.SetId(storagespace.FormatResourceID(*id))
|
||||
}
|
||||
|
||||
if name := shareStat.GetInfo().GetName(); name != "" {
|
||||
remoteItem.SetName(name)
|
||||
}
|
||||
|
||||
if etag := shareStat.GetInfo().GetEtag(); etag != "" {
|
||||
remoteItem.SetETag(etag)
|
||||
}
|
||||
|
||||
if mTime := shareStat.GetInfo().GetMtime(); mTime != nil {
|
||||
remoteItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime))
|
||||
}
|
||||
|
||||
if size := shareStat.GetInfo().GetSize(); size != 0 {
|
||||
remoteItem.SetSize(int64(size))
|
||||
}
|
||||
|
||||
parentReference := libregraph.NewItemReference()
|
||||
if spaceType := shareStat.GetInfo().GetSpace().GetSpaceType(); spaceType != "" {
|
||||
parentReference.SetDriveType(spaceType)
|
||||
}
|
||||
|
||||
if root := shareStat.GetInfo().GetSpace().GetRoot(); root != nil {
|
||||
parentReference.SetDriveId(storagespace.FormatResourceID(*root))
|
||||
}
|
||||
if !reflect.ValueOf(*parentReference).IsZero() {
|
||||
remoteItem.ParentReference = parentReference
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// the parentReference of the outer driveItem should be the drive
|
||||
// containing the mountpoint i.e. the share jail
|
||||
driveItem.ParentReference = libregraph.NewItemReference()
|
||||
driveItem.ParentReference.SetDriveType("virtual")
|
||||
driveItem.ParentReference.SetDriveId(storagespace.FormatStorageID(utils.ShareStorageProviderID, utils.ShareStorageSpaceID))
|
||||
driveItem.ParentReference.SetId(storagespace.FormatResourceID(storageprovider.ResourceId{
|
||||
StorageId: utils.ShareStorageProviderID,
|
||||
OpaqueId: utils.ShareStorageSpaceID,
|
||||
SpaceId: utils.ShareStorageSpaceID,
|
||||
}))
|
||||
if etag := shareStat.GetInfo().GetEtag(); etag != "" {
|
||||
driveItem.SetETag(etag)
|
||||
}
|
||||
|
||||
// connect the dots
|
||||
{
|
||||
if mTime := shareStat.GetInfo().GetMtime(); mTime != nil {
|
||||
t := cs3TimestampToTime(mTime)
|
||||
|
||||
driveItem.SetLastModifiedDateTime(t)
|
||||
remoteItem.SetLastModifiedDateTime(t)
|
||||
}
|
||||
|
||||
if size := shareStat.GetInfo().GetSize(); size != 0 {
|
||||
s := int64(size)
|
||||
|
||||
driveItem.SetSize(s)
|
||||
remoteItem.SetSize(s)
|
||||
}
|
||||
|
||||
if userID := shareStat.GetInfo().GetOwner(); userID != nil && userID.Type != cs3User.UserType_USER_TYPE_SPACE_OWNER {
|
||||
identity, err := g.cs3UserIdToIdentity(ctx, userID)
|
||||
if err != nil {
|
||||
// TODO: define a proper error behavior here. We don't
|
||||
// want the whole request to fail just because a single
|
||||
// resource owner couldn't be resolved. But, should be
|
||||
// really return the affect share in the response?
|
||||
// For now we just log a warning. The returned
|
||||
// identitySet will just contain the userid.
|
||||
g.logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get owner of shared resource")
|
||||
}
|
||||
|
||||
remoteItem.SetCreatedBy(libregraph.IdentitySet{User: &identity})
|
||||
driveItem.SetCreatedBy(libregraph.IdentitySet{User: &identity})
|
||||
}
|
||||
switch info := shareStat.GetInfo(); {
|
||||
case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER:
|
||||
folder := libregraph.NewFolder()
|
||||
|
||||
remoteItem.Folder = folder
|
||||
driveItem.Folder = folder
|
||||
case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE:
|
||||
file := libregraph.NewOpenGraphFile()
|
||||
|
||||
if mimeType := info.GetMimeType(); mimeType != "" {
|
||||
file.MimeType = &mimeType
|
||||
}
|
||||
|
||||
remoteItem.File = file
|
||||
driveItem.File = file
|
||||
}
|
||||
|
||||
if len(permissions) > 0 {
|
||||
remoteItem.Permissions = permissions
|
||||
}
|
||||
|
||||
if !reflect.ValueOf(*remoteItem).IsZero() {
|
||||
driveItem.RemoteItem = remoteItem
|
||||
}
|
||||
}
|
||||
|
||||
ch <- *driveItem
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// wait for concurrent requests to finish
|
||||
go func() {
|
||||
err = group.Wait()
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
driveItems := make([]libregraph.DriveItem, 0, len(receivedSharesByResourceID))
|
||||
for di := range ch {
|
||||
driveItems = append(driveItems, di)
|
||||
}
|
||||
|
||||
return driveItems, err
|
||||
}
|
||||
|
||||
func (g Graph) cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, receivedShare *collaboration.ReceivedShare) (*libregraph.Permission, error) {
|
||||
permission := libregraph.NewPermission()
|
||||
if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" {
|
||||
permission.SetId(id)
|
||||
}
|
||||
|
||||
if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil {
|
||||
permission.SetExpirationDateTime(cs3TimestampToTime(expiration))
|
||||
}
|
||||
|
||||
if permissionSet := receivedShare.GetShare().GetPermissions().GetPermissions(); permissionSet != nil {
|
||||
role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(
|
||||
*permissionSet,
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
g.config.FilesSharing.EnableResharing,
|
||||
)
|
||||
|
||||
if role != nil {
|
||||
permission.SetRoles([]string{role.GetId()})
|
||||
}
|
||||
|
||||
actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet)
|
||||
|
||||
// actions only make sense if no role is set
|
||||
if role == nil && len(actions) > 0 {
|
||||
permission.SetLibreGraphPermissionsActions(actions)
|
||||
}
|
||||
}
|
||||
|
||||
switch grantee := receivedShare.GetShare().GetGrantee(); {
|
||||
case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER:
|
||||
user, err := g.identityCache.GetUser(ctx, grantee.GetUserId().GetOpaqueId())
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not get user")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permission.SetGrantedToV2(libregraph.SharePointIdentitySet{
|
||||
User: &libregraph.Identity{
|
||||
DisplayName: user.GetDisplayName(),
|
||||
Id: user.Id,
|
||||
},
|
||||
})
|
||||
case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP:
|
||||
group, err := g.identityCache.GetGroup(ctx, grantee.GetGroupId().GetOpaqueId())
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not get group")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permission.SetGrantedToV2(libregraph.SharePointIdentitySet{
|
||||
Group: &libregraph.Identity{
|
||||
DisplayName: group.GetDisplayName(),
|
||||
Id: group.Id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return permission, nil
|
||||
}
|
||||
|
||||
func (g Graph) cs3UserIdToIdentity(ctx context.Context, cs3UserID *cs3User.UserId) (libregraph.Identity, error) {
|
||||
identity := libregraph.Identity{
|
||||
Id: libregraph.PtrString(cs3UserID.GetOpaqueId()),
|
||||
}
|
||||
var err error
|
||||
if cs3UserID.GetType() != cs3User.UserType_USER_TYPE_SPACE_OWNER {
|
||||
var user libregraph.User
|
||||
user, err = g.identityCache.GetUser(ctx, cs3UserID.GetOpaqueId())
|
||||
if err == nil {
|
||||
identity.SetDisplayName(user.GetDisplayName())
|
||||
}
|
||||
}
|
||||
return identity, err
|
||||
return cs3ReceivedSharesToDriveItems(ctx, g.logger, gatewayClient, g.identityCache, g.config.FilesSharing.EnableResharing, listReceivedSharesResponse.GetShares())
|
||||
}
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
cs3User "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/identity"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
|
||||
)
|
||||
|
||||
// StrictJSONUnmarshal is a wrapper around json.Unmarshal that returns an error if the json contains unknown fields.
|
||||
@@ -79,3 +87,341 @@ func (g Graph) GetGatewayClient(w http.ResponseWriter, r *http.Request) (gateway
|
||||
func IsShareJail(id storageprovider.ResourceId) bool {
|
||||
return id.GetStorageId() == utils.ShareStorageProviderID && id.GetSpaceId() == utils.ShareStorageSpaceID
|
||||
}
|
||||
|
||||
// userIdToIdentity looks the user for the supplied id using the cache and returns it
|
||||
// as a libregraph.Identity
|
||||
func userIdToIdentity(ctx context.Context, cache identity.IdentityCache, userID string) (libregraph.Identity, error) {
|
||||
identity := libregraph.Identity{
|
||||
Id: libregraph.PtrString(userID),
|
||||
}
|
||||
user, err := cache.GetUser(ctx, userID)
|
||||
if err == nil {
|
||||
identity.SetDisplayName(user.GetDisplayName())
|
||||
}
|
||||
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_SPACE_OWNER {
|
||||
return userIdToIdentity(ctx, cache, cs3UserID.GetOpaqueId())
|
||||
}
|
||||
return libregraph.Identity{Id: libregraph.PtrString(cs3UserID.GetOpaqueId())}, nil
|
||||
}
|
||||
|
||||
// groupIdToIdentity looks up the group for the supplied cs3 groupid using the cache and returns it
|
||||
// as a libregraph.Identity.
|
||||
func groupIdToIdentity(ctx context.Context, cache identity.IdentityCache, groupID string) (libregraph.Identity, error) {
|
||||
identity := libregraph.Identity{
|
||||
Id: libregraph.PtrString(groupID),
|
||||
}
|
||||
group, err := cache.GetGroup(ctx, groupID)
|
||||
if err == nil {
|
||||
identity.SetDisplayName(group.GetDisplayName())
|
||||
}
|
||||
return identity, err
|
||||
}
|
||||
|
||||
func cs3ReceivedSharesToDriveItems(ctx context.Context,
|
||||
logger *log.Logger,
|
||||
gatewayClient gateway.GatewayAPIClient,
|
||||
identityCache identity.IdentityCache,
|
||||
resharing bool,
|
||||
receivedShares []*collaboration.ReceivedShare) ([]libregraph.DriveItem, error) {
|
||||
|
||||
// doStat is a helper function that stat a resource.
|
||||
doStat := func(resourceId *storageprovider.ResourceId) (*storageprovider.StatResponse, error) {
|
||||
shareStat, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{
|
||||
Ref: &storageprovider.Reference{ResourceId: resourceId},
|
||||
})
|
||||
switch errCode := errorcode.FromCS3Status(shareStat.GetStatus(), err); {
|
||||
case errCode == nil:
|
||||
break
|
||||
// skip ItemNotFound shares, they might have been deleted in the meantime or orphans.
|
||||
case errCode.GetCode() == errorcode.ItemNotFound:
|
||||
return nil, nil
|
||||
default:
|
||||
logger.Error().Err(errCode).Msg("could not stat")
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
return shareStat, nil
|
||||
}
|
||||
|
||||
ch := make(chan libregraph.DriveItem)
|
||||
group := new(errgroup.Group)
|
||||
// Set max concurrency
|
||||
group.SetLimit(10)
|
||||
|
||||
receivedSharesByResourceID := make(map[string][]*collaboration.ReceivedShare, len(receivedShares))
|
||||
for _, receivedShare := range receivedShares {
|
||||
rIDStr := storagespace.FormatResourceID(*receivedShare.GetShare().GetResourceId())
|
||||
receivedSharesByResourceID[rIDStr] = append(receivedSharesByResourceID[rIDStr], receivedShare)
|
||||
}
|
||||
|
||||
for _, receivedSharesForResource := range receivedSharesByResourceID {
|
||||
receivedShares := receivedSharesForResource
|
||||
|
||||
group.Go(func() error {
|
||||
var err error // redeclare
|
||||
shareStat, err := doStat(receivedShares[0].GetShare().GetResourceId())
|
||||
if shareStat == nil || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
driveItem := libregraph.NewDriveItem()
|
||||
|
||||
permissions := make([]libregraph.Permission, 0, len(receivedShares))
|
||||
|
||||
var oldestReceivedShare *collaboration.ReceivedShare
|
||||
for _, receivedShare := range receivedShares {
|
||||
switch {
|
||||
case oldestReceivedShare == nil:
|
||||
fallthrough
|
||||
case utils.TSToTime(receivedShare.GetShare().GetCtime()).Before(utils.TSToTime(oldestReceivedShare.GetShare().GetCtime())):
|
||||
oldestReceivedShare = receivedShare
|
||||
}
|
||||
|
||||
permission, err := cs3ReceivedShareToLibreGraphPermissions(ctx, logger, identityCache, resharing, receivedShare)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If at least one of the shares was accepted, we consider the driveItem's synchronized
|
||||
// flag enabled.
|
||||
// Also we use the Mountpoint name of the first accepted mountpoint as the name of
|
||||
// of the driveItem
|
||||
if receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED {
|
||||
driveItem.SetClientSynchronize(true)
|
||||
if name := receivedShare.GetMountPoint().GetPath(); name != "" && driveItem.GetName() == "" {
|
||||
driveItem.SetName(receivedShare.GetMountPoint().GetPath())
|
||||
}
|
||||
}
|
||||
|
||||
// if at least one share is marked as hidden, consider the whole driveItem to be hidden
|
||||
if receivedShare.GetHidden() {
|
||||
driveItem.SetUIHidden(true)
|
||||
}
|
||||
|
||||
if userID := receivedShare.GetShare().GetCreator(); userID != nil {
|
||||
identity, err := cs3UserIdToIdentity(ctx, identityCache, userID)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get creator of the share")
|
||||
}
|
||||
|
||||
permission.SetInvitation(
|
||||
libregraph.SharingInvitation{
|
||||
InvitedBy: &libregraph.IdentitySet{
|
||||
User: &identity,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
permissions = append(permissions, *permission)
|
||||
|
||||
}
|
||||
|
||||
// To stay compatible with the usershareprovider and the webdav
|
||||
// service the id of the driveItem is composed of the StorageID and
|
||||
// SpaceID of the sharestorage appended with the opaque ID of
|
||||
// the oldest share for the resource:
|
||||
// '<sharestorageid>$<sharespaceid>!<share-opaque-id>
|
||||
// Note: This means that the driveitem ID will change when the oldest
|
||||
// shared is removed. It would be good to have are more stable ID here (e.g.
|
||||
// derived from the shared resource's ID. But as we need to use the same
|
||||
// ID across all services this means we needed to make similar adjustments
|
||||
// to the sharejail (usershareprovider, webdav). Which we can't currently do
|
||||
// as some clients rely on the IDs used there having a special format.
|
||||
driveItem.SetId(storagespace.FormatResourceID(storageprovider.ResourceId{
|
||||
StorageId: utils.ShareStorageProviderID,
|
||||
OpaqueId: oldestReceivedShare.GetShare().GetId().GetOpaqueId(),
|
||||
SpaceId: utils.ShareStorageSpaceID,
|
||||
}))
|
||||
|
||||
if !driveItem.HasUIHidden() {
|
||||
driveItem.SetUIHidden(false)
|
||||
}
|
||||
if !driveItem.HasClientSynchronize() {
|
||||
driveItem.SetClientSynchronize(false)
|
||||
if name := shareStat.GetInfo().GetName(); name != "" {
|
||||
driveItem.SetName(name)
|
||||
}
|
||||
}
|
||||
|
||||
remoteItem := libregraph.NewRemoteItem()
|
||||
{
|
||||
if id := shareStat.GetInfo().GetId(); id != nil {
|
||||
remoteItem.SetId(storagespace.FormatResourceID(*id))
|
||||
}
|
||||
|
||||
if name := shareStat.GetInfo().GetName(); name != "" {
|
||||
remoteItem.SetName(name)
|
||||
}
|
||||
|
||||
if etag := shareStat.GetInfo().GetEtag(); etag != "" {
|
||||
remoteItem.SetETag(etag)
|
||||
}
|
||||
|
||||
if mTime := shareStat.GetInfo().GetMtime(); mTime != nil {
|
||||
remoteItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime))
|
||||
}
|
||||
|
||||
if size := shareStat.GetInfo().GetSize(); size != 0 {
|
||||
remoteItem.SetSize(int64(size))
|
||||
}
|
||||
|
||||
parentReference := libregraph.NewItemReference()
|
||||
if spaceType := shareStat.GetInfo().GetSpace().GetSpaceType(); spaceType != "" {
|
||||
parentReference.SetDriveType(spaceType)
|
||||
}
|
||||
|
||||
if root := shareStat.GetInfo().GetSpace().GetRoot(); root != nil {
|
||||
parentReference.SetDriveId(storagespace.FormatResourceID(*root))
|
||||
}
|
||||
if !reflect.ValueOf(*parentReference).IsZero() {
|
||||
remoteItem.ParentReference = parentReference
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// the parentReference of the outer driveItem should be the drive
|
||||
// containing the mountpoint i.e. the share jail
|
||||
driveItem.ParentReference = libregraph.NewItemReference()
|
||||
driveItem.ParentReference.SetDriveType("virtual")
|
||||
driveItem.ParentReference.SetDriveId(storagespace.FormatStorageID(utils.ShareStorageProviderID, utils.ShareStorageSpaceID))
|
||||
driveItem.ParentReference.SetId(storagespace.FormatResourceID(storageprovider.ResourceId{
|
||||
StorageId: utils.ShareStorageProviderID,
|
||||
OpaqueId: utils.ShareStorageSpaceID,
|
||||
SpaceId: utils.ShareStorageSpaceID,
|
||||
}))
|
||||
if etag := shareStat.GetInfo().GetEtag(); etag != "" {
|
||||
driveItem.SetETag(etag)
|
||||
}
|
||||
|
||||
// connect the dots
|
||||
{
|
||||
if mTime := shareStat.GetInfo().GetMtime(); mTime != nil {
|
||||
t := cs3TimestampToTime(mTime)
|
||||
|
||||
driveItem.SetLastModifiedDateTime(t)
|
||||
remoteItem.SetLastModifiedDateTime(t)
|
||||
}
|
||||
|
||||
if size := shareStat.GetInfo().GetSize(); size != 0 {
|
||||
s := int64(size)
|
||||
|
||||
driveItem.SetSize(s)
|
||||
remoteItem.SetSize(s)
|
||||
}
|
||||
|
||||
if userID := shareStat.GetInfo().GetOwner(); userID != nil && userID.Type != cs3User.UserType_USER_TYPE_SPACE_OWNER {
|
||||
identity, err := cs3UserIdToIdentity(ctx, identityCache, userID)
|
||||
if err != nil {
|
||||
// TODO: define a proper error behavior here. We don't
|
||||
// want the whole request to fail just because a single
|
||||
// resource owner couldn't be resolved. But, should be
|
||||
// really return the affect share in the response?
|
||||
// For now we just log a warning. The returned
|
||||
// identitySet will just contain the userid.
|
||||
logger.Warn().Err(err).Str("userid", userID.String()).Msg("could not get owner of shared resource")
|
||||
}
|
||||
|
||||
remoteItem.SetCreatedBy(libregraph.IdentitySet{User: &identity})
|
||||
driveItem.SetCreatedBy(libregraph.IdentitySet{User: &identity})
|
||||
}
|
||||
switch info := shareStat.GetInfo(); {
|
||||
case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER:
|
||||
folder := libregraph.NewFolder()
|
||||
|
||||
remoteItem.Folder = folder
|
||||
driveItem.Folder = folder
|
||||
case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE:
|
||||
file := libregraph.NewOpenGraphFile()
|
||||
|
||||
if mimeType := info.GetMimeType(); mimeType != "" {
|
||||
file.MimeType = &mimeType
|
||||
}
|
||||
|
||||
remoteItem.File = file
|
||||
driveItem.File = file
|
||||
}
|
||||
|
||||
if len(permissions) > 0 {
|
||||
remoteItem.Permissions = permissions
|
||||
}
|
||||
|
||||
if !reflect.ValueOf(*remoteItem).IsZero() {
|
||||
driveItem.RemoteItem = remoteItem
|
||||
}
|
||||
}
|
||||
|
||||
ch <- *driveItem
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var err error
|
||||
// wait for concurrent requests to finish
|
||||
go func() {
|
||||
err = group.Wait()
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
driveItems := make([]libregraph.DriveItem, 0, len(receivedSharesByResourceID))
|
||||
for di := range ch {
|
||||
driveItems = append(driveItems, di)
|
||||
}
|
||||
|
||||
return driveItems, err
|
||||
}
|
||||
|
||||
func cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, logger *log.Logger,
|
||||
identityCache identity.IdentityCache, resharing bool, receivedShare *collaboration.ReceivedShare) (*libregraph.Permission, error) {
|
||||
permission := libregraph.NewPermission()
|
||||
if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" {
|
||||
permission.SetId(id)
|
||||
}
|
||||
|
||||
if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil {
|
||||
permission.SetExpirationDateTime(cs3TimestampToTime(expiration))
|
||||
}
|
||||
|
||||
if permissionSet := receivedShare.GetShare().GetPermissions().GetPermissions(); permissionSet != nil {
|
||||
role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(
|
||||
*permissionSet,
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
resharing,
|
||||
)
|
||||
|
||||
if role != nil {
|
||||
permission.SetRoles([]string{role.GetId()})
|
||||
}
|
||||
|
||||
actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet)
|
||||
|
||||
// actions only make sense if no role is set
|
||||
if role == nil && len(actions) > 0 {
|
||||
permission.SetLibreGraphPermissionsActions(actions)
|
||||
}
|
||||
}
|
||||
switch grantee := receivedShare.GetShare().GetGrantee(); {
|
||||
case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER:
|
||||
user, err := cs3UserIdToIdentity(ctx, identityCache, grantee.GetUserId())
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not get user")
|
||||
return nil, err
|
||||
}
|
||||
permission.SetGrantedToV2(libregraph.SharePointIdentitySet{User: &user})
|
||||
case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP:
|
||||
group, err := groupIdToIdentity(ctx, identityCache, grantee.GetGroupId().GetOpaqueId())
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not get group")
|
||||
return nil, err
|
||||
}
|
||||
permission.SetGrantedToV2(libregraph.SharePointIdentitySet{Group: &group})
|
||||
}
|
||||
|
||||
return permission, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user