3
0
Files
corteza/system/service/role.go
2020-02-18 09:11:48 +01:00

436 lines
9.6 KiB
Go

package service
import (
"context"
"strconv"
"github.com/pkg/errors"
"github.com/titpetric/factory"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/cortezaproject/corteza-server/pkg/eventbus"
"github.com/cortezaproject/corteza-server/pkg/handle"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/pkg/permissions"
"github.com/cortezaproject/corteza-server/system/repository"
"github.com/cortezaproject/corteza-server/system/service/event"
"github.com/cortezaproject/corteza-server/system/types"
)
const (
ErrRoleNameNotUnique = serviceError("RoleNameNotUnique")
ErrRoleHandleNotUnique = serviceError("RoleHandleNotUnique")
)
type (
role struct {
db *factory.DB
ctx context.Context
logger *zap.Logger
ac roleAccessController
eventbus eventDispatcher
user UserService
role repository.RoleRepository
}
roleAccessController interface {
CanAccess(context.Context) bool
CanCreateRole(context.Context) bool
CanReadRole(context.Context, *types.Role) bool
CanUpdateRole(context.Context, *types.Role) bool
CanDeleteRole(context.Context, *types.Role) bool
CanManageRoleMembers(context.Context, *types.Role) bool
FilterReadableRoles(ctx context.Context) *permissions.ResourceFilter
}
RoleService interface {
With(ctx context.Context) RoleService
FindByID(roleID uint64) (*types.Role, error)
FindByName(name string) (*types.Role, error)
FindByHandle(handle string) (*types.Role, error)
FindByAny(identifier interface{}) (*types.Role, error)
Find(types.RoleFilter) (types.RoleSet, types.RoleFilter, error)
Create(role *types.Role) (*types.Role, error)
Update(role *types.Role) (*types.Role, error)
Merge(roleID, targetroleID uint64) error
Move(roleID, organisationID uint64) error
Archive(ID uint64) error
Unarchive(ID uint64) error
Delete(ID uint64) error
Undelete(ID uint64) error
Membership(userID uint64) ([]*types.RoleMember, error)
MemberList(roleID uint64) ([]*types.RoleMember, error)
MemberAdd(roleID, userID uint64) error
MemberRemove(roleID, userID uint64) error
}
)
func Role(ctx context.Context) RoleService {
return (&role{
ac: DefaultAccessControl,
eventbus: eventbus.Service(),
logger: DefaultLogger.Named("role"),
user: DefaultUser.With(ctx),
}).With(ctx)
}
func (svc role) With(ctx context.Context) RoleService {
db := repository.DB(ctx)
return &role{
db: db,
ctx: ctx,
logger: svc.logger,
ac: svc.ac,
eventbus: svc.eventbus,
user: svc.user,
role: repository.Role(ctx, db),
}
}
func (svc role) log(ctx context.Context, fields ...zapcore.Field) *zap.Logger {
return logger.AddRequestID(ctx, svc.logger).With(fields...)
}
func (svc role) FindByID(roleID uint64) (*types.Role, error) {
return svc.findByID(roleID)
}
func (svc role) findByID(roleID uint64) (*types.Role, error) {
if roleID == 0 {
return nil, ErrInvalidID.withStack()
}
role, err := svc.role.FindByID(roleID)
if err != nil {
return nil, err
}
if !svc.ac.CanReadRole(svc.ctx, role) {
return nil, ErrNoPermissions.withStack()
}
return role, nil
}
func (svc role) Find(f types.RoleFilter) (types.RoleSet, types.RoleFilter, error) {
f.IsReadable = svc.ac.FilterReadableRoles(svc.ctx)
if f.Deleted > 0 {
// If list with deleted or suspended users is requested
// user must have access permissions to system (ie: is admin)
//
// not the best solution but ATM it allows us to have at least
// some kind of control over who can see deleted or archived roles
if !svc.ac.CanAccess(svc.ctx) {
return nil, f, ErrNoPermissions.withStack()
}
}
return svc.role.Find(f)
}
func (svc role) FindByName(rolename string) (*types.Role, error) {
return svc.role.FindByName(rolename)
}
func (svc role) FindByHandle(handle string) (*types.Role, error) {
return svc.role.FindByHandle(handle)
}
// FindByAny finds role by given identifier (id, handle, name)
func (svc role) FindByAny(identifier interface{}) (r *types.Role, err error) {
if ID, ok := identifier.(uint64); ok {
r, err = svc.FindByID(ID)
} else if strIdentifier, ok := identifier.(string); ok {
if ID, _ := strconv.ParseUint(strIdentifier, 10, 64); ID > 0 {
r, err = svc.FindByID(ID)
} else {
r, err = svc.FindByHandle(strIdentifier)
if err == nil && r.ID == 0 {
r, err = svc.FindByName(strIdentifier)
}
}
} else {
err = ErrInvalidID.withStack()
}
if err != nil {
return
}
return
}
func (svc role) Create(new *types.Role) (r *types.Role, err error) {
if !handle.IsValid(new.Handle) {
return nil, ErrInvalidHandle
}
if !svc.ac.CanCreateRole(svc.ctx) {
return nil, ErrNoCreatePermissions.withStack()
}
return r, svc.db.Transaction(func() (err error) {
if err = svc.eventbus.WaitFor(svc.ctx, event.RoleBeforeCreate(new, r)); err != nil {
return
}
if err = svc.UniqueCheck(new); err != nil {
return
}
if r, err = svc.role.Create(new); err != nil {
return
}
defer svc.eventbus.Dispatch(svc.ctx, event.RoleAfterCreate(new, r))
return
})
}
func (svc role) Update(upd *types.Role) (r *types.Role, err error) {
if upd.ID == 0 {
return nil, ErrInvalidID.withStack()
}
if !handle.IsValid(upd.Handle) {
return nil, ErrInvalidHandle.withStack()
}
if !svc.ac.CanUpdateRole(svc.ctx, upd) {
return nil, ErrNoUpdatePermissions.withStack()
}
return r, svc.db.Transaction(func() (err error) {
if r, err = svc.role.FindByID(upd.ID); err != nil {
return
}
if err = svc.eventbus.WaitFor(svc.ctx, event.RoleBeforeUpdate(upd, r)); err != nil {
return
}
if err = svc.UniqueCheck(upd); err != nil {
return
}
// Assign changed values
r.Name = upd.Name
r.Handle = upd.Handle
if r, err = svc.role.Update(r); err != nil {
return err
}
defer svc.eventbus.Dispatch(svc.ctx, event.RoleAfterUpdate(upd, r))
return nil
})
}
func (svc role) UniqueCheck(r *types.Role) (err error) {
if r.Handle != "" {
if ex, _ := svc.role.FindByHandle(r.Handle); ex != nil && ex.ID > 0 && ex.ID != r.ID {
return ErrRoleHandleNotUnique
}
}
if r.Name != "" {
if ex, _ := svc.role.FindByName(r.Name); ex != nil && ex.ID > 0 && ex.ID != r.ID {
return ErrRoleNameNotUnique
}
}
return nil
}
func (svc role) Delete(roleID uint64) (err error) {
var (
role *types.Role
)
if role, err = svc.findByID(roleID); err != nil {
return err
}
if !svc.ac.CanDeleteRole(svc.ctx, role) {
return ErrNoPermissions.withStack()
}
if err = svc.eventbus.WaitFor(svc.ctx, event.RoleBeforeDelete(nil, role)); err != nil {
return
}
if err = svc.role.DeleteByID(roleID); err != nil {
return
}
defer svc.eventbus.Dispatch(svc.ctx, event.RoleAfterDelete(nil, role))
return
}
func (svc role) Undelete(roleID uint64) error {
role, err := svc.findByID(roleID)
if err != nil {
return err
}
if !svc.ac.CanDeleteRole(svc.ctx, role) {
return ErrNoPermissions.withStack()
}
return svc.role.UndeleteByID(roleID)
}
func (svc role) Archive(roleID uint64) error {
role, err := svc.findByID(roleID)
if err != nil {
return err
}
if !svc.ac.CanUpdateRole(svc.ctx, role) {
return ErrNoPermissions.withStack()
}
return svc.role.ArchiveByID(roleID)
}
func (svc role) Unarchive(roleID uint64) error {
role, err := svc.findByID(roleID)
if err != nil {
return err
}
if !svc.ac.CanUpdateRole(svc.ctx, role) {
return ErrNoPermissions.withStack()
}
return svc.role.UnarchiveByID(roleID)
}
func (svc role) Merge(roleID, targetRoleID uint64) error {
role, err := svc.findByID(roleID)
if err != nil {
return err
}
if targetRoleID == 0 {
return ErrInvalidID.withStack()
}
if !svc.ac.CanUpdateRole(svc.ctx, role) {
return ErrNoPermissions.withStack()
}
return svc.role.MergeByID(roleID, targetRoleID)
}
func (svc role) Move(roleID, targetOrganisationID uint64) error {
role, err := svc.findByID(roleID)
if err != nil {
return err
}
if targetOrganisationID == 0 {
return ErrInvalidID.withStack()
}
if !svc.ac.CanUpdateRole(svc.ctx, role) {
return ErrNoPermissions.withStack()
}
return svc.role.MoveByID(roleID, targetOrganisationID)
}
func (svc role) Membership(userID uint64) ([]*types.RoleMember, error) {
return svc.role.MembershipsFindByUserID(userID)
}
func (svc role) MemberList(roleID uint64) ([]*types.RoleMember, error) {
if roleID == permissions.EveryoneRoleID {
return nil, ErrInvalidID.withStack()
}
_, err := svc.findByID(roleID)
if err != nil {
return nil, err
}
return svc.role.MemberFindByRoleID(roleID)
}
func (svc role) MemberAdd(roleID, userID uint64) (err error) {
var (
role *types.Role
user *types.User
)
if role, err = svc.findByID(roleID); err != nil {
return
}
if user, err = svc.user.FindByID(userID); err != nil {
return
}
if err = svc.eventbus.WaitFor(svc.ctx, event.RoleMemberBeforeAdd(user, role)); err != nil {
return
}
if !svc.ac.CanManageRoleMembers(svc.ctx, role) {
return errors.New("Not allowed to manage role members")
}
if err = svc.role.MemberAddByID(role.ID, user.ID); err != nil {
return
}
defer svc.eventbus.Dispatch(svc.ctx, event.RoleMemberAfterAdd(user, role))
return nil
}
func (svc role) MemberRemove(roleID, userID uint64) (err error) {
var (
role *types.Role
user *types.User
)
if role, err = svc.findByID(roleID); err != nil {
return
}
if user, err = svc.user.FindByID(userID); err != nil {
return
}
if err = svc.eventbus.WaitFor(svc.ctx, event.RoleMemberBeforeRemove(user, role)); err != nil {
return
}
if !svc.ac.CanManageRoleMembers(svc.ctx, role) {
return errors.New("Not allowed to manage role members")
}
if err = svc.role.MemberRemoveByID(role.ID, user.ID); err != nil {
return
}
defer svc.eventbus.Dispatch(svc.ctx, event.RoleMemberAfterRemove(user, role))
return nil
}
var _ RoleService = &role{}