Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

graph: implement delete permission for link permissions #7849

Merged
merged 4 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.7.0
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781
github.com/cs3org/reva/v2 v2.16.1-0.20231127154323-1e3027b83df5
github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9
github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25
github.com/disintegration/imaging v1.6.2
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1017,8 +1017,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY=
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cs3org/reva/v2 v2.16.1-0.20231127154323-1e3027b83df5 h1:+R3E25HVI3Ms9fzgPy8QNkby3u8x7oKpRerB/A9l5iM=
github.com/cs3org/reva/v2 v2.16.1-0.20231127154323-1e3027b83df5/go.mod h1:zcrrYVsBv/DwhpyO2/W5hoSZ/k6az6Z2EYQok65uqZY=
github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9 h1:5vKQcL1hPHEZKu9e8C9rl0ap3ofMBznmoSgi4lRYXec=
github.com/cs3org/reva/v2 v2.16.1-0.20231128104331-ea8d1336afc9/go.mod h1:zcrrYVsBv/DwhpyO2/W5hoSZ/k6az6Z2EYQok65uqZY=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
31 changes: 20 additions & 11 deletions services/graph/pkg/errorcode/cs3.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
)

// FromCS3Status converts a CS3 status code into a corresponding local Error representation.
// FromCS3Status converts a CS3 status code and an error into a corresponding local Error representation.
//
// It evaluates the provided CS3 status code and returns an equivalent graph Error.
// It takes a *cs3rpc.Status, an error, and a variadic parameter of type cs3rpc.Code.
// If the error is not nil, it creates an Error object with the error message and a GeneralException code.
// If the error is nil, it evaluates the provided CS3 status code and returns an equivalent graph Error.
// If the CS3 status code does not have a direct equivalent within the app,
// or is ignored, a general purpose Error is returned.
//
// This function is particularly useful when dealing with CS3 responses,
// and a unified error handling within the application is necessary.
func FromCS3Status(status *cs3rpc.Status, ignore ...cs3rpc.Code) *Error {
func FromCS3Status(status *cs3rpc.Status, inerr error, ignore ...cs3rpc.Code) *Error {
if inerr != nil {
return &Error{msg: inerr.Error(), errorCode: GeneralException}
}

err := &Error{errorCode: GeneralException, msg: "unspecified error has occurred"}

if status != nil {
Expand All @@ -40,8 +46,16 @@ func FromCS3Status(status *cs3rpc.Status, ignore ...cs3rpc.Code) *Error {
err.errorCode = NameAlreadyExists
case code == cs3rpc.Code_CODE_FAILED_PRECONDITION:
err.errorCode = PreconditionFailed
case code == cs3rpc.Code_CODE_OUT_OF_RANGE:
err.errorCode = InvalidRange
case code == cs3rpc.Code_CODE_UNIMPLEMENTED:
err.errorCode = NotSupported
case code == cs3rpc.Code_CODE_UNAVAILABLE:
err.errorCode = ServiceNotAvailable
case code == cs3rpc.Code_CODE_INSUFFICIENT_STORAGE:
err.errorCode = QuotaLimitReached
case code == cs3rpc.Code_CODE_LOCKED:
err.errorCode = ItemIsLocked
}

return err
Expand All @@ -50,13 +64,8 @@ func FromCS3Status(status *cs3rpc.Status, ignore ...cs3rpc.Code) *Error {
// FromStat transforms a *provider.StatResponse object and an error into an *Error.
//
// It takes a stat of type *provider.StatResponse, an error, and a variadic parameter of type cs3rpc.Code.
// If the error is not nil, it creates an Error object with the error message and a GeneralException code.
// If the error is nil, it invokes the FromCS3Status function with the StatResponse Status and the ignore codes.
// It invokes the FromCS3Status function with the StatResponse Status and the ignore codes.
func FromStat(stat *provider.StatResponse, err error, ignore ...cs3rpc.Code) *Error {
switch {
case err != nil:
return &Error{msg: err.Error(), errorCode: GeneralException}
default:
return FromCS3Status(stat.GetStatus(), ignore...)
}
// TODO: look into ResourceInfo to get the postprocessing state and map that to 425 status?
return FromCS3Status(stat.GetStatus(), err, ignore...)
}
48 changes: 25 additions & 23 deletions services/graph/pkg/errorcode/cs3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,37 @@ import (
func TestFromCS3Status(t *testing.T) {
var tests = []struct {
status *cs3rpc.Status
err error
ignore []cs3rpc.Code
result *errorcode.Error
}{
{nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "unspecified error has occurred"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_OK}, nil, nil},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_NOT_FOUND}, []cs3rpc.Code{cs3rpc.Code_CODE_NOT_FOUND}, nil},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_PERMISSION_DENIED}, []cs3rpc.Code{cs3rpc.Code_CODE_NOT_FOUND, cs3rpc.Code_CODE_PERMISSION_DENIED}, nil},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_NOT_FOUND, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.ItemNotFound, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_PERMISSION_DENIED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.AccessDenied, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNAUTHENTICATED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.Unauthenticated, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INVALID_ARGUMENT, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.InvalidRequest, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_ALREADY_EXISTS, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.NameAlreadyExists, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_FAILED_PRECONDITION, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.PreconditionFailed, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNIMPLEMENTED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.NotSupported, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INVALID, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_CANCELLED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNKNOWN, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_RESOURCE_EXHAUSTED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_ABORTED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_OUT_OF_RANGE, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INTERNAL, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNAVAILABLE, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_REDIRECTION, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INSUFFICIENT_STORAGE, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_LOCKED, Message: "msg"}, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{nil, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "unspecified error has occurred"))},
{nil, errors.New("test error"), nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "test error"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_OK}, nil, nil, nil},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_NOT_FOUND}, nil, []cs3rpc.Code{cs3rpc.Code_CODE_NOT_FOUND}, nil},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_PERMISSION_DENIED}, nil, []cs3rpc.Code{cs3rpc.Code_CODE_NOT_FOUND, cs3rpc.Code_CODE_PERMISSION_DENIED}, nil},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_NOT_FOUND, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.ItemNotFound, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_PERMISSION_DENIED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.AccessDenied, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNAUTHENTICATED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.Unauthenticated, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INVALID_ARGUMENT, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.InvalidRequest, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_ALREADY_EXISTS, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.NameAlreadyExists, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_FAILED_PRECONDITION, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.PreconditionFailed, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNIMPLEMENTED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.NotSupported, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INVALID, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_CANCELLED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNKNOWN, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_RESOURCE_EXHAUSTED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_ABORTED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_OUT_OF_RANGE, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.InvalidRange, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INTERNAL, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_UNAVAILABLE, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.ServiceNotAvailable, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_REDIRECTION, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.GeneralException, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_INSUFFICIENT_STORAGE, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.QuotaLimitReached, "msg"))},
{&cs3rpc.Status{Code: cs3rpc.Code_CODE_LOCKED, Message: "msg"}, nil, nil, conversions.ToPointer(errorcode.New(errorcode.ItemIsLocked, "msg"))},
}

for _, test := range tests {
if output := errorcode.FromCS3Status(test.status, test.ignore...); !reflect.DeepEqual(output, test.result) {
if output := errorcode.FromCS3Status(test.status, test.err, test.ignore...); !reflect.DeepEqual(output, test.result) {
t.Error("Test Failed: {} expected, recieved: {}", test.result, output)
}
}
Expand Down
4 changes: 4 additions & 0 deletions services/graph/pkg/errorcode/errorcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ func (e Error) Error() string {
return errString
}

func (e Error) GetCode() ErrorCode {
return e.errorCode
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not too happy with this. I think ideally we would use properly typed errors. This would all using things like errors.Is(graph.ErrorItemNotFound).

However it requires quite a bit of refactoring, so I am still pondering if that is worth it.


// RenderError render the Graph Error based on a code or default one
func RenderError(w http.ResponseWriter, r *http.Request, err error) {
var errcode Error
Expand Down
150 changes: 104 additions & 46 deletions services/graph/pkg/service/v0/driveitems.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,14 +504,7 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {

// 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)
_, itemID, err := g.GetDriveAndItemIDParam(r)
if err != nil {
errorcode.RenderError(w, r, err)
return
Expand All @@ -526,6 +519,56 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
}

ctx := r.Context()
isUserPermission := true

// Check if the id is refering to a User Share
sharedResourceId, err := g.getUserPermissionResourceID(ctx, permissionID)
var errcode *errorcode.Error
if err != nil && errors.As(err, &errcode) && errcode.GetCode() == errorcode.ItemNotFound {
// there is no user share with that ID, so lets check if it is refering to a public link
isUserPermission = false
sharedResourceId, err = g.getLinkPermissionResourceID(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 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
}

if isUserPermission {
err = g.removeUserShare(ctx, permissionID)
} else {
err = g.removePublicShare(ctx, permissionID)
}

if err != nil {
errorcode.RenderError(w, r, err)
return
}

render.Status(r, http.StatusNoContent)
render.NoContent(w, r)

return
}

func (g Graph) getUserPermissionResourceID(ctx context.Context, permissionID string) (*storageprovider.ResourceId, error) {
gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed")
return nil, err
}

getShareResp, err := gatewayClient.GetShare(ctx,
&collaboration.GetShareRequest{
Ref: &collaboration.ShareReference{
Expand All @@ -536,24 +579,17 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
},
},
})
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
if errCode := errorcode.FromCS3Status(getShareResp.GetStatus(), err); errCode != nil {
return nil, errCode
}
return getShareResp.Share.GetResourceId(), nil
}

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
func (g Graph) removeUserShare(ctx context.Context, permissionID string) error {
gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed")
return err
}

removeShareResp, err := gatewayClient.RemoveShare(ctx,
Expand All @@ -566,38 +602,60 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
},
},
})
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
if errCode := errorcode.FromCS3Status(removeShareResp.GetStatus(), err); errCode != nil {
return errCode
}
// We need to return an untyped nil here otherwise the error==nil check won't work
return nil
}

func (g Graph) extractDriveAndDriveItem(r *http.Request) (driveID storageprovider.ResourceId, itemID storageprovider.ResourceId, err error) {
driveID, err = parseIDParam(r, "driveID")
func (g Graph) getLinkPermissionResourceID(ctx context.Context, permissionID string) (*storageprovider.ResourceId, error) {
gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
g.logger.Debug().Err(err).Msg("could not parse driveID")
return driveID, itemID, errorcode.New(errorcode.InvalidRequest, "invalid driveID")
g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed")
return nil, err
}

getPublicShareResp, err := gatewayClient.GetPublicShare(ctx,
&link.GetPublicShareRequest{
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: permissionID,
},
},
},
},
)
if errCode := errorcode.FromCS3Status(getPublicShareResp.GetStatus(), err); errCode != nil {
return nil, errCode
}
return getPublicShareResp.Share.GetResourceId(), nil
}

itemID, err = parseIDParam(r, "itemID")
func (g Graph) removePublicShare(ctx context.Context, permissionID string) error {
gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
g.logger.Debug().Err(err).Msg("could not parse itemID")
return driveID, itemID, errorcode.New(errorcode.InvalidRequest, "invalid itemID")
g.logger.Debug().Err(err).Msg("selecting gatewaySelector failed")
return err
}

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")
removePublicShareResp, err := gatewayClient.RemovePublicShare(ctx,
&link.RemovePublicShareRequest{
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Id{
Id: &link.PublicShareId{
OpaqueId: permissionID,
},
},
},
})
if errcode := errorcode.FromCS3Status(removePublicShareResp.GetStatus(), err); errcode != nil {
return errcode
}
return driveID, itemID, nil
// We need to return an untyped nil here otherwise the error==nil check won't work
return nil
}

func (g Graph) getDriveItem(ctx context.Context, ref storageprovider.Reference) (*libregraph.DriveItem, error) {
Expand Down
Loading