From 211e98dce92eb38722b3a295ad04b2fe8befe9d6 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 30 Nov 2023 17:53:18 +0100 Subject: [PATCH] [wip] graph sharing: UpdatePermissions --- services/graph/pkg/service/v0/driveitems.go | 78 ++++++++++++++++++ .../graph/pkg/service/v0/driveitems_test.go | 82 +++++++++++++++++++ services/graph/pkg/service/v0/service.go | 10 ++- services/graph/pkg/validate/libregraph.go | 10 +++ 4 files changed, 178 insertions(+), 2 deletions(-) diff --git a/services/graph/pkg/service/v0/driveitems.go b/services/graph/pkg/service/v0/driveitems.go index 164d0cc0ba5..0230caf7eb4 100644 --- a/services/graph/pkg/service/v0/driveitems.go +++ b/services/graph/pkg/service/v0/driveitems.go @@ -609,6 +609,55 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, &ListResponse{Value: value}) } +// UpdatePermission updates a Permission of a Drive item +func (g Graph) UpdatePermission(w http.ResponseWriter, r *http.Request) { + _, itemID, err := g.GetDriveAndItemIDParam(r) + 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 driveID") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid driveID") + return + } + + permission := &libregraph.Permission{} + if err := StrictJSONUnmarshal(r.Body, permission); err != nil { + g.logger.Debug().Err(err).Interface("Body", r.Body).Msg("failed unmarshalling request body") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid request body") + return + } + + ctx := r.Context() + if err := validate.StructCtx(ctx, permission); err != nil { + g.logger.Debug().Err(err).Interface("Body", r.Body).Msg("invalid request body") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error()) + return + } + + _, sharedResourceId, err := g.getPermissionByID(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(sharedResourceId, &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 + } + + // TODO HERE the magic happens + render.Status(r, http.StatusNotImplemented) + return +} + // DeletePermission removes a Permission from a Drive item func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) { _, itemID, err := g.GetDriveAndItemIDParam(r) @@ -667,6 +716,35 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) { return } +func (g Graph) getPermissionByID(ctx context.Context, permissionID string) (*libregraph.Permission, *storageprovider.ResourceId, error) { + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed") + return nil, nil, err + } + + getShareResp, err := gatewayClient.GetShare(ctx, + &collaboration.GetShareRequest{ + Ref: &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: &collaboration.ShareId{ + OpaqueId: permissionID, + }, + }, + }, + }) + var errcode *errorcode.Error + if errcode = errorcode.FromCS3Status(getShareResp.GetStatus(), err); errcode != nil { + return nil, nil, *errcode + } + + permission, err := g.cs3UserShareToPermission(ctx, getShareResp.GetShare()) + if err != nil { + return nil, nil, err + } + return permission, getShareResp.GetShare().GetResourceId(), nil +} + func (g Graph) getUserPermissionResourceID(ctx context.Context, permissionID string) (*storageprovider.ResourceId, error) { gatewayClient, err := g.gatewaySelector.Next() if err != nil { diff --git a/services/graph/pkg/service/v0/driveitems_test.go b/services/graph/pkg/service/v0/driveitems_test.go index 0eab263318f..4cbd348dd7b 100644 --- a/services/graph/pkg/service/v0/driveitems_test.go +++ b/services/graph/pkg/service/v0/driveitems_test.go @@ -248,6 +248,88 @@ var _ = Describe("Driveitems", func() { }) }) + Describe("UpdatePermission", func() { + var ( + driveItemPermission *libregraph.Permission + getShareMockResponse *collaboration.GetShareResponse + getPublicShareMockResponse *link.GetPublicShareResponse + // updateShareMockResponse *collaboration.UpdateShareResponse + ) + BeforeEach(func() { + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "1$2") + rctx.URLParams.Add("itemID", "1$2!3") + rctx.URLParams.Add("permissionID", "permissionid") + + ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx) + ctx = revactx.ContextSetUser(ctx, currentUser) + + driveItemPermission = &libregraph.Permission{ + Id: libregraph.PtrString("permissionid"), + } + + getShareMock := gatewayClient.On("GetShare", + mock.Anything, + mock.MatchedBy(func(req *collaboration.GetShareRequest) bool { + return req.GetRef().GetId().GetOpaqueId() == "permissionid" + }), + ) + getShareMockResponse = &collaboration.GetShareResponse{ + Status: status.NewOK(ctx), + Share: &collaboration.Share{ + Id: &collaboration.ShareId{ + OpaqueId: "permissionid", + }, + ResourceId: &provider.ResourceId{ + StorageId: "1", + SpaceId: "2", + OpaqueId: "3", + }, + }, + } + + getShareMock.Return(getShareMockResponse, nil) + getPublicShareMock := gatewayClient.On("GetPublicShare", + mock.Anything, + mock.MatchedBy(func(req *link.GetPublicShareRequest) bool { + return req.GetRef().GetId().GetOpaqueId() == "permissionid" + }), + ) + getPublicShareMockResponse = &link.GetPublicShareResponse{ + Status: status.NewOK(ctx), + Share: &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: "permissionid", + }, + ResourceId: &provider.ResourceId{ + StorageId: "1", + SpaceId: "2", + OpaqueId: "3", + }, + }, + } + getPublicShareMock.Return(getPublicShareMockResponse, nil) + }) + It("fails when no share is found", func() { + expiratonTime := time.Now().Add(time.Hour) + driveItemPermission.ExpirationDateTime.Set(&expiratonTime) + + getShareMockResponse.Share = nil + getShareMockResponse.Status = status.NewNotFound(ctx, "not found") + getPublicShareMockResponse.Share = nil + getPublicShareMockResponse.Status = status.NewNotFound(ctx, "not found") + + body, err := driveItemPermission.MarshalJSON() + Expect(err).To(BeNil()) + svc.UpdatePermission( + rr, + httptest.NewRequest(http.MethodPatch, "/", strings.NewReader(string(body))). + WithContext(ctx), + ) + Expect(rr.Code).To(Equal(http.StatusNotFound)) + }) + }) + Describe("Invite", func() { var ( driveItemInvite *libregraph.DriveItemInvite diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index 7dc91060ceb..61211a9d90e 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -111,6 +111,7 @@ type Service interface { Invite(w http.ResponseWriter, r *http.Request) ListPermissions(w http.ResponseWriter, r *http.Request) + UpdatePermission(w http.ResponseWriter, r *http.Request) DeletePermission(w http.ResponseWriter, r *http.Request) CreateUploadSession(w http.ResponseWriter, r *http.Request) @@ -205,8 +206,13 @@ func NewService(opts ...Option) (Graph, error) { r.Get("/me/drive/sharedWithMe", svc.ListSharedWithMe) r.Route("/drives/{driveID}/items/{itemID}", func(r chi.Router) { r.Post("/invite", svc.Invite) - r.Get("/permissions", svc.ListPermissions) - r.Delete("/permissions/{permissionID}", svc.DeletePermission) + r.Route("/permissions", func(r chi.Router) { + r.Get("/", svc.ListPermissions) + r.Route("/{permissionID}", func(r chi.Router) { + r.Delete("/", svc.DeletePermission) + r.Patch("/", svc.UpdatePermission) + }) + }) r.Post("/createLink", svc.CreateLink) }) r.Route("/roleManagement/permissions/roleDefinitions", func(r chi.Router) { diff --git a/services/graph/pkg/validate/libregraph.go b/services/graph/pkg/validate/libregraph.go index 0317e2a5a03..25fa714ff0f 100644 --- a/services/graph/pkg/validate/libregraph.go +++ b/services/graph/pkg/validate/libregraph.go @@ -79,3 +79,13 @@ func driveItemInvite(v *validator.Validate) { }, s) } + +// permission validates libregraph.Permission +func permission(v *validator.Validate) { + s := libregraph.Permission{} + + v.RegisterStructValidationMapRules(map[string]string{ + "Roles": "max=1", + "ExpirationDateTime": "omitnil,gt", + }, s) +}