3
0

Refactor permission resources

This commit is contained in:
Denis Arh 2019-03-15 05:47:22 +01:00
parent 8a10839070
commit 78763c715d
38 changed files with 385 additions and 496 deletions

View File

@ -33,7 +33,7 @@ function types {
./build/gen-type-set --with-primary-key=false --types RecordValue --output crm/types/record_value.gen.go
./build/gen-type-set --types MessageAttachment --output messaging/types/attachment.gen.go
./build/gen-type-set --with-resources=true --types Channel --resource-type "rules.Resource" --imports "github.com/crusttech/crust/internal/rules" --output messaging/types/channel.gen.go
./build/gen-type-set --types Channel --output messaging/types/channel.gen.go
./build/gen-type-set --with-primary-key=false --types ChannelMember --output messaging/types/channel_member.gen.go
./build/gen-type-set --with-primary-key=false --types Command,CommandParam --output messaging/types/command.gen.go
./build/gen-type-set --types Mention --output messaging/types/mention.gen.go
@ -43,8 +43,8 @@ function types {
./build/gen-type-set --types User --output system/types/user.gen.go
./build/gen-type-set --types Application --output system/types/application.gen.go
./build/gen-type-set --with-resources=true --resource-type "rules.Resource" --imports "github.com/crusttech/crust/internal/rules" --types Role --output system/types/role.gen.go
./build/gen-type-set --with-resources=true --resource-type "rules.Resource" --imports "github.com/crusttech/crust/internal/rules" --types Organisation --output system/types/organisation.gen.go
./build/gen-type-set --types Role --output system/types/role.gen.go
./build/gen-type-set --types Organisation --output system/types/organisation.gen.go
./build/gen-type-set --types Credentials --output system/types/credentials.gen.go
green "OK"
}

View File

@ -91,24 +91,9 @@ func (set {{ $set }}Set) IDs() (IDs []uint64) {
return
}
{{ end }}
{{ if $.WithResources }}
// Resources returns a slice of types.Resource from all items in the set
//
// This function is auto-generated.
func (set {{ $set }}Set) Resources() (Resources []{{ $.ResourceType }}) {
Resources = make([]{{ $.ResourceType }}, len(set))
for i := range set {
Resources[i] = set[i].Resource()
}
return
}
{{ end }}
{{ end }}
`

View File

@ -156,7 +156,7 @@ func (r *record) Find(module *types.Module, filter string, sort string, page int
return nil, err
}
// Append order args to select args and execute actual query.
// append order args to select args and execute actual query.
if err := r.db().Select(&response.Records, sqlSelect, argsSelect...); err != nil {
return nil, err
}

View File

@ -71,7 +71,7 @@ func (svc *chart) Find() (cc types.ChartSet, err error) {
}
func (svc *chart) Create(mod *types.Chart) (c *types.Chart, err error) {
if !svc.prmSvc.CanCreateChart() {
if !svc.prmSvc.CanCreateChart(crmNamespace()) {
return nil, errors.New("not allowed to create this chart")
}
@ -112,7 +112,9 @@ func (svc *chart) Update(mod *types.Chart) (c *types.Chart, err error) {
}
func (svc *chart) DeleteByID(ID uint64) error {
if !svc.prmSvc.CanDeleteChartByID(ID) {
if c, err := svc.chartRepo.FindByID(ID); err != nil {
return errors.Wrap(err, "could not delete chart")
} else if !svc.prmSvc.CanDeleteChart(c) {
return errors.New("not allowed to delete this chart")
}

View File

@ -73,7 +73,7 @@ func (svc *module) Find() (mm types.ModuleSet, err error) {
}
func (svc *module) Create(mod *types.Module) (*types.Module, error) {
if !svc.prmSvc.CanCreateModule() {
if !svc.prmSvc.CanCreateModule(crmNamespace()) {
return nil, errors.New("not allowed to create this module")
}
@ -116,7 +116,9 @@ func (svc *module) Update(module *types.Module) (m *types.Module, err error) {
}
func (svc *module) DeleteByID(ID uint64) error {
if !svc.prmSvc.CanDeleteModuleByID(ID) {
if m, err := svc.moduleRepo.FindByID(ID); err != nil {
return errors.Wrap(err, "could not delete module")
} else if !svc.prmSvc.CanDeleteModule(m) {
return errors.New("not allowed to delete this module")
}

View File

@ -136,7 +136,7 @@ func (svc *page) Reorder(selfID uint64, pageIDs []uint64) error {
func (svc *page) Create(page *types.Page) (p *types.Page, err error) {
validate := func() error {
if !svc.prmSvc.CanCreatePage() {
if !svc.prmSvc.CanCreatePage(crmNamespace()) {
return errors.New("not allowed to create this module")
}
@ -189,7 +189,9 @@ func (svc *page) Update(page *types.Page) (p *types.Page, err error) {
}
func (svc *page) DeleteByID(ID uint64) error {
if !svc.prmSvc.CanDeletePageByID(ID) {
if p, err := svc.pageRepo.FindByID(ID); err != nil {
return errors.Wrap(err, "could not delete page")
} else if !svc.prmSvc.CanDeletePage(p) {
return errors.New("not allowed to delete this page")
}

View File

@ -17,8 +17,8 @@ type (
rules systemService.RulesService
}
resource interface {
Resource() internalRules.Resource
permissionResource interface {
PermissionResource() internalRules.Resource
}
PermissionsService interface {
@ -28,31 +28,26 @@ type (
CanAccess() bool
CanCreateNamspace() bool
CanCreateModule() bool
CanReadModule(r resource) bool
CanUpdateModule(r resource) bool
CanDeleteModule(r resource) bool
CanDeleteModuleByID(ID uint64) bool
CanCreateRecord(r resource) bool
CanReadRecord(r resource) bool
CanUpdateRecord(r resource) bool
CanDeleteRecord(r resource) bool
CanDeleteRecordByModuleID(ID uint64) bool
CanCreateChart() bool
CanReadChart(r resource) bool
CanUpdateChart(r resource) bool
CanDeleteChart(r resource) bool
CanDeleteChartByID(ID uint64) bool
CanCreateTrigger() bool
CanReadTrigger(r resource) bool
CanUpdateTrigger(r resource) bool
CanDeleteTrigger(r resource) bool
CanDeleteTriggerByID(ID uint64) bool
CanCreatePage() bool
CanReadPage(r resource) bool
CanUpdatePage(r resource) bool
CanDeletePage(r resource) bool
CanDeletePageByID(ID uint64) bool
CanCreateModule(r permissionResource) bool
CanReadModule(r permissionResource) bool
CanUpdateModule(r permissionResource) bool
CanDeleteModule(r permissionResource) bool
CanCreateRecord(r permissionResource) bool
CanReadRecord(r permissionResource) bool
CanUpdateRecord(r permissionResource) bool
CanDeleteRecord(r permissionResource) bool
CanCreateChart(r permissionResource) bool
CanReadChart(r permissionResource) bool
CanUpdateChart(r permissionResource) bool
CanDeleteChart(r permissionResource) bool
CanCreateTrigger(r permissionResource) bool
CanReadTrigger(r permissionResource) bool
CanUpdateTrigger(r permissionResource) bool
CanDeleteTrigger(r permissionResource) bool
CanCreatePage(r permissionResource) bool
CanReadPage(r permissionResource) bool
CanUpdatePage(r permissionResource) bool
CanDeletePage(r permissionResource) bool
}
effectivePermission struct {
@ -60,12 +55,16 @@ type (
Operation string `json:"operation"`
Allow bool `json:"allow"`
}
Compose struct{}
)
func (Compose) Resource() internalRules.Resource {
return internalRules.Resource{Service: "compose"}
// Creates a virtual namespace for CRM
//
// We need this until we're through with complete migration
// to Crust Compose
func crmNamespace() *types.Namespace {
return &types.Namespace{
ID: types.NamespaceCRM,
}
}
func Permissions() PermissionsService {
@ -84,10 +83,6 @@ func (p *permissions) With(ctx context.Context) PermissionsService {
}
}
func (p *permissions) baseResource() resource {
return &Compose{}
}
// Return permissions
func (p *permissions) Effective() (ee []effectivePermission, err error) {
ep := func(res, op string, allow bool) effectivePermission {
@ -102,136 +97,110 @@ func (p *permissions) Effective() (ee []effectivePermission, err error) {
ee = append(ee, ep("compose", "grant", p.CanGrant()))
ee = append(ee, ep("compose", "namespace.create", p.CanCreateNamspace()))
ee = append(ee, ep("compose:namespace:crm", "module.create", p.CanCreateModule()))
ee = append(ee, ep("compose:namespace:crm", "chart.create", p.CanCreateChart()))
ee = append(ee, ep("compose:namespace:crm", "trigger.create", p.CanCreateTrigger()))
ee = append(ee, ep("compose:namespace:crm", "page.create", p.CanCreatePage()))
ee = append(ee, ep("compose:namespace:crm", "module.create", p.CanCreateModule(crmNamespace())))
ee = append(ee, ep("compose:namespace:crm", "chart.create", p.CanCreateChart(crmNamespace())))
ee = append(ee, ep("compose:namespace:crm", "trigger.create", p.CanCreateTrigger(crmNamespace())))
ee = append(ee, ep("compose:namespace:crm", "page.create", p.CanCreatePage(crmNamespace())))
return
}
func (p *permissions) CanAccess() bool {
return p.checkAccess(p.baseResource(), "access")
return p.checkAccess(types.PermissionResource, "access")
}
func (p *permissions) CanGrant() bool {
return p.checkAccess(p.baseResource(), "grant")
return p.checkAccess(types.PermissionResource, "grant")
}
func (p *permissions) CanCreateNamspace() bool {
return p.checkAccess(p.baseResource(), "namespace.create")
return p.checkAccess(types.PermissionResource, "namespace.create")
}
func (p *permissions) CanCreateModule() bool {
func (p *permissions) CanCreateModule(ns permissionResource) bool {
// @todo move to func args when namespaces are implemented
ns := &types.Namespace{ID: types.NamespaceCRM}
return p.checkAccess(ns, "module.create")
}
func (p *permissions) CanReadModule(r resource) bool {
func (p *permissions) CanReadModule(r permissionResource) bool {
return p.checkAccess(r, "read")
}
func (p *permissions) CanUpdateModule(r resource) bool {
func (p *permissions) CanUpdateModule(r permissionResource) bool {
return p.checkAccess(r, "update")
}
func (p *permissions) CanDeleteModule(r resource) bool {
func (p *permissions) CanDeleteModule(r permissionResource) bool {
return p.checkAccess(r, "delete")
}
func (p *permissions) CanDeleteModuleByID(ID uint64) bool {
return p.CanDeleteModule(&types.Module{ID: ID})
}
func (p *permissions) CanCreateRecord(r resource) bool {
func (p *permissions) CanCreateRecord(r permissionResource) bool {
return p.checkAccess(r, "record.create")
}
func (p *permissions) CanReadRecord(r resource) bool {
func (p *permissions) CanReadRecord(r permissionResource) bool {
return p.checkAccess(r, "record.read")
}
func (p *permissions) CanUpdateRecord(r resource) bool {
func (p *permissions) CanUpdateRecord(r permissionResource) bool {
return p.checkAccess(r, "record.update")
}
func (p *permissions) CanDeleteRecord(r resource) bool {
func (p *permissions) CanDeleteRecord(r permissionResource) bool {
return p.checkAccess(r, "record.delete")
}
func (p *permissions) CanDeleteRecordByModuleID(moduleID uint64) bool {
return p.CanDeleteRecord(&types.Record{ModuleID: moduleID})
func (p *permissions) CanCreateChart(r permissionResource) bool {
return p.checkAccess(r, "chart.create")
}
func (p *permissions) CanCreateChart() bool {
// @todo move to func args when namespaces are implemented
ns := &types.Namespace{ID: types.NamespaceCRM}
return p.checkAccess(ns, "chart.create")
}
func (p *permissions) CanReadChart(r resource) bool {
func (p *permissions) CanReadChart(r permissionResource) bool {
return p.checkAccess(r, "read")
}
func (p *permissions) CanUpdateChart(r resource) bool {
func (p *permissions) CanUpdateChart(r permissionResource) bool {
return p.checkAccess(r, "update")
}
func (p *permissions) CanDeleteChart(r resource) bool {
func (p *permissions) CanDeleteChart(r permissionResource) bool {
return p.checkAccess(r, "delete")
}
func (p *permissions) CanDeleteChartByID(ID uint64) bool {
return p.CanDeleteChart(&types.Chart{ID: ID})
func (p *permissions) CanCreateTrigger(r permissionResource) bool {
return p.checkAccess(r, "trigger.create")
}
func (p *permissions) CanCreateTrigger() bool {
// @todo move to func args when namespaces are implemented
ns := &types.Namespace{ID: types.NamespaceCRM}
return p.checkAccess(ns, "trigger.create")
}
func (p *permissions) CanReadTrigger(r resource) bool {
func (p *permissions) CanReadTrigger(r permissionResource) bool {
return p.checkAccess(r, "read")
}
func (p *permissions) CanUpdateTrigger(r resource) bool {
func (p *permissions) CanUpdateTrigger(r permissionResource) bool {
return p.checkAccess(r, "update")
}
func (p *permissions) CanDeleteTrigger(r resource) bool {
func (p *permissions) CanDeleteTrigger(r permissionResource) bool {
return p.checkAccess(r, "delete")
}
func (p *permissions) CanDeleteTriggerByID(ID uint64) bool {
return p.CanDeleteTrigger(&types.Trigger{ID: ID})
}
func (p *permissions) CanCreatePage() bool {
func (p *permissions) CanCreatePage(r permissionResource) bool {
// @todo move to func args when namespaces are implemented
ns := &types.Namespace{ID: types.NamespaceCRM}
return p.checkAccess(ns, "page.create")
return p.checkAccess(r, "page.create")
}
func (p *permissions) CanReadPage(r resource) bool {
func (p *permissions) CanReadPage(r permissionResource) bool {
return p.checkAccess(r, "read")
}
func (p *permissions) CanUpdatePage(r resource) bool {
func (p *permissions) CanUpdatePage(r permissionResource) bool {
return p.checkAccess(r, "update")
}
func (p *permissions) CanDeletePage(r resource) bool {
func (p *permissions) CanDeletePage(r permissionResource) bool {
return p.checkAccess(r, "delete")
}
func (p *permissions) CanDeletePageByID(ID uint64) bool {
return p.CanDeletePage(&types.Page{ID: ID})
}
func (p *permissions) checkAccess(resource resource, operation string, fallbacks ...internalRules.CheckAccessFunc) bool {
access := p.rules.Check(resource.Resource().String(), operation, fallbacks...)
func (p *permissions) checkAccess(resource permissionResource, operation string, fallbacks ...internalRules.CheckAccessFunc) bool {
access := p.rules.Check(resource.PermissionResource(), operation, fallbacks...)
if access == internalRules.Allow {
return true
}

View File

@ -72,7 +72,7 @@ func (svc *trigger) Find(filter types.TriggerFilter) (tt types.TriggerSet, err e
}
func (svc *trigger) Create(trigger *types.Trigger) (p *types.Trigger, err error) {
if !svc.prmSvc.CanCreateTrigger() {
if !svc.prmSvc.CanCreateTrigger(crmNamespace()) {
return nil, errors.New("not allowed to create this trigger")
}
@ -110,7 +110,9 @@ func (svc *trigger) Update(trigger *types.Trigger) (t *types.Trigger, err error)
}
func (svc *trigger) DeleteByID(ID uint64) error {
if !svc.prmSvc.CanDeleteTriggerByID(ID) {
if t, err := svc.triggerRepo.FindByID(ID); err != nil {
return errors.Wrap(err, "could not delete trigger")
} else if !svc.prmSvc.CanDeleteTrigger(t) {
return errors.New("not allowed to delete this trigger")
}

View File

@ -22,12 +22,6 @@ type (
)
// Resource returns a system resource ID for this type
func (r *Chart) Resource() rules.Resource {
resource := rules.Resource{
Service: "compose",
Scope: "page",
ID: r.ID,
}
return resource
func (c Chart) PermissionResource() rules.Resource {
return ChartPermissionResource.AppendID(c.ID)
}

View File

@ -24,12 +24,6 @@ type (
)
// Resource returns a system resource ID for this type
func (r *Module) Resource() rules.Resource {
resource := rules.Resource{
Service: "compose",
Scope: "module",
ID: r.ID,
}
return resource
func (m Module) PermissionResource() rules.Resource {
return ModulePermissionResource.AppendID(m.ID)
}

View File

@ -15,12 +15,6 @@ const (
)
// Resource returns a system resource ID for this type
func (r *Namespace) Resource() rules.Resource {
resource := rules.Resource{
Service: "compose",
Scope: "namespace",
ID: r.ID,
}
return resource
func (n Namespace) PermissionResource() rules.Resource {
return NamespacePermissionResource.AppendID(n.ID)
}

View File

@ -40,12 +40,6 @@ type (
)
// Resource returns a system resource ID for this type
func (r *Page) Resource() rules.Resource {
resource := rules.Resource{
Service: "compose",
Scope: "page",
ID: r.ID,
}
return resource
func (p Page) PermissionResource() rules.Resource {
return PagePermissionResource.AppendID(p.ID)
}

View File

@ -0,0 +1,12 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
const PermissionResource = rules.Resource("compose")
const NamespacePermissionResource = rules.Resource("compose:namespace:")
const ChartPermissionResource = rules.Resource("compose:chart:")
const ModulePermissionResource = rules.Resource("compose:module:")
const PagePermissionResource = rules.Resource("compose:page:")
const TriggerPermissionResource = rules.Resource("compose:trigger:")

View File

@ -43,12 +43,6 @@ loop:
}
// Resource returns a system resource ID for this type
func (r *Record) Resource() rules.Resource {
resource := rules.Resource{
Service: "compose",
Scope: "module", // intentionally using module here so we can use Record's resource
ID: r.ModuleID,
}
return resource
func (r Record) PermissionResource() rules.Resource {
return ModulePermissionResource.AppendID(r.ModuleID)
}

View File

@ -47,12 +47,6 @@ func (set ActionSet) Value() (driver.Value, error) {
}
// Resource returns a system resource ID for this type
func (r *Trigger) Resource() rules.Resource {
resource := rules.Resource{
Service: "compose",
Scope: "trigger",
ID: r.ID,
}
return resource
func (t Trigger) PermissionResource() rules.Resource {
return TriggerPermissionResource.AppendID(t.ID)
}

View File

@ -9,7 +9,7 @@ import (
type ResourcesInterface interface {
With(ctx context.Context, db *factory.DB) ResourcesInterface
Check(resource string, operation string, fallbacks ...CheckAccessFunc) Access
Check(resource Resource, operation string, fallbacks ...CheckAccessFunc) Access
Grant(roleID uint64, rules []Rule) error
Read(roleID uint64) ([]Rule, error)

View File

@ -1,48 +1,77 @@
package rules
import (
"fmt"
"encoding/json"
"strconv"
"strings"
)
type Resource struct {
ID uint64 `json:"id,string"`
Name string `json:"name"`
Scope string `json:"scope"`
Service string `json:"service"`
type (
Resource string
)
const (
resourceDelimiter = ':'
resourceWildcard = '*'
)
func (r Resource) append(suffix string) Resource {
if !r.IsAppendable() {
panic("can not append to non appendable resource '" + r.String() + "'")
}
return Resource(r.String() + suffix)
}
type ResourceJSON struct {
ID uint64 `json:"id,string"`
Name string `json:"name"`
Scope string `json:"scope"`
Service string `json:"service"`
ResourceID string `json:"resource"`
// Resource to satisfty interfaces and ease development
func (r Resource) PermissionResource() Resource {
return r
}
func (r Resource) AppendID(ID uint64) Resource {
return r.append(strconv.FormatUint(ID, 10))
}
func (r Resource) AppendWildcard() Resource {
return r.TrimID().append(string(resourceWildcard))
}
// Trims off wildcard/id from resource
func (r Resource) TrimID() Resource {
s := r.String()
p := strings.LastIndexByte(s, resourceDelimiter)
if p > 0 {
return Resource(s[0 : p+1])
}
return r
}
// IsAppendable checks if Resource has trailing resource delimiter
func (r Resource) IsAppendable() bool {
return strings.IndexByte(r.String(), resourceDelimiter) > -1
}
// IsValid does basic resource validation
func (r Resource) IsValid() bool {
return len(r) > 0 && r[len(r)-1] != resourceDelimiter
}
// IsServiceLevel checks for resource delimiters - service level resources do not have it
func (r Resource) GetService() Resource {
s := r.String()
p := strings.IndexByte(s, resourceDelimiter)
if p > 0 {
return Resource(s[0:p])
}
return r
}
// HasWildcard checks if resource has wildcard char at the end
func (r Resource) HasWildcard() bool {
return len(r) > 0 && r[len(r)-1] == resourceWildcard
}
func (r Resource) String() string {
if r.ID > 0 {
return fmt.Sprintf("%s:%s:%d", r.Service, r.Scope, r.ID)
} else if r.Scope == "" {
return r.Service
}
return ""
return string(r)
}
func (r Resource) All() string {
return fmt.Sprintf("%s:%s:*", r.Service, r.Scope)
}
func (r Resource) MarshalJSON() ([]byte, error) {
return json.Marshal(ResourceJSON{
r.ID,
r.Name,
r.Scope,
r.Service,
r.String(),
})
}
var _ fmt.Stringer = Resource{}

View File

@ -3,33 +3,61 @@ package rules
import (
"testing"
"encoding/json"
"github.com/crusttech/crust/internal/test"
)
func TestResource(t *testing.T) {
var (
assert = test.Assert
sCases = []struct {
r Resource
s string
}{
{
Resource("a:b:c"),
"a:b:c"},
{
Resource("a:b:c").PermissionResource(),
"a:b:c"},
{
Resource("a:b:").AppendID(1),
"a:b:1"},
{
Resource("a:b:").AppendWildcard(),
"a:b:*"},
{
Resource("a:b:1").TrimID(),
"a:b:"},
{
Resource("a:b:1").GetService(),
"a"},
}
)
r := Resource{123, "Test name", "channel", "messaging"}
assert(t, r.String() == "messaging:channel:123", "Resource ID doesn't match, messaging:channel:123 != '%s'", r.String())
b, _ := json.Marshal(r)
{
r := ResourceJSON{}
json.Unmarshal(b, &r)
assert(t, r.ResourceID == "messaging:channel:123", "Decoded full-json resource ID doesn't match, messaging:channel:123 != '%s'", r.ResourceID)
for _, sc := range sCases {
assert(t, sc.r.String() == sc.s, "Resource check failed (%s != %s)", sc.r, sc.s)
}
{
r := Resource{}
json.Unmarshal(b, &r)
assert(t, r.String() == "messaging:channel:123", "Decoded full-json resource ID doesn't match, messaging:channel:123 != '%s'", r.String())
}
var r string
r = "a:"
assert(t, Resource(r).IsAppendable(), "Expecting resource %q to be appendable", r)
r = "a:1"
assert(t, Resource(r).IsAppendable(), "Expecting resource %q to be appendable", r)
r = "a:*"
assert(t, Resource(r).IsAppendable(), "Expecting resource %q to be appendable", r)
{
r.ID = 0
assert(t, r.String() == "", "Empty resource should return empty string, got '%s'", r.String())
}
r = "a"
assert(t, Resource(r).IsValid(), "Expecting resource %q to be valid", r)
r = "a:"
assert(t, !Resource(r).IsValid(), "Expecting resource %q not to be valid", r)
r = "a:1"
assert(t, Resource(r).IsValid(), "Expecting resource %q to be valid", r)
r = "a:*"
assert(t, Resource(r).IsValid(), "Expecting resource %q to be valid", r)
r = "a:1"
assert(t, !Resource(r).HasWildcard(), "Expecting resource %q to not have wildcard", r)
r = "a:*"
assert(t, Resource(r).HasWildcard(), "Expecting resource %q to have wildcard", r)
}

View File

@ -2,7 +2,6 @@ package rules
import (
"context"
"strings"
"github.com/titpetric/factory"
@ -10,7 +9,6 @@ import (
)
const (
delimiter = ":"
everyoneRoleId uint64 = 1
defaultAccess Access = Deny
)
@ -29,48 +27,43 @@ func NewResources(ctx context.Context, db *factory.DB) ResourcesInterface {
return (&resources{}).With(ctx, db)
}
func (r *resources) With(ctx context.Context, db *factory.DB) ResourcesInterface {
func (rr *resources) With(ctx context.Context, db *factory.DB) ResourcesInterface {
return &resources{
ctx: ctx,
db: db,
}
}
func (r *resources) identity() uint64 {
return auth.GetIdentityFromContext(r.ctx).Identity()
func (rr *resources) identity() uint64 {
return auth.GetIdentityFromContext(rr.ctx).Identity()
}
// IsAllowed function checks granted permission for specific resource and operation. Permission checks on
// global level are not allowed and will always return Deny.
func (r *resources) Check(resource string, operation string, fallbacks ...CheckAccessFunc) Access {
func (rr *resources) Check(r Resource, operation string, fallbacks ...CheckAccessFunc) Access {
// Number one check, do we have a valid identity?
if !auth.GetIdentityFromContext(r.ctx).Valid() {
if !auth.GetIdentityFromContext(rr.ctx).Valid() {
return Deny
}
parts := strings.Split(resource, delimiter)
// Permission check on global level is not allowed.
if parts[len(parts)-1] == "*" || parts[len(parts)-1] == "" {
if !r.IsValid() {
// Make sure we do not let through wildcard or undefined resources
return Deny
}
// Resource-specific check
checks := []CheckAccessFunc{
func() Access { return r.checkAccess(resource, operation) },
func() Access { return r.checkAccessEveryone(resource, operation) },
func() Access { return rr.checkAccess(r, operation) },
func() Access { return rr.checkAccessEveryone(r, operation) },
}
if len(parts) > 1 {
// If this is a non-service resource (so, not system, messaging),
// add checks for any-resource (ending with `*`)
parts[len(parts)-1] = "*"
anyResource := strings.Join(parts, delimiter)
if r.IsAppendable() {
wc := r.AppendWildcard()
checks = append(
checks,
func() Access { return r.checkAccess(anyResource, operation) },
func() Access { return r.checkAccessEveryone(anyResource, operation) },
func() Access { return rr.checkAccess(wc, operation) },
func() Access { return rr.checkAccessEveryone(wc, operation) },
)
}
@ -84,19 +77,19 @@ func (r *resources) Check(resource string, operation string, fallbacks ...CheckA
return defaultAccess
}
func (r *resources) checkAccess(resource string, operation string) Access {
user := r.identity()
result := []Access{}
query := []string{
// select rules
"select r.value from sys_rules r",
// join members
"inner join sys_role_member m on (m.rel_role = r.rel_role and m.rel_user=?)",
// add conditions
"where r.resource=? and r.operation=?",
}
queryString := strings.Join(query, " ")
if err := r.db.Select(&result, queryString, user, resource, operation); err != nil {
//
func (rr *resources) checkAccess(resource Resource, operation string) Access {
var result = make([]Access, 0)
user := rr.identity()
query := "" +
"SELECT r.value " +
" FROM sys_rules r" +
" INNER JOIN sys_role_member m ON (m.rel_role = r.rel_role AND m.rel_user = ?)" +
" WHERE r.resource = ? AND r.operation = ?"
if err := rr.db.Select(&result, query, user, resource, operation); err != nil {
// @todo: log error
return Deny
}
@ -115,16 +108,15 @@ func (r *resources) checkAccess(resource string, operation string) Access {
return Inherit
}
func (r *resources) checkAccessEveryone(resource string, operation string) Access {
result := []Access{}
query := []string{
// select rules
"select r.value from sys_rules r",
// add conditions
"where r.rel_role = ? and r.resource=? and r.operation=?",
}
queryString := strings.Join(query, " ")
if err := r.db.Select(&result, queryString, everyoneRoleId, resource, operation); err != nil {
func (rr *resources) checkAccessEveryone(resource Resource, operation string) Access {
var result = make([]Access, 0)
query := "" +
"SELECT r.value " +
" FROM sys_rules r" +
" WHERE r.rel_role = ? AND r.resource = ? AND r.operation = ?"
if err := rr.db.Select(&result, query, everyoneRoleId, resource, operation); err != nil {
// @todo: log error
return Deny
}
@ -136,17 +128,17 @@ func (r *resources) checkAccessEveryone(resource string, operation string) Acces
return Inherit
}
func (r *resources) Grant(roleID uint64, rules []Rule) error {
return r.db.Transaction(func() error {
func (rr *resources) Grant(roleID uint64, rules []Rule) error {
return rr.db.Transaction(func() error {
var err error
for _, rule := range rules {
rule.RoleID = roleID
switch rule.Value {
case Inherit:
_, err = r.db.NamedExec("delete from sys_rules where rel_role=:rel_role and resource=:resource and operation=:operation", rule)
_, err = rr.db.NamedExec("delete from sys_rules where rel_role=:rel_role and resource=:resource and operation=:operation", rule)
default:
err = r.db.Replace("sys_rules", rule)
err = rr.db.Replace("sys_rules", rule)
}
if err != nil {
return err
@ -156,19 +148,19 @@ func (r *resources) Grant(roleID uint64, rules []Rule) error {
})
}
func (r *resources) Read(roleID uint64) ([]Rule, error) {
func (rr *resources) Read(roleID uint64) ([]Rule, error) {
result := []Rule{}
query := "select * from sys_rules where rel_role = ?"
if err := r.db.Select(&result, query, roleID); err != nil {
if err := rr.db.Select(&result, query, roleID); err != nil {
return nil, err
}
return result, nil
}
func (r *resources) Delete(roleID uint64) error {
func (rr *resources) Delete(roleID uint64) error {
query := "delete from sys_rules where rel_role = ?"
if _, err := r.db.Exec(query, roleID); err != nil {
if _, err := rr.db.Exec(query, roleID); err != nil {
return err
}
return nil

View File

@ -9,10 +9,10 @@ const (
)
type Rule struct {
RoleID uint64 `json:"roleID,string" db:"rel_role"`
Resource string `json:"resource" db:"resource"`
Operation string `json:"operation" db:"operation"`
Value Access `json:"value,string" db:"value"`
RoleID uint64 `json:"roleID,string" db:"rel_role"`
Resource Resource `json:"resource" db:"resource"`
Operation string `json:"operation" db:"operation"`
Value Access `json:"value,string" db:"value"`
}
func (a *Access) UnmarshalJSON(data []byte) error {

View File

@ -18,6 +18,10 @@ type (
rules systemService.RulesService
}
resource interface {
PermissionResource() internalRules.Resource
}
PermissionsService interface {
With(context.Context) PermissionsService
@ -55,9 +59,9 @@ type (
}
effectivePermission struct {
Resource string `json:"resource"`
Operation string `json:"operation"`
Allow bool `json:"allow"`
Resource internalRules.Resource `json:"resource"`
Operation string `json:"operation"`
Allow bool `json:"allow"`
}
)
@ -78,7 +82,7 @@ func (p *permissions) With(ctx context.Context) PermissionsService {
}
func (p *permissions) Effective() (ee []effectivePermission, err error) {
ep := func(res, op string, allow bool) effectivePermission {
ep := func(res internalRules.Resource, op string, allow bool) effectivePermission {
return effectivePermission{
Resource: res,
Operation: op,
@ -86,41 +90,41 @@ func (p *permissions) Effective() (ee []effectivePermission, err error) {
}
}
ee = append(ee, ep("messaging", "access", p.CanAccess()))
ee = append(ee, ep("messaging", "grant", p.CanGrant()))
ee = append(ee, ep("messaging", "channel.public.create", p.CanCreatePublicChannel()))
ee = append(ee, ep("messaging", "channel.private.create", p.CanCreatePrivateChannel()))
ee = append(ee, ep("messaging", "channel.group.create", p.CanCreateGroupChannel()))
ee = append(ee, ep(types.PermissionResource, "access", p.CanAccess()))
ee = append(ee, ep(types.PermissionResource, "grant", p.CanGrant()))
ee = append(ee, ep(types.PermissionResource, "channel.public.create", p.CanCreatePublicChannel()))
ee = append(ee, ep(types.PermissionResource, "channel.private.create", p.CanCreatePrivateChannel()))
ee = append(ee, ep(types.PermissionResource, "channel.group.create", p.CanCreateGroupChannel()))
return
}
func (p *permissions) CanAccess() bool {
return p.checkAccess("messaging", "access")
return p.checkAccess(types.PermissionResource, "access")
}
func (p *permissions) CanGrant() bool {
return p.checkAccess("messaging", "grant")
return p.checkAccess(types.PermissionResource, "grant")
}
func (p *permissions) CanCreatePublicChannel() bool {
return p.checkAccess("messaging", "channel.public.create", p.allow())
return p.checkAccess(types.PermissionResource, "channel.public.create", p.allow())
}
func (p *permissions) CanCreatePrivateChannel() bool {
return p.checkAccess("messaging", "channel.private.create", p.allow())
return p.checkAccess(types.PermissionResource, "channel.private.create", p.allow())
}
func (p *permissions) CanCreateGroupChannel() bool {
return p.checkAccess("messaging", "channel.group.create", p.allow())
return p.checkAccess(types.PermissionResource, "channel.group.create", p.allow())
}
func (p *permissions) CanUpdateChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "update", p.isChannelOwnerFallback(ch))
return p.checkAccess(ch, "update", p.isChannelOwnerFallback(ch))
}
func (p *permissions) CanReadChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "read", p.canReadFallback(ch))
return p.checkAccess(ch, "read", p.canReadFallback(ch))
}
func (p *permissions) CanReadChannelByID(id uint64) bool {
@ -128,76 +132,76 @@ func (p *permissions) CanReadChannelByID(id uint64) bool {
}
func (p *permissions) CanJoinChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "join", p.canJoinFallback(ch))
return p.checkAccess(ch, "join", p.canJoinFallback(ch))
}
func (p *permissions) CanLeaveChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "leave", p.allow())
return p.checkAccess(ch, "leave", p.allow())
}
func (p *permissions) CanArchiveChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "archive", p.isChannelOwnerFallback(ch))
return p.checkAccess(ch, "archive", p.isChannelOwnerFallback(ch))
}
func (p *permissions) CanUnarchiveChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "unarchive", p.isChannelOwnerFallback(ch))
return p.checkAccess(ch, "unarchive", p.isChannelOwnerFallback(ch))
}
func (p *permissions) CanDeleteChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "delete", p.isChannelOwnerFallback(ch))
return p.checkAccess(ch, "delete", p.isChannelOwnerFallback(ch))
}
func (p *permissions) CanUndeleteChannel(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "undelete", p.isChannelOwnerFallback(ch))
return p.checkAccess(ch, "undelete", p.isChannelOwnerFallback(ch))
}
func (p *permissions) CanManageChannelMembers(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "members.manage", p.isChannelOwnerFallback(ch))
return p.checkAccess(ch, "members.manage", p.isChannelOwnerFallback(ch))
}
func (p *permissions) CanManageChannelWebhooks(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "webhooks.manage")
return p.checkAccess(ch, "webhooks.manage")
}
func (p *permissions) CanManageChannelAttachments(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "attachments.manage")
return p.checkAccess(ch, "attachments.manage")
}
func (p *permissions) CanSendMessage(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.send", p.canSendMessagesFallback(ch))
return p.checkAccess(ch, "message.send", p.canSendMessagesFallback(ch))
}
func (p *permissions) CanReplyMessage(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.reply", p.allow())
return p.checkAccess(ch, "message.reply", p.allow())
}
func (p *permissions) CanEmbedMessage(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.embed", p.allow())
return p.checkAccess(ch, "message.embed", p.allow())
}
func (p *permissions) CanAttachMessage(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.attach", p.allow())
return p.checkAccess(ch, "message.attach", p.allow())
}
func (p *permissions) CanUpdateOwnMessages(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.update.own", p.allow())
return p.checkAccess(ch, "message.update.own", p.allow())
}
func (p *permissions) CanUpdateMessages(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.update.all")
return p.checkAccess(ch, "message.update.all")
}
func (p *permissions) CanDeleteOwnMessages(ch *types.Channel) bool {
// @todo implement
return p.checkAccess(ch.Resource().String(), "message.delete.own", p.allow())
return p.checkAccess(ch, "message.delete.own", p.allow())
}
func (p *permissions) CanDeleteMessages(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.delete.all")
return p.checkAccess(ch, "message.delete.all")
}
func (p *permissions) CanReactMessage(ch *types.Channel) bool {
return p.checkAccess(ch.Resource().String(), "message.react", p.allow())
return p.checkAccess(ch, "message.react", p.allow())
}
func (p permissions) canJoinFallback(ch *types.Channel) func() internalRules.Access {
@ -255,8 +259,8 @@ func (p permissions) isChannelOwnerFallback(ch *types.Channel) func() internalRu
}
}
func (p *permissions) checkAccess(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) bool {
access := p.rules.Check(resource, operation, fallbacks...)
func (p *permissions) checkAccess(res resource, operation string, fallbacks ...internalRules.CheckAccessFunc) bool {
access := p.rules.Check(res.PermissionResource(), operation, fallbacks...)
if access == internalRules.Allow {
return true
}

View File

@ -1,9 +1,5 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
// Hello! This file is auto-generated.
type (
@ -69,16 +65,3 @@ func (set ChannelSet) IDs() (IDs []uint64) {
return
}
// Resources returns a slice of types.Resource from all items in the set
//
// This function is auto-generated.
func (set ChannelSet) Resources() (Resources []rules.Resource) {
Resources = make([]rules.Resource, len(set))
for i := range set {
Resources[i] = set[i].Resource()
}
return
}

View File

@ -61,16 +61,8 @@ type (
)
// Resource returns a system resource ID for this type
func (r *Channel) Resource() rules.Resource {
resource := rules.Resource{
Service: "messaging",
Scope: "channel",
}
if r != nil {
resource.ID = r.ID
resource.Name = r.Name
}
return resource
func (c Channel) PermissionResource() rules.Resource {
return ChannelPermissionResource.AppendID(c.ID)
}
func (c *Channel) IsValid() bool {

View File

@ -4,6 +4,7 @@ import (
"database/sql/driver"
"time"
"github.com/crusttech/crust/internal/rules"
systemTypes "github.com/crusttech/crust/system/types"
)
@ -126,3 +127,7 @@ func (mtype MessageType) Value() (driver.Value, error) {
func (m *Message) IsValid() bool {
return m.DeletedAt == nil
}
func (m Message) PermissionResource() rules.Resource {
return ChannelPermissionResource.AppendID(m.ChannelID)
}

View File

@ -0,0 +1,8 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
const PermissionResource = rules.Resource("messaging")
const ChannelPermissionResource = rules.Resource("messaging:channel:")

View File

@ -1,16 +0,0 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
type (
ResourceProvider interface {
Resource() rules.Resource
}
)
// These entities create resources in RBAC
var _ ResourceProvider = &Organisation{}
var _ ResourceProvider = &Role{}
var _ ResourceProvider = &Channel{}

View File

@ -1,11 +0,0 @@
package types
import (
"github.com/crusttech/crust/system/types"
)
type (
Role struct {
types.Role
}
)

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,10 @@ type (
rules RulesService
}
resource interface {
PermissionResource() internalRules.Resource
}
PermissionsService interface {
With(context.Context) PermissionsService
@ -77,55 +81,51 @@ func (p *permissions) Effective() (ee []effectivePermission, err error) {
}
func (p *permissions) CanAccess() bool {
return p.checkAccess("system", "access")
return p.checkAccess(types.PermissionResource, "access")
}
func (p *permissions) CanCreateOrganisation() bool {
return p.checkAccess("system", "organisation.create")
return p.checkAccess(types.PermissionResource, "organisation.create")
}
func (p *permissions) CanCreateRole() bool {
return p.checkAccess("system", "role.create")
return p.checkAccess(types.PermissionResource, "role.create")
}
func (p *permissions) CanCreateApplication() bool {
return p.checkAccess("system", "application.create")
return p.checkAccess(types.PermissionResource, "application.create")
}
func (p *permissions) CanReadRole(rl *types.Role) bool {
return p.checkAccess(rl.Resource().String(), "read", p.allow())
return p.checkAccess(rl, "read", p.allow())
}
func (p *permissions) CanUpdateRole(rl *types.Role) bool {
return p.checkAccess(rl.Resource().String(), "update")
return p.checkAccess(rl, "update")
}
func (p *permissions) CanDeleteRole(rl *types.Role) bool {
return p.checkAccess(rl.Resource().String(), "delete")
return p.checkAccess(rl, "delete")
}
func (p *permissions) CanManageRoleMembers(rl *types.Role) bool {
return p.checkAccess(rl.Resource().String(), "members.manage")
return p.checkAccess(rl, "members.manage")
}
func (p *permissions) CanReadApplication(app *types.Application) bool {
return p.checkAccess(app.Resource().String(), "read", p.allow())
return p.checkAccess(app, "read", p.allow())
}
func (p *permissions) CanUpdateApplication(app *types.Application) bool {
return p.checkAccess(app.Resource().String(), "update")
return p.checkAccess(app, "update")
}
func (p *permissions) CanDeleteApplication(app *types.Application) bool {
return p.checkAccess(app.Resource().String(), "delete")
return p.checkAccess(app, "delete")
}
func (p *permissions) checkAccess(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) bool {
access := p.rules.Check(resource, operation, fallbacks...)
if access == internalRules.Allow {
return true
}
return false
func (p *permissions) checkAccess(r resource, operation string, fallbacks ...internalRules.CheckAccessFunc) bool {
return p.rules.Check(r.PermissionResource(), operation, fallbacks...) == internalRules.Allow
}
func (p permissions) allow() func() internalRules.Access {

View File

@ -2,7 +2,6 @@ package service
import (
"context"
"strings"
"github.com/pkg/errors"
@ -35,7 +34,7 @@ type (
List() (interface{}, error)
Effective(filter string) ([]EffectiveRules, error)
Check(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access
Check(resource internalRules.Resource, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access
Read(roleID uint64) (interface{}, error)
Update(roleID uint64, rules []internalRules.Rule) (interface{}, error)
@ -60,11 +59,8 @@ func (p *rules) With(ctx context.Context) RulesService {
func (p *rules) List() (interface{}, error) {
perms := []types.Permission{}
for resource, operations := range permissionList {
err := p.checkServiceAccess(resource)
if err == nil {
for ops := range operations {
perms = append(perms, types.Permission{Resource: resource, Operation: ops})
}
for ops := range operations {
perms = append(perms, types.Permission{Resource: resource, Operation: ops})
}
}
return perms, nil
@ -87,7 +83,7 @@ func (p *rules) Effective(filter string) (eff []EffectiveRules, err error) {
return
}
func (p *rules) Check(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access {
func (p *rules) Check(resource internalRules.Resource, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access {
return p.resources.Check(resource, operation, fallbacks...)
}
@ -130,12 +126,11 @@ func (p *rules) Delete(roleID uint64) (interface{}, error) {
return nil, p.resources.Delete(roleID)
}
func (p *rules) checkServiceAccess(resource string) error {
service := strings.Split(resource, delimiter)[0]
grant := p.resources.Check(service, "grant")
func (p *rules) checkServiceAccess(resource internalRules.Resource) error {
// First, we trim off resource to get service - only service resource have grant operation
grant := p.resources.Check(resource.GetService(), "grant")
if grant == internalRules.Allow {
return nil
}
return errors.Errorf("No grant permissions for: %v", service)
return errors.Errorf("No grant permissions for: %q", resource)
}

View File

@ -1,9 +1,9 @@
package service
import (
"strings"
"github.com/pkg/errors"
internalRules "github.com/crusttech/crust/internal/rules"
)
var (
@ -15,16 +15,16 @@ var (
"role.create": true,
"application.create": true,
},
"system:organisation": map[string]bool{
"system:organisation:": map[string]bool{
"access": true,
},
"system:role": map[string]bool{
"system:role:": map[string]bool{
"read": true,
"update": true,
"delete": true,
"members.manage": true,
},
"system:application": map[string]bool{
"system:application:": map[string]bool{
"read": true,
"update": true,
"delete": true,
@ -36,7 +36,7 @@ var (
"channel.private.create": true,
"channel.group.create": true,
},
"messaging:channel": map[string]bool{
"messaging:channel:": map[string]bool{
"update": true,
"read": true,
"join": true,
@ -63,7 +63,7 @@ var (
"grant": true,
"namespace.create": true,
},
"compose:namespace": map[string]bool{
"compose:namespace:": map[string]bool{
"read": true,
"update": true,
"delete": true,
@ -72,7 +72,7 @@ var (
"trigger.create": true,
"page.create": true,
},
"compose:module": map[string]bool{
"compose:module:": map[string]bool{
"read": true,
"update": true,
"delete": true,
@ -81,17 +81,17 @@ var (
"record.update": true,
"record.delete": true,
},
"compose:chart": map[string]bool{
"compose:chart:": map[string]bool{
"read": true,
"update": true,
"delete": true,
},
"compose:trigger": map[string]bool{
"compose:trigger:": map[string]bool{
"read": true,
"update": true,
"delete": true,
},
"compose:page": map[string]bool{
"compose:page:": map[string]bool{
"read": true,
"update": true,
"delete": true,
@ -99,28 +99,18 @@ var (
}
)
func validatePermission(resource string, operation string) error {
resourceParts := strings.Split(resource, delimiter)
if len(resourceParts) < 1 {
return errors.Errorf("Invalid resource format, expected >= 1, got %d", len(resourceParts))
func validatePermission(resource internalRules.Resource, operation string) error {
if !resource.IsValid() {
return errors.Errorf("invalid resource format: %q", resource)
}
resourceName := resourceParts[0]
if len(resourceParts) > 1 {
resourceName = resourceParts[0] + delimiter + resourceParts[1]
}
res := resource.TrimID().String()
if service, ok := permissionList[resourceName]; ok {
if service, ok := permissionList[res]; ok {
if op := service[operation]; op {
if len(resourceParts) == 3 {
if val := resourceParts[2]; val != "" {
return nil
}
return errors.Errorf("Invalid resource format, missing resource ID")
}
return nil
}
return errors.Errorf("Unknown operation: '%s'", operation)
}
return errors.Errorf("Unknown resource name: '%s'", resourceName)
return errors.Errorf("Unknown resource name: '%s'", resource)
}

View File

@ -11,13 +11,13 @@ type (
RulesService interface {
List() (interface{}, error)
Effective(filter string) ([]service.EffectiveRules, error)
Check(resource string, operation string, fallbacks ...rules.CheckAccessFunc) rules.Access
Check(resource rules.Resource, operation string, fallbacks ...rules.CheckAccessFunc) rules.Access
Read(roleID uint64) (interface{}, error)
}
)
var DefaultRules = service.DefaultRules
func Rules(ctx context.Context) RulesService {
func Rules(ctx context.Context) service.RulesService {
return DefaultRules.With(ctx)
}

View File

@ -43,17 +43,9 @@ func (u *Application) Identity() uint64 {
return u.ID
}
// Resource returns a system resource ID for this type
func (u *Application) Resource() rules.Resource {
resource := rules.Resource{
Service: "system",
Scope: "application",
}
if u != nil {
resource.ID = u.ID
resource.Name = u.Name
}
return resource
// Resource returns a resource ID for this type
func (u Application) PermissionResource() rules.Resource {
return ApplicationPermissionResource.AppendID(u.ID)
}
func (au *ApplicationUnify) Scan(value interface{}) error {

View File

@ -1,9 +1,5 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
// Hello! This file is auto-generated.
type (
@ -69,16 +65,3 @@ func (set OrganisationSet) IDs() (IDs []uint64) {
return
}
// Resources returns a slice of types.Resource from all items in the set
//
// This function is auto-generated.
func (set OrganisationSet) Resources() (Resources []rules.Resource) {
Resources = make([]rules.Resource, len(set))
for i := range set {
Resources[i] = set[i].Resource()
}
return
}

View File

@ -23,15 +23,7 @@ type (
}
)
// Resource returns a system resource ID for this type
func (r *Organisation) Resource() rules.Resource {
resource := rules.Resource{
Service: "system",
Scope: "organisation",
}
if r != nil {
resource.ID = r.ID
resource.Name = r.Name
}
return resource
// Resource returns a resource ID for this type
func (o *Organisation) PermissionResource() rules.Resource {
return OrganisationPermissionResource.AppendID(o.ID)
}

View File

@ -0,0 +1,10 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
const PermissionResource = rules.Resource("system")
const ApplicationPermissionResource = rules.Resource("system:application:")
const OrganisationPermissionResource = rules.Resource("system:organisation:")
const RolePermissionResource = rules.Resource("system:role:")

View File

@ -1,9 +1,5 @@
package types
import (
"github.com/crusttech/crust/internal/rules"
)
// Hello! This file is auto-generated.
type (
@ -69,16 +65,3 @@ func (set RoleSet) IDs() (IDs []uint64) {
return
}
// Resources returns a slice of types.Resource from all items in the set
//
// This function is auto-generated.
func (set RoleSet) Resources() (Resources []rules.Resource) {
Resources = make([]rules.Resource, len(set))
for i := range set {
Resources[i] = set[i].Resource()
}
return
}

View File

@ -23,15 +23,7 @@ type (
}
)
// Resource returns a system resource ID for this type
func (r *Role) Resource() rules.Resource {
resource := rules.Resource{
Service: "system",
Scope: "role",
}
if r != nil {
resource.ID = r.ID
resource.Name = r.Name
}
return resource
// Resource returns a resource ID for this type
func (r *Role) PermissionResource() rules.Resource {
return RolePermissionResource.AppendID(r.ID)
}