refactor(graph): move CreateLink and SetPublicLinkPassword to the permissions service

This commit is contained in:
Ralf Haferkamp
2024-03-27 19:06:47 +01:00
committed by Ralf Haferkamp
parent 8124024caf
commit 2d643219e3
6 changed files with 531 additions and 534 deletions

View File

@@ -24,6 +24,64 @@ func (_m *DriveItemPermissionsProvider) EXPECT() *DriveItemPermissionsProvider_E
return &DriveItemPermissionsProvider_Expecter{mock: &_m.Mock}
}
// CreateLink provides a mock function with given fields: ctx, driveItemID, createLink
func (_m *DriveItemPermissionsProvider) CreateLink(ctx context.Context, driveItemID providerv1beta1.ResourceId, createLink libregraph.DriveItemCreateLink) (libregraph.Permission, error) {
ret := _m.Called(ctx, driveItemID, createLink)
if len(ret) == 0 {
panic("no return value specified for CreateLink")
}
var r0 libregraph.Permission
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemCreateLink) (libregraph.Permission, error)); ok {
return rf(ctx, driveItemID, createLink)
}
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemCreateLink) libregraph.Permission); ok {
r0 = rf(ctx, driveItemID, createLink)
} else {
r0 = ret.Get(0).(libregraph.Permission)
}
if rf, ok := ret.Get(1).(func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemCreateLink) error); ok {
r1 = rf(ctx, driveItemID, createLink)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DriveItemPermissionsProvider_CreateLink_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateLink'
type DriveItemPermissionsProvider_CreateLink_Call struct {
*mock.Call
}
// CreateLink is a helper method to define mock.On call
// - ctx context.Context
// - driveItemID providerv1beta1.ResourceId
// - createLink libregraph.DriveItemCreateLink
func (_e *DriveItemPermissionsProvider_Expecter) CreateLink(ctx interface{}, driveItemID interface{}, createLink interface{}) *DriveItemPermissionsProvider_CreateLink_Call {
return &DriveItemPermissionsProvider_CreateLink_Call{Call: _e.mock.On("CreateLink", ctx, driveItemID, createLink)}
}
func (_c *DriveItemPermissionsProvider_CreateLink_Call) Run(run func(ctx context.Context, driveItemID providerv1beta1.ResourceId, createLink libregraph.DriveItemCreateLink)) *DriveItemPermissionsProvider_CreateLink_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(providerv1beta1.ResourceId), args[2].(libregraph.DriveItemCreateLink))
})
return _c
}
func (_c *DriveItemPermissionsProvider_CreateLink_Call) Return(_a0 libregraph.Permission, _a1 error) *DriveItemPermissionsProvider_CreateLink_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *DriveItemPermissionsProvider_CreateLink_Call) RunAndReturn(run func(context.Context, providerv1beta1.ResourceId, libregraph.DriveItemCreateLink) (libregraph.Permission, error)) *DriveItemPermissionsProvider_CreateLink_Call {
_c.Call.Return(run)
return _c
}
// DeletePermission provides a mock function with given fields: ctx, itemID, permissionID
func (_m *DriveItemPermissionsProvider) DeletePermission(ctx context.Context, itemID providerv1beta1.ResourceId, permissionID string) error {
ret := _m.Called(ctx, itemID, permissionID)
@@ -244,6 +302,65 @@ func (_c *DriveItemPermissionsProvider_ListSpaceRootPermissions_Call) RunAndRetu
return _c
}
// SetPublicLinkPassword provides a mock function with given fields: ctx, driveItemID, permissionID, password
func (_m *DriveItemPermissionsProvider) SetPublicLinkPassword(ctx context.Context, driveItemID providerv1beta1.ResourceId, permissionID string, password string) (libregraph.Permission, error) {
ret := _m.Called(ctx, driveItemID, permissionID, password)
if len(ret) == 0 {
panic("no return value specified for SetPublicLinkPassword")
}
var r0 libregraph.Permission
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, string, string) (libregraph.Permission, error)); ok {
return rf(ctx, driveItemID, permissionID, password)
}
if rf, ok := ret.Get(0).(func(context.Context, providerv1beta1.ResourceId, string, string) libregraph.Permission); ok {
r0 = rf(ctx, driveItemID, permissionID, password)
} else {
r0 = ret.Get(0).(libregraph.Permission)
}
if rf, ok := ret.Get(1).(func(context.Context, providerv1beta1.ResourceId, string, string) error); ok {
r1 = rf(ctx, driveItemID, permissionID, password)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DriveItemPermissionsProvider_SetPublicLinkPassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPublicLinkPassword'
type DriveItemPermissionsProvider_SetPublicLinkPassword_Call struct {
*mock.Call
}
// SetPublicLinkPassword is a helper method to define mock.On call
// - ctx context.Context
// - driveItemID providerv1beta1.ResourceId
// - permissionID string
// - password string
func (_e *DriveItemPermissionsProvider_Expecter) SetPublicLinkPassword(ctx interface{}, driveItemID interface{}, permissionID interface{}, password interface{}) *DriveItemPermissionsProvider_SetPublicLinkPassword_Call {
return &DriveItemPermissionsProvider_SetPublicLinkPassword_Call{Call: _e.mock.On("SetPublicLinkPassword", ctx, driveItemID, permissionID, password)}
}
func (_c *DriveItemPermissionsProvider_SetPublicLinkPassword_Call) Run(run func(ctx context.Context, driveItemID providerv1beta1.ResourceId, permissionID string, password string)) *DriveItemPermissionsProvider_SetPublicLinkPassword_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(providerv1beta1.ResourceId), args[2].(string), args[3].(string))
})
return _c
}
func (_c *DriveItemPermissionsProvider_SetPublicLinkPassword_Call) Return(_a0 libregraph.Permission, _a1 error) *DriveItemPermissionsProvider_SetPublicLinkPassword_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *DriveItemPermissionsProvider_SetPublicLinkPassword_Call) RunAndReturn(run func(context.Context, providerv1beta1.ResourceId, string, string) (libregraph.Permission, error)) *DriveItemPermissionsProvider_SetPublicLinkPassword_Call {
_c.Call.Return(run)
return _c
}
// SpaceRootInvite provides a mock function with given fields: ctx, driveID, invite
func (_m *DriveItemPermissionsProvider) SpaceRootInvite(ctx context.Context, driveID providerv1beta1.ResourceId, invite libregraph.DriveItemInvite) (libregraph.Permission, error) {
ret := _m.Called(ctx, driveID, invite)

View File

@@ -37,6 +37,8 @@ type DriveItemPermissionsProvider interface {
ListSpaceRootPermissions(ctx context.Context, driveID storageprovider.ResourceId) (libregraph.CollectionOfPermissionsWithAllowedValues, error)
DeletePermission(ctx context.Context, itemID storageprovider.ResourceId, permissionID string) error
UpdatePermission(ctx context.Context, itemID storageprovider.ResourceId, permissionID string, newPermission libregraph.Permission) (libregraph.Permission, error)
CreateLink(ctx context.Context, driveItemID storageprovider.ResourceId, createLink libregraph.DriveItemCreateLink) (libregraph.Permission, error)
SetPublicLinkPassword(ctx context.Context, driveItemID storageprovider.ResourceId, permissionID string, password string) (libregraph.Permission, error)
}
// DriveItemPermissionsService contains the production business logic for everything that relates to permissions on drive items.

View File

@@ -2,8 +2,6 @@ package svc
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"strconv"
@@ -12,6 +10,7 @@ import (
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/go-chi/chi/v5"
@@ -22,118 +21,36 @@ import (
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
)
// CreateLink creates a public link on the cs3 api
func (g Graph) CreateLink(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling create link")
_, driveItemID, err := GetDriveAndItemIDParam(r, g.logger)
func (s DriveItemPermissionsService) CreateLink(ctx context.Context, driveItemID storageprovider.ResourceId, createLink libregraph.DriveItemCreateLink) (libregraph.Permission, error) {
gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
errorcode.RenderError(w, r, err)
return
}
var createLink libregraph.DriveItemCreateLink
body, err := io.ReadAll(r.Body)
if err := json.Unmarshal(body, &createLink); err != nil {
logger.Error().Err(err).Interface("body", r.Body).Msg("could not create link: invalid body schema definition")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid body schema definition")
return
}
createdLink, err := g.createLink(r.Context(), &driveItemID, createLink)
if err != nil {
errorcode.RenderError(w, r, err)
return
}
perm, err := g.libreGraphPermissionFromCS3PublicShare(createdLink)
if err != nil {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, *perm)
}
// SetLinkPassword sets public link password on the cs3 api
func (g Graph) SetLinkPassword(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
_, itemID, err := GetDriveAndItemIDParam(r, g.logger)
if err != nil {
errorcode.RenderError(w, r, err)
return
}
permissionID, err := url.PathUnescape(chi.URLParam(r, "permissionID"))
if err != nil {
g.logger.Debug().Err(err).Msg("could not parse permissionID")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid permissionID")
return
}
password := &libregraph.SharingLinkPassword{}
if err := StrictJSONUnmarshal(r.Body, password); 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
}
publicShare, err := g.getCS3PublicShareByID(ctx, permissionID)
if err != nil {
errorcode.RenderError(w, r, err)
return
}
// The resourceID of the shared resource need to match the item ID from the Request Path
// otherwise this is an invalid Request.
if !utils.ResourceIDEqual(publicShare.GetResourceId(), &itemID) {
g.logger.Debug().Msg("resourceID of shared does not match itemID")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "permissionID and itemID do not match")
return
}
newPermission, err := g.updatePublicLinkPassword(ctx, permissionID, password.GetPassword())
if err != nil {
errorcode.RenderError(w, r, err)
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, *newPermission)
}
func (g Graph) createLink(ctx context.Context, driveItemID *providerv1beta1.ResourceId, createLink libregraph.DriveItemCreateLink) (*link.PublicShare, error) {
gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
g.logger.Error().Err(err).Msg("could not select next gateway client")
return nil, errorcode.New(errorcode.GeneralException, err.Error())
s.logger.Error().Err(err).Msg("could not select next gateway client")
return libregraph.Permission{}, errorcode.New(errorcode.GeneralException, err.Error())
}
statResp, err := gatewayClient.Stat(
ctx,
&providerv1beta1.StatRequest{
Ref: &providerv1beta1.Reference{
ResourceId: driveItemID,
&storageprovider.StatRequest{
Ref: &storageprovider.Reference{
ResourceId: &driveItemID,
Path: ".",
},
})
if err != nil {
g.logger.Error().Err(err).Msg("transport error, could not stat resource")
return nil, errorcode.New(errorcode.GeneralException, err.Error())
s.logger.Error().Err(err).Msg("transport error, could not stat resource")
return libregraph.Permission{}, errorcode.New(errorcode.GeneralException, err.Error())
}
if code := statResp.GetStatus().GetCode(); code != rpc.Code_CODE_OK {
g.logger.Debug().Interface("itemID", driveItemID).Msg(statResp.GetStatus().GetMessage())
return nil, errorcode.New(cs3StatusToErrCode(code), statResp.GetStatus().GetMessage())
s.logger.Debug().Interface("itemID", driveItemID).Msg(statResp.GetStatus().GetMessage())
return libregraph.Permission{}, errorcode.New(cs3StatusToErrCode(code), statResp.GetStatus().GetMessage())
}
permissions, err := linktype.CS3ResourcePermissionsFromSharingLink(createLink, statResp.GetInfo().GetType())
if err != nil {
g.logger.Debug().Interface("createLink", createLink).Msg(err.Error())
return nil, errorcode.New(errorcode.InvalidRequest, "invalid link type")
s.logger.Debug().Interface("createLink", createLink).Msg(err.Error())
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "invalid link type")
}
if createLink.GetType() == libregraph.INTERNAL && len(createLink.GetPassword()) > 0 {
return nil, errorcode.New(errorcode.InvalidRequest, "password is redundant for the internal link")
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "password is redundant for the internal link")
}
req := link.CreatePublicShareRequest{
ResourceInfo: statResp.GetInfo(),
@@ -148,8 +65,8 @@ func (g Graph) createLink(ctx context.Context, driveItemID *providerv1beta1.Reso
if isSet {
expireTime := parseAndFillUpTime(expirationDate)
if expireTime == nil {
g.logger.Debug().Interface("createLink", createLink).Msg(err.Error())
return nil, errorcode.New(errorcode.InvalidRequest, "invalid expiration date")
s.logger.Debug().Interface("createLink", createLink).Msg(err.Error())
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "invalid expiration date")
}
req.GetGrant().Expiration = expireTime
}
@@ -163,67 +80,105 @@ func (g Graph) createLink(ctx context.Context, driveItemID *providerv1beta1.Reso
}
createResp, err := gatewayClient.CreatePublicShare(ctx, &req)
if err != nil {
g.logger.Error().Err(err).Msg("transport error, could not create link")
return nil, errorcode.New(errorcode.GeneralException, err.Error())
s.logger.Error().Err(err).Msg("transport error, could not create link")
return libregraph.Permission{}, errorcode.New(errorcode.GeneralException, err.Error())
}
if statusCode := createResp.GetStatus().GetCode(); statusCode != rpc.Code_CODE_OK {
return nil, errorcode.New(cs3StatusToErrCode(statusCode), createResp.Status.Message)
return libregraph.Permission{}, errorcode.New(cs3StatusToErrCode(statusCode), createResp.Status.Message)
}
return createResp.GetShare(), nil
link := createResp.GetShare()
perm, err := s.libreGraphPermissionFromCS3PublicShare(link)
if err != nil {
return libregraph.Permission{}, errorcode.New(errorcode.GeneralException, err.Error())
}
return *perm, nil
}
func parseAndFillUpTime(t *time.Time) *types.Timestamp {
if t == nil || t.IsZero() {
return nil
func (s DriveItemPermissionsService) SetPublicLinkPassword(ctx context.Context, driveItemId storageprovider.ResourceId, permissionID string, password string) (libregraph.Permission, error) {
publicShare, err := s.getCS3PublicShareByID(ctx, permissionID)
if err != nil {
return libregraph.Permission{}, err
}
// the link needs to be valid for the whole day
tLink := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
tLink = tLink.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
final := tLink.UnixNano()
return &types.Timestamp{
Seconds: uint64(final / 1000000000),
Nanos: uint32(final % 1000000000),
// The resourceID of the shared resource need to match the item ID from the Request Path
// otherwise this is an invalid Request.
if !utils.ResourceIDEqual(publicShare.GetResourceId(), &driveItemId) {
s.logger.Debug().Msg("resourceID of shared does not match itemID")
return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "permissionID and itemID do not match")
}
permission, err := s.updatePublicLinkPassword(ctx, permissionID, password)
if err != nil {
return libregraph.Permission{}, err
}
return *permission, nil
}
func (g BaseGraphService) updatePublicLinkPassword(ctx context.Context, permissionID string, password string) (*libregraph.Permission, error) {
gatewayClient, err := g.gatewaySelector.Next()
// CreateLink creates a public link on the cs3 api
func (api DriveItemPermissionsApi) CreateLink(w http.ResponseWriter, r *http.Request) {
logger := api.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling create link")
_, driveItemID, err := GetDriveAndItemIDParam(r, &logger)
if err != nil {
return nil, err
errorcode.RenderError(w, r, err)
return
}
changeLinkRes, err := gatewayClient.UpdatePublicShare(ctx, &link.UpdatePublicShareRequest{
Update: &link.UpdatePublicShareRequest_Update{
Type: link.UpdatePublicShareRequest_Update_TYPE_PASSWORD,
Grant: &link.Grant{
Password: password,
},
},
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: permissionID,
},
},
},
})
if errCode := errorcode.FromCS3Status(changeLinkRes.GetStatus(), err); errCode != nil {
return nil, *errCode
var createLink libregraph.DriveItemCreateLink
if err = StrictJSONUnmarshal(r.Body, &createLink); err != nil {
logger.Error().Err(err).Interface("body", r.Body).Msg("could not create link: invalid body schema definition")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid body schema definition")
return
}
permission, err := g.libreGraphPermissionFromCS3PublicShare(changeLinkRes.GetShare())
perm, err := api.driveItemPermissionsService.CreateLink(r.Context(), driveItemID, createLink)
if err != nil {
return nil, err
errorcode.RenderError(w, r, err)
return
}
return permission, nil
render.Status(r, http.StatusOK)
render.JSON(w, r, perm)
}
func (g BaseGraphService) updatePublicLinkPermission(ctx context.Context, permissionID string, itemID *providerv1beta1.ResourceId, newPermission *libregraph.Permission) (perm *libregraph.Permission, err error) {
gatewayClient, err := g.gatewaySelector.Next()
// SetLinkPassword sets public link password on the cs3 api
func (api DriveItemPermissionsApi) SetLinkPassword(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
_, itemID, err := GetDriveAndItemIDParam(r, &api.logger)
if err != nil {
g.logger.Error().Err(err).Msg("could not select next gateway client")
errorcode.RenderError(w, r, err)
return
}
permissionID, err := url.PathUnescape(chi.URLParam(r, "permissionID"))
if err != nil {
api.logger.Debug().Err(err).Msg("could not parse permissionID")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid permissionID")
return
}
password := &libregraph.SharingLinkPassword{}
if err = StrictJSONUnmarshal(r.Body, password); 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
}
newPermission, err := api.driveItemPermissionsService.SetPublicLinkPassword(ctx, itemID, permissionID, password.GetPassword())
if err != nil {
errorcode.RenderError(w, r, err)
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, newPermission)
}
func (s DriveItemPermissionsService) updatePublicLinkPermission(ctx context.Context, permissionID string, itemID *providerv1beta1.ResourceId, newPermission *libregraph.Permission) (perm *libregraph.Permission, err error) {
gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
s.logger.Error().Err(err).Msg("could not select next gateway client")
return nil, errorcode.New(errorcode.GeneralException, err.Error())
}
@@ -246,7 +201,7 @@ func (g BaseGraphService) updatePublicLinkPermission(ctx context.Context, permis
Type: link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION,
Grant: &link.Grant{Expiration: parseAndFillUpTime(&expirationDate)},
}
perm, err = g.updatePublicLink(ctx, permissionID, update)
perm, err = s.updatePublicLink(ctx, permissionID, update)
if err != nil {
return nil, err
}
@@ -258,7 +213,7 @@ func (g BaseGraphService) updatePublicLinkPermission(ctx context.Context, permis
Type: link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME,
DisplayName: changedLink.GetLibreGraphDisplayName(),
}
perm, err = g.updatePublicLink(ctx, permissionID, update)
perm, err = s.updatePublicLink(ctx, permissionID, update)
if err != nil {
return nil, err
}
@@ -278,13 +233,13 @@ func (g BaseGraphService) updatePublicLinkPermission(ctx context.Context, permis
Permissions: &link.PublicSharePermissions{Permissions: permissions},
},
}
perm, err = g.updatePublicLink(ctx, permissionID, update)
perm, err = s.updatePublicLink(ctx, permissionID, update)
if err != nil {
return nil, err
}
// reset the password for the internal link
if changedLink == libregraph.INTERNAL {
perm, err = g.updatePublicLinkPassword(ctx, permissionID, "")
perm, err = s.updatePublicLinkPassword(ctx, permissionID, "")
if err != nil {
return nil, err
}
@@ -294,8 +249,39 @@ func (g BaseGraphService) updatePublicLinkPermission(ctx context.Context, permis
return perm, err
}
func (g BaseGraphService) updatePublicLink(ctx context.Context, permissionID string, update *link.UpdatePublicShareRequest_Update) (*libregraph.Permission, error) {
gatewayClient, err := g.gatewaySelector.Next()
func (s DriveItemPermissionsService) updatePublicLinkPassword(ctx context.Context, permissionID string, password string) (*libregraph.Permission, error) {
gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
return nil, err
}
changeLinkRes, err := gatewayClient.UpdatePublicShare(ctx, &link.UpdatePublicShareRequest{
Update: &link.UpdatePublicShareRequest_Update{
Type: link.UpdatePublicShareRequest_Update_TYPE_PASSWORD,
Grant: &link.Grant{
Password: password,
},
},
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: permissionID,
},
},
},
})
if errCode := errorcode.FromCS3Status(changeLinkRes.GetStatus(), err); errCode != nil {
return nil, *errCode
}
permission, err := s.libreGraphPermissionFromCS3PublicShare(changeLinkRes.GetShare())
if err != nil {
return nil, err
}
return permission, nil
}
func (s DriveItemPermissionsService) updatePublicLink(ctx context.Context, permissionID string, update *link.UpdatePublicShareRequest_Update) (*libregraph.Permission, error) {
gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
return nil, err
}
@@ -315,9 +301,26 @@ func (g BaseGraphService) updatePublicLink(ctx context.Context, permissionID str
return nil, *errCode
}
permission, err := g.libreGraphPermissionFromCS3PublicShare(changeLinkRes.GetShare())
permission, err := s.libreGraphPermissionFromCS3PublicShare(changeLinkRes.GetShare())
if err != nil {
return nil, err
}
return permission, nil
}
func parseAndFillUpTime(t *time.Time) *types.Timestamp {
if t == nil || t.IsZero() {
return nil
}
// the link needs to be valid for the whole day
tLink := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
tLink = tLink.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
final := tLink.UnixNano()
return &types.Timestamp{
Seconds: uint64(final / 1000000000),
Nanos: uint32(final % 1000000000),
}
}

View File

@@ -0,0 +1,258 @@
package svc_test
import (
"context"
"errors"
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "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/utils"
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
. "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/config/defaults"
"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/linktype"
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
"github.com/stretchr/testify/mock"
)
var _ = Describe("createLinkTests", func() {
var (
svc service.DriveItemPermissionsService
driveItemId provider.ResourceId
ctx context.Context
gatewayClient *cs3mocks.GatewayAPIClient
gatewaySelector *mocks.Selectable[gateway.GatewayAPIClient]
currentUser = &userpb.User{
Id: &userpb.UserId{
OpaqueId: "user",
},
}
)
const (
ViewerLinkString = "Viewer Link"
)
BeforeEach(func() {
var err error
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))
cfg := defaults.FullDefaultConfig()
svc, err = service.NewDriveItemPermissionsService(logger, gatewaySelector, cache, cfg)
Expect(err).ToNot(HaveOccurred())
driveItemId = provider.ResourceId{
StorageId: "1",
SpaceId: "2",
OpaqueId: "3",
}
ctx = revactx.ContextSetUser(context.Background(), currentUser)
})
Describe("CreateLink", func() {
var (
driveItemCreateLink libregraph.DriveItemCreateLink
statResponse *provider.StatResponse
createLinkResponse *link.CreatePublicShareResponse
)
BeforeEach(func() {
driveItemCreateLink = libregraph.DriveItemCreateLink{
Type: nil,
ExpirationDateTime: nil,
Password: nil,
DisplayName: nil,
LibreGraphQuickLink: nil,
}
statResponse = &provider.StatResponse{
Status: status.NewOK(ctx),
Info: &provider.ResourceInfo{Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER},
}
createLinkResponse = &link.CreatePublicShareResponse{
Status: status.NewOK(ctx),
}
linkType, err := libregraph.NewSharingLinkTypeFromValue("view")
Expect(err).ToNot(HaveOccurred())
driveItemCreateLink.Type = linkType
driveItemCreateLink.ExpirationDateTime = libregraph.PtrTime(time.Now().Add(time.Hour))
permissions, err := linktype.CS3ResourcePermissionsFromSharingLink(driveItemCreateLink, provider.ResourceType_RESOURCE_TYPE_CONTAINER)
Expect(err).ToNot(HaveOccurred())
createLinkResponse.Share = &link.PublicShare{
Id: &link.PublicShareId{OpaqueId: "123"},
Expiration: utils.TimeToTS(*driveItemCreateLink.ExpirationDateTime),
PasswordProtected: false,
DisplayName: ViewerLinkString,
Token: "SomeGOODCoffee",
Permissions: &link.PublicSharePermissions{Permissions: permissions},
}
})
// Public Shares / "links" in graph terms
It("creates a public link as expected (happy path)", func() {
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
gatewayClient.On("CreatePublicShare", mock.Anything, mock.Anything).Return(createLinkResponse, nil)
perm, err := svc.CreateLink(context.Background(), driveItemId, driveItemCreateLink)
Expect(err).ToNot(HaveOccurred())
Expect(perm.GetId()).To(Equal("123"))
Expect(perm.GetExpirationDateTime().Unix()).To(Equal(driveItemCreateLink.ExpirationDateTime.Unix()))
Expect(perm.GetHasPassword()).To(Equal(false))
Expect(perm.GetLink().LibreGraphDisplayName).To(Equal(libregraph.PtrString(ViewerLinkString)))
link := perm.GetLink()
respLinkType := link.GetType()
expected, err := libregraph.NewSharingLinkTypeFromValue("view")
Expect(err).ToNot(HaveOccurred())
Expect(&respLinkType).To(Equal(expected))
})
It("handles a failing CreateLink", func() {
statResponse.Info = &provider.ResourceInfo{Type: provider.ResourceType_RESOURCE_TYPE_FILE}
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
gatewayClient.On("CreatePublicShare", mock.Anything, mock.Anything).Return(createLinkResponse, nil)
linkType, err := libregraph.NewSharingLinkTypeFromValue("edit")
Expect(err).ToNot(HaveOccurred())
driveItemCreateLink.Type = linkType
permissions, err := linktype.CS3ResourcePermissionsFromSharingLink(driveItemCreateLink, provider.ResourceType_RESOURCE_TYPE_CONTAINER)
Expect(err).ToNot(HaveOccurred())
createLinkResponse.Status = status.NewInternal(ctx, "transport error")
createLinkResponse.Share = &link.PublicShare{
Id: &link.PublicShareId{OpaqueId: "123"},
Permissions: &link.PublicSharePermissions{Permissions: permissions},
}
perm, err := svc.CreateLink(context.Background(), driveItemId, driveItemCreateLink)
Expect(err).To(MatchError(errorcode.New(errorcode.GeneralException, "transport error")))
Expect(perm).To(BeZero())
})
It("fails when the stat returns access denied", func() {
err := errors.New("no permission to stat the file")
statResponse.Status = status.NewPermissionDenied(ctx, err, err.Error())
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
perm, err := svc.CreateLink(context.Background(), driveItemId, driveItemCreateLink)
Expect(err).To(MatchError(errorcode.New(errorcode.AccessDenied, "no permission to stat the file")))
Expect(perm).To(BeZero())
})
It("fails when the stat returns resource is locked", func() {
err := errors.New("the resource is locked")
statResponse.Status = status.NewLocked(ctx, err.Error())
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
perm, err := svc.CreateLink(context.Background(), driveItemId, driveItemCreateLink)
Expect(err).To(MatchError(errorcode.New(errorcode.ItemIsLocked, "the resource is locked")))
Expect(perm).To(BeZero())
})
It("succeeds when the link type mapping is not successful", func() {
// we need to send a valid link type
linkType, err := libregraph.NewSharingLinkTypeFromValue("edit")
Expect(err).ToNot(HaveOccurred())
driveItemCreateLink.Type = linkType
permissions := &provider.ResourcePermissions{
CreateContainer: true,
InitiateFileUpload: true,
Move: true,
}
// return different permissions which do not match a link type
createLinkResponse.Share = &link.PublicShare{
Id: &link.PublicShareId{OpaqueId: "123"},
Expiration: utils.TimeToTS(*driveItemCreateLink.ExpirationDateTime),
PasswordProtected: false,
DisplayName: ViewerLinkString,
Token: "SomeGOODCoffee",
Permissions: &link.PublicSharePermissions{Permissions: permissions},
}
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil)
gatewayClient.On("CreatePublicShare", mock.Anything, mock.Anything).Return(createLinkResponse, nil)
perm, err := svc.CreateLink(context.Background(), driveItemId, driveItemCreateLink)
Expect(err).ToNot(HaveOccurred())
Expect(perm.GetId()).To(Equal("123"))
Expect(perm.GetExpirationDateTime().Unix()).To(Equal(driveItemCreateLink.ExpirationDateTime.Unix()))
Expect(perm.GetHasPassword()).To(Equal(false))
Expect(perm.GetLink().LibreGraphDisplayName).To(Equal(libregraph.PtrString(ViewerLinkString)))
respLink := perm.GetLink()
// some conversion gymnastics
respLinkType := respLink.GetType()
Expect(err).ToNot(HaveOccurred())
mockLink := libregraph.SharingLink{}
lt, _ := linktype.SharingLinkTypeFromCS3Permissions(&link.PublicSharePermissions{Permissions: permissions})
mockLink.Type = lt
expectedType := mockLink.GetType()
Expect(&respLinkType).To(Equal(&expectedType))
libreGraphActions := perm.LibreGraphPermissionsActions
Expect(libreGraphActions[0]).To(Equal("libre.graph/driveItem/children/create"))
Expect(libreGraphActions[1]).To(Equal("libre.graph/driveItem/upload/create"))
Expect(libreGraphActions[2]).To(Equal("libre.graph/driveItem/path/update"))
})
})
Describe("SetLinPassword", func() {
var (
updatePublicShareMockResponse link.UpdatePublicShareResponse
getPublicShareResponse link.GetPublicShareResponse
)
const TestLinkName = "Test Link"
BeforeEach(func() {
updatePublicShareMockResponse = link.UpdatePublicShareResponse{
Status: status.NewOK(ctx),
Share: &link.PublicShare{DisplayName: TestLinkName},
}
getPublicShareResponse = link.GetPublicShareResponse{
Status: status.NewOK(ctx),
Share: &link.PublicShare{
Id: &link.PublicShareId{
OpaqueId: "permissionid",
},
ResourceId: &driveItemId,
Permissions: &link.PublicSharePermissions{
Permissions: linktype.NewViewLinkPermissionSet().GetPermissions(),
},
Token: "token",
},
}
})
It("updates the password on a public share", func() {
gatewayClient.On("GetPublicShare", mock.Anything, mock.Anything).Return(&getPublicShareResponse, nil)
updatePublicShareMockResponse.Share.Permissions = &link.PublicSharePermissions{
Permissions: linktype.NewViewLinkPermissionSet().Permissions,
}
updatePublicShareMockResponse.Share.PasswordProtected = true
gatewayClient.On("UpdatePublicShare",
mock.Anything,
mock.MatchedBy(func(req *link.UpdatePublicShareRequest) bool {
return req.GetRef().GetId().GetOpaqueId() == "permissionid"
}),
).Return(&updatePublicShareMockResponse, nil)
perm, err := svc.SetPublicLinkPassword(context.Background(), driveItemId, "permissionid", "OC123!")
Expect(err).ToNot(HaveOccurred())
linkType := perm.Link.GetType()
Expect(string(linkType)).To(Equal("view"))
Expect(perm.GetHasPassword()).To(BeTrue())
})
})
})

View File

@@ -1,381 +0,0 @@
package svc_test
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "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/rgrpc/todo/pool"
"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/shared"
"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/defaults"
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
"github.com/stretchr/testify/mock"
"google.golang.org/grpc"
)
var _ = Describe("createLinkTests", func() {
var (
svc service.Service
ctx context.Context
cfg *config.Config
gatewayClient *cs3mocks.GatewayAPIClient
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
eventsPublisher mocks.Publisher
identityBackend *identitymocks.Backend
currentUser = &userpb.User{
Id: &userpb.UserId{
OpaqueId: "user",
},
}
rr *httptest.ResponseRecorder
)
const (
ViewerLinkString = "Viewer Link"
ItemID = "f0042750-23c5-441c-9f2c-ff7c53e5bd2a$cd621428-dfbe-44c1-9393-65bf0dd440a6!1177add3-b4eb-434e-a2e8-1859b31b17bf"
DriveId = "f0042750-23c5-441c-9f2c-ff7c53e5bd2a$cd621428-dfbe-44c1-9393-65bf0dd440a6!cd621428-dfbe-44c1-9393-65bf0dd440a6"
)
BeforeEach(func() {
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
rr = httptest.NewRecorder()
pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway")
gatewayClient = &cs3mocks.GatewayAPIClient{}
gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient](
"GatewaySelector",
"com.owncloud.api.gateway",
func(cc *grpc.ClientConn) gateway.GatewayAPIClient {
return gatewayClient
},
)
identityBackend = &identitymocks.Backend{}
rr = httptest.NewRecorder()
ctx = context.Background()
cfg = defaults.FullDefaultConfig()
cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
cfg.TokenManager.JWTSecret = "loremipsum"
cfg.Commons = &shared.Commons{}
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
cfg.FilesSharing.EnableResharing = true
svc, _ = service.NewService(
service.Config(cfg),
service.WithGatewaySelector(gatewaySelector),
service.EventsPublisher(&eventsPublisher),
service.WithIdentityBackend(identityBackend),
)
})
Describe("CreateLink", func() {
var (
itemID string
driveItemCreateLink *libregraph.DriveItemCreateLink
statMock *mock.Call
statResponse *provider.StatResponse
createLinkResponse *link.CreatePublicShareResponse
createLinkMock *mock.Call
)
BeforeEach(func() {
itemID = ItemID
rctx := chi.NewRouteContext()
rctx.URLParams.Add("driveID", DriveId)
rctx.URLParams.Add("itemID", itemID)
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
ctx = revactx.ContextSetUser(ctx, currentUser)
driveItemCreateLink = &libregraph.DriveItemCreateLink{
Type: nil,
ExpirationDateTime: nil,
Password: nil,
DisplayName: nil,
LibreGraphQuickLink: nil,
}
statMock = gatewayClient.On("Stat", mock.Anything, mock.Anything)
statResponse = &provider.StatResponse{
Status: status.NewOK(ctx),
}
statMock.Return(statResponse, nil)
createLinkMock = gatewayClient.On("CreatePublicShare", mock.Anything, mock.Anything)
createLinkResponse = &link.CreatePublicShareResponse{
Status: status.NewOK(ctx),
}
createLinkMock.Return(createLinkResponse, nil)
linkType, err := libregraph.NewSharingLinkTypeFromValue("view")
Expect(err).ToNot(HaveOccurred())
driveItemCreateLink.Type = linkType
driveItemCreateLink.ExpirationDateTime = libregraph.PtrTime(time.Now().Add(time.Hour))
permissions, err := linktype.CS3ResourcePermissionsFromSharingLink(*driveItemCreateLink, provider.ResourceType_RESOURCE_TYPE_CONTAINER)
Expect(err).ToNot(HaveOccurred())
createLinkResponse.Share = &link.PublicShare{
Id: &link.PublicShareId{OpaqueId: "123"},
Expiration: utils.TimeToTS(*driveItemCreateLink.ExpirationDateTime),
PasswordProtected: false,
DisplayName: ViewerLinkString,
Token: "SomeGOODCoffee",
Permissions: &link.PublicSharePermissions{Permissions: permissions},
}
statResponse.Info = &provider.ResourceInfo{Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER}
})
toJSONReader := func(v any) *strings.Reader {
driveItemInviteBytes, err := json.Marshal(v)
Expect(err).ToNot(HaveOccurred())
return strings.NewReader(string(driveItemInviteBytes))
}
// Public Shares / "links" in graph terms
It("creates a public link as expected (happy path)", func() {
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemCreateLink)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusOK))
var createLinkResponseBody *libregraph.Permission
err := json.Unmarshal(rr.Body.Bytes(), &createLinkResponseBody)
Expect(err).ToNot(HaveOccurred())
Expect(createLinkResponseBody.GetId()).To(Equal("123"))
Expect(createLinkResponseBody.GetExpirationDateTime().Unix()).To(Equal(driveItemCreateLink.ExpirationDateTime.Unix()))
Expect(createLinkResponseBody.GetHasPassword()).To(Equal(false))
Expect(createLinkResponseBody.GetLink().LibreGraphDisplayName).To(Equal(libregraph.PtrString(ViewerLinkString)))
link := createLinkResponseBody.GetLink()
respLinkType := link.GetType()
expected, err := libregraph.NewSharingLinkTypeFromValue("view")
Expect(err).ToNot(HaveOccurred())
Expect(&respLinkType).To(Equal(expected))
})
It("handles a failing CreateLink", func() {
linkType, err := libregraph.NewSharingLinkTypeFromValue("edit")
Expect(err).ToNot(HaveOccurred())
driveItemCreateLink.Type = linkType
permissions, err := linktype.CS3ResourcePermissionsFromSharingLink(*driveItemCreateLink, provider.ResourceType_RESOURCE_TYPE_CONTAINER)
Expect(err).ToNot(HaveOccurred())
createLinkResponse.Status = status.NewInternal(ctx, "transport error")
createLinkResponse.Share = &link.PublicShare{
Id: &link.PublicShareId{OpaqueId: "123"},
Permissions: &link.PublicSharePermissions{Permissions: permissions},
}
statResponse.Info = &provider.ResourceInfo{Type: provider.ResourceType_RESOURCE_TYPE_FILE}
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemCreateLink)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusInternalServerError))
var odataError libregraph.OdataError
err = json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("generalException"))
Expect(getError.GetMessage()).To(Equal("transport error"))
})
It("fails due to an invalid spaceID", func() {
driveItemCreateLink = libregraph.NewDriveItemCreateLink()
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/graph/v1beta1/drives/space-id/items/item-id/createShare", nil),
)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
var odataError libregraph.OdataError
err := json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("invalidRequest"))
Expect(getError.GetMessage()).To(Equal("invalid driveID"))
})
It("fails due to an empty itemID", func() {
driveItemCreateLink = libregraph.NewDriveItemCreateLink()
itemID = ""
rctx := chi.NewRouteContext()
rctx.URLParams.Add("driveID", DriveId)
rctx.URLParams.Add("itemID", itemID)
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
ctx = revactx.ContextSetUser(ctx, currentUser)
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", nil).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
var odataError libregraph.OdataError
err := json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("invalidRequest"))
Expect(getError.GetMessage()).To(Equal("invalid itemID"))
})
It("fails due to an itemID on a different storage", func() {
driveItemCreateLink = libregraph.NewDriveItemCreateLink()
// use wrong storageID within itemID
itemID = "f0042750-23c5-441c-9f2c-ff7c53e5bd2b$cd621428-dfbe-44c1-9393-65bf0dd440a6!1177add3-b4eb-434e-a2e8-1859b31b17bf"
rctx := chi.NewRouteContext()
rctx.URLParams.Add("driveID", DriveId)
rctx.URLParams.Add("itemID", itemID)
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
ctx = revactx.ContextSetUser(ctx, currentUser)
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", nil).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusNotFound))
var odataError libregraph.OdataError
err := json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("itemNotFound"))
Expect(getError.GetMessage()).To(Equal("driveID and itemID do not match"))
})
// Public Shares / "links" in graph terms
It("fails when creating a public link with an empty request body", func() {
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", nil).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
var odataError libregraph.OdataError
err := json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("invalidRequest"))
Expect(getError.GetMessage()).To(Equal("invalid body schema definition"))
})
It("fails when the stat returns access denied", func() {
err := errors.New("no permission to stat the file")
statResponse.Status = status.NewPermissionDenied(ctx, err, err.Error())
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemCreateLink)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusForbidden))
var odataError libregraph.OdataError
err = json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("accessDenied"))
Expect(getError.GetMessage()).To(Equal("no permission to stat the file"))
})
It("fails when the stat returns resource is locked", func() {
err := errors.New("the resource is locked")
statResponse.Status = status.NewLocked(ctx, err.Error())
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemCreateLink)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusLocked))
var odataError libregraph.OdataError
err = json.Unmarshal(rr.Body.Bytes(), &odataError)
Expect(err).ToNot(HaveOccurred())
getError := odataError.GetError()
Expect(getError.GetCode()).To(Equal("itemIsLocked"))
Expect(getError.GetMessage()).To(Equal("the resource is locked"))
})
It("succeeds when the link type mapping is not successful", func() {
// we need to send a valid link type
linkType, err := libregraph.NewSharingLinkTypeFromValue("edit")
Expect(err).ToNot(HaveOccurred())
driveItemCreateLink.Type = linkType
permissions := &provider.ResourcePermissions{
CreateContainer: true,
InitiateFileUpload: true,
Move: true,
}
// return different permissions which do not match a link type
createLinkResponse.Share = &link.PublicShare{
Id: &link.PublicShareId{OpaqueId: "123"},
Expiration: utils.TimeToTS(*driveItemCreateLink.ExpirationDateTime),
PasswordProtected: false,
DisplayName: ViewerLinkString,
Token: "SomeGOODCoffee",
Permissions: &link.PublicSharePermissions{Permissions: permissions},
}
svc.CreateLink(
rr,
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemCreateLink)).
WithContext(ctx),
)
Expect(rr.Code).To(Equal(http.StatusOK))
var createLinkResponseBody *libregraph.Permission
err = json.Unmarshal(rr.Body.Bytes(), &createLinkResponseBody)
Expect(err).ToNot(HaveOccurred())
Expect(createLinkResponseBody.GetId()).To(Equal("123"))
Expect(createLinkResponseBody.GetExpirationDateTime().Unix()).To(Equal(driveItemCreateLink.ExpirationDateTime.Unix()))
Expect(createLinkResponseBody.GetHasPassword()).To(Equal(false))
Expect(createLinkResponseBody.GetLink().LibreGraphDisplayName).To(Equal(libregraph.PtrString(ViewerLinkString)))
respLink := createLinkResponseBody.GetLink()
// some conversion gymnastics
respLinkType := respLink.GetType()
Expect(err).ToNot(HaveOccurred())
mockLink := libregraph.SharingLink{}
lt, _ := linktype.SharingLinkTypeFromCS3Permissions(&link.PublicSharePermissions{Permissions: permissions})
mockLink.Type = lt
expectedType := mockLink.GetType()
Expect(&respLinkType).To(Equal(&expectedType))
libreGraphActions := createLinkResponseBody.LibreGraphPermissionsActions
Expect(libreGraphActions[0]).To(Equal("libre.graph/driveItem/children/create"))
Expect(libreGraphActions[1]).To(Equal("libre.graph/driveItem/upload/create"))
Expect(libreGraphActions[2]).To(Equal("libre.graph/driveItem/path/update"))
})
})
})

View File

@@ -109,8 +109,6 @@ type Service interface {
GetRootDriveChildren(w http.ResponseWriter, r *http.Request)
GetDriveItem(w http.ResponseWriter, r *http.Request)
GetDriveItemChildren(w http.ResponseWriter, r *http.Request)
CreateLink(w http.ResponseWriter, r *http.Request)
SetLinkPassword(writer http.ResponseWriter, request *http.Request)
CreateUploadSession(w http.ResponseWriter, r *http.Request)
@@ -250,10 +248,10 @@ func NewService(opts ...Option) (Graph, error) {
r.Route("/{permissionID}", func(r chi.Router) {
r.Delete("/", driveItemPermissionsApi.DeletePermission)
r.Patch("/", driveItemPermissionsApi.UpdatePermission)
r.Post("/setPassword", svc.SetLinkPassword)
r.Post("/setPassword", driveItemPermissionsApi.SetLinkPassword)
})
})
r.Post("/createLink", svc.CreateLink)
r.Post("/createLink", driveItemPermissionsApi.CreateLink)
})
})
})