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:
committed by
Ralf Haferkamp
parent
531c926853
commit
c6d28caa31
@@ -8,6 +8,7 @@ packages:
|
|||||||
dir: "mocks"
|
dir: "mocks"
|
||||||
interfaces:
|
interfaces:
|
||||||
DrivesDriveItemProvider:
|
DrivesDriveItemProvider:
|
||||||
|
DriveItemPermissionsProvider:
|
||||||
HTTPClient:
|
HTTPClient:
|
||||||
Permissions:
|
Permissions:
|
||||||
Publisher:
|
Publisher:
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
package mocks
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
package mocks
|
||||||
|
|
||||||
|
|||||||
@@ -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}})
|
||||||
|
}
|
||||||
@@ -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))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
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"
|
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/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)
|
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
|
// UpdatePermission updates a Permission of a Drive item
|
||||||
func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) {
|
func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) {
|
||||||
_, itemID, err := GetDriveAndItemIDParam(r, g.logger)
|
_, itemID, err := GetDriveAndItemIDParam(r, g.logger)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
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"
|
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||||
@@ -22,7 +21,6 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
roleconversions "github.com/cs3org/reva/v2/pkg/conversions"
|
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/errorcode"
|
||||||
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
|
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
|
||||||
|
|
||||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
|
||||||
|
|
||||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||||
"github.com/cs3org/reva/v2/pkg/utils"
|
"github.com/cs3org/reva/v2/pkg/utils"
|
||||||
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
|
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/ocis-pkg/shared"
|
||||||
"github.com/owncloud/ocis/v2/services/graph/mocks"
|
"github.com/owncloud/ocis/v2/services/graph/mocks"
|
||||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
"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() {
|
Describe("GetRootDriveChildren", func() {
|
||||||
It("handles ListStorageSpaces not found", func() {
|
It("handles ListStorageSpaces not found", func() {
|
||||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
|
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ type Service interface {
|
|||||||
CreateLink(w http.ResponseWriter, r *http.Request)
|
CreateLink(w http.ResponseWriter, r *http.Request)
|
||||||
SetLinkPassword(writer http.ResponseWriter, request *http.Request)
|
SetLinkPassword(writer http.ResponseWriter, request *http.Request)
|
||||||
|
|
||||||
Invite(w http.ResponseWriter, r *http.Request)
|
|
||||||
ListPermissions(w http.ResponseWriter, r *http.Request)
|
ListPermissions(w http.ResponseWriter, r *http.Request)
|
||||||
UpdatePermission(w http.ResponseWriter, r *http.Request)
|
UpdatePermission(w http.ResponseWriter, r *http.Request)
|
||||||
DeletePermission(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
|
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) {
|
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
|
||||||
r.Use(middleware.StripSlashes)
|
r.Use(middleware.StripSlashes)
|
||||||
|
|
||||||
@@ -231,7 +240,7 @@ func NewService(opts ...Option) (Graph, error) {
|
|||||||
r.Post("/root/children", drivesDriveItemApi.CreateDriveItem)
|
r.Post("/root/children", drivesDriveItemApi.CreateDriveItem)
|
||||||
r.Route("/items/{itemID}", func(r chi.Router) {
|
r.Route("/items/{itemID}", func(r chi.Router) {
|
||||||
r.Delete("/", drivesDriveItemApi.DeleteDriveItem)
|
r.Delete("/", drivesDriveItemApi.DeleteDriveItem)
|
||||||
r.Post("/invite", svc.Invite)
|
r.Post("/invite", driveItemPermissionsApi.Invite)
|
||||||
r.Route("/permissions", func(r chi.Router) {
|
r.Route("/permissions", func(r chi.Router) {
|
||||||
r.Get("/", svc.ListPermissions)
|
r.Get("/", svc.ListPermissions)
|
||||||
r.Route("/{permissionID}", func(r chi.Router) {
|
r.Route("/{permissionID}", func(r chi.Router) {
|
||||||
|
|||||||
@@ -889,7 +889,7 @@ Feature: Send a sharing invitations
|
|||||||
"code": {
|
"code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"generalException"
|
"invalidRequest"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
@@ -940,7 +940,7 @@ Feature: Send a sharing invitations
|
|||||||
"properties": {
|
"properties": {
|
||||||
"code": {
|
"code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "generalException"
|
"pattern": "invalidRequest"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1113,7 +1113,7 @@ Feature: Send a sharing invitations
|
|||||||
"code": {
|
"code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"generalException"
|
"invalidRequest"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
@@ -1224,7 +1224,7 @@ Feature: Send a sharing invitations
|
|||||||
"properties": {
|
"properties": {
|
||||||
"code": {
|
"code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["generalException"]
|
"enum": ["invalidRequest"]
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -2375,14 +2375,14 @@ Feature: Send a sharing invitations
|
|||||||
| sharee | grp1 |
|
| sharee | grp1 |
|
||||||
| shareType | group |
|
| shareType | group |
|
||||||
| permissionsAction | <permissions-action> |
|
| 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:
|
When user "Alice" sends the following share invitation using the Graph API:
|
||||||
| resource | FolderToShare |
|
| resource | FolderToShare |
|
||||||
| space | Personal |
|
| space | Personal |
|
||||||
| sharee | Brian |
|
| sharee | Brian |
|
||||||
| shareType | user |
|
| shareType | user |
|
||||||
| permissionsAction | <permissions-action> |
|
| permissionsAction | <permissions-action> |
|
||||||
Then the HTTP status code should be "400"
|
Then the HTTP status code should be "400"
|
||||||
Examples:
|
Examples:
|
||||||
| permissions-action |
|
| permissions-action |
|
||||||
| permissions/create |
|
| permissions/create |
|
||||||
|
|||||||
Reference in New Issue
Block a user