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 Nov 17, 2021
1 parent f3ced34 commit 3e2558f
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 0 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,7 @@ require (

// this is a transitive replace. See https://github.com/libregraph/lico/blob/master/go.mod#L38
replace github.com/crewjam/saml => github.com/crewjam/saml v0.4.5

replace github.com/cs3org/go-cs3apis => /home/corby/work/go/src/github.com/c0rby/cs3apis/build/go-cs3apis/

replace github.com/cs3org/reva => /home/corby/work/go/src/github.com/c0rby/reva/
108 changes: 108 additions & 0 deletions go.sum

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions settings/pkg/server/grpc/server.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
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/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
Expand Down Expand Up @@ -34,5 +39,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
}
46 changes: 46 additions & 0 deletions settings/pkg/service/v0/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions settings/pkg/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
19 changes: 19 additions & 0 deletions settings/pkg/store/filesystem/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions storage/pkg/command/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,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,
Expand Down
1 change: 1 addition & 0 deletions storage/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ type Reva struct {
StoragePublicLink PublicStorage
StorageMetadata StoragePort
AppProvider AppProvider
Permissions Port
// Configs can be used to configure the reva instance.
// Services and Ports will be ignored if this is used
Configs map[string]interface{}
Expand Down
8 changes: 8 additions & 0 deletions storage/pkg/flagset/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ func GatewayWithConfig(cfg *config.Config) []cli.Flag {
Destination: &cfg.Reva.StoragePublicLink.MountPath,
},
// public-link has no mount id

&cli.StringFlag{
Name: "permissions-endpoint",
Value: flags.OverrideDefaultString(cfg.Reva.Permissions.Endpoint, "localhost:9191"),
Usage: "permissions endpoint",
EnvVars: []string{"STORAGE_PERMISSIONS_ENDPOINT"},
Destination: &cfg.Reva.Permissions.Endpoint,
},
}

flags = append(flags, TracingWithConfig(cfg)...)
Expand Down

0 comments on commit 3e2558f

Please sign in to comment.