Resource/operation combo whitelist (refactored validation)
This commit is contained in:
@@ -14,7 +14,7 @@ type (
|
||||
|
||||
accessControlPermissionServicer interface {
|
||||
Can(context.Context, permissions.Resource, permissions.Operation, ...permissions.CheckAccessFunc) bool
|
||||
Grant(context.Context, ...*permissions.Rule) error
|
||||
Grant(context.Context, permissions.Whitelist, ...*permissions.Rule) error
|
||||
}
|
||||
|
||||
permissionResource interface {
|
||||
@@ -149,7 +149,7 @@ func (svc accessControl) can(ctx context.Context, res permissionResource, op per
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
return svc.permissions.Grant(ctx, rr...)
|
||||
return svc.permissions.Grant(ctx, svc.Whitelist(), rr...)
|
||||
}
|
||||
|
||||
// DefaultRules returns list of default rules for this compose service
|
||||
@@ -201,3 +201,59 @@ func (svc accessControl) DefaultRules() permissions.RuleSet {
|
||||
allowAdm(pages, "delete"),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc accessControl) Whitelist() permissions.Whitelist {
|
||||
var wl = permissions.Whitelist{}
|
||||
|
||||
wl.Set(
|
||||
types.ComposePermissionResource,
|
||||
"access",
|
||||
"grant",
|
||||
"namespace.create",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.NamespacePermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
"module.create",
|
||||
"chart.create",
|
||||
"trigger.create",
|
||||
"page.create",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.ModulePermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
"record.create",
|
||||
"record.read",
|
||||
"record.update",
|
||||
"record.delete",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.ChartPermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.TriggerPermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.PagePermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
)
|
||||
|
||||
return wl
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ type (
|
||||
|
||||
// CheckAccessFunc function.
|
||||
CheckAccessFunc func() Access
|
||||
|
||||
Whitelist map[Resource]map[Operation]bool
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -52,3 +54,25 @@ func Allowed() Access {
|
||||
func Denied() Access {
|
||||
return Deny
|
||||
}
|
||||
|
||||
func (wl *Whitelist) Set(r Resource, oo ...Operation) {
|
||||
(*wl)[r] = map[Operation]bool{}
|
||||
|
||||
for _, o := range oo {
|
||||
(*wl)[r][o] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (wl Whitelist) Check(rule *Rule) bool {
|
||||
if rule == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
res := rule.Resource.TrimID()
|
||||
|
||||
if _, ok := wl[res]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return wl[res][rule.Operation]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -83,10 +84,16 @@ func (svc service) Check(res Resource, op Operation, roles ...uint64) (v Access)
|
||||
// Grant appends and/or overwrites internal rules slice
|
||||
//
|
||||
// All rules with Inherit are removed
|
||||
func (svc *service) Grant(ctx context.Context, rules ...*Rule) (err error) {
|
||||
func (svc *service) Grant(ctx context.Context, wl Whitelist, rules ...*Rule) (err error) {
|
||||
svc.l.Lock()
|
||||
defer svc.l.Unlock()
|
||||
|
||||
for _, r := range rules {
|
||||
if !wl.Check(r) {
|
||||
return errors.Errorf("invalid rule: '%s' on '%s'", r.Operation, r.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
if svc.rules, err = svc.rules.merge(rules...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type (
|
||||
|
||||
accessControlPermissionServicer interface {
|
||||
Can(context.Context, permissions.Resource, permissions.Operation, ...permissions.CheckAccessFunc) bool
|
||||
Grant(context.Context, ...*permissions.Rule) error
|
||||
Grant(context.Context, permissions.Whitelist, ...*permissions.Rule) error
|
||||
}
|
||||
|
||||
permissionResource interface {
|
||||
@@ -209,7 +209,7 @@ func (svc accessControl) can(ctx context.Context, res permissionResource, op per
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
return svc.permissions.Grant(ctx, rr...)
|
||||
return svc.permissions.Grant(ctx, svc.Whitelist(), rr...)
|
||||
}
|
||||
|
||||
// DefaultRules returns list of default rules for this compose service
|
||||
@@ -261,3 +261,45 @@ func (svc accessControl) DefaultRules() permissions.RuleSet {
|
||||
allowAdm(channels, "message.react"),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc accessControl) Whitelist() permissions.Whitelist {
|
||||
var wl = permissions.Whitelist{}
|
||||
|
||||
wl.Set(
|
||||
types.MessagingPermissionResource,
|
||||
"access",
|
||||
"grant",
|
||||
"channel.public.create",
|
||||
"channel.private.create",
|
||||
"channel.group.create",
|
||||
"webhook.create",
|
||||
"webhook.manage.all",
|
||||
"webhook.manage.own",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.ChannelPermissionResource,
|
||||
"update",
|
||||
"read",
|
||||
"join",
|
||||
"leave",
|
||||
"delete",
|
||||
"undelete",
|
||||
"archive",
|
||||
"unarchive",
|
||||
"members.manage",
|
||||
"webhooks.manage",
|
||||
"attachments.manage",
|
||||
"message.send",
|
||||
"message.reply",
|
||||
"message.embed",
|
||||
"message.attach",
|
||||
"message.update.own",
|
||||
"message.update.all",
|
||||
"message.delete.own",
|
||||
"message.delete.all",
|
||||
"message.react",
|
||||
)
|
||||
|
||||
return wl
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/SentimensRG/ctx"
|
||||
"github.com/namsral/flag"
|
||||
"github.com/titpetric/factory"
|
||||
"go.uber.org/zap/zapcore"
|
||||
@@ -63,8 +63,8 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
}
|
||||
|
||||
systemService.Init(ctx.Background())
|
||||
Init(ctx.Background())
|
||||
systemService.Init(context.Background())
|
||||
Init(context.Background())
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ type (
|
||||
|
||||
accessControlPermissionServicer interface {
|
||||
Can(context.Context, permissions.Resource, permissions.Operation, ...permissions.CheckAccessFunc) bool
|
||||
Grant(context.Context, ...*permissions.Rule) error
|
||||
Grant(context.Context, permissions.Whitelist, ...*permissions.Rule) error
|
||||
}
|
||||
|
||||
permissionResource interface {
|
||||
@@ -120,7 +120,7 @@ func (svc accessControl) can(ctx context.Context, res permissionResource, op per
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
return svc.permissions.Grant(ctx, rr...)
|
||||
return svc.permissions.Grant(ctx, svc.Whitelist(), rr...)
|
||||
}
|
||||
|
||||
// DefaultRules returns list of default rules for this compose service
|
||||
@@ -167,3 +167,50 @@ func (svc accessControl) DefaultRules() permissions.RuleSet {
|
||||
allowAdm(roles, "members.manage"),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc accessControl) Whitelist() permissions.Whitelist {
|
||||
var wl = permissions.Whitelist{}
|
||||
|
||||
wl.Set(
|
||||
types.SystemPermissionResource,
|
||||
"access",
|
||||
"grant",
|
||||
"settings.read",
|
||||
"settings.manage",
|
||||
"organisation.create",
|
||||
"role.create",
|
||||
"user.create",
|
||||
"application.create",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.OrganisationPermissionResource,
|
||||
"access",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.ApplicationPermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.UserPermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
"suspend",
|
||||
"unsuspend",
|
||||
)
|
||||
|
||||
wl.Set(
|
||||
types.RolePermissionResource,
|
||||
"read",
|
||||
"update",
|
||||
"delete",
|
||||
"members.manage",
|
||||
)
|
||||
|
||||
return wl
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
package service
|
||||
|
||||
var (
|
||||
permissionList = map[string]map[string]bool{
|
||||
"system": map[string]bool{
|
||||
"access": true,
|
||||
"grant": true,
|
||||
"settings.read": true,
|
||||
"settings.manage": true,
|
||||
"organisation.create": true,
|
||||
"role.create": true,
|
||||
"application.create": true,
|
||||
},
|
||||
"system:organisation:": map[string]bool{
|
||||
"access": true,
|
||||
},
|
||||
"system:role:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
"members.manage": true,
|
||||
},
|
||||
"system:application:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
},
|
||||
"messaging": map[string]bool{
|
||||
"access": true,
|
||||
"grant": true,
|
||||
"channel.public.create": true,
|
||||
"channel.private.create": true,
|
||||
"channel.group.create": true,
|
||||
},
|
||||
"messaging:channel:": map[string]bool{
|
||||
"update": true,
|
||||
"read": true,
|
||||
"join": true,
|
||||
"leave": true,
|
||||
"delete": true,
|
||||
"undelete": true,
|
||||
"archive": true,
|
||||
"unarchive": true,
|
||||
"members.manage": true,
|
||||
"webhooks.manage": true,
|
||||
"attachments.manage": true,
|
||||
"message.send": true,
|
||||
"message.reply": true,
|
||||
"message.embed": true,
|
||||
"message.attach": true,
|
||||
"message.update.own": true,
|
||||
"message.update.all": true,
|
||||
"message.delete.own": true,
|
||||
"message.delete.all": true,
|
||||
"message.react": true,
|
||||
},
|
||||
"compose": map[string]bool{
|
||||
"access": true,
|
||||
"grant": true,
|
||||
"namespace.create": true,
|
||||
},
|
||||
"compose:namespace:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
"module.create": true,
|
||||
"chart.create": true,
|
||||
"trigger.create": true,
|
||||
"page.create": true,
|
||||
},
|
||||
"compose:module:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
"record.create": true,
|
||||
"record.read": true,
|
||||
"record.update": true,
|
||||
"record.delete": true,
|
||||
},
|
||||
"compose:chart:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
},
|
||||
"compose:trigger:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
},
|
||||
"compose:page:": map[string]bool{
|
||||
"read": true,
|
||||
"update": true,
|
||||
"delete": true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// func validatePermission(resource internalpermissions.Resource, operation string) error {
|
||||
// if !resource.IsValid() {
|
||||
// return errors.Errorf("invalid resource format: %q", resource)
|
||||
// }
|
||||
//
|
||||
// res := resource.TrimID().String()
|
||||
//
|
||||
// if service, ok := permissionList[res]; ok {
|
||||
// if op := service[operation]; op {
|
||||
// return nil
|
||||
// }
|
||||
// return errors.Errorf("Unknown operation: '%s'", operation)
|
||||
// }
|
||||
// return errors.Errorf("Unknown resource name: '%s'", resource)
|
||||
// }
|
||||
@@ -1,17 +0,0 @@
|
||||
// +build unit
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/crusttech/crust/internal/test"
|
||||
)
|
||||
|
||||
func TestPermissionsValidation(t *testing.T) {
|
||||
test.Error(t, validatePermission("bogus", "bogus"), "expected error")
|
||||
test.Error(t, validatePermission("bogus", "bogus"), "expected error")
|
||||
test.Error(t, validatePermission("messaging:channel", "bogus"), "expected error")
|
||||
test.Error(t, validatePermission("messaging:channel:", "message.send"), "expected error")
|
||||
test.NoError(t, validatePermission("messaging:channel:1", "message.send"), "expected valid response")
|
||||
}
|
||||
Reference in New Issue
Block a user