From 350abb0571a10ca2a9281cfb1f0761c8da572f48 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Tue, 26 Mar 2024 18:16:24 +0100 Subject: [PATCH] feat(graph): add GET /drive//root/permissions suppport Partial Fix: #8351 --- .../mocks/drive_item_permissions_provider.go | 57 +++++++++++++++++++ .../service/v0/api_driveitem_permissions.go | 53 +++++++++++++++-- .../v0/api_driveitem_permissions_test.go | 48 ++++++++++++++++ services/graph/pkg/service/v0/service.go | 3 + 4 files changed, 156 insertions(+), 5 deletions(-) diff --git a/services/graph/mocks/drive_item_permissions_provider.go b/services/graph/mocks/drive_item_permissions_provider.go index 49b76f131d8..853c3d104c1 100644 --- a/services/graph/mocks/drive_item_permissions_provider.go +++ b/services/graph/mocks/drive_item_permissions_provider.go @@ -139,6 +139,63 @@ func (_c *DriveItemPermissionsProvider_ListPermissions_Call) RunAndReturn(run fu return _c } +// ListSpaceRootPermissions provides a mock function with given fields: ctx, driveID +func (_m *DriveItemPermissionsProvider) ListSpaceRootPermissions(ctx context.Context, driveID providerv1beta1.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error) { + ret := _m.Called(ctx, driveID) + + if len(ret) == 0 { + panic("no return value specified for ListSpaceRootPermissions") + } + + var r0 libregraph.CollectionOfPermissionsWithAllowedValues + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error)); ok { + return rf(ctx, driveID) + } + if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId) libregraph.CollectionOfPermissionsWithAllowedValues); ok { + r0 = rf(ctx, driveID) + } else { + r0 = ret.Get(0).(libregraph.CollectionOfPermissionsWithAllowedValues) + } + + if rf, ok := ret.Get(1).(func(context.Context, providerv1beta1.ResourceId) error); ok { + r1 = rf(ctx, driveID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DriveItemPermissionsProvider_ListSpaceRootPermissions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListSpaceRootPermissions' +type DriveItemPermissionsProvider_ListSpaceRootPermissions_Call struct { + *mock.Call +} + +// ListSpaceRootPermissions is a helper method to define mock.On call +// - ctx context.Context +// - driveID providerv1beta1.ResourceId +func (_e *DriveItemPermissionsProvider_Expecter) ListSpaceRootPermissions(ctx interface{}, driveID interface{}) *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call { + return &DriveItemPermissionsProvider_ListSpaceRootPermissions_Call{Call: _e.mock.On("ListSpaceRootPermissions", ctx, driveID)} +} + +func (_c *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call) Run(run func(ctx context.Context, driveID providerv1beta1.ResourceId)) *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(providerv1beta1.ResourceId)) + }) + return _c +} + +func (_c *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call) Return(_a0 libregraph.CollectionOfPermissionsWithAllowedValues, _a1 error) *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call) RunAndReturn(run func(context.Context, providerv1beta1.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error)) *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call { + _c.Call.Return(run) + return _c +} + // SpaceRootInvite provides a mock function with given fields: ctx, driveID, invite func (_m *DriveItemPermissionsProvider) SpaceRootInvite(ctx context.Context, driveID providerv1beta1.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) { ret := _m.Called(ctx, driveID, invite) diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions.go b/services/graph/pkg/service/v0/api_driveitem_permissions.go index 9bf4d9134d0..9f03b0c8c42 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions.go @@ -26,10 +26,13 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/validate" ) +const invalidIdMsg = "invalid driveID or itemID" + type DriveItemPermissionsProvider interface { Invite(ctx context.Context, resourceId storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) SpaceRootInvite(ctx context.Context, driveID storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) ListPermissions(ctx context.Context, itemID storageprovider.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error) + ListSpaceRootPermissions(ctx context.Context, driveID storageprovider.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error) } // DriveItemPermissionsService contains the production business logic for everything that relates to permissions on drive items. @@ -263,6 +266,27 @@ func (s DriveItemPermissionsService) ListPermissions(ctx context.Context, itemID return collectionOfPermissions, nil } +// ListSpaceRootPermissions handles ListPermissions request on project spaces +func (s DriveItemPermissionsService) ListSpaceRootPermissions(ctx context.Context, driveID storageprovider.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error) { + collectionOfPermissions := libregraph.CollectionOfPermissionsWithAllowedValues{} + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return collectionOfPermissions, err + } + + space, err := utils.GetSpace(ctx, storagespace.FormatResourceID(driveID), gatewayClient) + if err != nil { + return collectionOfPermissions, err + } + + if space.SpaceType != "project" { + return collectionOfPermissions, errorcode.New(errorcode.InvalidRequest, "unsupported space type") + } + + rootResourceID := space.GetRoot() + return s.ListPermissions(ctx, *rootResourceID) +} + // DriveItemPermissionsService is the api that registers the http endpoints which expose needed operation to the graph api. // the business logic is delegated to the permissions service and further down to the cs3 client. type DriveItemPermissionsApi struct { @@ -281,9 +305,8 @@ func NewDriveItemPermissionsApi(driveItemPermissionService DriveItemPermissionsP func (api DriveItemPermissionsApi) Invite(w http.ResponseWriter, r *http.Request) { _, itemID, err := GetDriveAndItemIDParam(r, &api.logger) if err != nil { - msg := "invalid driveID or itemID" - api.logger.Debug().Err(err).Msg(msg) - errorcode.InvalidRequest.Render(w, r, http.StatusUnprocessableEntity, msg) + api.logger.Debug().Err(err).Msg(invalidIdMsg) + errorcode.InvalidRequest.Render(w, r, http.StatusUnprocessableEntity, invalidIdMsg) return } @@ -348,8 +371,7 @@ func (api DriveItemPermissionsApi) SpaceRootInvite(w http.ResponseWriter, r *htt func (api DriveItemPermissionsApi) ListPermissions(w http.ResponseWriter, r *http.Request) { _, itemID, err := GetDriveAndItemIDParam(r, &api.logger) if err != nil { - msg := "invalid driveID or itemID" - api.logger.Debug().Err(err).Msg(msg) + api.logger.Debug().Err(err).Msg(invalidIdMsg) errorcode.RenderError(w, r, err) return } @@ -365,3 +387,24 @@ func (api DriveItemPermissionsApi) ListPermissions(w http.ResponseWriter, r *htt render.Status(r, http.StatusOK) render.JSON(w, r, permissions) } + +func (api DriveItemPermissionsApi) ListSpaceRootPermissions(w http.ResponseWriter, r *http.Request) { + driveID, err := parseIDParam(r, "driveID") + if err != nil { + msg := "could not parse driveID" + api.logger.Debug().Err(err).Msg(msg) + errorcode.InvalidRequest.Render(w, r, http.StatusUnprocessableEntity, msg) + return + } + + ctx := r.Context() + permissions, err := api.driveItemPermissionsService.ListSpaceRootPermissions(ctx, driveID) + + if err != nil { + errorcode.RenderError(w, r, err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, permissions) +} diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions_test.go b/services/graph/pkg/service/v0/api_driveitem_permissions_test.go index ed5d838ab8f..676f730f062 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions_test.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions_test.go @@ -369,6 +369,54 @@ var _ = Describe("DriveItemPermissionsService", func() { Expect(len(permissions.LibreGraphPermissionsRolesAllowedValues)).ToNot(BeZero()) Expect(len(permissions.Value)).To(Equal(2)) }) + }) + Describe("ListSpaceRootPermissions", func() { + var ( + listSpacesResponse *provider.ListStorageSpacesResponse + driveId provider.ResourceId + listPublicSharesResponse *link.ListPublicSharesResponse + ) + + BeforeEach(func() { + driveId = provider.ResourceId{ + StorageId: "1", + SpaceId: "2", + } + + listSpacesResponse = &provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Id: &provider.StorageSpaceId{ + OpaqueId: "2", + }, + }, + }, + } + listPublicSharesResponse = &link.ListPublicSharesResponse{ + Status: status.NewOK(ctx), + } + }) + + It("adds a user to a space as expected (happy path)", func() { + listSpacesResponse.StorageSpaces[0].SpaceType = "project" + listSpacesResponse.StorageSpaces[0].Root = &provider.ResourceId{ + StorageId: "1", + SpaceId: "2", + OpaqueId: "2", + } + + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listSpacesResponse, nil) + gatewayClient.On("ListPublicShares", mock.Anything, mock.Anything).Return(listPublicSharesResponse, nil) + statResponse.Info = &provider.ResourceInfo{ + Id: listSpacesResponse.StorageSpaces[0].Root, + PermissionSet: roleconversions.NewViewerRole(false).CS3ResourcePermissions(), + } + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil) + permissions, err := driveItemPermissionsService.ListSpaceRootPermissions(context.Background(), driveId) + Expect(err).ToNot(HaveOccurred()) + Expect(len(permissions.LibreGraphPermissionsActionsAllowedValues)).ToNot(BeZero()) + }) }) }) diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index 012b82b7476..adea52f9b79 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -241,6 +241,9 @@ func NewService(opts ...Option) (Graph, error) { r.Route("/root", func(r chi.Router) { r.Post("/children", drivesDriveItemApi.CreateDriveItem) r.Post("/invite", driveItemPermissionsApi.SpaceRootInvite) + r.Route("/permissions", func(r chi.Router) { + r.Get("/", driveItemPermissionsApi.ListSpaceRootPermissions) + }) }) r.Route("/items/{itemID}", func(r chi.Router) { r.Delete("/", drivesDriveItemApi.DeleteDriveItem)