mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-24 16:41:35 -04:00
feat(graph): add POST /drive/<id>/root/invite suppport
Partial Fix: #8351
This commit is contained in:
committed by
Ralf Haferkamp
parent
c6d28caa31
commit
18d46c1416
@@ -82,6 +82,64 @@ func (_c *DriveItemPermissionsProvider_Invite_Call) RunAndReturn(run func(contex
|
||||
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)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SpaceRootInvite")
|
||||
}
|
||||
|
||||
var r0 libregraph.Permission
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemInvite) (libregraph.Permission, error)); ok {
|
||||
return rf(ctx, driveID, invite)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemInvite) libregraph.Permission); ok {
|
||||
r0 = rf(ctx, driveID, invite)
|
||||
} else {
|
||||
r0 = ret.Get(0).(libregraph.Permission)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemInvite) error); ok {
|
||||
r1 = rf(ctx, driveID, invite)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DriveItemPermissionsProvider_SpaceRootInvite_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SpaceRootInvite'
|
||||
type DriveItemPermissionsProvider_SpaceRootInvite_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SpaceRootInvite is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - driveID providerv1beta1.ResourceId
|
||||
// - invite libregraph.DriveItemInvite
|
||||
func (_e *DriveItemPermissionsProvider_Expecter) SpaceRootInvite(ctx interface{}, driveID interface{}, invite interface{}) *DriveItemPermissionsProvider_SpaceRootInvite_Call {
|
||||
return &DriveItemPermissionsProvider_SpaceRootInvite_Call{Call: _e.mock.On("SpaceRootInvite", ctx, driveID, invite)}
|
||||
}
|
||||
|
||||
func (_c *DriveItemPermissionsProvider_SpaceRootInvite_Call) Run(run func(ctx context.Context, driveID providerv1beta1.ResourceId, invite libregraph.DriveItemInvite)) *DriveItemPermissionsProvider_SpaceRootInvite_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(providerv1beta1.ResourceId), args[2].(libregraph.DriveItemInvite))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *DriveItemPermissionsProvider_SpaceRootInvite_Call) Return(_a0 libregraph.Permission, _a1 error) *DriveItemPermissionsProvider_SpaceRootInvite_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *DriveItemPermissionsProvider_SpaceRootInvite_Call) RunAndReturn(run func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemInvite) (libregraph.Permission, error)) *DriveItemPermissionsProvider_SpaceRootInvite_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewDriveItemPermissionsProvider creates a new instance of DriveItemPermissionsProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewDriveItemPermissionsProvider(t interface {
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"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/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
@@ -23,7 +23,8 @@ import (
|
||||
)
|
||||
|
||||
type DriveItemPermissionsProvider interface {
|
||||
Invite(ctx context.Context, resourceId provider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error)
|
||||
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)
|
||||
}
|
||||
|
||||
// DriveItemPermissionsService contains the production business logic for everything that relates to permissions on drive items.
|
||||
@@ -45,7 +46,7 @@ func NewDriveItemPermissionsService(logger log.Logger, gatewaySelector pool.Sele
|
||||
}
|
||||
|
||||
// Invite invites a user to a drive item.
|
||||
func (s DriveItemPermissionsService) Invite(ctx context.Context, resourceId provider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
|
||||
func (s DriveItemPermissionsService) Invite(ctx context.Context, resourceId storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return libregraph.Permission{}, err
|
||||
@@ -167,6 +168,26 @@ func (s DriveItemPermissionsService) Invite(ctx context.Context, resourceId prov
|
||||
return *permission, nil
|
||||
}
|
||||
|
||||
// SpaceRootInvite handles invitation request on project spaces
|
||||
func (s DriveItemPermissionsService) SpaceRootInvite(ctx context.Context, driveID storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return libregraph.Permission{}, err
|
||||
}
|
||||
|
||||
space, err := utils.GetSpace(ctx, storagespace.FormatResourceID(driveID), gatewayClient)
|
||||
if err != nil {
|
||||
return libregraph.Permission{}, err
|
||||
}
|
||||
|
||||
if space.SpaceType != "project" {
|
||||
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "unsupported space type")
|
||||
}
|
||||
|
||||
rootResourceID := space.GetRoot()
|
||||
return s.Invite(ctx, *rootResourceID, invite)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -215,3 +236,36 @@ func (api DriveItemPermissionsApi) Invite(w http.ResponseWriter, r *http.Request
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: []interface{}{permission}})
|
||||
}
|
||||
|
||||
func (api DriveItemPermissionsApi) SpaceRootInvite(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
|
||||
}
|
||||
|
||||
driveItemInvite := &libregraph.DriveItemInvite{}
|
||||
if err = StrictJSONUnmarshal(r.Body, driveItemInvite); err != nil {
|
||||
api.logger.Debug().Err(err).Interface("Body", r.Body).Msg("failed unmarshalling request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
if err = validate.StructCtx(ctx, driveItemInvite); err != nil {
|
||||
api.logger.Debug().Err(err).Interface("Body", r.Body).Msg("invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
permission, err := api.driveItemPermissionsService.SpaceRootInvite(ctx, driveID, *driveItemInvite)
|
||||
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: []interface{}{permission}})
|
||||
}
|
||||
|
||||
@@ -188,10 +188,107 @@ var _ = Describe("DriveItemPermissionsService", func() {
|
||||
Expect(permission.GetLibreGraphPermissionsActions()).To(HaveLen(1))
|
||||
Expect(permission.GetLibreGraphPermissionsActions()[0]).To(Equal(unifiedrole.DriveItemContentRead))
|
||||
})
|
||||
It("fails with a missing driveritem", func() {
|
||||
statResponse.Status = status.NewNotFound(context.Background(), "not found")
|
||||
permission, err := driveItemPermissionsService.Invite(context.Background(), driveItemId, driveItemInvite)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(errorcode.New(errorcode.ItemNotFound, "not found")))
|
||||
Expect(permission).To(BeZero())
|
||||
})
|
||||
})
|
||||
Describe("SpaceRootInvite", func() {
|
||||
var (
|
||||
listSpacesResponse *provider.ListStorageSpacesResponse
|
||||
createShareResponse *collaboration.CreateShareResponse
|
||||
driveItemInvite libregraph.DriveItemInvite
|
||||
driveId provider.ResourceId
|
||||
statResponse *provider.StatResponse
|
||||
getUserResponse *userpb.GetUserResponse
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
driveId = provider.ResourceId{
|
||||
StorageId: "1",
|
||||
SpaceId: "2",
|
||||
}
|
||||
ctx := revactx.ContextSetUser(context.Background(), currentUser)
|
||||
|
||||
statResponse = &provider.StatResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}
|
||||
|
||||
listSpacesResponse = &provider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*provider.StorageSpace{
|
||||
{
|
||||
Id: &provider.StorageSpaceId{
|
||||
OpaqueId: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
getUserResponse = &userpb.GetUserResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
User: &userpb.User{
|
||||
Id: &userpb.UserId{OpaqueId: "1"},
|
||||
DisplayName: "Cem Kaner",
|
||||
},
|
||||
}
|
||||
|
||||
createShareResponse = &collaboration.CreateShareResponse{
|
||||
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: "3",
|
||||
}
|
||||
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listSpacesResponse, nil)
|
||||
gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
|
||||
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
|
||||
gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil)
|
||||
driveItemInvite.Recipients = []libregraph.DriveRecipient{
|
||||
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")},
|
||||
}
|
||||
driveItemInvite.ExpirationDateTime = libregraph.PtrTime(time.Now().Add(time.Hour))
|
||||
createShareResponse.Share = &collaboration.Share{
|
||||
Id: &collaboration.ShareId{OpaqueId: "123"},
|
||||
Expiration: utils.TimeToTS(*driveItemInvite.ExpirationDateTime),
|
||||
}
|
||||
|
||||
permission, err := driveItemPermissionsService.SpaceRootInvite(context.Background(), driveId, driveItemInvite)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(permission.GetId()).To(Equal("123"))
|
||||
Expect(permission.GetExpirationDateTime().Equal(*driveItemInvite.ExpirationDateTime)).To(BeTrue())
|
||||
Expect(permission.GrantedToV2.User.GetDisplayName()).To(Equal(getUserResponse.User.DisplayName))
|
||||
Expect(permission.GrantedToV2.User.GetId()).To(Equal("1"))
|
||||
})
|
||||
It("rejects to add a user to a personal space", func() {
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listSpacesResponse, nil)
|
||||
driveItemInvite.Recipients = []libregraph.DriveRecipient{
|
||||
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")},
|
||||
}
|
||||
driveItemInvite.ExpirationDateTime = libregraph.PtrTime(time.Now().Add(time.Hour))
|
||||
createShareResponse.Share = &collaboration.Share{
|
||||
Id: &collaboration.ShareId{OpaqueId: "123"},
|
||||
Expiration: utils.TimeToTS(*driveItemInvite.ExpirationDateTime),
|
||||
}
|
||||
|
||||
permission, err := driveItemPermissionsService.SpaceRootInvite(context.Background(), driveId, driveItemInvite)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(errorcode.New(errorcode.InvalidRequest, "unsupported space type")))
|
||||
Expect(permission).To(BeZero())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("DriveItemPermissionsApiApi", func() {
|
||||
var _ = Describe("DriveItemPermissionsApi", func() {
|
||||
var (
|
||||
mockProvider *mocks.DriveItemPermissionsProvider
|
||||
httpAPI svc.DriveItemPermissionsApi
|
||||
@@ -286,4 +383,39 @@ var _ = Describe("DriveItemPermissionsApiApi", func() {
|
||||
Expect(responseRecorder.Code).To(Equal(http.StatusOK))
|
||||
})
|
||||
})
|
||||
Describe("SpaceRootInvite", func() {
|
||||
It("call the Invite provider with the correct arguments", func() {
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
inviteJson, err := json.Marshal(invite)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
onInvite := mockProvider.On("SpaceRootInvite", mock.Anything, mock.Anything, mock.Anything)
|
||||
onInvite.Return(func(ctx context.Context, driveID storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
|
||||
Expect(storagespace.FormatResourceID(driveID)).To(Equal("1$2"))
|
||||
return libregraph.Permission{}, nil
|
||||
}).Once()
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(inviteJson)).
|
||||
WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, rCTX),
|
||||
)
|
||||
httpAPI.SpaceRootInvite(responseRecorder, request)
|
||||
|
||||
Expect(responseRecorder.Code).To(Equal(http.StatusOK))
|
||||
})
|
||||
It("call the Invite provider with the correct arguments", func() {
|
||||
rCTX.URLParams.Add("driveID", "")
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
inviteJson, err := json.Marshal(invite)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(inviteJson)).
|
||||
WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, rCTX),
|
||||
)
|
||||
httpAPI.SpaceRootInvite(responseRecorder, request)
|
||||
|
||||
Expect(responseRecorder.Code).To(Equal(http.StatusUnprocessableEntity))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -237,7 +237,10 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
r.Route("/drives", func(r chi.Router) {
|
||||
r.Get("/", svc.GetAllDrives(APIVersion_1_Beta_1))
|
||||
r.Route("/{driveID}", func(r chi.Router) {
|
||||
r.Post("/root/children", drivesDriveItemApi.CreateDriveItem)
|
||||
r.Route("/root", func(r chi.Router) {
|
||||
r.Post("/children", drivesDriveItemApi.CreateDriveItem)
|
||||
r.Post("/invite", driveItemPermissionsApi.SpaceRootInvite)
|
||||
})
|
||||
r.Route("/items/{itemID}", func(r chi.Router) {
|
||||
r.Delete("/", drivesDriveItemApi.DeleteDriveItem)
|
||||
r.Post("/invite", driveItemPermissionsApi.Invite)
|
||||
|
||||
Reference in New Issue
Block a user