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

Spaces fixes #2419

Merged
merged 7 commits into from
Jan 7, 2022
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
5 changes: 5 additions & 0 deletions changelog/unreleased/spaces-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Bugfix: list project spaces for share recipients

The sharing handler now uses the ListProvider call on the registry when sharing by reference. Furthermore, the decomposedfs now checks permissions on the root of a space so that a space is listed for users that have access to a space.

https://github.com/cs3org/reva/pull/2419
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ import (
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions"
"github.com/cs3org/reva/internal/http/services/owncloud/ocs/response"
"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/rgrpc/status"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
sdk "github.com/cs3org/reva/pkg/sdk/common"
"github.com/cs3org/reva/pkg/utils"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -88,15 +90,15 @@ func (h *Handler) addSpaceMember(w http.ResponseWriter, r *http.Request, info *p

ref := &provider.Reference{ResourceId: info.Id}

providers, err := h.findProviders(ctx, ref)
p, err := h.findProvider(ctx, ref)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider", err)
return
}

providerClient, err := h.getStorageProviderClient(providers[0])
providerClient, err := h.getStorageProviderClient(p)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider", err)
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider client", err)
return
}

Expand Down Expand Up @@ -136,15 +138,15 @@ func (h *Handler) removeSpaceMember(w http.ResponseWriter, r *http.Request, spac
return
}

providers, err := h.findProviders(ctx, &ref)
p, err := h.findProvider(ctx, &ref)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider", err)
return
}

providerClient, err := h.getStorageProviderClient(providers[0])
providerClient, err := h.getStorageProviderClient(p)
if err != nil {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider", err)
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "error getting storage provider client", err)
return
}

Expand All @@ -169,45 +171,57 @@ func (h *Handler) removeSpaceMember(w http.ResponseWriter, r *http.Request, spac
func (h *Handler) getStorageProviderClient(p *registry.ProviderInfo) (provider.ProviderAPIClient, error) {
c, err := pool.GetStorageProviderServiceClient(p.Address)
if err != nil {
err = errors.Wrap(err, "gateway: error getting a storage provider client")
err = errors.Wrap(err, "shares spaces: error getting a storage provider client")
return nil, err
}

return c, nil
}

func (h *Handler) findProviders(ctx context.Context, ref *provider.Reference) ([]*registry.ProviderInfo, error) {
func (h *Handler) findProvider(ctx context.Context, ref *provider.Reference) (*registry.ProviderInfo, error) {
c, err := pool.GetStorageRegistryClient(h.storageRegistryAddr)
if err != nil {
return nil, errors.Wrap(err, "gateway: error getting storage registry client")
return nil, errors.Wrap(err, "shares spaces: error getting storage registry client")
}

res, err := c.GetStorageProviders(ctx, &registry.GetStorageProvidersRequest{
Ref: ref,
})
filters := map[string]string{}
if ref.Path != "" {
filters["path"] = ref.Path
}
if ref.ResourceId != nil {
filters["storage_id"] = ref.ResourceId.StorageId
filters["opaque_id"] = ref.ResourceId.OpaqueId
}

listReq := &registry.ListStorageProvidersRequest{
Opaque: &types.Opaque{},
}
sdk.EncodeOpaqueMap(listReq.Opaque, filters)

res, err := c.ListStorageProviders(ctx, listReq)

if err != nil {
return nil, errors.Wrap(err, "gateway: error calling GetStorageProvider")
return nil, errors.Wrap(err, "shares spaces: error calling ListStorageProviders")
}

if res.Status.Code != rpc.Code_CODE_OK {
switch res.Status.Code {
case rpc.Code_CODE_NOT_FOUND:
return nil, errtypes.NotFound("gateway: storage provider not found for reference:" + ref.String())
return nil, errtypes.NotFound("shares spaces: storage provider not found for reference:" + ref.String())
case rpc.Code_CODE_PERMISSION_DENIED:
return nil, errtypes.PermissionDenied("gateway: " + res.Status.Message + " for " + ref.String() + " with code " + res.Status.Code.String())
return nil, errtypes.PermissionDenied("shares spaces: " + res.Status.Message + " for " + ref.String() + " with code " + res.Status.Code.String())
case rpc.Code_CODE_INVALID_ARGUMENT, rpc.Code_CODE_FAILED_PRECONDITION, rpc.Code_CODE_OUT_OF_RANGE:
return nil, errtypes.BadRequest("gateway: " + res.Status.Message + " for " + ref.String() + " with code " + res.Status.Code.String())
return nil, errtypes.BadRequest("shares spaces: " + res.Status.Message + " for " + ref.String() + " with code " + res.Status.Code.String())
case rpc.Code_CODE_UNIMPLEMENTED:
return nil, errtypes.NotSupported("gateway: " + res.Status.Message + " for " + ref.String() + " with code " + res.Status.Code.String())
return nil, errtypes.NotSupported("shares spaces: " + res.Status.Message + " for " + ref.String() + " with code " + res.Status.Code.String())
default:
return nil, status.NewErrorFromCode(res.Status.Code, "gateway")
return nil, status.NewErrorFromCode(res.Status.Code, "shares spaces")
}
}

if res.Providers == nil {
return nil, errtypes.NotFound("gateway: provider is nil")
if len(res.Providers) < 1 {
return nil, errtypes.NotFound("shares spaces: no provider found")
}

return res.Providers, nil
return res.Providers[0], nil
}
4 changes: 2 additions & 2 deletions pkg/storage/utils/decomposedfs/grants.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ func (fs *Decomposedfs) RemoveGrant(ctx context.Context, ref *provider.Reference

var attr string
if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
attr = xattrs.GrantPrefix + xattrs.GroupAcePrefix + g.Grantee.GetGroupId().OpaqueId
attr = xattrs.GrantGroupAcePrefix + g.Grantee.GetGroupId().OpaqueId
} else {
attr = xattrs.GrantPrefix + xattrs.UserAcePrefix + g.Grantee.GetUserId().OpaqueId
attr = xattrs.GrantUserAcePrefix + g.Grantee.GetUserId().OpaqueId
}

np := fs.lu.InternalPath(node.ID)
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/decomposedfs/grants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ var _ = Describe("Grants", func() {
Expect(err).ToNot(HaveOccurred())

localPath := path.Join(env.Root, "nodes", n.ID)
attr, err := xattr.Get(localPath, xattrs.GrantPrefix+xattrs.UserAcePrefix+grant.Grantee.GetUserId().OpaqueId)
attr, err := xattr.Get(localPath, xattrs.GrantUserAcePrefix+grant.Grantee.GetUserId().OpaqueId)
Expect(err).ToNot(HaveOccurred())
Expect(string(attr)).To(Equal("\x00t=A:f=:p=rw"))
})
Expand Down
8 changes: 4 additions & 4 deletions pkg/storage/utils/decomposedfs/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -833,15 +833,15 @@ func (n *Node) ReadUserPermissions(ctx context.Context, u *userpb.User) (ap prov
// 1. we can start iterating over the acls / grants on the node or
// 2. we can iterate over the number of groups
// The current implementation tries to be defensive for cases where users have hundreds or thousands of groups, so we iterate over the existing acls.
userace := xattrs.GrantPrefix + xattrs.UserAcePrefix + u.Id.OpaqueId
userace := xattrs.GrantUserAcePrefix + u.Id.OpaqueId
userFound := false
for i := range grantees {
switch {
// we only need to find the user once
case !userFound && grantees[i] == userace:
g, err = n.ReadGrant(ctx, grantees[i])
case strings.HasPrefix(grantees[i], xattrs.GrantPrefix+xattrs.GroupAcePrefix): // only check group grantees
gr := strings.TrimPrefix(grantees[i], xattrs.GrantPrefix+xattrs.GroupAcePrefix)
case strings.HasPrefix(grantees[i], xattrs.GrantGroupAcePrefix): // only check group grantees
gr := strings.TrimPrefix(grantees[i], xattrs.GrantGroupAcePrefix)
if groupsMap[gr] {
g, err = n.ReadGrant(ctx, grantees[i])
} else {
Expand Down Expand Up @@ -921,7 +921,7 @@ func (n *Node) hasUserShares(ctx context.Context) bool {
}

for i := range g {
if strings.Contains(g[i], xattrs.GrantPrefix+xattrs.UserAcePrefix) {
if strings.HasPrefix(g[i], xattrs.GrantUserAcePrefix) {
return true
}
}
Expand Down
85 changes: 46 additions & 39 deletions pkg/storage/utils/decomposedfs/node/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,60 +200,67 @@ func (p *Permissions) HasPermission(ctx context.Context, n *Node, check func(*pr
groupsMap[u.Groups[i]] = true
}

var g *provider.Grant
// for all segments, starting at the leaf
cn := n
for cn.ID != n.SpaceRoot.ID {
if ok := nodeHasPermission(ctx, cn, groupsMap, u.Id.OpaqueId, check); ok {
return true, nil
}

var grantees []string
if grantees, err = cn.ListGrantees(ctx); err != nil {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", cn).Msg("error listing grantees")
return false, err
if cn, err = cn.Parent(); err != nil {
return false, errors.Wrap(err, "Decomposedfs: error getting parent "+cn.ParentID)
}
}

userace := xattrs.GrantPrefix + xattrs.UserAcePrefix + u.Id.OpaqueId
userFound := false
for i := range grantees {
// we only need the find the user once per node
switch {
case !userFound && grantees[i] == userace:
// also check permissions on root, eg. for for project spaces
return nodeHasPermission(ctx, cn, groupsMap, u.Id.OpaqueId, check), nil
}

func nodeHasPermission(ctx context.Context, cn *Node, groupsMap map[string]bool, userid string, check func(*provider.ResourcePermissions) bool) (ok bool) {

var grantees []string
var err error
if grantees, err = cn.ListGrantees(ctx); err != nil {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", cn).Msg("error listing grantees")
return false
}

userace := xattrs.GrantUserAcePrefix + userid
userFound := false
for i := range grantees {
// we only need the find the user once per node
var g *provider.Grant
switch {
case !userFound && grantees[i] == userace:
g, err = cn.ReadGrant(ctx, grantees[i])
case strings.HasPrefix(grantees[i], xattrs.GrantGroupAcePrefix):
gr := strings.TrimPrefix(grantees[i], xattrs.GrantGroupAcePrefix)
if groupsMap[gr] {
g, err = cn.ReadGrant(ctx, grantees[i])
case strings.HasPrefix(grantees[i], xattrs.GrantPrefix+xattrs.GroupAcePrefix):
gr := strings.TrimPrefix(grantees[i], xattrs.GrantPrefix+xattrs.GroupAcePrefix)
if groupsMap[gr] {
g, err = cn.ReadGrant(ctx, grantees[i])
} else {
// no need to check attribute
continue
}
default:
} else {
// no need to check attribute
continue
}

switch {
case err == nil:
appctx.GetLogger(ctx).Debug().Interface("node", cn).Str("grant", grantees[i]).Interface("permissions", g.GetPermissions()).Msg("checking permissions")
if check(g.GetPermissions()) {
return true, nil
}
case isAttrUnset(err):
err = nil
appctx.GetLogger(ctx).Error().Interface("node", cn).Str("grant", grantees[i]).Interface("grantees", grantees).Msg("grant vanished from node after listing")
default:
appctx.GetLogger(ctx).Error().Err(err).Interface("node", cn).Str("grant", grantees[i]).Msg("error reading permissions")
return false, err
}
default:
// no need to check attribute
continue
}

if cn, err = cn.Parent(); err != nil {
return false, errors.Wrap(err, "Decomposedfs: error getting parent "+cn.ParentID)
switch {
case err == nil:
appctx.GetLogger(ctx).Debug().Interface("node", cn).Str("grant", grantees[i]).Interface("permissions", g.GetPermissions()).Msg("checking permissions")
if check(g.GetPermissions()) {
return true
}
case isAttrUnset(err):
appctx.GetLogger(ctx).Error().Interface("node", cn).Str("grant", grantees[i]).Interface("grantees", grantees).Msg("grant vanished from node after listing")
default:
appctx.GetLogger(ctx).Error().Err(err).Interface("node", cn).Str("grant", grantees[i]).Msg("error reading permissions")
return false
}
}

// NOTE: this log is being printed 1 million times on a simple test. TODO: Check if this is expected
// appctx.GetLogger(ctx).Debug().Interface("permissions", NoPermissions()).Interface("node", n).Interface("user", u).Msg("no grant found, returning default permissions")
return false, nil
return false
}

func (p *Permissions) getUserAndPermissions(ctx context.Context, n *Node) (*userv1beta1.User, *provider.ResourcePermissions) {
Expand Down
Loading