upd(messaging): add permission checking
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
type ResourcesInterface interface {
|
||||
With(ctx context.Context, db *factory.DB) ResourcesInterface
|
||||
|
||||
IsAllowed(resource string, operation string) Access
|
||||
Check(resource string, operation string) Access
|
||||
|
||||
Grant(roleID uint64, rules []Rule) error
|
||||
Read(roleID uint64) ([]Rule, error)
|
||||
|
||||
@@ -35,7 +35,7 @@ func (r *resources) identity() uint64 {
|
||||
|
||||
// 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) IsAllowed(resource string, operation string) Access {
|
||||
func (r *resources) Check(resource string, operation string) Access {
|
||||
parts := strings.Split(resource, delimiter)
|
||||
|
||||
// Permission check on global level is not allowed.
|
||||
|
||||
@@ -40,8 +40,8 @@ func TestRules(t *testing.T) {
|
||||
|
||||
// default (unset=deny), forbidden check ...:*
|
||||
{
|
||||
Expect(rules.Inherit, resources.IsAllowed("messaging:channel:1", "update"), "messaging:channel:1 update - Inherit")
|
||||
Expect(rules.Deny, resources.IsAllowed("messaging:channel:*", "update"), "messaging:channel:* update - Deny")
|
||||
Expect(rules.Inherit, resources.Check("messaging:channel:1", "update"), "messaging:channel:1 update - Inherit")
|
||||
Expect(rules.Deny, resources.Check("messaging:channel:*", "update"), "messaging:channel:* update - Deny")
|
||||
}
|
||||
|
||||
// allow messaging:channel:2 update,delete
|
||||
@@ -53,9 +53,9 @@ func TestRules(t *testing.T) {
|
||||
err := resources.Grant(roleID, list)
|
||||
NoError(t, err, "expect no error")
|
||||
|
||||
Expect(rules.Inherit, resources.IsAllowed("messaging:channel:1", "update"), "messaging:channel:1 update - Inherit")
|
||||
Expect(rules.Allow, resources.IsAllowed("messaging:channel:2", "update"), "messaging:channel:2 update - Allow")
|
||||
Expect(rules.Deny, resources.IsAllowed("messaging:channel:*", "update"), "messaging:channel:* update - Deny")
|
||||
Expect(rules.Inherit, resources.Check("messaging:channel:1", "update"), "messaging:channel:1 update - Inherit")
|
||||
Expect(rules.Allow, resources.Check("messaging:channel:2", "update"), "messaging:channel:2 update - Allow")
|
||||
Expect(rules.Deny, resources.Check("messaging:channel:*", "update"), "messaging:channel:* update - Deny")
|
||||
}
|
||||
|
||||
// list grants for test role
|
||||
@@ -79,9 +79,9 @@ func TestRules(t *testing.T) {
|
||||
err := resources.Grant(roleID, list)
|
||||
NoError(t, err, "expect no error")
|
||||
|
||||
Expect(rules.Deny, resources.IsAllowed("messaging:channel:1", "update"), "messaging:channel:1 update - Deny")
|
||||
Expect(rules.Allow, resources.IsAllowed("messaging:channel:2", "update"), "messaging:channel:2 update - Allow")
|
||||
Expect(rules.Deny, resources.IsAllowed("messaging:channel:*", "update"), "messaging:channel:* update - Deny")
|
||||
Expect(rules.Deny, resources.Check("messaging:channel:1", "update"), "messaging:channel:1 update - Deny")
|
||||
Expect(rules.Allow, resources.Check("messaging:channel:2", "update"), "messaging:channel:2 update - Allow")
|
||||
Expect(rules.Deny, resources.Check("messaging:channel:*", "update"), "messaging:channel:* update - Deny")
|
||||
}
|
||||
|
||||
// reset messaging:channel:1, messaging:channel:2
|
||||
@@ -95,8 +95,8 @@ func TestRules(t *testing.T) {
|
||||
err := resources.Grant(roleID, list)
|
||||
NoError(t, err, "expect no error")
|
||||
|
||||
Expect(rules.Inherit, resources.IsAllowed("messaging:channel:1", "update"), "messaging:channel:1 update - Inherit")
|
||||
Expect(rules.Inherit, resources.IsAllowed("messaging:channel:2", "update"), "messaging:channel:2 update - Inherit")
|
||||
Expect(rules.Inherit, resources.Check("messaging:channel:1", "update"), "messaging:channel:1 update - Inherit")
|
||||
Expect(rules.Inherit, resources.Check("messaging:channel:2", "update"), "messaging:channel:2 update - Inherit")
|
||||
}
|
||||
|
||||
// [messaging:channel:*,update] - allow, [messaging:channel:1, deny]
|
||||
@@ -110,8 +110,8 @@ func TestRules(t *testing.T) {
|
||||
err := resources.Grant(roleID, list)
|
||||
NoError(t, err, "expected no error")
|
||||
|
||||
Expect(rules.Deny, resources.IsAllowed("messaging:channel:1", "update"), "messaging:channel:1 update - Deny")
|
||||
Expect(rules.Allow, resources.IsAllowed("messaging:channel:2", "update"), "messaging:channel:2 update - Allow")
|
||||
Expect(rules.Deny, resources.Check("messaging:channel:1", "update"), "messaging:channel:1 update - Deny")
|
||||
Expect(rules.Allow, resources.Check("messaging:channel:2", "update"), "messaging:channel:2 update - Allow")
|
||||
}
|
||||
|
||||
// list all by roleID
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func MountRoutes() func(chi.Router) {
|
||||
// Initialize handers & controllers.
|
||||
// Initialize handlers & controllers.
|
||||
return func(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.MiddlewareValidOnly404)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/organization"
|
||||
"github.com/crusttech/crust/messaging/repository"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
@@ -19,7 +20,9 @@ type (
|
||||
ctx context.Context
|
||||
|
||||
usr systemService.UserService
|
||||
|
||||
evl EventService
|
||||
prm PermissionsService
|
||||
|
||||
channel repository.ChannelRepository
|
||||
cmember repository.ChannelMemberRepository
|
||||
@@ -52,10 +55,11 @@ type (
|
||||
Undelete(ID uint64) (*types.Channel, error)
|
||||
RecordView(userID, channelID, lastMessageID uint64) error
|
||||
}
|
||||
)
|
||||
|
||||
// channelSecurity interface {
|
||||
// CanRead(ch *types.Channel) bool
|
||||
// }
|
||||
var (
|
||||
ErrUnknownChannelType = errors.New("Unknown ChannelType")
|
||||
ErrNoPermission = errors.New("No permissions")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -67,6 +71,7 @@ func Channel() ChannelService {
|
||||
return (&channel{
|
||||
usr: systemService.DefaultUser,
|
||||
evl: DefaultEvent,
|
||||
prm: DefaultPermissions,
|
||||
}).With(context.Background())
|
||||
}
|
||||
|
||||
@@ -78,6 +83,7 @@ func (svc *channel) With(ctx context.Context) ChannelService {
|
||||
|
||||
usr: svc.usr.With(ctx),
|
||||
evl: svc.evl.With(ctx),
|
||||
prm: svc.prm.With(ctx),
|
||||
|
||||
channel: repository.Channel(ctx, db),
|
||||
cmember: repository.ChannelMember(ctx, db),
|
||||
@@ -197,13 +203,10 @@ func (svc *channel) FindMembers(channelID uint64) (out types.ChannelMemberSet, e
|
||||
}
|
||||
|
||||
func (svc *channel) Create(in *types.Channel) (out *types.Channel, err error) {
|
||||
// @todo: [SECURITY] permission check if user can add channel
|
||||
|
||||
return out, svc.db.Transaction(func() (err error) {
|
||||
var msg *types.Message
|
||||
|
||||
// @todo get organisation from somewhere
|
||||
var organisationID uint64 = 0
|
||||
var organisationID = organization.Crust().ID
|
||||
|
||||
var chCreatorID = repository.Identity(svc.ctx)
|
||||
|
||||
@@ -220,23 +223,19 @@ func (svc *channel) Create(in *types.Channel) (out *types.Channel, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// @todo [SECURITY] check if channel topic can be set
|
||||
if in.Topic != "" && false {
|
||||
if in.Topic != "" && svc.prm.CanUpdate(in) {
|
||||
return errors.New("Not allowed to set channel topic")
|
||||
}
|
||||
|
||||
// @todo [SECURITY] check if user can create public channels
|
||||
if in.Type == types.ChannelTypePublic && false {
|
||||
if in.Type == types.ChannelTypePublic && svc.prm.CanCreatePublicChannel() {
|
||||
return errors.New("Not allowed to create public channels")
|
||||
}
|
||||
|
||||
// @todo [SECURITY] check if user can create private channels
|
||||
if in.Type == types.ChannelTypePrivate && false {
|
||||
return errors.New("Not allowed to create public channels")
|
||||
if in.Type == types.ChannelTypePrivate && svc.prm.CanCreatePrivateChannel() {
|
||||
return errors.New("Not allowed to create private channels")
|
||||
}
|
||||
|
||||
// @todo [SECURITY] check if user can create private channels
|
||||
if in.Type == types.ChannelTypeGroup && false {
|
||||
if in.Type == types.ChannelTypeGroup && svc.prm.CanCreateDirectChannel() {
|
||||
return errors.New("Not allowed to create group channels")
|
||||
}
|
||||
|
||||
|
||||
158
messaging/service/permissions.go
Normal file
158
messaging/service/permissions.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
internalRules "github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/messaging/repository"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
)
|
||||
|
||||
type (
|
||||
permissions struct {
|
||||
db db
|
||||
ctx context.Context
|
||||
|
||||
prm systemService.PermissionsService
|
||||
}
|
||||
|
||||
// Fallback option
|
||||
Fallback func() bool
|
||||
|
||||
PermissionsService interface {
|
||||
With(context.Context) PermissionsService
|
||||
|
||||
CanAccessMessaging() bool
|
||||
CanGrantMessaging() bool
|
||||
CanCreatePublicChannel() bool
|
||||
CanCreatePrivateChannel() bool
|
||||
CanCreateDirectChannel() bool
|
||||
|
||||
CanUpdate(ch *types.Channel) bool
|
||||
CanRead(ch *types.Channel) bool
|
||||
CanJoin(ch *types.Channel) bool
|
||||
CanLeave(ch *types.Channel) bool
|
||||
|
||||
CanManageMembers(ch *types.Channel) bool
|
||||
CanManageWebhooks(ch *types.Channel) bool
|
||||
CanManageAttachments(ch *types.Channel) bool
|
||||
|
||||
CanSendMessage(ch *types.Channel) bool
|
||||
CanReplyMessage(ch *types.Channel) bool
|
||||
CanEmbedMessage(ch *types.Channel) bool
|
||||
CanAttachMessage(ch *types.Channel) bool
|
||||
CanUpdateOwnMessages(ch *types.Channel) bool
|
||||
CanUpdateMessages(ch *types.Channel) bool
|
||||
CanReactMessage(ch *types.Channel) bool
|
||||
}
|
||||
)
|
||||
|
||||
func Permissions() PermissionsService {
|
||||
return (&permissions{
|
||||
prm: systemService.Permissions(),
|
||||
}).With(context.Background())
|
||||
}
|
||||
|
||||
func (p *permissions) With(ctx context.Context) PermissionsService {
|
||||
db := repository.DB(ctx)
|
||||
return &permissions{
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
|
||||
prm: p.prm.With(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *permissions) CanAccessMessaging() bool {
|
||||
return p.checkAccess("messaging", "access")
|
||||
}
|
||||
|
||||
func (p *permissions) CanGrantMessaging() bool {
|
||||
return p.checkAccess("messaging", "grant")
|
||||
}
|
||||
|
||||
func (p *permissions) CanCreatePublicChannel() bool {
|
||||
return p.checkAccess("messaging", "channel.public.create")
|
||||
}
|
||||
|
||||
func (p *permissions) CanCreatePrivateChannel() bool {
|
||||
return p.checkAccess("messaging", "channel.private.create")
|
||||
}
|
||||
|
||||
func (p *permissions) CanCreateDirectChannel() bool {
|
||||
return p.checkAccess("messaging", "channel.direct.create")
|
||||
}
|
||||
|
||||
func (p *permissions) CanUpdate(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "update")
|
||||
}
|
||||
|
||||
func (p *permissions) CanRead(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "read")
|
||||
}
|
||||
|
||||
func (p *permissions) CanJoin(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "join")
|
||||
}
|
||||
|
||||
func (p *permissions) CanLeave(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "leave")
|
||||
}
|
||||
|
||||
func (p *permissions) CanManageMembers(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "members.manage")
|
||||
}
|
||||
|
||||
func (p *permissions) CanManageWebhooks(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "webhooks.manage")
|
||||
}
|
||||
|
||||
func (p *permissions) CanManageAttachments(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "attachments.manage")
|
||||
}
|
||||
|
||||
func (p *permissions) CanSendMessage(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.send")
|
||||
}
|
||||
|
||||
func (p *permissions) CanReplyMessage(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.reply")
|
||||
}
|
||||
|
||||
func (p *permissions) CanEmbedMessage(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.embed")
|
||||
}
|
||||
|
||||
func (p *permissions) CanAttachMessage(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.attach")
|
||||
}
|
||||
|
||||
func (p *permissions) CanUpdateOwnMessages(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.update.own")
|
||||
}
|
||||
|
||||
func (p *permissions) CanUpdateMessages(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.update.all")
|
||||
}
|
||||
|
||||
func (p *permissions) CanReactMessage(ch *types.Channel) bool {
|
||||
return p.checkAccess(ch.Resource().String(), "message.react")
|
||||
}
|
||||
|
||||
func (p *permissions) checkAccess(resource string, operation string, fbs ...Fallback) bool {
|
||||
access := p.prm.Check(resource, operation)
|
||||
switch access {
|
||||
case internalRules.Allow:
|
||||
return true
|
||||
case internalRules.Deny:
|
||||
return false
|
||||
default:
|
||||
for _, fb := range fbs {
|
||||
if fb() == true {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
115
messaging/service/permissions_test.go
Normal file
115
messaging/service/permissions_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/rules"
|
||||
. "github.com/crusttech/crust/internal/test"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
systemTypes "github.com/crusttech/crust/system/types"
|
||||
)
|
||||
|
||||
func TestPermissions(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
// Create user with role and add it to context.
|
||||
userSvc := systemService.User().With(ctx)
|
||||
user := &systemTypes.User{
|
||||
Name: "John Doe",
|
||||
Username: "johndoe",
|
||||
SatosaID: "1234",
|
||||
}
|
||||
err := user.GeneratePassword("johndoe")
|
||||
NoError(t, err, "expected no error generating password, got %v", err)
|
||||
|
||||
_, err = userSvc.Create(user)
|
||||
NoError(t, err, "expected no error creating user, got %v", err)
|
||||
|
||||
roleSvc := systemService.Role().With(ctx)
|
||||
role := &systemTypes.Role{
|
||||
Name: "Test role v1",
|
||||
}
|
||||
role, err = roleSvc.Create(role)
|
||||
NoError(t, err, "expected no error creating role, got %v", err)
|
||||
|
||||
err = roleSvc.MemberAdd(role.ID, user.ID)
|
||||
NoError(t, err, "expected no error adding user to role, got %v", err)
|
||||
|
||||
// Set Identity.
|
||||
ctx = auth.SetIdentityToContext(ctx, user)
|
||||
|
||||
// Generate services.
|
||||
channelSvc := Channel().With(ctx)
|
||||
permissionsSvc := Permissions().With(ctx)
|
||||
systemPermissionSvc := systemService.Permissions().With(ctx)
|
||||
|
||||
// Test `access` to messaging service.
|
||||
ret := permissionsSvc.CanAccessMessaging()
|
||||
Assert(t, ret == false, "expected CanAccessMessaging == false, got %v", ret)
|
||||
|
||||
// Add `access` to messaging service.
|
||||
list := []rules.Rule{
|
||||
rules.Rule{Resource: "messaging", Operation: "access", Value: rules.Allow},
|
||||
}
|
||||
_, err = systemPermissionSvc.Update(role.ID, list)
|
||||
NoError(t, err, "expected no error, got %v", err)
|
||||
|
||||
// Test `access` to messaging service.
|
||||
ret = permissionsSvc.CanAccessMessaging()
|
||||
Assert(t, ret == true, "expected CanAccessMessaging == true, got %v", ret)
|
||||
|
||||
// Create test channel.
|
||||
ch := &types.Channel{
|
||||
Name: "TestChan",
|
||||
Topic: "No topic",
|
||||
}
|
||||
ch, err = channelSvc.Create(ch)
|
||||
NoError(t, err, "expected no error, got %v", err)
|
||||
|
||||
// @Todo: add permission for create channel and test it.
|
||||
|
||||
// Test CanRead permissions. [1 - no permission, 2 - allow]
|
||||
{
|
||||
ret = permissionsSvc.CanRead(ch)
|
||||
Assert(t, ret == false, "expected CanRead == false, got %v")
|
||||
|
||||
// Add [messaging:channel:*, read, allow]
|
||||
list = []rules.Rule{
|
||||
rules.Rule{Resource: "messaging:channel:*", Operation: "read", Value: rules.Allow},
|
||||
}
|
||||
_, err = systemPermissionSvc.Update(role.ID, list)
|
||||
NoError(t, err, "expected no error, got %v", err)
|
||||
|
||||
ret = permissionsSvc.CanRead(ch)
|
||||
Assert(t, ret == true, "expected CanRead == true, got %v")
|
||||
}
|
||||
|
||||
// Test CanJoin permissions [1 - deny, 2 - allow for resourceID]
|
||||
{
|
||||
// Add [messaging:channel:*, join, deny]
|
||||
list = []rules.Rule{
|
||||
rules.Rule{Resource: "messaging:channel:*", Operation: "join", Value: rules.Deny},
|
||||
}
|
||||
_, err = systemPermissionSvc.Update(role.ID, list)
|
||||
NoError(t, err, "expected no error, got %v", err)
|
||||
|
||||
ret = permissionsSvc.CanJoin(ch)
|
||||
Assert(t, ret == false, "expected CanJoin == false, got %v")
|
||||
|
||||
// Add [messaging:channel:ID, join, allow]
|
||||
list = []rules.Rule{
|
||||
rules.Rule{Resource: ch.Resource().String(), Operation: "join", Value: rules.Allow},
|
||||
}
|
||||
_, err = systemPermissionSvc.Update(role.ID, list)
|
||||
NoError(t, err, "expected no error, got %v", err)
|
||||
|
||||
ret = permissionsSvc.CanJoin(ch)
|
||||
Assert(t, ret == true, "expected CanJoin == true, got %v")
|
||||
}
|
||||
|
||||
// Remove test channel.
|
||||
channelSvc.Delete(ch.ID)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
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
|
||||
role *systemTypes.Role
|
||||
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
|
||||
|
||||
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{
|
||||
role: &systemTypes.Role{},
|
||||
}).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,
|
||||
role: r.role,
|
||||
|
||||
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.role.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.role.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.role.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.role.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.role.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.role.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.role.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,10 +5,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
internalRules "github.com/crusttech/crust/internal/rules"
|
||||
"github.com/crusttech/crust/internal/store"
|
||||
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -18,12 +15,13 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
o sync.Once
|
||||
DefaultAttachment AttachmentService
|
||||
DefaultChannel ChannelService
|
||||
DefaultMessage MessageService
|
||||
DefaultPubSub *pubSub
|
||||
DefaultEvent EventService
|
||||
o sync.Once
|
||||
DefaultAttachment AttachmentService
|
||||
DefaultChannel ChannelService
|
||||
DefaultMessage MessageService
|
||||
DefaultPubSub *pubSub
|
||||
DefaultEvent EventService
|
||||
DefaultPermissions PermissionsService
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -33,11 +31,7 @@ func Init() {
|
||||
log.Fatalf("Failed to initialize stor: %v", err)
|
||||
}
|
||||
|
||||
scopes := internalRules.NewScope()
|
||||
scopes.Add(&types.Organisation{})
|
||||
scopes.Add(&types.Role{})
|
||||
scopes.Add(&types.Channel{})
|
||||
|
||||
DefaultPermissions = Permissions()
|
||||
DefaultEvent = Event()
|
||||
DefaultAttachment = Attachment(fs)
|
||||
DefaultMessage = Message()
|
||||
|
||||
@@ -58,7 +58,8 @@ type (
|
||||
// Resource returns a system resource ID for this type
|
||||
func (r *Channel) Resource() rules.Resource {
|
||||
resource := rules.Resource{
|
||||
Scope: "channel",
|
||||
Service: "messaging",
|
||||
Scope: "channel",
|
||||
}
|
||||
if r != nil {
|
||||
resource.ID = r.ID
|
||||
|
||||
@@ -21,7 +21,7 @@ type (
|
||||
|
||||
func (Permissions) New() *Permissions {
|
||||
ctrl := &Permissions{}
|
||||
ctrl.svc.perm = service.DefaultPermission
|
||||
ctrl.svc.perm = service.DefaultPermissions
|
||||
return ctrl
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,16 @@ type (
|
||||
With(ctx context.Context) PermissionsService
|
||||
|
||||
List() (interface{}, error)
|
||||
|
||||
Check(resource string, operation string) rules.Access
|
||||
|
||||
Read(roleID uint64) (interface{}, error)
|
||||
Update(roleID uint64, rules []rules.Rule) (interface{}, error)
|
||||
Delete(roleID uint64) (interface{}, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Permission() PermissionsService {
|
||||
func Permissions() PermissionsService {
|
||||
return (&permissions{}).With(context.Background())
|
||||
}
|
||||
|
||||
@@ -50,6 +53,10 @@ func (p *permissions) List() (interface{}, error) {
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
func (p *permissions) Check(resource string, operation string) rules.Access {
|
||||
return p.resources.Check(resource, operation)
|
||||
}
|
||||
|
||||
func (p *permissions) Read(roleID uint64) (interface{}, error) {
|
||||
return p.resources.Read(roleID)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestPermission(t *testing.T) {
|
||||
ctx = internalAuth.SetIdentityToContext(ctx, user)
|
||||
|
||||
// Create permission service.
|
||||
permissionSvc := Permission().With(ctx)
|
||||
permissionSvc := Permissions().With(ctx)
|
||||
|
||||
// Update rules for test role.
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ var (
|
||||
DefaultAuth AuthService
|
||||
DefaultUser UserService
|
||||
DefaultRole RoleService
|
||||
DefaultPermission PermissionsService
|
||||
DefaultPermissions PermissionsService
|
||||
DefaultOrganisation OrganisationService
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ func Init() {
|
||||
DefaultAuth = Auth()
|
||||
DefaultUser = User()
|
||||
DefaultRole = Role()
|
||||
DefaultPermission = Permission()
|
||||
DefaultPermissions = Permissions()
|
||||
DefaultOrganisation = Organisation()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ type (
|
||||
// Resource returns a system resource ID for this type
|
||||
func (r *Organisation) Resource() rules.Resource {
|
||||
resource := rules.Resource{
|
||||
Scope: "organisation",
|
||||
Service: "system",
|
||||
Scope: "organisation",
|
||||
}
|
||||
if r != nil {
|
||||
resource.ID = r.ID
|
||||
|
||||
@@ -26,7 +26,8 @@ type (
|
||||
// Resource returns a system resource ID for this type
|
||||
func (r *Role) Resource() rules.Resource {
|
||||
resource := rules.Resource{
|
||||
Scope: "team",
|
||||
Service: "system",
|
||||
Scope: "role",
|
||||
}
|
||||
if r != nil {
|
||||
resource.ID = r.ID
|
||||
|
||||
Reference in New Issue
Block a user