diff --git a/go.mod b/go.mod index 52fca6537..01fa1f248 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/asim/go-micro/plugins/wrapper/trace/opencensus/v4 v4.0.0-20211220083148-8e52761edb49 github.com/blevesearch/bleve/v2 v2.3.0 github.com/coreos/go-oidc/v3 v3.1.0 - github.com/cs3org/go-cs3apis v0.0.0-20211214102047-7ce3134d7bf8 + github.com/cs3org/go-cs3apis v0.0.0-20211214102128-4e8745ab1654 github.com/cs3org/reva v1.16.1-0.20220121134812-59d1aa30eb60 github.com/disintegration/imaging v1.6.2 github.com/glauth/glauth/v2 v2.0.0-20211021011345-ef3151c28733 diff --git a/go.sum b/go.sum index 4afaffd14..a160ccbb9 100644 --- a/go.sum +++ b/go.sum @@ -322,8 +322,9 @@ github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3p github.com/crewjam/saml v0.4.5 h1:H9u+6CZAESUKHxMyxUbVn0IawYvKZn4nt3d4ccV4O/M= github.com/crewjam/saml v0.4.5/go.mod h1:qCJQpUtZte9R1ZjUBcW8qtCNlinbO363ooNl02S68bk= 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/cs3org/reva v1.16.1-0.20220121134812-59d1aa30eb60 h1:XaraDDlNXPv5GREzwkP7+8IEMDXbzzeHsekfUQABJzc= github.com/cs3org/reva v1.16.1-0.20220121134812-59d1aa30eb60/go.mod h1:/BofcMJgfqTIHNiCp1uXr9ABcgylp27U2W4fjYUR6Fg= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= diff --git a/settings/pkg/server/grpc/server.go b/settings/pkg/server/grpc/server.go index a3a2bde3b..c313f853d 100644 --- a/settings/pkg/server/grpc/server.go +++ b/settings/pkg/server/grpc/server.go @@ -1,10 +1,15 @@ package grpc import ( + "context" + + permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1" "github.com/owncloud/ocis/ocis-pkg/service/grpc" "github.com/owncloud/ocis/ocis-pkg/version" "github.com/owncloud/ocis/settings/pkg/proto/v0" svc "github.com/owncloud/ocis/settings/pkg/service/v0" + "go-micro.dev/v4/api" + "go-micro.dev/v4/server" ) // Server initializes a new go-micro service ready to run @@ -35,5 +40,37 @@ func Server(opts ...Option) grpc.Service { options.Logger.Fatal().Err(err).Msg("could not register Permission service handler") } + if err := RegisterCS3PermissionsServiceHandler(service.Server(), handle); err != nil { + options.Logger.Fatal().Err(err).Msg("could not register CS3 Permission service handler") + } + return service } + +func RegisterCS3PermissionsServiceHandler(s server.Server, hdlr permissions.PermissionsAPIServer, opts ...server.HandlerOption) error { + type permissionsService interface { + CheckPermission(context.Context, *permissions.CheckPermissionRequest, *permissions.CheckPermissionResponse) error + } + type PermissionsAPI struct { + permissionsService + } + h := &permissionsServiceHandler{hdlr} + opts = append(opts, api.WithEndpoint(&api.Endpoint{ + Name: "PermissionsService.Checkpermission", + Path: []string{"/api/v0/permissions/check-permission"}, + Method: []string{"POST"}, + Body: "*", + Handler: "rpc", + })) + return s.Handle(s.NewHandler(&PermissionsAPI{h}, opts...)) +} + +type permissionsServiceHandler struct { + api permissions.PermissionsAPIServer +} + +func (h *permissionsServiceHandler) CheckPermission(ctx context.Context, req *permissions.CheckPermissionRequest, res *permissions.CheckPermissionResponse) error { + r, err := h.api.CheckPermission(ctx, req) + *res = *r + return err +} diff --git a/settings/pkg/service/v0/service.go b/settings/pkg/service/v0/service.go index 6dbf8cda7..6f335a561 100644 --- a/settings/pkg/service/v0/service.go +++ b/settings/pkg/service/v0/service.go @@ -4,6 +4,9 @@ import ( "context" "fmt" + permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1" + rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/owncloud/ocis/ocis-pkg/log" "github.com/owncloud/ocis/ocis-pkg/middleware" "github.com/owncloud/ocis/ocis-pkg/roles" @@ -36,6 +39,49 @@ func NewService(cfg *config.Config, logger log.Logger) Service { return service } +func (g Service) CheckPermission(ctx context.Context, req *permissions.CheckPermissionRequest) (*permissions.CheckPermissionResponse, error) { + spec := req.SubjectRef.Spec + + var accountID string + switch ref := spec.(type) { + case *permissions.SubjectReference_UserId: + accountID = ref.UserId.OpaqueId + case *permissions.SubjectReference_GroupId: + accountID = ref.GroupId.OpaqueId + } + + assignments, err := g.manager.ListRoleAssignments(accountID) + if err != nil { + return &permissions.CheckPermissionResponse{ + Status: status.NewInternal(ctx, err, err.Error()), + }, nil + } + + roleIDs := make([]string, 0, len(assignments)) + for _, a := range assignments { + roleIDs = append(roleIDs, a.RoleId) + } + + permission, err := g.manager.ReadPermissionByName(req.Permission, roleIDs) + if err != nil { + return &permissions.CheckPermissionResponse{ + Status: status.NewInternal(ctx, err, err.Error()), + }, nil + } + + if permission == nil { + return &permissions.CheckPermissionResponse{ + Status: &rpcv1beta1.Status{ + Code: rpcv1beta1.Code_CODE_PERMISSION_DENIED, + }, + }, nil + } + + return &permissions.CheckPermissionResponse{ + Status: status.NewOK(ctx), + }, nil +} + // RegisterDefaultRoles composes default roles and saves them. Skipped if the roles already exist. func (g Service) RegisterDefaultRoles() { // FIXME: we're writing default roles per service start (i.e. twice at the moment, for http and grpc server). has to happen only once. diff --git a/settings/pkg/settings/settings.go b/settings/pkg/settings/settings.go index 830ebc8ff..4f78e3e59 100644 --- a/settings/pkg/settings/settings.go +++ b/settings/pkg/settings/settings.go @@ -50,4 +50,5 @@ type RoleAssignmentManager interface { type PermissionManager interface { ListPermissionsByResource(resource *proto.Resource, roleIDs []string) ([]*proto.Permission, error) ReadPermissionByID(permissionID string, roleIDs []string) (*proto.Permission, error) + ReadPermissionByName(name string, roleIDs []string) (*proto.Permission, error) } diff --git a/settings/pkg/store/filesystem/permissions.go b/settings/pkg/store/filesystem/permissions.go index 63b6a6e76..db9f67a6c 100644 --- a/settings/pkg/store/filesystem/permissions.go +++ b/settings/pkg/store/filesystem/permissions.go @@ -38,6 +38,25 @@ func (s Store) ReadPermissionByID(permissionID string, roleIDs []string) (*proto return nil, nil } +// ReadPermissionByName finds the permission in the roles, specified by the provided roleIDs +func (s Store) ReadPermissionByName(name string, roleIDs []string) (*proto.Permission, error) { + for _, roleID := range roleIDs { + role, err := s.ReadBundle(roleID) + if err != nil { + s.Logger.Debug().Str("roleID", roleID).Msg("role not found, skipping") + continue + } + for _, permission := range role.Settings { + if permission.Name == name { + if value, ok := permission.Value.(*proto.Setting_PermissionValue); ok { + return value.PermissionValue, nil + } + } + } + } + return nil, nil +} + // extractPermissionsByResource collects all permissions from the provided role that match the requested resource func extractPermissionsByResource(resource *proto.Resource, role *proto.Bundle) []*proto.Permission { permissions := make([]*proto.Permission, 0) diff --git a/storage/pkg/command/gateway.go b/storage/pkg/command/gateway.go index b3a3cf482..093832809 100644 --- a/storage/pkg/command/gateway.go +++ b/storage/pkg/command/gateway.go @@ -140,6 +140,7 @@ func gatewayConfigFromStruct(c *cli.Context, cfg *config.Config, logger log.Logg "preferencessvc": cfg.Reva.Users.Endpoint, "userprovidersvc": cfg.Reva.Users.Endpoint, "groupprovidersvc": cfg.Reva.Groups.Endpoint, + "permissionssvc": cfg.Reva.Permissions.Endpoint, // sharing is located on the sharing service "usershareprovidersvc": cfg.Reva.Sharing.Endpoint, "publicshareprovidersvc": cfg.Reva.Sharing.Endpoint, diff --git a/storage/pkg/config/config.go b/storage/pkg/config/config.go index a2c738bfc..8a93c6dcb 100644 --- a/storage/pkg/config/config.go +++ b/storage/pkg/config/config.go @@ -199,10 +199,11 @@ type StoragePort struct { DataServerURL string `ocisConfig:"data_server_url"` // for HTTP ports with only one http service - HTTPPrefix string `ocisConfig:"http_prefix"` - TempFolder string `ocisConfig:"temp_folder"` - ReadOnly bool `ocisConfig:"read_only"` - DataProvider DataProvider `ocisConfig:"data_provider"` + HTTPPrefix string `ocisConfig:"http_prefix"` + TempFolder string `ocisConfig:"temp_folder"` + ReadOnly bool `ocisConfig:"read_only"` + DataProvider DataProvider `ocisConfig:"data_provider"` + GatewayEndpoint string `ocisConfig:"gateway_endpoint"` } // PublicStorage configures a public storage provider @@ -474,6 +475,7 @@ type Reva struct { StoragePublicLink PublicStorage `ocisConfig:"storage_public_link"` StorageMetadata StoragePort `ocisConfig:"storage_metadata"` AppProvider AppProvider `ocisConfig:"app_provider"` + Permissions Port `ocisConfig:"permissions"` // Configs can be used to configure the reva instance. // Services and Ports will be ignored if this is used Configs map[string]interface{} `ocisConfig:"configs"` diff --git a/storage/pkg/config/defaultconfig.go b/storage/pkg/config/defaultconfig.go index 23602bbf1..13be167e9 100644 --- a/storage/pkg/config/defaultconfig.go +++ b/storage/pkg/config/defaultconfig.go @@ -421,6 +421,9 @@ func DefaultConfig() *Config { OpenURL: "/app/open", NewURL: "/app/new", }, + Permissions: Port{ + Endpoint: "localhost:9191", + }, Configs: nil, UploadMaxChunkSize: 1e+8, UploadHTTPMethodOverride: "",