mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-07 04:40:05 -06:00
refactor(graph): move CreateLink and SetPublicLinkPassword to the permissions service
This commit is contained in:
committed by
Ralf Haferkamp
parent
8124024caf
commit
2d643219e3
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user