enhancement: add graph beta listPermissions endpoint (#7753)
* enhancement: add graph beta listPermissions endpoint besides the new api endpoint it includes several utilities to simplify the graph api development. * resolve drive and item id from the request path * generic pointer and value utilities * space root detection * update GetDriveAndItemIDParam signature to return a error * move errorcode package * enhancement: add generic error code handling * fix: rebase
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package errorcode
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
)
|
||||
|
||||
// FromCS3Status converts a CS3 status code into a corresponding local Error representation.
|
||||
//
|
||||
// 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 {
|
||||
err := &Error{errorCode: GeneralException, msg: "unspecified error has occurred"}
|
||||
|
||||
if status != nil {
|
||||
err.msg = status.GetMessage()
|
||||
}
|
||||
|
||||
code := status.GetCode()
|
||||
switch {
|
||||
case slices.Contains(ignore, status.GetCode()):
|
||||
fallthrough
|
||||
case code == cs3rpc.Code_CODE_OK:
|
||||
err = nil
|
||||
case code == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
err.errorCode = ItemNotFound
|
||||
case code == cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
err.errorCode = AccessDenied
|
||||
case code == cs3rpc.Code_CODE_UNAUTHENTICATED:
|
||||
err.errorCode = Unauthenticated
|
||||
case code == cs3rpc.Code_CODE_INVALID_ARGUMENT:
|
||||
err.errorCode = InvalidRequest
|
||||
case code == cs3rpc.Code_CODE_ALREADY_EXISTS:
|
||||
err.errorCode = NameAlreadyExists
|
||||
case code == cs3rpc.Code_CODE_FAILED_PRECONDITION:
|
||||
err.errorCode = PreconditionFailed
|
||||
case code == cs3rpc.Code_CODE_UNIMPLEMENTED:
|
||||
err.errorCode = NotSupported
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 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.
|
||||
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...)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package errorcode_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
func TestFromCS3Status(t *testing.T) {
|
||||
var tests = []struct {
|
||||
status *cs3rpc.Status
|
||||
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"))},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if output := errorcode.FromCS3Status(test.status, test.ignore...); !reflect.DeepEqual(output, test.result) {
|
||||
t.Error("Test Failed: {} expected, recieved: {}", test.result, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStat(t *testing.T) {
|
||||
var tests = []struct {
|
||||
stat *provider.StatResponse
|
||||
err error
|
||||
result *errorcode.Error
|
||||
}{
|
||||
{nil, errors.New("some error"), conversions.ToPointer(errorcode.New(errorcode.GeneralException, "some error"))},
|
||||
{&provider.StatResponse{Status: &cs3rpc.Status{Code: cs3rpc.Code_CODE_OK}}, nil, nil},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if output := errorcode.FromStat(test.stat, test.err); !reflect.DeepEqual(output, test.result) {
|
||||
t.Error("Test Failed: {} expected, recieved: {}", test.result, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
cs3group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
cs3user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// Errors used by the interfaces
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
revautils "github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/jellydator/ttlcache/v3"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// IdentityCache implements a simple ttl based cache for looking up users and groups by ID
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/libregraph/idm/pkg/ldapdn"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
type educationClassAttributeMap struct {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
type educationConfig struct {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"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/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
type educationUserAttributeMap struct {
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/libregraph/idm/pkg/ldapdn"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
type groupAttributeMap struct {
|
||||
|
||||
@@ -3,15 +3,16 @@ package middleware
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
gmmetadata "go-micro.dev/v4/metadata"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/auth/scope"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/token/manager/jwt"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/account"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
opkgm "github.com/owncloud/ocis/v2/ocis-pkg/middleware"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
gmmetadata "go-micro.dev/v4/metadata"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// authOptions initializes the available default options.
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/roles"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
settings "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"crypto/subtle"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// ListApplications implements the Service interface.
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
merrors "go-micro.dev/v4/errors"
|
||||
)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/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"
|
||||
@@ -25,12 +26,16 @@ import (
|
||||
"golang.org/x/crypto/sha3"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/publicshare"
|
||||
"github.com/cs3org/reva/v2/pkg/share"
|
||||
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/validate"
|
||||
)
|
||||
@@ -244,16 +249,82 @@ func (g Graph) GetDriveItemChildren(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, &ListResponse{Value: files})
|
||||
}
|
||||
|
||||
// Invite invites a user to a storage drive (space).
|
||||
func (g Graph) Invite(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))
|
||||
// ListPermissions lists the permissions of a driveItem
|
||||
func (g Graph) ListPermissions(w http.ResponseWriter, r *http.Request) {
|
||||
gatewayClient, ok := g.GetGatewayClient(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
_, itemID, err := g.extractDriveAndDriveItem(r)
|
||||
_, itemID, err := g.GetDriveAndItemIDParam(r)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
|
||||
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
|
||||
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse)
|
||||
errCode.Render(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
permissionSet := *statResponse.GetInfo().GetPermissionSet()
|
||||
allowedActions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(permissionSet)
|
||||
|
||||
collectionOfPermissions := libregraph.CollectionOfPermissionsWithAllowedValues{
|
||||
LibreGraphPermissionsActionsAllowedValues: allowedActions,
|
||||
LibreGraphPermissionsRolesAllowedValues: conversions.ToValueSlice(
|
||||
unifiedrole.GetApplicableRoleDefinitionsForActions(
|
||||
allowedActions,
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
g.config.FilesSharing.EnableResharing,
|
||||
false,
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
for i, definition := range collectionOfPermissions.LibreGraphPermissionsRolesAllowedValues {
|
||||
// the openapi spec defines that the rolePermissions should not be part of the response
|
||||
definition.RolePermissions = nil
|
||||
collectionOfPermissions.LibreGraphPermissionsRolesAllowedValues[i] = definition
|
||||
}
|
||||
|
||||
driveItems := make(driveItemsByResourceID)
|
||||
driveItems, err = g.listUserShares(ctx, []*collaboration.Filter{
|
||||
share.ResourceIDFilter(conversions.ToPointer(itemID)),
|
||||
}, driveItems)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
driveItems, err = g.listPublicShares(ctx, []*link.ListPublicSharesRequest_Filter{
|
||||
publicshare.ResourceIDFilter(conversions.ToPointer(itemID)),
|
||||
}, driveItems)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, driveItem := range driveItems {
|
||||
collectionOfPermissions.Value = append(collectionOfPermissions.Value, driveItem.Permissions...)
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, collectionOfPermissions)
|
||||
}
|
||||
|
||||
// Invite invites a user to a storage drive (space).
|
||||
func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
gatewayClient, ok := g.GetGatewayClient(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
_, itemID, err := g.GetDriveAndItemIDParam(r)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
@@ -268,23 +339,13 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
if err = validate.StructCtx(ctx, driveItemInvite); err != nil {
|
||||
if err := validate.StructCtx(ctx, driveItemInvite); 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
|
||||
}
|
||||
|
||||
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
|
||||
switch {
|
||||
case err != nil:
|
||||
fallthrough
|
||||
case statResponse.GetStatus().GetCode() != cs3rpc.Code_CODE_OK:
|
||||
g.logger.Debug().Err(err).Interface("itemID", itemID).Interface("Stat", statResponse).Msg("stat failed")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
|
||||
unifiedRolePermissions := []libregraph.UnifiedRolePermission{{AllowedResourceActions: driveItemInvite.LibreGraphPermissionsActions}}
|
||||
unifiedRolePermissions := []*libregraph.UnifiedRolePermission{{AllowedResourceActions: driveItemInvite.LibreGraphPermissionsActions}}
|
||||
for _, roleId := range driveItemInvite.GetRoles() {
|
||||
role, err := unifiedrole.NewUnifiedRoleFromID(roleId, g.config.FilesSharing.EnableResharing)
|
||||
if err != nil {
|
||||
@@ -293,7 +354,14 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
unifiedRolePermissions = append(unifiedRolePermissions, role.GetRolePermissions()...)
|
||||
unifiedRolePermissions = append(unifiedRolePermissions, conversions.ToPointerSlice(role.GetRolePermissions())...)
|
||||
}
|
||||
|
||||
statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
|
||||
if errCode := errorcode.FromStat(statResponse, err); errCode != nil {
|
||||
g.logger.Warn().Err(errCode).Interface("stat.res", statResponse)
|
||||
errCode.Render(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
createShareErrors := sync.Map{}
|
||||
@@ -349,7 +417,7 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
|
||||
Group: &libregraph.Identity{
|
||||
DisplayName: group.GetDisplayName(),
|
||||
Id: libregraph.PtrString(group.GetId()),
|
||||
Id: conversions.ToPointer(group.GetId()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
@@ -368,7 +436,7 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
|
||||
User: &libregraph.Identity{
|
||||
DisplayName: user.GetDisplayName(),
|
||||
Id: libregraph.PtrString(user.GetId()),
|
||||
Id: conversions.ToPointer(user.GetId()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -388,7 +456,7 @@ func (g Graph) Invite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if id := createShareResponse.GetShare().GetId().GetOpaqueId(); id != "" {
|
||||
permission.Id = libregraph.PtrString(id)
|
||||
permission.Id = conversions.ToPointer(id)
|
||||
}
|
||||
|
||||
if expiration := createShareResponse.GetShare().GetExpiration(); expiration != nil {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/go-chi/chi/v5"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@@ -23,12 +24,15 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
|
||||
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/owncloud/ocis/v2/ocis-pkg/conversions"
|
||||
"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"
|
||||
@@ -233,10 +237,7 @@ var _ = Describe("Driveitems", func() {
|
||||
|
||||
Describe("Invite", func() {
|
||||
var (
|
||||
itemID string
|
||||
driveItemInvite *libregraph.DriveItemInvite
|
||||
listSharesMock *mock.Call
|
||||
listSharesResponse *collaboration.ListSharesResponse
|
||||
statMock *mock.Call
|
||||
statResponse *provider.StatResponse
|
||||
getUserResponse *userpb.GetUserResponse
|
||||
@@ -248,10 +249,9 @@ var _ = Describe("Driveitems", func() {
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
itemID = "f0042750-23c5-441c-9f2c-ff7c53e5bd2a$cd621428-dfbe-44c1-9393-65bf0dd440a6!1177add3-b4eb-434e-a2e8-1859b31b17bf"
|
||||
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", itemID)
|
||||
rctx.URLParams.Add("driveID", "1$2")
|
||||
rctx.URLParams.Add("itemID", "1$2!3")
|
||||
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
|
||||
ctx = revactx.ContextSetUser(ctx, currentUser)
|
||||
@@ -289,12 +289,6 @@ var _ = Describe("Driveitems", func() {
|
||||
}
|
||||
getGroupMock.Return(getGroupResponse, nil)
|
||||
|
||||
listSharesMock = gatewayClient.On("ListShares", mock.Anything, mock.Anything)
|
||||
listSharesResponse = &collaboration.ListSharesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}
|
||||
listSharesMock.Return(listSharesResponse, nil)
|
||||
|
||||
createShareMock = gatewayClient.On("CreateShare", mock.Anything, mock.Anything)
|
||||
createShareResponse = &collaboration.CreateShareResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
@@ -378,53 +372,6 @@ var _ = Describe("Driveitems", func() {
|
||||
Expect(jsonData.Get(`0.@libre\.graph\.permissions\.actions.0`).String()).To(Equal(unifiedrole.DriveItemContentRead))
|
||||
})
|
||||
|
||||
It("validates the driveID", func() {
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "")
|
||||
|
||||
ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx)
|
||||
|
||||
svc.Invite(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
|
||||
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.Invite(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
|
||||
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.Invite(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
|
||||
WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("fails if the request body is empty", func() {
|
||||
svc.Invite(
|
||||
rr,
|
||||
@@ -450,26 +397,6 @@ var _ = Describe("Driveitems", func() {
|
||||
}, http.StatusBadRequest),
|
||||
)
|
||||
|
||||
DescribeTable("Stat",
|
||||
func(prep func(), code int) {
|
||||
prep()
|
||||
svc.Invite(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodPost, "/", toJSONReader(driveItemInvite)).
|
||||
WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(code))
|
||||
statMock.Parent.AssertNumberOfCalls(GinkgoT(), "Stat", 1)
|
||||
},
|
||||
Entry("fails if not ok", func() {
|
||||
statResponse.Status = status.NewNotFound(context.Background(), "")
|
||||
}, http.StatusInternalServerError),
|
||||
Entry("fails if errors", func() {
|
||||
statMock.Return(nil, errors.New("error"))
|
||||
}, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
DescribeTable("GetGroup",
|
||||
func(prep func(), code int) {
|
||||
driveItemInvite.Recipients = []libregraph.DriveRecipient{
|
||||
@@ -538,6 +465,90 @@ var _ = Describe("Driveitems", func() {
|
||||
)
|
||||
})
|
||||
|
||||
Describe("ListPermissions", func() {
|
||||
var (
|
||||
statMock *mock.Call
|
||||
statResponse *provider.StatResponse
|
||||
listSharesMock *mock.Call
|
||||
listSharesResponse *collaboration.ListSharesResponse
|
||||
listPublicSharesMock *mock.Call
|
||||
listPublicSharesResponse *link.ListPublicSharesResponse
|
||||
)
|
||||
|
||||
toResourceID := func(in string) *provider.ResourceId {
|
||||
out, err := storagespace.ParseID(in)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
return &out
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "1$2")
|
||||
rctx.URLParams.Add("itemID", "1$2!3")
|
||||
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, rctx)
|
||||
ctx = revactx.ContextSetUser(ctx, currentUser)
|
||||
|
||||
statMock = gatewayClient.On("Stat", mock.Anything, mock.Anything)
|
||||
statResponse = &provider.StatResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Info: &provider.ResourceInfo{
|
||||
Id: toResourceID("1$2!3"),
|
||||
PermissionSet: unifiedrole.PermissionsToCS3ResourcePermissions(
|
||||
conversions.ToPointerSlice(unifiedrole.NewViewerUnifiedRole(true).GetRolePermissions()),
|
||||
),
|
||||
Owner: &userpb.UserId{},
|
||||
},
|
||||
}
|
||||
statMock.Return(statResponse, nil)
|
||||
|
||||
listSharesMock = gatewayClient.On("ListShares", mock.Anything, mock.Anything)
|
||||
listSharesResponse = &collaboration.ListSharesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Shares: []*collaboration.Share{{
|
||||
Id: &collaboration.ShareId{OpaqueId: "123"},
|
||||
ResourceId: toResourceID("1$2!3"),
|
||||
Grantee: &provider.Grantee{},
|
||||
Permissions: &collaboration.SharePermissions{
|
||||
Permissions: unifiedrole.PermissionsToCS3ResourcePermissions(
|
||||
conversions.ToPointerSlice(unifiedrole.NewViewerUnifiedRole(true).GetRolePermissions()),
|
||||
),
|
||||
},
|
||||
}},
|
||||
}
|
||||
listSharesMock.Return(listSharesResponse, nil)
|
||||
|
||||
listPublicSharesMock = gatewayClient.On("ListPublicShares", mock.Anything, mock.Anything)
|
||||
listPublicSharesResponse = &link.ListPublicSharesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}
|
||||
listPublicSharesMock.Return(listPublicSharesResponse, nil)
|
||||
})
|
||||
|
||||
It("lists permissions", func() {
|
||||
svc.ListPermissions(
|
||||
rr,
|
||||
httptest.NewRequest(http.MethodGet, "/", nil).
|
||||
WithContext(ctx),
|
||||
)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
actions := gjson.Get(rr.Body.String(), `@libre\.graph\.permissions\.actions\.allowedValues`)
|
||||
Expect(actions.Get("#").Num).To(Equal(float64(7)))
|
||||
|
||||
roles := gjson.Get(rr.Body.String(), `@libre\.graph\.permissions\.roles\.allowedValues`)
|
||||
Expect(roles.Get("#").Num).To(Equal(float64(1)))
|
||||
Expect(roles.Get("0.id").Str).To(Equal("b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"))
|
||||
Expect(roles.Get("0.rolePermissions").Exists()).To(BeFalse())
|
||||
|
||||
value := gjson.Get(rr.Body.String(), "value")
|
||||
Expect(value.Get("#").Num).To(Equal(float64(1)))
|
||||
Expect(value.Get("0.id").Str).To(Equal("123"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetRootDriveChildren", func() {
|
||||
It("handles ListStorageSpaces not found", func() {
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
|
||||
|
||||
@@ -19,19 +19,20 @@ import (
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
||||
v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
settingsServiceExt "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
|
||||
"github.com/pkg/errors"
|
||||
merrors "go-micro.dev/v4/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
||||
v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
settingsServiceExt "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"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"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"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/service/v0/errorcode"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
type schoolList struct {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// GetEducationUsers implements the Service interface.
|
||||
|
||||
@@ -26,8 +26,8 @@ import (
|
||||
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
||||
"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/service/v0/errorcode"
|
||||
)
|
||||
|
||||
//go:generate make -C ../../.. generate
|
||||
|
||||
@@ -29,8 +29,8 @@ import (
|
||||
"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"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"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"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -12,11 +12,12 @@ 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"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
// CreateLink creates a public link on the cs3 api
|
||||
@@ -24,8 +25,9 @@ func (g Graph) CreateLink(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling create link")
|
||||
|
||||
_, driveItemID, ok := g.GetDriveAndItemIDParam(w, r)
|
||||
if !ok {
|
||||
_, driveItemID, err := g.GetDriveAndItemIDParam(r)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package net
|
||||
|
||||
const (
|
||||
// "github.com/cs3org/reva/v2/internal/http/services/datagateway" is internal so we redeclare it here
|
||||
// HeaderTokenTransport holds the header key for the reva transfer token
|
||||
HeaderTokenTransport = "X-Reva-Transfer"
|
||||
// HeaderIfModifiedSince is used to mimic/pass on caching headers when using grpc
|
||||
HeaderIfModifiedSince = "If-Modified-Since"
|
||||
)
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"github.com/CiscoM31/godata"
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// ChangeOwnPassword implements the Service interface. It allows the user to change
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
|
||||
)
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ type Service interface {
|
||||
CreateLink(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
Invite(w http.ResponseWriter, r *http.Request)
|
||||
ListPermissions(w http.ResponseWriter, r *http.Request)
|
||||
DeletePermission(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
GetTags(w http.ResponseWriter, r *http.Request)
|
||||
@@ -202,8 +203,9 @@ 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.Post("/createLink", svc.CreateLink)
|
||||
r.Get("/permissions", svc.ListPermissions)
|
||||
r.Delete("/permissions/{permissionID}", svc.DeletePermission)
|
||||
r.Post("/createLink", svc.CreateLink)
|
||||
})
|
||||
r.Route("/roleManagement/permissions/roleDefinitions", func(r chi.Router) {
|
||||
r.Get("/", svc.GetRoleDefinitions)
|
||||
@@ -222,13 +224,13 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
})
|
||||
r.Route("/me", func(r chi.Router) {
|
||||
r.Get("/", svc.GetMe)
|
||||
r.Get("/drive", svc.GetUserDrive)
|
||||
r.Route("/drives", func(r chi.Router) {
|
||||
r.Get("/", svc.GetDrives)
|
||||
})
|
||||
r.Get("/drive/root/children", svc.GetRootDriveChildren)
|
||||
r.Post("/changePassword", svc.ChangeOwnPassword)
|
||||
r.Patch("/", svc.PatchMe)
|
||||
r.Route("/drive", func(r chi.Router) {
|
||||
r.Get("/", svc.GetUserDrive)
|
||||
r.Get("/root/children", svc.GetRootDriveChildren)
|
||||
})
|
||||
r.Get("/drives", svc.GetDrives)
|
||||
r.Post("/changePassword", svc.ChangeOwnPassword)
|
||||
})
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.With(requireAdmin).Get("/", svc.GetUsers)
|
||||
|
||||
@@ -9,12 +9,14 @@ import (
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/share"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/share"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
|
||||
"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/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
|
||||
)
|
||||
|
||||
@@ -27,13 +29,13 @@ func (g Graph) GetSharedByMe(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
driveItems := make(driveItemsByResourceID)
|
||||
var err error
|
||||
driveItems, err = g.listUserShares(ctx, driveItems)
|
||||
driveItems, err = g.listUserShares(ctx, nil, driveItems)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
driveItems, err = g.listPublicShares(ctx, driveItems)
|
||||
driveItems, err = g.listPublicShares(ctx, nil, driveItems)
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
@@ -48,19 +50,21 @@ func (g Graph) GetSharedByMe(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, &ListResponse{Value: res})
|
||||
}
|
||||
|
||||
func (g Graph) listUserShares(ctx context.Context, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
|
||||
func (g Graph) listUserShares(ctx context.Context, filters []*collaboration.Filter, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not select next gateway client")
|
||||
return driveItems, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
|
||||
filters := []*collaboration.Filter{
|
||||
concreteFilters := []*collaboration.Filter{
|
||||
share.UserGranteeFilter(),
|
||||
share.GroupGranteeFilter(),
|
||||
}
|
||||
concreteFilters = append(concreteFilters, filters...)
|
||||
|
||||
lsUserSharesRequest := collaboration.ListSharesRequest{
|
||||
Filters: filters,
|
||||
Filters: concreteFilters,
|
||||
}
|
||||
|
||||
lsUserSharesResponse, err := gatewayClient.ListShares(ctx, &lsUserSharesRequest)
|
||||
@@ -77,7 +81,7 @@ func (g Graph) listUserShares(ctx context.Context, driveItems driveItemsByResour
|
||||
return driveItems, nil
|
||||
}
|
||||
|
||||
func (g Graph) listPublicShares(ctx context.Context, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
|
||||
func (g Graph) listPublicShares(ctx context.Context, filters []*link.ListPublicSharesRequest_Filter, driveItems driveItemsByResourceID) (driveItemsByResourceID, error) {
|
||||
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
@@ -85,10 +89,11 @@ func (g Graph) listPublicShares(ctx context.Context, driveItems driveItemsByReso
|
||||
return driveItems, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
|
||||
filters := []*link.ListPublicSharesRequest_Filter{}
|
||||
var concreteFilters []*link.ListPublicSharesRequest_Filter
|
||||
concreteFilters = append(concreteFilters, filters...)
|
||||
|
||||
req := link.ListPublicSharesRequest{
|
||||
Filters: filters,
|
||||
Filters: concreteFilters,
|
||||
}
|
||||
|
||||
lsPublicSharesResponse, err := gatewayClient.ListPublicShares(ctx, &req)
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
|
||||
"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/service/v0/errorcode"
|
||||
)
|
||||
|
||||
// ListSharedWithMe lists the files shared with the current user.
|
||||
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
var _ = Describe("SharedWithMe", func() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
"go-micro.dev/v4/metadata"
|
||||
)
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ import (
|
||||
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"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/service/v0/errorcode"
|
||||
ocissettingssvc "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
// StrictJSONUnmarshal is a wrapper around json.Unmarshal that returns an error if the json contains unknown fields.
|
||||
@@ -16,36 +18,56 @@ func StrictJSONUnmarshal(r io.Reader, v interface{}) error {
|
||||
return dec.Decode(v)
|
||||
}
|
||||
|
||||
// IsSpaceRoot returns true if the resourceID is a space root.
|
||||
func IsSpaceRoot(rid *storageprovider.ResourceId) bool {
|
||||
if rid == nil {
|
||||
return false
|
||||
}
|
||||
if rid.GetSpaceId() == "" || rid.GetOpaqueId() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return rid.GetSpaceId() == rid.GetOpaqueId()
|
||||
}
|
||||
|
||||
// GetDriveAndItemIDParam parses the driveID and itemID from the request,
|
||||
// validates the common fields and returns the parsed IDs if ok.
|
||||
func (g Graph) GetDriveAndItemIDParam(w http.ResponseWriter, r *http.Request) (storageprovider.ResourceId, storageprovider.ResourceId, bool) {
|
||||
func (g Graph) GetDriveAndItemIDParam(r *http.Request) (storageprovider.ResourceId, storageprovider.ResourceId, error) {
|
||||
empty := storageprovider.ResourceId{}
|
||||
|
||||
driveID, err := parseIDParam(r, "driveID")
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Msg("could not parse driveID")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid driveID")
|
||||
return empty, empty, false
|
||||
return empty, empty, errorcode.New(errorcode.InvalidRequest, "invalid driveID")
|
||||
}
|
||||
|
||||
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 empty, empty, false
|
||||
return empty, empty, errorcode.New(errorcode.InvalidRequest, "invalid itemID")
|
||||
}
|
||||
|
||||
if itemID.GetOpaqueId() == "" {
|
||||
g.logger.Debug().Interface("driveID", driveID).Interface("itemID", itemID).Msg("empty item opaqueID")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid itemID")
|
||||
return empty, empty, false
|
||||
return empty, empty, 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")
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "driveID and itemID do not match")
|
||||
return empty, empty, false
|
||||
return empty, empty, errorcode.New(errorcode.ItemNotFound, "driveID and itemID do not match")
|
||||
}
|
||||
|
||||
return driveID, itemID, true
|
||||
return driveID, itemID, nil
|
||||
}
|
||||
|
||||
// GetGatewayClient returns a gateway client from the gatewaySelector.
|
||||
func (g Graph) GetGatewayClient(w http.ResponseWriter, r *http.Request) (gateway.GatewayAPIClient, bool) {
|
||||
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 nil, false
|
||||
}
|
||||
|
||||
return gatewayClient, true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package svc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/go-chi/chi/v5"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
)
|
||||
|
||||
var _ = Describe("Utils", func() {
|
||||
var (
|
||||
svc service.Graph
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
cfg := defaults.FullDefaultConfig()
|
||||
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
|
||||
|
||||
svc, _ = service.NewService(
|
||||
service.Config(cfg),
|
||||
)
|
||||
})
|
||||
|
||||
DescribeTable("GetDriveAndItemIDParam",
|
||||
func(driveID, itemID string, shouldPass bool) {
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", driveID)
|
||||
rctx.URLParams.Add("itemID", itemID)
|
||||
|
||||
extractedDriveID, extractedItemID, err := svc.GetDriveAndItemIDParam(
|
||||
httptest.NewRequest(http.MethodGet, "/", nil).
|
||||
WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, rctx),
|
||||
),
|
||||
)
|
||||
|
||||
switch shouldPass {
|
||||
case true:
|
||||
Expect(err).To(BeNil())
|
||||
parsedItemID, _ := storagespace.ParseID(itemID)
|
||||
Expect(extractedItemID).To(Equal(parsedItemID))
|
||||
|
||||
parsedDriveID, _ := storagespace.ParseID(driveID)
|
||||
Expect(extractedDriveID).To(Equal(parsedDriveID))
|
||||
default:
|
||||
Expect(err).ToNot(BeNil())
|
||||
}
|
||||
},
|
||||
Entry("fails: invalid driveID", "", "1$2!3", false),
|
||||
Entry("fails: invalid itemID", "1$2", "", false),
|
||||
Entry("fails: incompatible driveID and itemID", "1$2", "3$4!5", false),
|
||||
Entry("fails: no itemID opaqueId", "1$2", "3$4", false),
|
||||
Entry("pass: valid driveID and itemID", "1$2", "1$2!5", true),
|
||||
)
|
||||
|
||||
DescribeTable("IsSpaceRoot",
|
||||
func(resourceID *provider.ResourceId, isRoot bool) {
|
||||
Expect(service.IsSpaceRoot(resourceID)).To(Equal(isRoot))
|
||||
},
|
||||
Entry("spaceId and opaqueID equal", &provider.ResourceId{
|
||||
StorageId: "1",
|
||||
OpaqueId: "2",
|
||||
SpaceId: "2",
|
||||
}, true),
|
||||
Entry("nil", nil, false),
|
||||
Entry("spaceID empty", &provider.ResourceId{
|
||||
StorageId: "1",
|
||||
OpaqueId: "2",
|
||||
}, false),
|
||||
Entry("opaqueID empty", &provider.ResourceId{
|
||||
StorageId: "1",
|
||||
SpaceId: "3",
|
||||
}, false),
|
||||
Entry("spaceID and opaqueID unequal", &provider.ResourceId{
|
||||
OpaqueId: "2",
|
||||
SpaceId: "3",
|
||||
}, false),
|
||||
)
|
||||
})
|
||||
@@ -1,7 +1,9 @@
|
||||
package unifiedrole
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/conversions"
|
||||
@@ -217,8 +219,72 @@ func GetBuiltinRoleDefinitionList(resharing bool) []*libregraph.UnifiedRoleDefin
|
||||
}
|
||||
}
|
||||
|
||||
// GetApplicableRoleDefinitionsForActions returns a list of role definitions
|
||||
// that match the provided actions and constraints
|
||||
func GetApplicableRoleDefinitionsForActions(actions []string, constraints string, resharing, descending bool) []*libregraph.UnifiedRoleDefinition {
|
||||
var definitions []*libregraph.UnifiedRoleDefinition
|
||||
|
||||
for _, definition := range GetBuiltinRoleDefinitionList(resharing) {
|
||||
match := true
|
||||
|
||||
for _, permission := range definition.GetRolePermissions() {
|
||||
if permission.GetCondition() != constraints {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
|
||||
for _, action := range permission.GetAllowedResourceActions() {
|
||||
if !slices.Contains(actions, action) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
definitions = append(definitions, definition)
|
||||
}
|
||||
|
||||
return WeightRoleDefinitions(definitions, descending)
|
||||
}
|
||||
|
||||
// WeightRoleDefinitions sorts the provided role definitions by the number of permissions[n].actions they grant,
|
||||
// the implementation is optimistic and assumes that the weight relies on the number of available actions.
|
||||
// descending - false - sorts the roles from least to most permissions
|
||||
// descending - true - sorts the roles from most to least permissions
|
||||
func WeightRoleDefinitions(roleDefinitions []*libregraph.UnifiedRoleDefinition, descending bool) []*libregraph.UnifiedRoleDefinition {
|
||||
slices.SortFunc(roleDefinitions, func(i, j *libregraph.UnifiedRoleDefinition) int {
|
||||
var ia []string
|
||||
for _, rp := range i.GetRolePermissions() {
|
||||
ia = append(ia, rp.GetAllowedResourceActions()...)
|
||||
}
|
||||
|
||||
var ja []string
|
||||
for _, rp := range j.GetRolePermissions() {
|
||||
ja = append(ja, rp.GetAllowedResourceActions()...)
|
||||
}
|
||||
|
||||
switch descending {
|
||||
case true:
|
||||
return cmp.Compare(len(ja), len(ia))
|
||||
default:
|
||||
return cmp.Compare(len(ia), len(ja))
|
||||
}
|
||||
})
|
||||
|
||||
for i, definition := range roleDefinitions {
|
||||
definition.LibreGraphWeight = libregraph.PtrInt32(int32(i) + 1)
|
||||
}
|
||||
|
||||
// return for the sage of consistency, optional because the slice is modified in place
|
||||
return roleDefinitions
|
||||
}
|
||||
|
||||
// PermissionsToCS3ResourcePermissions converts the provided libregraph UnifiedRolePermissions to a cs3 ResourcePermissions
|
||||
func PermissionsToCS3ResourcePermissions(unifiedRolePermissions []libregraph.UnifiedRolePermission) *provider.ResourcePermissions {
|
||||
func PermissionsToCS3ResourcePermissions(unifiedRolePermissions []*libregraph.UnifiedRolePermission) *provider.ResourcePermissions {
|
||||
p := &provider.ResourcePermissions{}
|
||||
|
||||
for _, permission := range unifiedRolePermissions {
|
||||
|
||||
@@ -2,8 +2,11 @@ package unifiedrole_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
rConversions "github.com/cs3org/reva/v2/pkg/conversions"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/conversions"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/types"
|
||||
@@ -14,24 +17,26 @@ import (
|
||||
|
||||
var _ = Describe("unifiedroles", func() {
|
||||
DescribeTable("CS3ResourcePermissionsToUnifiedRole",
|
||||
func(legacyRole *conversions.Role, unifiedRole *libregraph.UnifiedRoleDefinition) {
|
||||
func(legacyRole *rConversions.Role, unifiedRole *libregraph.UnifiedRoleDefinition) {
|
||||
cs3perm := legacyRole.CS3ResourcePermissions()
|
||||
|
||||
r := unifiedrole.CS3ResourcePermissionsToUnifiedRole(*cs3perm, unifiedrole.UnifiedRoleConditionGrantee, true)
|
||||
Expect(r.GetId()).To(Equal(unifiedRole.GetId()))
|
||||
|
||||
},
|
||||
Entry(conversions.RoleViewer, conversions.NewViewerRole(true), unifiedrole.NewViewerUnifiedRole(true)),
|
||||
Entry(conversions.RoleEditor, conversions.NewEditorRole(true), unifiedrole.NewEditorUnifiedRole(true)),
|
||||
Entry(conversions.RoleFileEditor, conversions.NewFileEditorRole(true), unifiedrole.NewFileEditorUnifiedRole(true)),
|
||||
Entry(conversions.RoleCoowner, conversions.NewCoownerRole(), unifiedrole.NewCoownerUnifiedRole()),
|
||||
Entry(conversions.RoleManager, conversions.NewManagerRole(), unifiedrole.NewManagerUnifiedRole()),
|
||||
Entry(rConversions.RoleViewer, rConversions.NewViewerRole(true), unifiedrole.NewViewerUnifiedRole(true)),
|
||||
Entry(rConversions.RoleEditor, rConversions.NewEditorRole(true), unifiedrole.NewEditorUnifiedRole(true)),
|
||||
Entry(rConversions.RoleFileEditor, rConversions.NewFileEditorRole(true), unifiedrole.NewFileEditorUnifiedRole(true)),
|
||||
Entry(rConversions.RoleCoowner, rConversions.NewCoownerRole(), unifiedrole.NewCoownerUnifiedRole()),
|
||||
Entry(rConversions.RoleManager, rConversions.NewManagerRole(), unifiedrole.NewManagerUnifiedRole()),
|
||||
)
|
||||
|
||||
DescribeTable("UnifiedRolePermissionsToCS3ResourcePermissions",
|
||||
func(cs3Role *conversions.Role, libregraphRole *libregraph.UnifiedRoleDefinition, match bool) {
|
||||
func(cs3Role *rConversions.Role, libregraphRole *libregraph.UnifiedRoleDefinition, match bool) {
|
||||
permsFromCS3 := cs3Role.CS3ResourcePermissions()
|
||||
permsFromUnifiedRole := unifiedrole.PermissionsToCS3ResourcePermissions(libregraphRole.RolePermissions)
|
||||
permsFromUnifiedRole := unifiedrole.PermissionsToCS3ResourcePermissions(
|
||||
conversions.ToPointerSlice(libregraphRole.RolePermissions),
|
||||
)
|
||||
|
||||
var matcher types.GomegaMatcher
|
||||
|
||||
@@ -43,14 +48,203 @@ var _ = Describe("unifiedroles", func() {
|
||||
|
||||
Expect(permsFromCS3).To(matcher)
|
||||
},
|
||||
Entry(conversions.RoleViewer, conversions.NewViewerRole(true), unifiedrole.NewViewerUnifiedRole(true), true),
|
||||
Entry(conversions.RoleEditor, conversions.NewEditorRole(true), unifiedrole.NewEditorUnifiedRole(true), true),
|
||||
Entry(conversions.RoleFileEditor, conversions.NewFileEditorRole(true), unifiedrole.NewFileEditorUnifiedRole(true), true),
|
||||
Entry(conversions.RoleCoowner, conversions.NewCoownerRole(), unifiedrole.NewCoownerUnifiedRole(), true),
|
||||
Entry(conversions.RoleManager, conversions.NewManagerRole(), unifiedrole.NewManagerUnifiedRole(), true),
|
||||
Entry("no match", conversions.NewFileEditorRole(true), unifiedrole.NewManagerUnifiedRole(), false),
|
||||
Entry(rConversions.RoleViewer, rConversions.NewViewerRole(true), unifiedrole.NewViewerUnifiedRole(true), true),
|
||||
Entry(rConversions.RoleEditor, rConversions.NewEditorRole(true), unifiedrole.NewEditorUnifiedRole(true), true),
|
||||
Entry(rConversions.RoleFileEditor, rConversions.NewFileEditorRole(true), unifiedrole.NewFileEditorUnifiedRole(true), true),
|
||||
Entry(rConversions.RoleCoowner, rConversions.NewCoownerRole(), unifiedrole.NewCoownerUnifiedRole(), true),
|
||||
Entry(rConversions.RoleManager, rConversions.NewManagerRole(), unifiedrole.NewManagerUnifiedRole(), true),
|
||||
Entry("no match", rConversions.NewFileEditorRole(true), unifiedrole.NewManagerUnifiedRole(), false),
|
||||
)
|
||||
|
||||
DescribeTable("WeightRoleDefinitions",
|
||||
func(roleDefinitions []*libregraph.UnifiedRoleDefinition, descending bool, expectedDefinitions []*libregraph.UnifiedRoleDefinition) {
|
||||
|
||||
for i, generatedDefinition := range unifiedrole.WeightRoleDefinitions(roleDefinitions, descending) {
|
||||
Expect(generatedDefinition.Id).To(Equal(expectedDefinitions[i].Id))
|
||||
}
|
||||
},
|
||||
|
||||
Entry("ascending",
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
},
|
||||
false,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
},
|
||||
),
|
||||
|
||||
Entry("descending",
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
},
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
{
|
||||
rolesToAction := func(definitions ...*libregraph.UnifiedRoleDefinition) []string {
|
||||
var actions []string
|
||||
|
||||
for _, definition := range definitions {
|
||||
for _, permission := range definition.GetRolePermissions() {
|
||||
for _, action := range permission.GetAllowedResourceActions() {
|
||||
if slices.Contains(actions, action) {
|
||||
continue
|
||||
}
|
||||
actions = append(actions, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
DescribeTable("GetApplicableRoleDefinitionsForActions",
|
||||
func(givenActions []string, constraints string, resharing bool, expectedDefinitions []*libregraph.UnifiedRoleDefinition) {
|
||||
|
||||
generatedDefinitions := unifiedrole.GetApplicableRoleDefinitionsForActions(givenActions, constraints, resharing, false)
|
||||
|
||||
Expect(len(generatedDefinitions)).To(Equal(len(expectedDefinitions)))
|
||||
|
||||
for i, generatedDefinition := range generatedDefinitions {
|
||||
Expect(generatedDefinition.Id).To(Equal(expectedDefinitions[i].Id))
|
||||
Expect(*generatedDefinition.LibreGraphWeight).To(Equal(int32(i + 1)))
|
||||
}
|
||||
|
||||
generatedActions := rolesToAction(generatedDefinitions...)
|
||||
Expect(len(givenActions) >= len(generatedActions)).To(BeTrue())
|
||||
|
||||
for _, generatedAction := range generatedActions {
|
||||
Expect(slices.Contains(givenActions, generatedAction)).To(BeTrue())
|
||||
}
|
||||
},
|
||||
|
||||
Entry(
|
||||
"ViewerUnifiedRole",
|
||||
rolesToAction(unifiedrole.NewViewerUnifiedRole(false)),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
false,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"ViewerUnifiedRole | share",
|
||||
rolesToAction(unifiedrole.NewViewerUnifiedRole(true)),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(true),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"NewFileEditorUnifiedRole",
|
||||
rolesToAction(unifiedrole.NewFileEditorUnifiedRole(false)),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
false,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"NewFileEditorUnifiedRole - share",
|
||||
rolesToAction(unifiedrole.NewFileEditorUnifiedRole(true)),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewViewerUnifiedRole(true),
|
||||
unifiedrole.NewFileEditorUnifiedRole(true),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"NewEditorUnifiedRole",
|
||||
rolesToAction(unifiedrole.NewEditorUnifiedRole(false)),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
false,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewUploaderUnifiedRole(),
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
unifiedrole.NewEditorUnifiedRole(false),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"NewEditorUnifiedRole - share",
|
||||
rolesToAction(unifiedrole.NewEditorUnifiedRole(true)),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewUploaderUnifiedRole(),
|
||||
unifiedrole.NewViewerUnifiedRole(true),
|
||||
unifiedrole.NewFileEditorUnifiedRole(true),
|
||||
unifiedrole.NewEditorUnifiedRole(true),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"GetBuiltinRoleDefinitionList",
|
||||
rolesToAction(unifiedrole.GetBuiltinRoleDefinitionList(false)...),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
false,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewUploaderUnifiedRole(),
|
||||
unifiedrole.NewViewerUnifiedRole(false),
|
||||
unifiedrole.NewFileEditorUnifiedRole(false),
|
||||
unifiedrole.NewEditorUnifiedRole(false),
|
||||
unifiedrole.NewCoownerUnifiedRole(),
|
||||
unifiedrole.NewManagerUnifiedRole(),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"GetBuiltinRoleDefinitionList - share",
|
||||
rolesToAction(unifiedrole.GetBuiltinRoleDefinitionList(true)...),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewUploaderUnifiedRole(),
|
||||
unifiedrole.NewViewerUnifiedRole(true),
|
||||
unifiedrole.NewFileEditorUnifiedRole(true),
|
||||
unifiedrole.NewEditorUnifiedRole(true),
|
||||
unifiedrole.NewCoownerUnifiedRole(),
|
||||
unifiedrole.NewManagerUnifiedRole(),
|
||||
},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"single",
|
||||
[]string{unifiedrole.DriveItemQuotaRead},
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{},
|
||||
),
|
||||
|
||||
Entry(
|
||||
"mixed",
|
||||
append(rolesToAction(unifiedrole.NewUploaderUnifiedRole()), unifiedrole.DriveItemQuotaRead),
|
||||
unifiedrole.UnifiedRoleConditionGrantee,
|
||||
true,
|
||||
[]*libregraph.UnifiedRoleDefinition{
|
||||
unifiedrole.NewUploaderUnifiedRole(),
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
var newUnifiedRoleFromIDEntries []TableEntry
|
||||
for _, resharing := range []bool{true, false} {
|
||||
|
||||
Reference in New Issue
Block a user