graph: Add delete permission endpoint
Allows the owner to remove permissions of a driveItem (i.e. to delete a share). For now this does not work for "link" permission (i.e. public shares)
This commit is contained in:
committed by
Ralf Haferkamp
parent
a1dd520262
commit
4a94388a25
@@ -19,6 +19,7 @@ import (
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"golang.org/x/crypto/sha3"
|
||||
@@ -252,23 +253,9 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
driveID, err := parseIDParam(r, "driveID")
|
||||
_, itemID, err := g.extractDriveAndDriveItem(r)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("could not parse driveID")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid driveID")
|
||||
return
|
||||
}
|
||||
|
||||
itemID, err := parseIDParam(r, "itemID")
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("could not parse itemID")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid itemID")
|
||||
return
|
||||
}
|
||||
|
||||
if driveID.GetStorageId() != itemID.GetStorageId() || driveID.GetSpaceId() != itemID.GetSpaceId() {
|
||||
g.logger.Debug().Interface("driveID", driveID).Interface("itemID", itemID).Msg("driveID and itemID do not match")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "driveID and itemID do not match")
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -447,6 +434,104 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, &ListResponse{Value: value})
|
||||
}
|
||||
|
||||
// DeletePermission removes a Permission from a Drive item
|
||||
func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
|
||||
_, itemID, err := g.extractDriveAndDriveItem(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
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
getShareResp, err := gatewayClient.GetShare(ctx,
|
||||
&collaboration.GetShareRequest{
|
||||
Ref: &collaboration.ShareReference{
|
||||
Spec: &collaboration.ShareReference_Id{
|
||||
Id: &collaboration.ShareId{
|
||||
OpaqueId: permissionID,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
switch {
|
||||
case err != nil:
|
||||
fallthrough
|
||||
case getShareResp.Status.GetCode() != cs3rpc.Code_CODE_OK:
|
||||
g.logger.Debug().Err(err).Interface("permissionID", permissionID).Interface("GetShare", getShareResp).Msg("GetShare failed")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
|
||||
sharedResourceId := getShareResp.GetShare().GetResourceId()
|
||||
// The resourceID of the shared resource need to matched the item ID from the Request Path
|
||||
// otherwise this is an invalid Request.
|
||||
if sharedResourceId.GetStorageId() != itemID.GetStorageId() ||
|
||||
sharedResourceId.GetSpaceId() != itemID.GetSpaceId() ||
|
||||
sharedResourceId.GetOpaqueId() != itemID.GetOpaqueId() {
|
||||
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
|
||||
}
|
||||
|
||||
removeShareResp, err := gatewayClient.RemoveShare(ctx,
|
||||
&collaboration.RemoveShareRequest{
|
||||
Ref: &collaboration.ShareReference{
|
||||
Spec: &collaboration.ShareReference_Id{
|
||||
Id: &collaboration.ShareId{
|
||||
OpaqueId: permissionID,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
switch {
|
||||
case err != nil:
|
||||
fallthrough
|
||||
case removeShareResp.Status.GetCode() != cs3rpc.Code_CODE_OK:
|
||||
g.logger.Debug().Err(err).Interface("permissionID", permissionID).Interface("GetShare", getShareResp).Msg("GetShare failed")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (g Graph) extractDriveAndDriveItem(r *http.Request) (driveID storageprovider.ResourceId, itemID storageprovider.ResourceId, err error) {
|
||||
driveID, err = parseIDParam(r, "driveID")
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("could not parse driveID")
|
||||
return driveID, itemID, errorcode.New(errorcode.InvalidRequest, "invalid driveID")
|
||||
}
|
||||
|
||||
itemID, err = parseIDParam(r, "itemID")
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("could not parse itemID")
|
||||
return driveID, itemID, errorcode.New(errorcode.InvalidRequest, "invalid itemID")
|
||||
}
|
||||
|
||||
if driveID.GetStorageId() != itemID.GetStorageId() || driveID.GetSpaceId() != itemID.GetSpaceId() {
|
||||
g.logger.Debug().Interface("driveID", driveID).Interface("itemID", itemID).Msg("driveID and itemID do not match")
|
||||
return driveID, itemID, errorcode.New(errorcode.InvalidRequest, "driveID and itemID do not match")
|
||||
}
|
||||
return driveID, itemID, nil
|
||||
}
|
||||
|
||||
func (g Graph) getDriveItem(ctx context.Context, ref storageprovider.Reference) (*libregraph.DriveItem, error) {
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
|
||||
@@ -100,6 +100,137 @@ var _ = Describe("Driveitems", func() {
|
||||
)
|
||||
})
|
||||
|
||||
Describe("DeletePermission", func() {
|
||||
It("validates the driveID", func() {
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "")
|
||||
|
||||
ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx)
|
||||
|
||||
svc.DeletePermission(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", nil).WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("validates the itemID", func() {
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "f0042750-23c5-441c-9f2c-ff7c53e5bd2a$cd621428-dfbe-44c1-9393-65bf0dd440a6!cd621428-dfbe-44c1-9393-65bf0dd440a6")
|
||||
rctx.URLParams.Add("itemID", "")
|
||||
|
||||
ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx)
|
||||
|
||||
svc.DeletePermission(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", nil).WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("checks if the itemID and driveID is compatible to each other", func() {
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "1$2!3")
|
||||
rctx.URLParams.Add("itemID", "4$5!6")
|
||||
|
||||
ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx)
|
||||
|
||||
svc.DeletePermission(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", nil).WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("deletes a permission as expected", func() {
|
||||
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)
|
||||
|
||||
rmShareMock := gatewayClient.On("RemoveShare",
|
||||
mock.Anything,
|
||||
mock.MatchedBy(func(req *collaboration.RemoveShareRequest) bool {
|
||||
return req.GetRef().GetId().GetOpaqueId() == "permissionid"
|
||||
}),
|
||||
)
|
||||
rmShareMockResponse := &collaboration.RemoveShareResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}
|
||||
rmShareMock.Return(rmShareMockResponse, nil)
|
||||
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "1$2")
|
||||
rctx.URLParams.Add("itemID", "1$2!3")
|
||||
rctx.URLParams.Add("permissionID", "permissionid")
|
||||
|
||||
ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx)
|
||||
|
||||
svc.DeletePermission(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", nil).WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
})
|
||||
|
||||
It("fails to delete permission when the item id does not match the shared resource's id", func() {
|
||||
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: "3",
|
||||
SpaceId: "4",
|
||||
OpaqueId: "5",
|
||||
},
|
||||
},
|
||||
}
|
||||
getShareMock.Return(getShareMockResponse, nil)
|
||||
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "1$2")
|
||||
rctx.URLParams.Add("itemID", "1$2!3")
|
||||
rctx.URLParams.Add("permissionID", "permissionid")
|
||||
|
||||
ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx)
|
||||
|
||||
svc.DeletePermission(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", nil).WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Invite", func() {
|
||||
var (
|
||||
itemID string
|
||||
|
||||
@@ -109,6 +109,7 @@ type Service interface {
|
||||
GetDriveItemChildren(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
Invite(w http.ResponseWriter, r *http.Request)
|
||||
DeletePermission(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
GetTags(w http.ResponseWriter, r *http.Request)
|
||||
AssignTags(w http.ResponseWriter, r *http.Request)
|
||||
@@ -198,7 +199,10 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
r.Route("/v1beta1", func(r chi.Router) {
|
||||
r.Get("/me/drive/sharedByMe", svc.GetSharedByMe)
|
||||
r.Get("/me/drive/sharedWithMe", svc.ListSharedWithMe)
|
||||
r.Post("/drives/{driveID}/items/{itemID}/invite", svc.Invite)
|
||||
r.Route("/drives/{driveID}/items/{itemID}", func(r chi.Router) {
|
||||
r.Post("/invite", svc.Invite)
|
||||
r.Delete("/permissions/{permissionID}", svc.DeletePermission)
|
||||
})
|
||||
r.Route("/roleManagement/permissions/roleDefinitions", func(r chi.Router) {
|
||||
r.Get("/", svc.GetRoleDefinitions)
|
||||
r.Get("/{roleID}", svc.GetRoleDefinition)
|
||||
|
||||
Reference in New Issue
Block a user