refactor(graph): move "Invite" to new drive item permissions service

This introduces the new DriveItemPermissionsService and DriveItemPermissionsApi to
allow for better separation of the business logic and the API handling.

As a starting point the Invite method was moved to the new service. More to follow.
This commit is contained in:
Ralf Haferkamp
2024-03-25 18:14:16 +01:00
committed by Ralf Haferkamp
parent 531c926853
commit c6d28caa31
10 changed files with 623 additions and 541 deletions

View File

@@ -8,6 +8,7 @@ packages:
dir: "mocks"
interfaces:
DrivesDriveItemProvider:
DriveItemPermissionsProvider:
HTTPClient:
Permissions:
Publisher:

View File

@@ -0,0 +1,97 @@
// Code generated by mockery v2.40.2. DO NOT EDIT.
package mocks
import (
context "context"
libregraph "github.com/owncloud/libre-graph-api-go"
mock "github.com/stretchr/testify/mock"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
)
// DriveItemPermissionsProvider is an autogenerated mock type for the DriveItemPermissionsProvider type
type DriveItemPermissionsProvider struct {
mock.Mock
}
type DriveItemPermissionsProvider_Expecter struct {
mock *mock.Mock
}
func (_m *DriveItemPermissionsProvider) EXPECT() *DriveItemPermissionsProvider_Expecter {
return &DriveItemPermissionsProvider_Expecter{mock: &_m.Mock}
}
// Invite provides a mock function with given fields: ctx, resourceId, invite
func (_m *DriveItemPermissionsProvider) Invite(ctx context.Context, resourceId providerv1beta1.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
ret := _m.Called(ctx, resourceId, invite)
if len(ret) == 0 {
panic("no return value specified for Invite")
}
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, resourceId, invite)
}
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemInvite) libregraph.Permission); ok {
r0 = rf(ctx, resourceId, 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, resourceId, invite)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DriveItemPermissionsProvider_Invite_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Invite'
type DriveItemPermissionsProvider_Invite_Call struct {
*mock.Call
}
// Invite is a helper method to define mock.On call
// - ctx context.Context
// - resourceId providerv1beta1.ResourceId
// - invite libregraph.DriveItemInvite
func (_e *DriveItemPermissionsProvider_Expecter) Invite(ctx interface{}, resourceId interface{}, invite interface{}) *DriveItemPermissionsProvider_Invite_Call {
return &DriveItemPermissionsProvider_Invite_Call{Call: _e.mock.On("Invite", ctx, resourceId, invite)}
}
func (_c *DriveItemPermissionsProvider_Invite_Call) Run(run func(ctx context.Context, resourceId providerv1beta1.ResourceId, invite libregraph.DriveItemInvite)) *DriveItemPermissionsProvider_Invite_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_Invite_Call) Return(_a0 libregraph.Permission, _a1 error) *DriveItemPermissionsProvider_Invite_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *DriveItemPermissionsProvider_Invite_Call) RunAndReturn(run func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemInvite) (libregraph.Permission, error)) *DriveItemPermissionsProvider_Invite_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 {
mock.TestingT
Cleanup(func())
}) *DriveItemPermissionsProvider {
mock := &DriveItemPermissionsProvider{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.40.1. DO NOT EDIT.
// Code generated by mockery v2.40.2. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.40.1. DO NOT EDIT.
// Code generated by mockery v2.40.2. DO NOT EDIT.
package mocks

View File

@@ -0,0 +1,217 @@
package svc
import (
"context"
"net/http"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
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/utils"
"github.com/go-chi/render"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
"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"
"github.com/owncloud/ocis/v2/services/graph/pkg/validate"
)
type DriveItemPermissionsProvider interface {
Invite(ctx context.Context, resourceId provider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error)
}
// DriveItemPermissionsService contains the production business logic for everything that relates to permissions on drive items.
type DriveItemPermissionsService struct {
logger log.Logger
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
identityCache identity.IdentityCache
resharingEnabled bool
}
// NewDriveItemPermissionsService creates a new DriveItemPermissionsService
func NewDriveItemPermissionsService(logger log.Logger, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], identityCache identity.IdentityCache, resharing bool) (DriveItemPermissionsService, error) {
return DriveItemPermissionsService{
logger: log.Logger{Logger: logger.With().Str("graph api", "DrivesDriveItemService").Logger()},
gatewaySelector: gatewaySelector,
identityCache: identityCache,
resharingEnabled: resharing,
}, nil
}
// Invite invites a user to a drive item.
func (s DriveItemPermissionsService) Invite(ctx context.Context, resourceId provider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
return libregraph.Permission{}, err
}
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &resourceId}})
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
s.logger.Warn().Err(errCode).Interface("stat.res", statResponse).Msg("stat failed")
return libregraph.Permission{}, *errCode
}
resourceInfo := statResponse.GetInfo()
condition := unifiedrole.UnifiedRoleConditionGrantee
if IsSpaceRoot(resourceInfo.GetId()) {
condition = unifiedrole.UnifiedRoleConditionOwner
}
unifiedRolePermissions := []*libregraph.UnifiedRolePermission{{AllowedResourceActions: invite.LibreGraphPermissionsActions}}
for _, roleID := range invite.GetRoles() {
role, err := unifiedrole.NewUnifiedRoleFromID(roleID, s.resharingEnabled)
if err != nil {
s.logger.Debug().Err(err).Interface("role", invite.GetRoles()[0]).Msg("unable to convert requested role")
return libregraph.Permission{}, err
}
allowedResourceActions := unifiedrole.GetAllowedResourceActions(role, condition)
if len(allowedResourceActions) == 0 {
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "role not applicable to this resource")
}
unifiedRolePermissions = append(unifiedRolePermissions, conversions.ToPointerSlice(role.GetRolePermissions())...)
}
driveRecipient := invite.GetRecipients()[0]
objectID := driveRecipient.GetObjectId()
cs3ResourcePermissions := unifiedrole.PermissionsToCS3ResourcePermissions(unifiedRolePermissions)
createShareRequest := &collaboration.CreateShareRequest{
ResourceInfo: resourceInfo,
Grant: &collaboration.ShareGrant{
Permissions: &collaboration.SharePermissions{
Permissions: cs3ResourcePermissions,
},
},
}
permission := &libregraph.Permission{}
if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(*cs3ResourcePermissions, condition, s.resharingEnabled); role != nil {
permission.Roles = []string{role.GetId()}
}
if len(permission.GetRoles()) == 0 {
permission.LibreGraphPermissionsActions = unifiedrole.CS3ResourcePermissionsToLibregraphActions(*cs3ResourcePermissions)
}
switch driveRecipient.GetLibreGraphRecipientType() {
case "group":
group, err := s.identityCache.GetGroup(ctx, objectID)
if err != nil {
s.logger.Debug().Err(err).Interface("groupId", objectID).Msg("failed group lookup")
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, err.Error())
}
createShareRequest.GetGrant().Grantee = &storageprovider.Grantee{
Type: storageprovider.GranteeType_GRANTEE_TYPE_GROUP,
Id: &storageprovider.Grantee_GroupId{GroupId: &grouppb.GroupId{
OpaqueId: group.GetId(),
}},
}
permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
Group: &libregraph.Identity{
DisplayName: group.GetDisplayName(),
Id: conversions.ToPointer(group.GetId()),
},
}
default:
user, err := s.identityCache.GetUser(ctx, objectID)
if err != nil {
s.logger.Debug().Err(err).Interface("userId", objectID).Msg("failed user lookup")
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, err.Error())
}
createShareRequest.GetGrant().Grantee = &storageprovider.Grantee{
Type: storageprovider.GranteeType_GRANTEE_TYPE_USER,
Id: &storageprovider.Grantee_UserId{UserId: &userpb.UserId{
OpaqueId: user.GetId(),
}},
}
permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
User: &libregraph.Identity{
DisplayName: user.GetDisplayName(),
Id: conversions.ToPointer(user.GetId()),
},
}
}
if invite.ExpirationDateTime != nil {
createShareRequest.GetGrant().Expiration = utils.TimeToTS(*invite.ExpirationDateTime)
}
createShareResponse, err := gatewayClient.CreateShare(ctx, createShareRequest)
if errCode := errorcode.FromCS3Status(createShareResponse.GetStatus(), err); errCode != nil {
s.logger.Debug().Err(err).Msg("share creation failed")
return libregraph.Permission{}, *errCode
}
if id := createShareResponse.GetShare().GetId().GetOpaqueId(); id != "" {
permission.Id = conversions.ToPointer(id)
} else if IsSpaceRoot(resourceInfo.GetId()) {
// permissions on a space root are not handled by a share manager so
// they don't get a share-id
permission.SetId(identitySetToSpacePermissionID(permission.GetGrantedToV2()))
}
if expiration := createShareResponse.GetShare().GetExpiration(); expiration != nil {
permission.SetExpirationDateTime(utils.TSToTime(expiration))
}
return *permission, nil
}
// 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 {
logger log.Logger
driveItemPermissionsService DriveItemPermissionsProvider
}
// NewDriveItemPermissionsApi creates a new DriveItemPermissionsApi
func NewDriveItemPermissionsApi(driveItemPermissionService DriveItemPermissionsProvider, logger log.Logger) (DriveItemPermissionsApi, error) {
return DriveItemPermissionsApi{
logger: log.Logger{Logger: logger.With().Str("graph api", "DrivesDriveItemApi").Logger()},
driveItemPermissionsService: driveItemPermissionService,
}, nil
}
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)
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.Invite(ctx, itemID, *driveItemInvite)
if err != nil {
errorcode.RenderError(w, r, err)
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, &ListResponse{Value: []interface{}{permission}})
}

View File

@@ -0,0 +1,289 @@
package svc_test
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
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"
revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
"github.com/go-chi/chi/v5"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/graph/mocks"
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
"github.com/owncloud/ocis/v2/services/graph/pkg/identity"
svc "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
"github.com/stretchr/testify/mock"
"github.com/tidwall/gjson"
)
var _ = Describe("DriveItemPermissionsService", func() {
var (
driveItemPermissionsService svc.DriveItemPermissionsService
gatewayClient *cs3mocks.GatewayAPIClient
gatewaySelector *mocks.Selectable[gateway.GatewayAPIClient]
currentUser = &userpb.User{
Id: &userpb.UserId{
OpaqueId: "user",
},
}
)
BeforeEach(func() {
logger := log.NewLogger()
gatewayClient = cs3mocks.NewGatewayAPIClient(GinkgoT())
gatewaySelector = mocks.NewSelectable[gateway.GatewayAPIClient](GinkgoT())
gatewaySelector.On("Next").Return(gatewayClient, nil)
cache := identity.NewIdentityCache(identity.IdentityCacheWithGatewaySelector(gatewaySelector))
service, err := svc.NewDriveItemPermissionsService(logger, gatewaySelector, cache, false)
Expect(err).ToNot(HaveOccurred())
driveItemPermissionsService = service
})
Describe("Invite", func() {
var (
createShareResponse *collaboration.CreateShareResponse
driveItemInvite libregraph.DriveItemInvite
driveItemId provider.ResourceId
statResponse *provider.StatResponse
getUserResponse *userpb.GetUserResponse
getGroupResponse *grouppb.GetGroupResponse
)
BeforeEach(func() {
driveItemId = provider.ResourceId{
StorageId: "1",
SpaceId: "2",
OpaqueId: "3",
}
ctx := revactx.ContextSetUser(context.Background(), currentUser)
statResponse = &provider.StatResponse{
Status: status.NewOK(ctx),
}
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
getUserResponse = &userpb.GetUserResponse{
Status: status.NewOK(ctx),
User: &userpb.User{
Id: &userpb.UserId{OpaqueId: "1"},
DisplayName: "Cem Kaner",
},
}
getGroupResponse = &grouppb.GetGroupResponse{
Status: status.NewOK(ctx),
Group: &grouppb.Group{
Id: &grouppb.GroupId{OpaqueId: "2"},
GroupName: "Florida Institute of Technology",
DisplayName: "Florida Institute of Technology",
},
}
createShareResponse = &collaboration.CreateShareResponse{
Status: status.NewOK(ctx),
}
})
It("creates user shares as expected (happy path)", func() {
gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, 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.Invite(context.Background(), driveItemId, 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("creates group shares as expected (happy path)", func() {
gatewayClient.On("GetGroup", mock.Anything, mock.Anything).Return(getGroupResponse, nil)
gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil)
driveItemInvite.Recipients = []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("2"), LibreGraphRecipientType: libregraph.PtrString("group")},
}
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.Invite(context.Background(), driveItemId, driveItemInvite)
Expect(err).ToNot(HaveOccurred())
Expect(permission.GetId()).To(Equal("123"))
Expect(permission.GetExpirationDateTime().Equal(*driveItemInvite.ExpirationDateTime)).To(BeTrue())
Expect(permission.GrantedToV2.Group.GetDisplayName()).To(Equal(getGroupResponse.Group.DisplayName))
Expect(permission.GrantedToV2.Group.GetId()).To(Equal("2"))
})
It("with roles (happy path)", func() {
gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil)
driveItemInvite.Recipients = []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")},
}
driveItemInvite.Roles = []string{unifiedrole.NewViewerUnifiedRole(true).GetId()}
permission, err := driveItemPermissionsService.Invite(context.Background(), driveItemId, driveItemInvite)
Expect(err).ToNot(HaveOccurred())
Expect(permission.GetRoles()).To(HaveLen(1))
Expect(permission.GetRoles()[0]).To(Equal(unifiedrole.NewViewerUnifiedRole(true).GetId()))
})
It("fails with wrong role", func() {
driveItemInvite.Recipients = []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")},
}
driveItemInvite.Roles = []string{unifiedrole.NewManagerUnifiedRole().GetId()}
permission, err := driveItemPermissionsService.Invite(context.Background(), driveItemId, driveItemInvite)
Expect(err).To(MatchError(errorcode.New(errorcode.InvalidRequest, "role not applicable to this resource")))
Expect(permission).To(BeZero())
})
It("with actions (happy path)", func() {
gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil)
driveItemInvite.Recipients = []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")},
}
driveItemInvite.Roles = nil
driveItemInvite.LibreGraphPermissionsActions = []string{unifiedrole.DriveItemContentRead}
permission, err := driveItemPermissionsService.Invite(context.Background(), driveItemId, driveItemInvite)
Expect(err).ToNot(HaveOccurred())
Expect(permission).NotTo(BeZero())
Expect(permission.GetRoles()).To(HaveLen(0))
Expect(permission.GetLibreGraphPermissionsActions()).To(HaveLen(1))
Expect(permission.GetLibreGraphPermissionsActions()[0]).To(Equal(unifiedrole.DriveItemContentRead))
})
})
})
var _ = Describe("DriveItemPermissionsApiApi", func() {
var (
mockProvider *mocks.DriveItemPermissionsProvider
httpAPI svc.DriveItemPermissionsApi
rCTX *chi.Context
invite libregraph.DriveItemInvite
)
BeforeEach(func() {
logger := log.NewLogger()
mockProvider = mocks.NewDriveItemPermissionsProvider(GinkgoT())
api, err := svc.NewDriveItemPermissionsApi(mockProvider, logger)
Expect(err).ToNot(HaveOccurred())
httpAPI = api
rCTX = chi.NewRouteContext()
rCTX.URLParams.Add("driveID", "1$2")
invite = libregraph.DriveItemInvite{
Recipients: []libregraph.DriveRecipient{
{
ObjectId: libregraph.PtrString("1"),
LibreGraphRecipientType: libregraph.PtrString("user")},
},
Roles: []string{unifiedrole.NewViewerUnifiedRole(true).GetId()},
}
})
checkDriveIDAndItemIDValidation := func(handler http.HandlerFunc) {
rCTX.URLParams.Add("itemID", "3$4!5")
responseRecorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodPost, "/", nil).
WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, rCTX),
)
handler(responseRecorder, request)
Expect(responseRecorder.Code).To(Equal(http.StatusUnprocessableEntity))
jsonData := gjson.Get(responseRecorder.Body.String(), "error")
Expect(jsonData.Get("message").String()).To(Equal("invalid driveID or itemID"))
}
Describe("Invite", func() {
It("validates the driveID and itemID url param", func() {
checkDriveIDAndItemIDValidation(httpAPI.Invite)
})
It("return an error when the Invite provider errors", func() {
rCTX.URLParams.Add("itemID", "1$2!3")
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),
)
onInvite := mockProvider.On("Invite", mock.Anything, mock.Anything, mock.Anything)
onInvite.Return(func(ctx context.Context, resourceID storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
return libregraph.Permission{}, errors.New("any")
}).Once()
httpAPI.Invite(responseRecorder, request)
Expect(responseRecorder.Code).To(Equal(http.StatusInternalServerError))
})
It("call the Invite provider with the correct arguments", func() {
rCTX.URLParams.Add("itemID", "1$2!3")
responseRecorder := httptest.NewRecorder()
inviteJson, err := json.Marshal(invite)
Expect(err).ToNot(HaveOccurred())
onInvite := mockProvider.On("Invite", mock.Anything, mock.Anything, mock.Anything)
onInvite.Return(func(ctx context.Context, resourceID storageprovider.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
Expect(storagespace.FormatResourceID(resourceID)).To(Equal("1$2!3"))
return libregraph.Permission{}, nil
}).Once()
request := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(inviteJson)).
WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, rCTX),
)
httpAPI.Invite(responseRecorder, request)
Expect(responseRecorder.Code).To(Equal(http.StatusOK))
})
})
})

View File

@@ -15,7 +15,6 @@ import (
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
@@ -452,156 +451,6 @@ func (g Graph) ListPermissions(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, collectionOfPermissions)
}
// Invite invites a user to a storage drive (space).
func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
gatewayClient, ok := g.GetGatewayClient(w, r)
if !ok {
return
}
_, itemID, err := GetDriveAndItemIDParam(r, g.logger)
if err != nil {
errorcode.RenderError(w, r, err)
return
}
driveItemInvite := &libregraph.DriveItemInvite{}
if err := StrictJSONUnmarshal(r.Body, driveItemInvite); err != nil {
g.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 {
g.logger.Debug().Err(err).Interface("Body", r.Body).Msg("invalid request body")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse).Msg("stat failed")
errCode.Render(w, r)
return
}
condition := unifiedrole.UnifiedRoleConditionGrantee
if IsSpaceRoot(statResponse.GetInfo().GetId()) {
condition = unifiedrole.UnifiedRoleConditionOwner
}
unifiedRolePermissions := []*libregraph.UnifiedRolePermission{{AllowedResourceActions: driveItemInvite.LibreGraphPermissionsActions}}
for _, roleID := range driveItemInvite.GetRoles() {
role, err := unifiedrole.NewUnifiedRoleFromID(roleID, g.config.FilesSharing.EnableResharing)
if err != nil {
g.logger.Debug().Err(err).Interface("role", driveItemInvite.GetRoles()[0]).Msg("unable to convert requested role")
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
return
}
allowedResourceActions := unifiedrole.GetAllowedResourceActions(role, condition)
if len(allowedResourceActions) == 0 {
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "role not applicable to this resource")
return
}
unifiedRolePermissions = append(unifiedRolePermissions, conversions.ToPointerSlice(role.GetRolePermissions())...)
}
driveRecipient := driveItemInvite.GetRecipients()[0]
objectID := driveRecipient.GetObjectId()
cs3ResourcePermissions := unifiedrole.PermissionsToCS3ResourcePermissions(unifiedRolePermissions)
createShareRequest := &collaboration.CreateShareRequest{
ResourceInfo: statResponse.GetInfo(),
Grant: &collaboration.ShareGrant{
Permissions: &collaboration.SharePermissions{
Permissions: cs3ResourcePermissions,
},
},
}
permission := &libregraph.Permission{}
if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole(*cs3ResourcePermissions, condition, g.config.FilesSharing.EnableResharing); role != nil {
permission.Roles = []string{role.GetId()}
}
if len(permission.GetRoles()) == 0 {
permission.LibreGraphPermissionsActions = unifiedrole.CS3ResourcePermissionsToLibregraphActions(*cs3ResourcePermissions)
}
switch driveRecipient.GetLibreGraphRecipientType() {
case "group":
group, err := g.identityCache.GetGroup(ctx, objectID)
if err != nil {
g.logger.Debug().Err(err).Interface("groupId", objectID).Msg("failed group lookup")
errorcode.GeneralException.Render(w, r, http.StatusBadRequest, err.Error())
return
}
createShareRequest.GetGrant().Grantee = &storageprovider.Grantee{
Type: storageprovider.GranteeType_GRANTEE_TYPE_GROUP,
Id: &storageprovider.Grantee_GroupId{GroupId: &grouppb.GroupId{
OpaqueId: group.GetId(),
}},
}
permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
Group: &libregraph.Identity{
DisplayName: group.GetDisplayName(),
Id: conversions.ToPointer(group.GetId()),
},
}
default:
user, err := g.identityCache.GetUser(ctx, objectID)
if err != nil {
g.logger.Debug().Err(err).Interface("userId", objectID).Msg("failed user lookup")
errorcode.GeneralException.Render(w, r, http.StatusBadRequest, err.Error())
return
}
createShareRequest.GetGrant().Grantee = &storageprovider.Grantee{
Type: storageprovider.GranteeType_GRANTEE_TYPE_USER,
Id: &storageprovider.Grantee_UserId{UserId: &userpb.UserId{
OpaqueId: user.GetId(),
}},
}
permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
User: &libregraph.Identity{
DisplayName: user.GetDisplayName(),
Id: conversions.ToPointer(user.GetId()),
},
}
}
if driveItemInvite.ExpirationDateTime != nil {
createShareRequest.GetGrant().Expiration = utils.TimeToTS(*driveItemInvite.ExpirationDateTime)
}
createShareResponse, err := gatewayClient.CreateShare(ctx, createShareRequest)
if errCode := errorcode.FromCS3Status(createShareResponse.GetStatus(), err); errCode != nil {
g.logger.Debug().Err(err).Msg("share creation failed")
errCode.Render(w, r)
return
}
if id := createShareResponse.GetShare().GetId().GetOpaqueId(); id != "" {
permission.Id = conversions.ToPointer(id)
} else if IsSpaceRoot(statResponse.GetInfo().GetId()) {
// permissions on a space root are not handled by a share manager, so
// they don't get a share-id
permission.SetId(identitySetToSpacePermissionID(permission.GetGrantedToV2()))
}
if expiration := createShareResponse.GetShare().GetExpiration(); expiration != nil {
permission.SetExpirationDateTime(utils.TSToTime(expiration))
}
render.Status(r, http.StatusOK)
render.JSON(w, r, &ListResponse{Value: []interface{}{permission}})
}
// UpdatePermission updates a Permission of a Drive item
func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) {
_, itemID, err := GetDriveAndItemIDParam(r, g.logger)

View File

@@ -11,7 +11,6 @@ import (
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
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"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
@@ -22,7 +21,6 @@ import (
. "github.com/onsi/gomega"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/stretchr/testify/mock"
"github.com/tidwall/gjson"
"google.golang.org/grpc"
roleconversions "github.com/cs3org/reva/v2/pkg/conversions"
@@ -30,15 +28,12 @@ import (
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
"github.com/cs3org/reva/v2/pkg/storagespace"
revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/utils"
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
"github.com/owncloud/ocis/v2/services/graph/mocks"
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
@@ -1074,381 +1069,6 @@ var _ = Describe("Driveitems", func() {
})
})
Describe("Invite", func() {
var (
driveItemInvite *libregraph.DriveItemInvite
statMock *mock.Call
statResponse *provider.StatResponse
getUserResponse *userpb.GetUserResponse
getUserMock *mock.Call
getGroupResponse *grouppb.GetGroupResponse
getGroupMock *mock.Call
createShareResponse *collaboration.CreateShareResponse
createShareMock *mock.Call
)
BeforeEach(func() {
rctx := chi.NewRouteContext()
rctx.URLParams.Add("driveID", "1$2")
rctx.URLParams.Add("itemID", "1$2!3")
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
ctx = revactx.ContextSetUser(ctx, currentUser)
driveItemInvite = &libregraph.DriveItemInvite{
Recipients: []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")},
},
Roles: []string{unifiedrole.NewViewerUnifiedRole(true).GetId()},
}
statMock = gatewayClient.On("Stat", mock.Anything, mock.Anything)
statResponse = &provider.StatResponse{
Status: status.NewOK(ctx),
}
statMock.Return(statResponse, nil)
getUserMock = gatewayClient.On("GetUser", mock.Anything, mock.Anything)
getUserResponse = &userpb.GetUserResponse{
Status: status.NewOK(ctx),
User: &userpb.User{
Id: &userpb.UserId{OpaqueId: "1"},
DisplayName: "Cem Kaner",
},
}
getUserMock.Return(getUserResponse, nil)
getGroupMock = gatewayClient.On("GetGroup", mock.Anything, mock.Anything)
getGroupResponse = &grouppb.GetGroupResponse{
Status: status.NewOK(ctx),
Group: &grouppb.Group{
Id: &grouppb.GroupId{OpaqueId: "2"},
GroupName: "Florida Institute of Technology",
},
}
getGroupMock.Return(getGroupResponse, nil)
createShareMock = gatewayClient.On("CreateShare", mock.Anything, mock.Anything)
createShareResponse = &collaboration.CreateShareResponse{
Status: status.NewOK(ctx),
}
createShareMock.Return(createShareResponse, nil)
})
toJSONReader := func(v any) *strings.Reader {
driveItemInviteBytes, err := json.Marshal(v)
Expect(err).ToNot(HaveOccurred())
return strings.NewReader(string(driveItemInviteBytes))
}
It("creates user shares as expected (happy path)", func() {
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),
}
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
jsonData := gjson.Get(rr.Body.String(), "value")
Expect(rr.Code).To(Equal(http.StatusOK))
Expect(jsonData.Get("#").Num).To(Equal(float64(1)))
Expect(jsonData.Get("0.id").Str).To(Equal("123"))
Expect(jsonData.Get("0.expirationDateTime").Str).To(Equal(driveItemInvite.ExpirationDateTime.Format(time.RFC3339Nano)))
Expect(jsonData.Get("#.grantedToV2.user.displayName").Array()[0].Str).To(Equal(getUserResponse.User.DisplayName))
Expect(jsonData.Get("#.grantedToV2.user.id").Array()[0].Str).To(Equal("1"))
})
It("creates group shares as expected (happy path)", func() {
driveItemInvite.Recipients = []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("2"), LibreGraphRecipientType: libregraph.PtrString("group")},
}
driveItemInvite.ExpirationDateTime = libregraph.PtrTime(time.Now().Add(time.Hour))
createShareResponse.Share = &collaboration.Share{
Id: &collaboration.ShareId{OpaqueId: "123"},
Expiration: utils.TimeToTS(*driveItemInvite.ExpirationDateTime),
}
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
jsonData := gjson.Get(rr.Body.String(), "value")
Expect(rr.Code).To(Equal(http.StatusOK))
Expect(jsonData.Get("#").Num).To(Equal(float64(1)))
Expect(jsonData.Get("0.id").Str).To(Equal("123"))
Expect(jsonData.Get("0.expirationDateTime").Str).To(Equal(driveItemInvite.ExpirationDateTime.Format(time.RFC3339Nano)))
Expect(jsonData.Get("#.grantedToV2.group.displayName").Array()[0].Str).To(Equal(getGroupResponse.Group.GroupName))
Expect(jsonData.Get("#.grantedToV2.group.id").Array()[0].Str).To(Equal("2"))
})
It("with roles (happy path)", func() {
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
jsonData := gjson.Get(rr.Body.String(), "value")
Expect(rr.Code).To(Equal(http.StatusOK))
Expect(jsonData.Get(`0.@libre\.graph\.permissions\.actions`).Exists()).To(BeFalse())
Expect(jsonData.Get("0.roles.#").Num).To(Equal(float64(1)))
Expect(jsonData.Get("0.roles.0").String()).To(Equal(unifiedrole.NewViewerUnifiedRole(true).GetId()))
})
It("fails with wrong role", func() {
driveItemInvite.Roles = []string{unifiedrole.NewManagerUnifiedRole().GetId()}
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("with actions (happy path)", func() {
driveItemInvite.Roles = nil
driveItemInvite.LibreGraphPermissionsActions = []string{unifiedrole.DriveItemContentRead}
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
jsonData := gjson.Get(rr.Body.String(), "value")
Expect(rr.Code).To(Equal(http.StatusOK))
Expect(jsonData.Get("0.roles").Exists()).To(BeFalse())
Expect(jsonData.Get(`0.@libre\.graph\.permissions\.actions.#`).Num).To(Equal(float64(1)))
Expect(jsonData.Get(`0.@libre\.graph\.permissions\.actions.0`).String()).To(Equal(unifiedrole.DriveItemContentRead))
})
It("fails if the request body is empty", func() {
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", nil).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
DescribeTable("request validations",
func(body func() *strings.Reader, code int) {
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", body()).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(code))
},
Entry("fails on unknown fields", func() *strings.Reader {
return strings.NewReader(`{"unknown":"field"}`)
}, http.StatusBadRequest),
)
DescribeTable("GetGroup",
func(prep func(), code int) {
driveItemInvite.Recipients = []libregraph.DriveRecipient{
{ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("group")},
}
prep()
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(code))
getGroupMock.Parent.AssertNumberOfCalls(GinkgoT(), "GetGroup", 1)
},
Entry("fails if not ok", func() {
getGroupResponse.Status = status.NewNotFound(context.Background(), "")
}, http.StatusBadRequest),
Entry("fails if errors", func() {
getGroupMock.Return(nil, errors.New("error"))
}, http.StatusBadRequest),
)
DescribeTable("GetUser",
func(prep func(), code int) {
prep()
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(code))
getUserMock.Parent.AssertNumberOfCalls(GinkgoT(), "GetUser", 1)
},
Entry("fails if not ok", func() {
getUserResponse.Status = status.NewInvalid(context.Background(), "")
}, http.StatusBadRequest),
Entry("fails if errors", func() {
getUserMock.Return(nil, errors.New("error"))
}, http.StatusBadRequest),
)
DescribeTable("CreateShare",
func(prep func(), code int) {
prep()
svc.Invite(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(code))
createShareMock.Parent.AssertNumberOfCalls(GinkgoT(), "CreateShare", 1)
},
Entry("fails if not ok", func() {
createShareResponse.Status = status.NewNotFound(context.Background(), "")
}, http.StatusNotFound),
Entry("fails if errors", func() {
createShareMock.Return(nil, errors.New("error"))
}, http.StatusInternalServerError),
)
})
Describe("ListPermissions", func() {
var (
statMock *mock.Call
statResponse *provider.StatResponse
listSharesMock *mock.Call
listSharesResponse *collaboration.ListSharesResponse
listPublicSharesMock *mock.Call
listPublicSharesResponse *link.ListPublicSharesResponse
rctx *chi.Context
)
toResourceID := func(in string) *provider.ResourceId {
out, err := storagespace.ParseID(in)
Expect(err).To(BeNil())
return &out
}
BeforeEach(func() {
rctx = chi.NewRouteContext()
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
ctx = revactx.ContextSetUser(ctx, currentUser)
statMock = gatewayClient.On("Stat", mock.Anything, mock.Anything)
statResponse = &provider.StatResponse{
Status: status.NewOK(ctx),
Info: &provider.ResourceInfo{
Id: toResourceID("1$2!3"),
PermissionSet: unifiedrole.PermissionsToCS3ResourcePermissions(
conversions.ToPointerSlice(unifiedrole.NewViewerUnifiedRole(true).GetRolePermissions()),
),
Owner: &userpb.UserId{},
},
}
statMock.Return(statResponse, nil)
listSharesMock = gatewayClient.On("ListShares", mock.Anything, mock.Anything)
listSharesResponse = &collaboration.ListSharesResponse{
Status: status.NewOK(ctx),
Shares: []*collaboration.Share{{
Id: &collaboration.ShareId{OpaqueId: "123"},
ResourceId: toResourceID("1$2!3"),
Grantee: &provider.Grantee{},
Permissions: &collaboration.SharePermissions{
Permissions: unifiedrole.PermissionsToCS3ResourcePermissions(
conversions.ToPointerSlice(unifiedrole.NewViewerUnifiedRole(true).GetRolePermissions()),
),
},
}},
}
listSharesMock.Return(listSharesResponse, nil)
listPublicSharesMock = gatewayClient.On("ListPublicShares", mock.Anything, mock.Anything)
listPublicSharesResponse = &link.ListPublicSharesResponse{
Status: status.NewOK(ctx),
}
listPublicSharesMock.Return(listPublicSharesResponse, nil)
})
It("lists permissions", func() {
rctx.URLParams.Add("driveID", "1$2")
rctx.URLParams.Add("itemID", "1$2!3")
svc.ListPermissions(
rr,
httptest.NewRequest(http.MethodGet, "/", nil).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusOK))
actions := gjson.Get(rr.Body.String(), `@libre\.graph\.permissions\.actions\.allowedValues`)
Expect(actions.Get("#").Num).To(Equal(float64(7)))
roles := gjson.Get(rr.Body.String(), `@libre\.graph\.permissions\.roles\.allowedValues`)
Expect(roles.Get("#").Num).To(Equal(float64(1)))
Expect(roles.Get("0.id").Str).To(Equal("b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"))
Expect(roles.Get("0.rolePermissions").Exists()).To(BeFalse())
value := gjson.Get(rr.Body.String(), "value")
Expect(value.Get("#").Num).To(Equal(float64(1)))
Expect(value.Get("0.id").Str).To(Equal("123"))
})
It("lists permissions on a storage space", func() {
rctx.URLParams.Add("driveID", "1$2")
rctx.URLParams.Add("itemID", "1$2!2")
statResponse.Info.Id.OpaqueId = "2"
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listSpacesResponse, nil)
getUserMock := gatewayClient.On("GetUser", mock.Anything, mock.Anything)
getUserMockResponse := &userpb.GetUserResponse{
Status: status.NewOK(ctx),
User: &userpb.User{
Id: &userpb.UserId{OpaqueId: "userid"},
DisplayName: "Test User",
},
}
getUserMock.Return(getUserMockResponse, nil)
svc.ListPermissions(
rr,
httptest.NewRequest(http.MethodGet, "/", nil).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusOK))
p := libregraph.NewCollectionOfPermissions()
err := json.Unmarshal(rr.Body.Bytes(), p)
Expect(err).To(BeNil())
permissions := p.GetValue()
Expect(len(permissions)).To(Equal(1))
Expect(permissions[0].GetId()).ToNot(Equal(""))
})
})
Describe("GetRootDriveChildren", func() {
It("handles ListStorageSpaces not found", func() {
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{

View File

@@ -112,7 +112,6 @@ type Service interface {
CreateLink(w http.ResponseWriter, r *http.Request)
SetLinkPassword(writer http.ResponseWriter, request *http.Request)
Invite(w http.ResponseWriter, r *http.Request)
ListPermissions(w http.ResponseWriter, r *http.Request)
UpdatePermission(w http.ResponseWriter, r *http.Request)
DeletePermission(w http.ResponseWriter, r *http.Request)
@@ -214,6 +213,16 @@ func NewService(opts ...Option) (Graph, error) {
return svc, err
}
driveItemPermissionsService, err := NewDriveItemPermissionsService(options.Logger, options.GatewaySelector, identityCache, options.Config.FilesSharing.EnableResharing)
if err != nil {
return svc, err
}
driveItemPermissionsApi, err := NewDriveItemPermissionsApi(driveItemPermissionsService, options.Logger)
if err != nil {
return svc, err
}
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
r.Use(middleware.StripSlashes)
@@ -231,7 +240,7 @@ func NewService(opts ...Option) (Graph, error) {
r.Post("/root/children", drivesDriveItemApi.CreateDriveItem)
r.Route("/items/{itemID}", func(r chi.Router) {
r.Delete("/", drivesDriveItemApi.DeleteDriveItem)
r.Post("/invite", svc.Invite)
r.Post("/invite", driveItemPermissionsApi.Invite)
r.Route("/permissions", func(r chi.Router) {
r.Get("/", svc.ListPermissions)
r.Route("/{permissionID}", func(r chi.Router) {

View File

@@ -889,7 +889,7 @@ Feature: Send a sharing invitations
"code": {
"type": "string",
"enum": [
"generalException"
"invalidRequest"
]
},
"message": {
@@ -940,7 +940,7 @@ Feature: Send a sharing invitations
"properties": {
"code": {
"type": "string",
"pattern": "generalException"
"pattern": "invalidRequest"
},
"message": {
"type": "string",
@@ -1113,7 +1113,7 @@ Feature: Send a sharing invitations
"code": {
"type": "string",
"enum": [
"generalException"
"invalidRequest"
]
},
"message": {
@@ -1224,7 +1224,7 @@ Feature: Send a sharing invitations
"properties": {
"code": {
"type": "string",
"enum": ["generalException"]
"enum": ["invalidRequest"]
},
"message": {
"type": "string",
@@ -2375,14 +2375,14 @@ Feature: Send a sharing invitations
| sharee | grp1 |
| shareType | group |
| permissionsAction | <permissions-action> |
Then the HTTP status code should be "400"
Then the HTTP status code should be "400"
When user "Alice" sends the following share invitation using the Graph API:
| resource | FolderToShare |
| space | Personal |
| sharee | Brian |
| shareType | user |
| permissionsAction | <permissions-action> |
Then the HTTP status code should be "400"
Then the HTTP status code should be "400"
Examples:
| permissions-action |
| permissions/create |