Implement permission provisioning and watchers
Remove rule reset from roles cli command Add generic "provision" command for each binary and (re)set perm. rules Permission rules are now separated and part of AccessControl service Facility for watchers was added.
This commit is contained in:
@@ -50,11 +50,18 @@ func main() {
|
||||
switch command {
|
||||
case "help":
|
||||
flag.PrintDefaults()
|
||||
case "provision":
|
||||
if err := compose.Provision(ctx); err != nil {
|
||||
println("Failed to provision compose: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
// Checks subscription, will os.Exit(1) if there is an error
|
||||
// Disabled for now, system service is the only one that validates subscription
|
||||
// ctx = subscription.Monitor(ctx)
|
||||
|
||||
compose.StartWatchers(ctx)
|
||||
|
||||
if err := compose.StartRestAPI(ctx); err != nil {
|
||||
log.Fatal("failed to start compose REST API", zap.Error(err))
|
||||
}
|
||||
|
||||
@@ -78,6 +78,20 @@ func main() {
|
||||
switch command {
|
||||
case "help":
|
||||
flag.PrintDefaults()
|
||||
|
||||
case "provision":
|
||||
if err := system.Provision(ctx); err != nil {
|
||||
println("Failed to provision system: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := compose.Provision(ctx); err != nil {
|
||||
println("Failed to provision compose: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := messaging.Provision(ctx); err != nil {
|
||||
println("Failed to provision messaging: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
// Initialize configuration of our services
|
||||
if err := system.Init(ctx); err != nil {
|
||||
@@ -103,6 +117,10 @@ func main() {
|
||||
go metrics.NewMonitor(flags.monitor.Interval)
|
||||
}
|
||||
|
||||
system.StartWatchers(ctx)
|
||||
compose.StartWatchers(ctx)
|
||||
messaging.StartWatchers(ctx)
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
// logging, cors and such
|
||||
|
||||
@@ -50,6 +50,12 @@ func main() {
|
||||
switch command {
|
||||
case "help":
|
||||
flag.PrintDefaults()
|
||||
|
||||
case "provision":
|
||||
if err := messaging.Provision(ctx); err != nil {
|
||||
println("Failed to provision messagign: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
// Checks subscription, will os.Exit(1) if there is an error
|
||||
// Disabled for now, system service is the only one that validates subscription
|
||||
|
||||
@@ -46,6 +46,11 @@ func main() {
|
||||
switch command {
|
||||
case "help":
|
||||
flag.PrintDefaults()
|
||||
case "provision":
|
||||
if err := system.Provision(ctx); err != nil {
|
||||
println("Failed to provision system: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
// Checks subscription, will os.Exit(1) if there is an error
|
||||
ctx = subscription.Monitor(ctx)
|
||||
|
||||
@@ -9,7 +9,12 @@ import (
|
||||
|
||||
type (
|
||||
accessControl struct {
|
||||
permissions permissions.Verifier
|
||||
permissions accessControlPermissionServicer
|
||||
}
|
||||
|
||||
accessControlPermissionServicer interface {
|
||||
Can(context.Context, permissions.Resource, permissions.Operation, ...permissions.CheckAccessFunc) bool
|
||||
Grant(context.Context, ...*permissions.Rule) error
|
||||
}
|
||||
|
||||
permissionResource interface {
|
||||
@@ -17,9 +22,9 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func AccessControl(pv permissions.Verifier) *accessControl {
|
||||
func AccessControl(perm accessControlPermissionServicer) *accessControl {
|
||||
return &accessControl{
|
||||
permissions: pv,
|
||||
permissions: perm,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,3 +147,57 @@ func (svc accessControl) CanDeletePage(ctx context.Context, r *types.Page) bool
|
||||
func (svc accessControl) can(ctx context.Context, res permissionResource, op permissions.Operation, ff ...permissions.CheckAccessFunc) bool {
|
||||
return svc.permissions.Can(ctx, res.PermissionResource(), op, ff...)
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
return svc.permissions.Grant(ctx, rr...)
|
||||
}
|
||||
|
||||
// DefaultRules returns list of default rules for this compose service
|
||||
func (svc accessControl) DefaultRules() permissions.RuleSet {
|
||||
var (
|
||||
compose = types.ComposePermissionResource
|
||||
namespaces = types.NamespacePermissionResource.AppendWildcard()
|
||||
modules = types.ModulePermissionResource.AppendWildcard()
|
||||
charts = types.ChartPermissionResource.AppendWildcard()
|
||||
triggers = types.TriggerPermissionResource.AppendWildcard()
|
||||
pages = types.PagePermissionResource.AppendWildcard()
|
||||
|
||||
allowAdm = func(res permissions.Resource, op permissions.Operation) *permissions.Rule {
|
||||
return &permissions.Rule{
|
||||
permissions.AdminRoleID,
|
||||
res,
|
||||
op,
|
||||
permissions.Allow}
|
||||
}
|
||||
)
|
||||
|
||||
return permissions.RuleSet{
|
||||
{permissions.EveryoneRoleID, compose, "access", permissions.Allow},
|
||||
allowAdm(compose, "namespace.create"),
|
||||
allowAdm(compose, "access"),
|
||||
allowAdm(compose, "grant"),
|
||||
allowAdm(namespaces, "read"),
|
||||
allowAdm(namespaces, "update"),
|
||||
allowAdm(namespaces, "delete"),
|
||||
allowAdm(namespaces, "page.create"),
|
||||
allowAdm(namespaces, "module.create"),
|
||||
allowAdm(namespaces, "chart.create"),
|
||||
allowAdm(namespaces, "trigger.create"),
|
||||
allowAdm(modules, "read"),
|
||||
allowAdm(modules, "update"),
|
||||
allowAdm(modules, "delete"),
|
||||
allowAdm(modules, "record.create"),
|
||||
allowAdm(modules, "record.read"),
|
||||
allowAdm(modules, "record.update"),
|
||||
allowAdm(modules, "record.delete"),
|
||||
allowAdm(charts, "read"),
|
||||
allowAdm(charts, "update"),
|
||||
allowAdm(charts, "delete"),
|
||||
allowAdm(triggers, "read"),
|
||||
allowAdm(triggers, "update"),
|
||||
allowAdm(triggers, "delete"),
|
||||
allowAdm(pages, "read"),
|
||||
allowAdm(pages, "update"),
|
||||
allowAdm(pages, "delete"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,10 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
}
|
||||
|
||||
systemService.Init()
|
||||
Init()
|
||||
ctx := context.Background()
|
||||
|
||||
systemService.Init(ctx)
|
||||
Init(ctx)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -16,9 +16,16 @@ type (
|
||||
db interface {
|
||||
Transaction(callback func() error) error
|
||||
}
|
||||
|
||||
permissionServicer interface {
|
||||
accessControlPermissionServicer
|
||||
Watch(ctx context.Context)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
permSvc permissionServicer
|
||||
|
||||
DefaultLogger *zap.Logger
|
||||
|
||||
DefaultAccessControl *accessControl
|
||||
@@ -33,9 +40,7 @@ var (
|
||||
DefaultNamespace NamespaceService
|
||||
)
|
||||
|
||||
func Init() error {
|
||||
ctx := context.Background()
|
||||
|
||||
func Init(ctx context.Context) error {
|
||||
DefaultLogger = logger.Default().Named("compose.service")
|
||||
|
||||
fs, err := store.New("var/store")
|
||||
@@ -43,8 +48,12 @@ func Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
pv := permissions.Service(permissions.Repository(repository.DB(ctx), "compose_permission_rules"))
|
||||
DefaultAccessControl = AccessControl(pv)
|
||||
permSvc = permissions.Service(
|
||||
ctx,
|
||||
DefaultLogger,
|
||||
permissions.Repository(repository.DB(ctx), "compose_permission_rules"))
|
||||
|
||||
DefaultAccessControl = AccessControl(permSvc)
|
||||
|
||||
DefaultRecord = Record()
|
||||
DefaultModule = Module()
|
||||
@@ -58,6 +67,10 @@ func Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Watchers(ctx context.Context) {
|
||||
permSvc.Watch(ctx)
|
||||
}
|
||||
|
||||
// Data is stale when new date does not match updatedAt or createdAt (before first update)
|
||||
func isStale(new *time.Time, updatedAt *time.Time, createdAt time.Time) bool {
|
||||
if new == nil {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crusttech/crust/compose/internal/service"
|
||||
)
|
||||
|
||||
// Provision orchestrates various tasks after deployment
|
||||
//
|
||||
func Provision(ctx context.Context) (err error) {
|
||||
if err = resetDefaultPermissionRules(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// @todo move migration here
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Resets default permission rules for compose resources
|
||||
func resetDefaultPermissionRules(ctx context.Context) error {
|
||||
var ac = service.DefaultAccessControl
|
||||
|
||||
return ac.Grant(ctx, ac.DefaultRules()...)
|
||||
}
|
||||
+5
-1
@@ -46,7 +46,7 @@ func Init(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// Don't change this to init(), it needs Database
|
||||
return service.Init()
|
||||
return service.Init(ctx)
|
||||
}
|
||||
|
||||
func InitDatabase(ctx context.Context) error {
|
||||
@@ -64,6 +64,10 @@ func InitDatabase(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartWatchers(ctx context.Context) {
|
||||
service.Watchers(ctx)
|
||||
}
|
||||
|
||||
func StartRestAPI(ctx context.Context) error {
|
||||
logger.Default().Info("Starting HTTP server", zap.String("address", flags.http.Addr))
|
||||
listener, err := net.Listen("tcp", flags.http.Addr)
|
||||
|
||||
@@ -10,7 +10,12 @@ import (
|
||||
|
||||
type (
|
||||
accessControl struct {
|
||||
permissions permissions.Verifier
|
||||
permissions accessControlPermissionServicer
|
||||
}
|
||||
|
||||
accessControlPermissionServicer interface {
|
||||
Can(context.Context, permissions.Resource, permissions.Operation, ...permissions.CheckAccessFunc) bool
|
||||
Grant(context.Context, ...*permissions.Rule) error
|
||||
}
|
||||
|
||||
permissionResource interface {
|
||||
@@ -18,9 +23,9 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func AccessControl(pv permissions.Verifier) *accessControl {
|
||||
func AccessControl(perm accessControlPermissionServicer) *accessControl {
|
||||
return &accessControl{
|
||||
permissions: pv,
|
||||
permissions: perm,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,3 +199,58 @@ func (svc accessControl) isChannelOwnerFallback(ctx context.Context, ch *types.C
|
||||
func (svc accessControl) can(ctx context.Context, res permissionResource, op permissions.Operation, ff ...permissions.CheckAccessFunc) bool {
|
||||
return svc.permissions.Can(ctx, res.PermissionResource(), op, ff...)
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
return svc.permissions.Grant(ctx, rr...)
|
||||
}
|
||||
|
||||
// DefaultRules returns list of default rules for this compose service
|
||||
func (svc accessControl) DefaultRules() permissions.RuleSet {
|
||||
var (
|
||||
messaging = types.MessagingPermissionResource
|
||||
channels = types.ChannelPermissionResource.AppendWildcard()
|
||||
webhooks = types.WebhookPermissionResource.AppendWildcard()
|
||||
|
||||
allowAdm = func(res permissions.Resource, op permissions.Operation) *permissions.Rule {
|
||||
return &permissions.Rule{
|
||||
permissions.AdminRoleID,
|
||||
res,
|
||||
op,
|
||||
permissions.Allow}
|
||||
}
|
||||
)
|
||||
|
||||
return permissions.RuleSet{
|
||||
{permissions.EveryoneRoleID, messaging, "access", permissions.Allow},
|
||||
|
||||
allowAdm(messaging, "access"),
|
||||
allowAdm(messaging, "grant"),
|
||||
allowAdm(messaging, "channel.public.create"),
|
||||
allowAdm(messaging, "channel.private.create"),
|
||||
allowAdm(messaging, "channel.group.create"),
|
||||
|
||||
allowAdm(channels, "update"),
|
||||
allowAdm(channels, "leave"),
|
||||
allowAdm(channels, "read"),
|
||||
allowAdm(channels, "join"),
|
||||
allowAdm(channels, "delete"),
|
||||
allowAdm(channels, "undelete"),
|
||||
allowAdm(channels, "archive"),
|
||||
allowAdm(channels, "unarchive"),
|
||||
allowAdm(channels, "members.manage"),
|
||||
allowAdm(channels, "webhooks.manage"),
|
||||
allowAdm(channels, "attachments.manage"),
|
||||
allowAdm(channels, "message.attach"),
|
||||
allowAdm(channels, "message.update.all"),
|
||||
allowAdm(channels, "message.update.own"),
|
||||
allowAdm(channels, "message.delete.all"),
|
||||
allowAdm(channels, "message.delete.own"),
|
||||
allowAdm(channels, "message.embed"),
|
||||
allowAdm(channels, "message.send"),
|
||||
allowAdm(channels, "message.reply"),
|
||||
allowAdm(channels, "message.react"),
|
||||
|
||||
allowAdm(webhooks, "webhook.manage.all"),
|
||||
allowAdm(webhooks, "webhook.manage.own"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/SentimensRG/ctx"
|
||||
"github.com/namsral/flag"
|
||||
"github.com/titpetric/factory"
|
||||
"go.uber.org/zap/zapcore"
|
||||
@@ -62,8 +63,8 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
}
|
||||
|
||||
systemService.Init()
|
||||
Init()
|
||||
systemService.Init(ctx.Background())
|
||||
Init(ctx.Background())
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -18,9 +18,16 @@ type (
|
||||
db interface {
|
||||
Transaction(callback func() error) error
|
||||
}
|
||||
|
||||
permissionServicer interface {
|
||||
accessControlPermissionServicer
|
||||
Watch(ctx context.Context)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
permSvc permissionServicer
|
||||
|
||||
DefaultLogger *zap.Logger
|
||||
|
||||
DefaultAccessControl *accessControl
|
||||
@@ -34,7 +41,7 @@ var (
|
||||
DefaultWebhook WebhookService
|
||||
)
|
||||
|
||||
func Init() error {
|
||||
func Init(ctx context.Context) error {
|
||||
fs, err := store.New("var/store")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -49,10 +56,11 @@ func Init() error {
|
||||
|
||||
DefaultLogger = logger.Default().Named("messaging.service")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
pv := permissions.Service(permissions.Repository(repository.DB(ctx), "compose_permission_rules"))
|
||||
DefaultAccessControl = AccessControl(pv)
|
||||
permSvc = permissions.Service(
|
||||
ctx,
|
||||
DefaultLogger,
|
||||
permissions.Repository(repository.DB(ctx), "compose_permission_rules"))
|
||||
DefaultAccessControl = AccessControl(permSvc)
|
||||
|
||||
DefaultEvent = Event(ctx)
|
||||
DefaultChannel = Channel(ctx)
|
||||
@@ -65,6 +73,10 @@ func Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Watchers(ctx context.Context) {
|
||||
permSvc.Watch(ctx)
|
||||
}
|
||||
|
||||
func timeNowPtr() *time.Time {
|
||||
now := time.Now()
|
||||
return &now
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crusttech/crust/messaging/internal/service"
|
||||
)
|
||||
|
||||
// Provision orchestrates various tasks after deployment
|
||||
//
|
||||
func Provision(ctx context.Context) (err error) {
|
||||
if err = resetDefaultPermissionRules(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// @todo move migration here
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Resets default permission rules for compose resources
|
||||
func resetDefaultPermissionRules(ctx context.Context) error {
|
||||
var ac = service.DefaultAccessControl
|
||||
|
||||
return ac.Grant(ctx, ac.DefaultRules()...)
|
||||
}
|
||||
+5
-1
@@ -48,7 +48,7 @@ func Init(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// Don't change this, it needs Database
|
||||
service.Init()
|
||||
service.Init(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -68,6 +68,10 @@ func InitDatabase(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartWatchers(ctx context.Context) {
|
||||
service.Watchers(ctx)
|
||||
}
|
||||
|
||||
func StartRestAPI(ctx context.Context) error {
|
||||
logger.Default().Info("Starting HTTP server", zap.String("address", flags.http.Addr))
|
||||
listener, err := net.Listen("tcp", flags.http.Addr)
|
||||
|
||||
+1
-133
@@ -2,15 +2,12 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/system/internal/repository"
|
||||
"github.com/crusttech/crust/system/types"
|
||||
)
|
||||
@@ -21,12 +18,6 @@ func rolesCmd(ctx context.Context, db *factory.DB) *cobra.Command {
|
||||
Short: "Role management",
|
||||
}
|
||||
|
||||
resetCmd := &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "Reset roles",
|
||||
Run: rolesResetCmd(ctx, db),
|
||||
}
|
||||
|
||||
addUserCmd := &cobra.Command{
|
||||
Use: "useradd [role-ID-or-name-or-handle] [user-ID-or-email]",
|
||||
Short: "Add user to role",
|
||||
@@ -34,134 +25,11 @@ func rolesCmd(ctx context.Context, db *factory.DB) *cobra.Command {
|
||||
Run: rolesUserAddCmd(ctx, db),
|
||||
}
|
||||
|
||||
cmd.AddCommand(resetCmd, addUserCmd)
|
||||
cmd.AddCommand(addUserCmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func rolesResetCmd(ctx context.Context, db *factory.DB) func(cmd *cobra.Command, args []string) {
|
||||
return func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
err error
|
||||
roleRepo = repository.Role(ctx, db)
|
||||
ruleRepo = rules.NewResources(ctx, db)
|
||||
)
|
||||
|
||||
// Recreates core roles (will not reset memberships!)
|
||||
err = types.RoleSet{
|
||||
&types.Role{ID: 1, Handle: "everyone", Name: "Everyone"},
|
||||
&types.Role{ID: 2, Handle: "admins", Name: "Administrators"},
|
||||
}.Walk(func(r *types.Role) error {
|
||||
// Update will exec REPLACE and take care of creation as well
|
||||
r.CreatedAt = time.Now()
|
||||
_, err := roleRepo.Update(r)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("could not reset roles: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rules := []rules.Rule{
|
||||
{1, "system", "user.create", 2},
|
||||
{1, "compose", "access", 2},
|
||||
{1, "messaging", "access", 2},
|
||||
{2, "compose", "namespace.create", 2},
|
||||
{2, "compose", "access", 2},
|
||||
{2, "compose", "grant", 2},
|
||||
{2, "compose:namespace:*", "read", 2},
|
||||
{2, "compose:namespace:*", "update", 2},
|
||||
{2, "compose:namespace:*", "delete", 2},
|
||||
{2, "compose:namespace:*", "page.create", 2},
|
||||
{2, "compose:namespace:*", "module.create", 2},
|
||||
{2, "compose:namespace:*", "chart.create", 2},
|
||||
{2, "compose:namespace:*", "trigger.create", 2},
|
||||
{2, "compose:chart:*", "read", 2},
|
||||
{2, "compose:chart:*", "update", 2},
|
||||
{2, "compose:chart:*", "delete", 2},
|
||||
{2, "compose:trigger:*", "read", 2},
|
||||
{2, "compose:trigger:*", "update", 2},
|
||||
{2, "compose:trigger:*", "delete", 2},
|
||||
{2, "compose:page:*", "read", 2},
|
||||
{2, "compose:page:*", "update", 2},
|
||||
{2, "compose:page:*", "delete", 2},
|
||||
{2, "system", "access", 2},
|
||||
{2, "system", "grant", 2},
|
||||
{2, "system", "settings.read", 2},
|
||||
{2, "system", "settings.manage", 2},
|
||||
{2, "system", "organisation.create", 2},
|
||||
{2, "system", "application.create", 2},
|
||||
{2, "system", "user.create", 2},
|
||||
{2, "system", "role.create", 2},
|
||||
{2, "system:organisation:*", "access", 2},
|
||||
{2, "system:application:*", "read", 2},
|
||||
{2, "system:application:*", "update", 2},
|
||||
{2, "system:application:*", "delete", 2},
|
||||
{2, "system:user:*", "read", 2},
|
||||
{2, "system:user:*", "update", 2},
|
||||
{2, "system:user:*", "suspend", 2},
|
||||
{2, "system:user:*", "unsuspend", 2},
|
||||
{2, "system:user:*", "delete", 2},
|
||||
{2, "system:role:*", "read", 2},
|
||||
{2, "system:role:*", "update", 2},
|
||||
{2, "system:role:*", "delete", 2},
|
||||
{2, "system:role:*", "members.manage", 2},
|
||||
{2, "messaging", "access", 2},
|
||||
{2, "messaging", "grant", 2},
|
||||
{2, "messaging", "channel.public.create", 2},
|
||||
{2, "messaging", "channel.private.create", 2},
|
||||
{2, "messaging", "channel.group.create", 2},
|
||||
{2, "messaging:channel:*", "update", 2},
|
||||
{2, "messaging:channel:*", "leave", 2},
|
||||
{2, "messaging:channel:*", "read", 2},
|
||||
{2, "messaging:channel:*", "join", 2},
|
||||
{2, "messaging:channel:*", "delete", 2},
|
||||
{2, "messaging:channel:*", "undelete", 2},
|
||||
{2, "messaging:channel:*", "archive", 2},
|
||||
{2, "messaging:channel:*", "unarchive", 2},
|
||||
{2, "messaging:channel:*", "members.manage", 2},
|
||||
{2, "messaging:channel:*", "webhooks.manage", 2},
|
||||
{2, "messaging:channel:*", "attachments.manage", 2},
|
||||
{2, "messaging:channel:*", "message.attach", 2},
|
||||
{2, "messaging:channel:*", "message.update.all", 2},
|
||||
{2, "messaging:channel:*", "message.update.own", 2},
|
||||
{2, "messaging:channel:*", "message.delete.all", 2},
|
||||
{2, "messaging:channel:*", "message.delete.own", 2},
|
||||
{2, "messaging:channel:*", "message.embed", 2},
|
||||
{2, "messaging:channel:*", "message.send", 2},
|
||||
{2, "messaging:channel:*", "message.reply", 2},
|
||||
{2, "messaging:channel:*", "message.react", 2},
|
||||
{2, "compose:module:*", "read", 2},
|
||||
{2, "compose:module:*", "update", 2},
|
||||
{2, "compose:module:*", "delete", 2},
|
||||
{2, "compose:module:*", "record.create", 2},
|
||||
{2, "compose:module:*", "record.read", 2},
|
||||
{2, "compose:module:*", "record.update", 2},
|
||||
{2, "compose:module:*", "record.delete", 2},
|
||||
}
|
||||
|
||||
var purgedRoles = map[uint64]bool{}
|
||||
for _, r := range rules {
|
||||
if !purgedRoles[r.RoleID] {
|
||||
if err = ruleRepo.Delete(r.RoleID); err != nil {
|
||||
fmt.Printf("could not reset rules: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
purgedRoles[r.RoleID] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Recreates rules for core roles
|
||||
err = ruleRepo.Reset(rules)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("could not reset rules: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rolesUserAddCmd(ctx context.Context, db *factory.DB) func(cmd *cobra.Command, args []string) {
|
||||
return func(cmd *cobra.Command, args []string) {
|
||||
// Create role and user repository.
|
||||
|
||||
@@ -9,7 +9,12 @@ import (
|
||||
|
||||
type (
|
||||
accessControl struct {
|
||||
permissions permissions.Verifier
|
||||
permissions accessControlPermissionServicer
|
||||
}
|
||||
|
||||
accessControlPermissionServicer interface {
|
||||
Can(context.Context, permissions.Resource, permissions.Operation, ...permissions.CheckAccessFunc) bool
|
||||
Grant(context.Context, ...*permissions.Rule) error
|
||||
}
|
||||
|
||||
permissionResource interface {
|
||||
@@ -17,9 +22,9 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func AccessControl(pv permissions.Verifier) *accessControl {
|
||||
func AccessControl(perm accessControlPermissionServicer) *accessControl {
|
||||
return &accessControl{
|
||||
permissions: pv,
|
||||
permissions: perm,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,3 +118,52 @@ func (svc accessControl) CanDeleteUser(ctx context.Context, u *types.User) bool
|
||||
func (svc accessControl) can(ctx context.Context, res permissionResource, op permissions.Operation, ff ...permissions.CheckAccessFunc) bool {
|
||||
return svc.permissions.Can(ctx, res.PermissionResource(), op, ff...)
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
return svc.permissions.Grant(ctx, rr...)
|
||||
}
|
||||
|
||||
// DefaultRules returns list of default rules for this compose service
|
||||
func (svc accessControl) DefaultRules() permissions.RuleSet {
|
||||
var (
|
||||
sys = types.SystemPermissionResource
|
||||
applications = types.ApplicationPermissionResource.AppendWildcard()
|
||||
organisations = types.OrganisationPermissionResource.AppendWildcard()
|
||||
roles = types.RolePermissionResource.AppendWildcard()
|
||||
users = types.UserPermissionResource.AppendWildcard()
|
||||
|
||||
allowAdm = func(res permissions.Resource, op permissions.Operation) *permissions.Rule {
|
||||
return &permissions.Rule{
|
||||
permissions.AdminRoleID,
|
||||
res,
|
||||
op,
|
||||
permissions.Allow}
|
||||
}
|
||||
)
|
||||
|
||||
return permissions.RuleSet{
|
||||
{permissions.EveryoneRoleID, sys, "user.create", permissions.Allow},
|
||||
|
||||
allowAdm(sys, "access"),
|
||||
allowAdm(sys, "grant"),
|
||||
allowAdm(sys, "settings.read"),
|
||||
allowAdm(sys, "settings.manage"),
|
||||
allowAdm(sys, "organisation.create"),
|
||||
allowAdm(sys, "application.create"),
|
||||
allowAdm(sys, "user.create"),
|
||||
allowAdm(sys, "role.create"),
|
||||
allowAdm(organisations, "access"),
|
||||
allowAdm(applications, "read"),
|
||||
allowAdm(applications, "update"),
|
||||
allowAdm(applications, "delete"),
|
||||
allowAdm(users, "read"),
|
||||
allowAdm(users, "update"),
|
||||
allowAdm(users, "suspend"),
|
||||
allowAdm(users, "unsuspend"),
|
||||
allowAdm(users, "delete"),
|
||||
allowAdm(roles, "read"),
|
||||
allowAdm(roles, "update"),
|
||||
allowAdm(roles, "delete"),
|
||||
allowAdm(roles, "members.manage"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,14 @@ type (
|
||||
db interface {
|
||||
Transaction(callback func() error) error
|
||||
}
|
||||
permissionServicer interface {
|
||||
accessControlPermissionServicer
|
||||
Watch(ctx context.Context)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
permSvc permissionServicer
|
||||
DefaultLogger *zap.Logger
|
||||
|
||||
DefaultAccessControl *accessControl
|
||||
@@ -33,15 +38,16 @@ var (
|
||||
DefaultApplication ApplicationService
|
||||
)
|
||||
|
||||
func Init() (err error) {
|
||||
ctx := context.Background()
|
||||
|
||||
func Init(ctx context.Context) (err error) {
|
||||
intSet := internalSettings.NewService(internalSettings.NewRepository(repository.DB(ctx), "sys_settings"))
|
||||
|
||||
DefaultLogger = logger.Default().Named("system.service")
|
||||
|
||||
pv := permissions.Service(permissions.Repository(repository.DB(ctx), "compose_permission_rules"))
|
||||
DefaultAccessControl = AccessControl(pv)
|
||||
permSvc = permissions.Service(
|
||||
ctx,
|
||||
DefaultLogger,
|
||||
permissions.Repository(repository.DB(ctx), "compose_permission_rules"))
|
||||
DefaultAccessControl = AccessControl(permSvc)
|
||||
|
||||
DefaultSettings = Settings(ctx, intSet)
|
||||
|
||||
@@ -60,3 +66,7 @@ func Init() (err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Watchers(ctx context.Context) {
|
||||
permSvc.Watch(ctx)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crusttech/crust/system/internal/service"
|
||||
)
|
||||
|
||||
// Provision orchestrates various tasks after deployment
|
||||
//
|
||||
func Provision(ctx context.Context) (err error) {
|
||||
if err = resetDefaultPermissionRules(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// @todo move migration here
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Resets default permission rules for compose resources
|
||||
func resetDefaultPermissionRules(ctx context.Context) error {
|
||||
var ac = service.DefaultAccessControl
|
||||
|
||||
return ac.Grant(ctx, ac.DefaultRules()...)
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crusttech/crust/system/internal/service"
|
||||
)
|
||||
|
||||
func Init() error {
|
||||
err := service.Init()
|
||||
func Init(ctx context.Context) error {
|
||||
err := service.Init(ctx)
|
||||
DefaultRole = service.DefaultRole
|
||||
// DefaultRules = service.DefaultRules
|
||||
DefaultUser = service.DefaultUser
|
||||
return err
|
||||
}
|
||||
|
||||
func Watchers(ctx context.Context) {
|
||||
service.Watchers(ctx)
|
||||
}
|
||||
|
||||
+5
-1
@@ -51,7 +51,7 @@ func Init(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// Don't change this, it needs database connection
|
||||
if err = service.Init(); err != nil {
|
||||
if err = service.Init(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ func InitDatabase(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartWatchers(ctx context.Context) {
|
||||
service.Watchers(ctx)
|
||||
}
|
||||
|
||||
func StartRestAPI(ctx context.Context) error {
|
||||
// Load settings from the database,
|
||||
// for now, only at start-up time.
|
||||
|
||||
Reference in New Issue
Block a user