Skip to content

Commit

Permalink
first prototype of a CS3 permissions service
Browse files Browse the repository at this point in the history
  • Loading branch information
David Christofas committed Dec 17, 2021
1 parent 7196229 commit cd07f15
Show file tree
Hide file tree
Showing 20 changed files with 120 additions and 35 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/cs3-permissions-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Use CS3 permissions API

Added calls to the CS3 permissions API to the decomposedfs in order to check the user permissions.

https://github.com/cs3org/reva/pull/2341
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/cheggaaa/pb v1.0.29
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e
github.com/cs3org/go-cs3apis v0.0.0-20211214102047-7ce3134d7bf8
github.com/cs3org/go-cs3apis v0.0.0-20211214102128-4e8745ab1654
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59
github.com/gdexlab/go-render v1.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
github.com/cs3org/go-cs3apis v0.0.0-20211214102047-7ce3134d7bf8 h1:PqOprF37OvwCbAN5W23znknGk6N/LMayqLAeP904FHE=
github.com/cs3org/go-cs3apis v0.0.0-20211214102047-7ce3134d7bf8/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cs3org/go-cs3apis v0.0.0-20211214102128-4e8745ab1654 h1:ha5tiuuFyDrwKUrVEc3TrRDFgTKVQ9NGDRmEP0PRPno=
github.com/cs3org/go-cs3apis v0.0.0-20211214102128-4e8745ab1654/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
1 change: 1 addition & 0 deletions internal/grpc/services/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type config struct {
EtagCacheTTL int `mapstructure:"etag_cache_ttl"`
AllowedUserAgents map[string][]string `mapstructure:"allowed_user_agents"` // map[path][]user-agent
CreateHomeCacheTTL int `mapstructure:"create_home_cache_ttl"`
PermissionsEndpoint string `mapstructure:"permissionssvc"`
}

// sets defaults
Expand Down
39 changes: 39 additions & 0 deletions internal/grpc/services/gateway/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2018-2021 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package gateway

import (
"context"

permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
"github.com/cs3org/reva/pkg/rgrpc/status"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/pkg/errors"
)

func (s *svc) CheckPermission(ctx context.Context, req *permissions.CheckPermissionRequest) (*permissions.CheckPermissionResponse, error) {
c, err := pool.GetPermissionsClient(s.c.PermissionsEndpoint)
if err != nil {
err = errors.Wrap(err, "gateway: error calling GetPermissionssClient")
return &permissions.CheckPermissionResponse{
Status: status.NewInternal(ctx, err, "error getting permissions client"),
}, nil
}
return c.CheckPermission(ctx, req)
}
15 changes: 1 addition & 14 deletions internal/grpc/services/storageprovider/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package storageprovider

import (
"context"
"encoding/json"
"fmt"
"net/url"
"os"
Expand Down Expand Up @@ -579,19 +578,7 @@ func hasNodeID(s *provider.StorageSpace) bool {
func (s *service) ListStorageSpaces(ctx context.Context, req *provider.ListStorageSpacesRequest) (*provider.ListStorageSpacesResponse, error) {
log := appctx.GetLogger(ctx)

// This is just a quick hack to get the users permission into reva.
// Replace this as soon as we have a proper system to check the users permissions.
opaque := req.Opaque
var permissions map[string]struct{}
if opaque != nil {
entry := opaque.Map["permissions"]
err := json.Unmarshal(entry.Value, &permissions)
if err != nil {
return nil, err
}
}

spaces, err := s.storage.ListStorageSpaces(ctx, req.Filters, permissions)
spaces, err := s.storage.ListStorageSpaces(ctx, req.Filters)
if err != nil {
var st *rpc.Status
switch err.(type) {
Expand Down
21 changes: 21 additions & 0 deletions pkg/rgrpc/todo/pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1"
invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1"
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
preferences "github.com/cs3org/go-cs3apis/cs3/preferences/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
Expand Down Expand Up @@ -70,6 +71,7 @@ var (
ocmCores = newProvider()
publicShareProviders = newProvider()
preferencesProviders = newProvider()
permissionsProviders = newProvider()
appRegistries = newProvider()
appProviders = newProvider()
storageRegistries = newProvider()
Expand Down Expand Up @@ -349,6 +351,25 @@ func GetPreferencesClient(endpoint string) (preferences.PreferencesAPIClient, er
return v, nil
}

// GetPermissionsClient returns a new PermissionsClient.
func GetPermissionsClient(endpoint string) (permissions.PermissionsAPIClient, error) {
permissionsProviders.m.Lock()
defer permissionsProviders.m.Unlock()

if c, ok := permissionsProviders.conn[endpoint]; ok {
return c.(permissions.PermissionsAPIClient), nil
}

conn, err := NewConn(endpoint)
if err != nil {
return nil, err
}

v := permissions.NewPermissionsAPIClient(conn)
permissionsProviders.conn[endpoint] = v
return v, nil
}

// GetAppRegistryClient returns a new AppRegistryClient.
func GetAppRegistryClient(endpoint string) (appregistry.RegistryAPIClient, error) {
appRegistries.m.Lock()
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/fs/nextcloud/nextcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ func (nc *StorageDriver) Unlock(ctx context.Context, ref *provider.Reference) er
}

// ListStorageSpaces as defined in the storage.FS interface
func (nc *StorageDriver) ListStorageSpaces(ctx context.Context, f []*provider.ListStorageSpacesRequest_Filter, _ map[string]struct{}) ([]*provider.StorageSpace, error) {
func (nc *StorageDriver) ListStorageSpaces(ctx context.Context, f []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
bodyStr, _ := json.Marshal(f)
_, respBody, err := nc.do(ctx, Action{"ListStorageSpaces", string(bodyStr)})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/fs/nextcloud/nextcloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,7 @@ var _ = Describe("Nextcloud", func() {
},
}
filters := []*provider.ListStorageSpacesRequest_Filter{filter1, filter2, filter3}
spaces, err := nc.ListStorageSpaces(ctx, filters, nil)
spaces, err := nc.ListStorageSpaces(ctx, filters)
Expect(err).ToNot(HaveOccurred())
Expect(len(spaces)).To(Equal(1))
// https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1341-L1366
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/fs/owncloud/owncloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -2255,7 +2255,7 @@ func (fs *ocfs) RestoreRecycleItem(ctx context.Context, basePath, key, relativeP
return fs.propagate(ctx, tgt)
}

func (fs *ocfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, _ map[string]struct{}) ([]*provider.StorageSpace, error) {
func (fs *ocfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
return nil, errtypes.NotSupported("list storage spaces")
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/fs/owncloudsql/owncloudsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1950,7 +1950,7 @@ func (fs *owncloudsqlfs) HashFile(path string) (string, string, string, error) {
}
}

func (fs *owncloudsqlfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, _ map[string]struct{}) ([]*provider.StorageSpace, error) {
func (fs *owncloudsqlfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
// TODO(corby): Implement
return nil, errtypes.NotSupported("list storage spaces")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/fs/s3/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ func (fs *s3FS) RestoreRecycleItem(ctx context.Context, basePath, key, relativeP
return errtypes.NotSupported("restore recycle")
}

func (fs *s3FS) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, _ map[string]struct{}) ([]*provider.StorageSpace, error) {
func (fs *s3FS) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
return nil, errtypes.NotSupported("list storage spaces")
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type FS interface {
GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error)
RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error
Unlock(ctx context.Context, ref *provider.Reference) error
ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, permissions map[string]struct{}) ([]*provider.StorageSpace, error)
ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error)
CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error)
UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/storage/utils/decomposedfs/decomposedfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
ctxpkg "github.com/cs3org/reva/pkg/ctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/logger"
"github.com/cs3org/reva/pkg/sharedconf"
"github.com/cs3org/reva/pkg/storage"
"github.com/cs3org/reva/pkg/storage/utils/chunking"
"github.com/cs3org/reva/pkg/storage/utils/decomposedfs/node"
Expand Down Expand Up @@ -101,6 +102,8 @@ func NewDefault(m map[string]interface{}, bs tree.Blobstore) (storage.FS, error)
lu.Options = o

tp := tree.New(o.Root, o.TreeTimeAccounting, o.TreeSizeAccounting, lu, bs)

o.GatewayAddr = sharedconf.GetGatewaySVC(o.GatewayAddr)
return New(o, lu, p, tp)
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/storage/utils/decomposedfs/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type Options struct {
Owner string `mapstructure:"owner"`
OwnerIDP string `mapstructure:"owner_idp"`
OwnerType string `mapstructure:"owner_type"`

GatewayAddr string `mapstructure:"gateway_addr"`
}

// New returns a new Options instance for the given configuration
Expand Down
45 changes: 35 additions & 10 deletions pkg/storage/utils/decomposedfs/spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ import (
"strings"

userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
permissionsv1beta1 "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
ocsconv "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions"
"github.com/cs3org/reva/pkg/appctx"
ctxpkg "github.com/cs3org/reva/pkg/ctx"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/storage/utils/decomposedfs/node"
"github.com/cs3org/reva/pkg/storage/utils/decomposedfs/xattrs"
"github.com/cs3org/reva/pkg/utils"
Expand Down Expand Up @@ -156,7 +158,7 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
// The list can be filtered by space type or space id.
// Spaces are persisted with symlinks in /spaces/<type>/<spaceid> pointing to ../../nodes/<nodeid>, the root node of the space
// The spaceid is a concatenation of storageid + "!" + nodeid
func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, permissions map[string]struct{}) ([]*provider.StorageSpace, error) {
func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
// TODO check filters

// TODO when a space symlink is broken delete the space for cleanup
Expand Down Expand Up @@ -200,6 +202,28 @@ func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provide
return spaces, nil
}

client, err := pool.GetGatewayServiceClient(fs.o.GatewayAddr)
if err != nil {
return nil, err
}

checkRes, err := client.CheckPermission(ctx, &permissionsv1beta1.CheckPermissionRequest{
Permission: "list-all-spaces",
SubjectRef: &permissionsv1beta1.SubjectReference{
Spec: &permissionsv1beta1.SubjectReference_UserId{
UserId: u.Id,
},
},
})
if err != nil {
return nil, err
}

canListAllSpaces := false
if checkRes.Status.Code == v1beta11.Code_CODE_OK {
canListAllSpaces = true
}

for i := range matches {
// always read link in case storage space id != node id
if target, err := os.Readlink(matches[i]); err != nil {
Expand All @@ -226,7 +250,7 @@ func (fs *Decomposedfs) ListStorageSpaces(ctx context.Context, filter []*provide
}

// TODO apply more filters
space, err := fs.storageSpaceFromNode(ctx, n, matches[i], spaceType, permissions)
space, err := fs.storageSpaceFromNode(ctx, n, matches[i], spaceType, canListAllSpaces)
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Interface("node", n).Msg("could not convert to storage space")
continue
Expand Down Expand Up @@ -329,7 +353,7 @@ func (fs *Decomposedfs) createStorageSpace(ctx context.Context, spaceType, nodeI
return nil
}

func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, node *node.Node, nodePath, spaceType string, permissions map[string]struct{}) (*provider.StorageSpace, error) {
func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, node *node.Node, nodePath, spaceType string, canListAllSpaces bool) (*provider.StorageSpace, error) {
owner, err := node.Owner()
if err != nil {
return nil, err
Expand Down Expand Up @@ -357,13 +381,14 @@ func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, node *node.Nod
user := ctxpkg.ContextMustGetUser(ctx)

// filter out spaces user cannot access (currently based on stat permission)
_, canListAllSpaces := permissions["list-all-spaces"]
p, err := node.ReadUserPermissions(ctx, user)
if err != nil {
return nil, err
}
if !(canListAllSpaces || p.Stat) {
return nil, errors.New("user is not allowed to Stat the space")
if !canListAllSpaces {
p, err := node.ReadUserPermissions(ctx, user)
if err != nil {
return nil, err
}
if !p.Stat {
return nil, errors.New("user is not allowed to Stat the space")
}
}

space.Owner = &userv1beta1.User{ // FIXME only return a UserID, not a full blown user object
Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/eosfs/eosfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1617,7 +1617,7 @@ func (fs *eosfs) RestoreRecycleItem(ctx context.Context, basePath, key, relative
return fs.c.RestoreDeletedEntry(ctx, auth, key)
}

func (fs *eosfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, _ map[string]struct{}) ([]*provider.StorageSpace, error) {
func (fs *eosfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
return nil, errtypes.NotSupported("list storage spaces")
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/storage/utils/localfs/localfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,7 @@ func (fs *localfs) RestoreRecycleItem(ctx context.Context, basePath, key, relati
return fs.propagate(ctx, localRestorePath)
}

func (fs *localfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, _ map[string]struct{}) ([]*provider.StorageSpace, error) {
func (fs *localfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) {
return nil, errtypes.NotSupported("list storage spaces")
}

Expand Down
1 change: 1 addition & 0 deletions tests/oc-integration-tests/drone/frontend.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ files_namespace = "/users"
webdav_namespace = "/home"

[http.services.ocs]
storage_registry_svc = "localhost:19000"

[http.services.ocs.capabilities.capabilities.core.status]
version = "10.0.11.5"
Expand Down
1 change: 1 addition & 0 deletions tests/oc-integration-tests/local/frontend.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ webdav_namespace = "/home"

# serve /ocs which contains the sharing and user provisioning api of owncloud classic
[http.services.ocs]
storage_registry_svc = "localhost:19000"

[http.services.ocs.capabilities.capabilities.core.status]
version = "10.0.11.5"
Expand Down

0 comments on commit cd07f15

Please sign in to comment.