upd(messaging): messaging repository/permissions to service/rules
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package organization
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crusttech/crust/system/types"
|
||||
)
|
||||
|
||||
func GetFromContext(ctx context.Context) types.Organisation {
|
||||
if orgID, ok := ctx.Value("organizationID").(uint64); ok {
|
||||
return types.Organisation{ID: orgID}
|
||||
} else {
|
||||
return types.Organisation{ID: 1}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
systemTypes "github.com/crusttech/crust/system/types"
|
||||
)
|
||||
|
||||
type PermissionsRepository interface {
|
||||
With(context.Context, *factory.DB) PermissionsRepository
|
||||
|
||||
// Applies mostly to admin panel
|
||||
isAdmin(org *types.Organisation) bool
|
||||
|
||||
// Individual rules for administration
|
||||
canManageOrganisation(org *types.Organisation) bool
|
||||
canManageRoles(org *types.Organisation) bool
|
||||
canManageChannels(org *types.Organisation) bool
|
||||
|
||||
// types.Team derived from identity uses a wildcard match
|
||||
canManageWebhooks(org *types.Organisation, ch *types.Channel) bool
|
||||
|
||||
// Messaging rules
|
||||
canSendMessages(org *types.Organisation, ch *types.Channel) bool
|
||||
canEmbedLinks(org *types.Organisation, ch *types.Channel) bool
|
||||
canAttachFiles(org *types.Organisation, ch *types.Channel) bool
|
||||
canEditOwnMessages(org *types.Organisation, ch *types.Channel) bool
|
||||
canEditMessages(org *types.Organisation, ch *types.Channel) bool
|
||||
canReact(org *types.Organisation, ch *types.Channel) bool
|
||||
}
|
||||
|
||||
type permissions struct {
|
||||
*repository
|
||||
|
||||
// identity is passed with context
|
||||
resources rules.ResourcesInterface
|
||||
team *systemTypes.Team
|
||||
}
|
||||
|
||||
func Permissions(ctx context.Context, db *factory.DB) PermissionsRepository {
|
||||
return (&permissions{
|
||||
team: &systemTypes.Team{},
|
||||
}).With(ctx, db)
|
||||
}
|
||||
|
||||
func (r *permissions) With(ctx context.Context, db *factory.DB) PermissionsRepository {
|
||||
return &permissions{
|
||||
team: r.team,
|
||||
resources: rules.NewResources(ctx, db),
|
||||
repository: r.repository.With(ctx, db),
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrPermissionsNotLoggedIn = repositoryError("PermissionsNotLoggedIn")
|
||||
)
|
||||
|
||||
// @todo: honor defaults from (org/team/channel).Permissions()
|
||||
|
||||
func (r *permissions) isAdmin(org *types.Organisation) bool {
|
||||
op := "admin"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canManageOrganisation(org *types.Organisation) bool {
|
||||
op := "manage.organisation"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canManageRoles(org *types.Organisation) bool {
|
||||
op := "manage.roles"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canManageChannels(org *types.Organisation) bool {
|
||||
op := "manage.channels"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canManageWebhooks(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "manage.webhooks"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canSendMessages(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "text.send"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canEmbedLinks(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "text.embed"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canAttachFiles(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "text.attach"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canEditOwnMessages(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "text.edit_own"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canEditMessages(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "text.edit_all"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) canReact(org *types.Organisation, ch *types.Channel) bool {
|
||||
op := "text.react"
|
||||
return r.hasAccess(op, org.PermissionDefault(op), org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *permissions) hasAccess(operation string, value rules.Access, scopes ...string) bool {
|
||||
// reverse scopes from to order it from most-least significant
|
||||
// aka: [0]channel [1]teams [2]org
|
||||
last := len(scopes) - 1
|
||||
for i := 0; i < len(scopes)/2; i++ {
|
||||
scopes[i], scopes[last-i] = scopes[last-i], scopes[i]
|
||||
}
|
||||
|
||||
for _, scope := range scopes {
|
||||
if scope == "" {
|
||||
continue
|
||||
}
|
||||
switch r.resources.IsAllowed(scope, operation) {
|
||||
case rules.Allow:
|
||||
return true
|
||||
case rules.Deny:
|
||||
return false
|
||||
default: // inherit
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/organization"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -24,6 +26,12 @@ func Identity(ctx context.Context) uint64 {
|
||||
return auth.GetIdentityFromContext(ctx).Identity()
|
||||
}
|
||||
|
||||
func Organization(ctx context.Context) *types.Organisation {
|
||||
return &types.Organisation{
|
||||
organization.GetFromContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *repository) With(ctx context.Context, db *factory.DB) *repository {
|
||||
return &repository{
|
||||
ctx: ctx,
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crusttech/crust/internal/rules"
|
||||
internalRules "github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/messaging/repository"
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ type (
|
||||
db db
|
||||
ctx context.Context
|
||||
|
||||
scopes rules.ScopeInterface
|
||||
scopes internalRules.ScopeInterface
|
||||
}
|
||||
|
||||
PermissionsService interface {
|
||||
@@ -22,11 +22,11 @@ type (
|
||||
|
||||
List() (interface{}, error)
|
||||
Get(teamID uint64, scope string, resource string) (interface{}, error)
|
||||
Set(teamID uint64, rules []rules.Rules) (interface{}, error)
|
||||
Set(teamID uint64, rules []internalRules.Rules) (interface{}, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Permissions(scopes rules.ScopeInterface) PermissionsService {
|
||||
func Permissions(scopes internalRules.ScopeInterface) PermissionsService {
|
||||
return (&permissions{
|
||||
scopes: scopes,
|
||||
}).With(context.Background())
|
||||
@@ -49,6 +49,6 @@ func (p *permissions) Get(teamID uint64, scope string, resource string) (interfa
|
||||
return nil, errors.New("service.permissions.get: not implemented")
|
||||
}
|
||||
|
||||
func (p *permissions) Set(teamID uint64, rules []rules.Rules) (interface{}, error) {
|
||||
func (p *permissions) Set(teamID uint64, rules []internalRules.Rules) (interface{}, error) {
|
||||
return nil, errors.New("service.permissions.set: not implemented")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
internalRules "github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/messaging/repository"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
systemTypes "github.com/crusttech/crust/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
rules struct {
|
||||
db db
|
||||
ctx context.Context
|
||||
|
||||
// identity is passed with context
|
||||
resources internalRules.ResourcesInterface
|
||||
team *systemTypes.Team
|
||||
org *types.Organisation
|
||||
}
|
||||
|
||||
RulesService interface {
|
||||
With(context.Context) RulesService
|
||||
|
||||
// Applies mostly to admin panel
|
||||
isAdmin() bool
|
||||
|
||||
// Individual rules for administration
|
||||
canManageOrganisation() bool
|
||||
canManageRoles() bool
|
||||
canManageChannels() bool
|
||||
|
||||
// types.Team derived from identity uses a wildcard match
|
||||
canManageWebhooks(ch *types.Channel) bool
|
||||
|
||||
// Messaging rules
|
||||
canSendMessages(ch *types.Channel) bool
|
||||
canEmbedLinks(ch *types.Channel) bool
|
||||
canAttachFiles(ch *types.Channel) bool
|
||||
canUpdateOwnMessages(ch *types.Channel) bool
|
||||
canUpdateMessages(ch *types.Channel) bool
|
||||
canReact(ch *types.Channel) bool
|
||||
}
|
||||
)
|
||||
|
||||
func Rules() RulesService {
|
||||
return (&rules{
|
||||
team: &systemTypes.Team{},
|
||||
}).With(context.Background())
|
||||
}
|
||||
|
||||
func (r *rules) With(ctx context.Context) RulesService {
|
||||
db := repository.DB(ctx)
|
||||
org := repository.Organization(ctx)
|
||||
return &rules{
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
org: org,
|
||||
team: r.team,
|
||||
|
||||
resources: internalRules.NewResources(ctx, db),
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: honor defaults from (org/team/channel).Permissions()
|
||||
|
||||
func (r *rules) isAdmin() bool {
|
||||
op := "admin"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canManageOrganisation() bool {
|
||||
op := "manage.organisation"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canManageRoles() bool {
|
||||
op := "manage.roles"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canManageChannels() bool {
|
||||
op := "manage.channels"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canManageWebhooks(ch *types.Channel) bool {
|
||||
op := "manage.webhooks"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canSendMessages(ch *types.Channel) bool {
|
||||
op := "message.send"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canEmbedLinks(ch *types.Channel) bool {
|
||||
op := "message.embed"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canAttachFiles(ch *types.Channel) bool {
|
||||
op := "message.attach"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canUpdateOwnMessages(ch *types.Channel) bool {
|
||||
op := "message.update_own"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canUpdateMessages(ch *types.Channel) bool {
|
||||
op := "message.update_all"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) canReact(ch *types.Channel) bool {
|
||||
op := "message.react"
|
||||
return r.hasAccess(op, r.org.PermissionDefault(op), r.org.Resource().String(), r.team.Resource().All(), ch.Resource().String())
|
||||
}
|
||||
|
||||
func (r *rules) hasAccess(operation string, value internalRules.Access, scopes ...string) bool {
|
||||
// reverse scopes from to order it from most-least significant
|
||||
// aka: [0]channel [1]teams [2]org
|
||||
last := len(scopes) - 1
|
||||
for i := 0; i < len(scopes)/2; i++ {
|
||||
scopes[i], scopes[last-i] = scopes[last-i], scopes[i]
|
||||
}
|
||||
|
||||
for _, scope := range scopes {
|
||||
if scope == "" {
|
||||
continue
|
||||
}
|
||||
switch r.resources.IsAllowed(scope, operation) {
|
||||
case internalRules.Allow:
|
||||
return true
|
||||
case internalRules.Deny:
|
||||
return false
|
||||
default: // inherit
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/crusttech/crust/internal/rules"
|
||||
internalRules "github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/internal/store"
|
||||
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
@@ -34,7 +34,7 @@ func Init() {
|
||||
log.Fatalf("Failed to initialize stor: %v", err)
|
||||
}
|
||||
|
||||
scopes := rules.NewScope()
|
||||
scopes := internalRules.NewScope()
|
||||
scopes.Add(&types.Organisation{})
|
||||
scopes.Add(&types.Team{})
|
||||
scopes.Add(&types.Channel{})
|
||||
|
||||
Reference in New Issue
Block a user