From 05006f7dd83c135840d2543ccbc5c225c04dc3a2 Mon Sep 17 00:00:00 2001 From: Mitja Zivkovic Date: Sat, 9 Mar 2019 22:13:04 +0100 Subject: [PATCH] add(system) permissions helper service and checks --- system/repository/role.go | 12 ++--- system/service/permissions.go | 83 +++++++++++++++++++++++++++++++++++ system/service/role.go | 78 ++++++++++++++++++++++++-------- system/service/service.go | 2 + 4 files changed, 151 insertions(+), 24 deletions(-) create mode 100644 system/service/permissions.go diff --git a/system/repository/role.go b/system/repository/role.go index 401868ed3..d4a415420 100644 --- a/system/repository/role.go +++ b/system/repository/role.go @@ -28,8 +28,8 @@ type ( MoveByID(id, targetOrganisationID uint64) error MemberFindByRoleID(roleID uint64) ([]*types.RoleMember, error) - MemberAddByID(id, userID uint64) error - MemberRemoveByID(id, userID uint64) error + MemberAddByID(roleID, userID uint64) error + MemberRemoveByID(roleID, userID uint64) error } role struct { @@ -147,17 +147,17 @@ func (r *role) MemberFindByRoleID(roleID uint64) (mm []*types.RoleMember, err er return rval, r.db().Select(&rval, sql, roleID) } -func (r *role) MemberAddByID(id, userID uint64) error { +func (r *role) MemberAddByID(roleID, userID uint64) error { mod := &types.RoleMember{ - RoleID: id, + RoleID: roleID, UserID: userID, } return r.db().Replace(r.members, mod) } -func (r *role) MemberRemoveByID(id, userID uint64) error { +func (r *role) MemberRemoveByID(roleID, userID uint64) error { mod := &types.RoleMember{ - RoleID: id, + RoleID: roleID, UserID: userID, } return r.db().Delete(r.members, mod, "rel_role", "rel_user") diff --git a/system/service/permissions.go b/system/service/permissions.go new file mode 100644 index 000000000..02835bc29 --- /dev/null +++ b/system/service/permissions.go @@ -0,0 +1,83 @@ +package service + +import ( + "context" + + internalRules "github.com/crusttech/crust/internal/rules" + "github.com/crusttech/crust/system/repository" + "github.com/crusttech/crust/system/types" +) + +type ( + permissions struct { + db db + ctx context.Context + + rules RulesService + } + + PermissionsService interface { + With(context.Context) PermissionsService + + CanCreateOrganisation() bool + CanCreateRole() bool + CanCreateApplication() bool + + CanReadRole(rl *types.Role) bool + CanUpdateRole(rl *types.Role) bool + CanDeleteRole(rl *types.Role) bool + CanManageRoleMembers(rl *types.Role) bool + } +) + +func Permissions() PermissionsService { + return (&permissions{ + rules: DefaultRules, + }).With(context.Background()) +} + +func (p *permissions) With(ctx context.Context) PermissionsService { + db := repository.DB(ctx) + return &permissions{ + db: db, + ctx: ctx, + + rules: p.rules.With(ctx), + } +} + +func (p *permissions) CanCreateOrganisation() bool { + return p.checkAccess("system", "application.create") +} + +func (p *permissions) CanCreateRole() bool { + return p.checkAccess("system", "role.create") +} + +func (p *permissions) CanCreateApplication() bool { + return p.checkAccess("system", "application.create") +} + +func (p *permissions) CanReadRole(rl *types.Role) bool { + return p.checkAccess(rl.Resource().String(), "read") +} + +func (p *permissions) CanUpdateRole(rl *types.Role) bool { + return p.checkAccess(rl.Resource().String(), "update") +} + +func (p *permissions) CanDeleteRole(rl *types.Role) bool { + return p.checkAccess(rl.Resource().String(), "delete") +} + +func (p *permissions) CanManageRoleMembers(rl *types.Role) bool { + return p.checkAccess(rl.Resource().String(), "members.manage") +} + +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 +} diff --git a/system/service/role.go b/system/service/role.go index 1d6837799..970d478af 100644 --- a/system/service/role.go +++ b/system/service/role.go @@ -3,6 +3,7 @@ package service import ( "context" + "github.com/pkg/errors" "github.com/titpetric/factory" "github.com/crusttech/crust/system/repository" @@ -14,6 +15,8 @@ type ( db *factory.DB ctx context.Context + prm PermissionsService + role repository.RoleRepository } @@ -39,36 +42,62 @@ type ( ) func Role() RoleService { - return (&role{}).With(context.Background()) + return (&role{ + prm: DefaultPermissions, + }).With(context.Background()) } func (svc *role) With(ctx context.Context) RoleService { db := repository.DB(ctx) return &role{ - db: db, - ctx: ctx, + db: db, + ctx: ctx, + + prm: svc.prm.With(ctx), + role: repository.Role(ctx, db), } } func (svc *role) FindByID(id uint64) (*types.Role, error) { - // @todo: permission check if current user has access to this role - return svc.role.FindByID(id) + role, err := svc.role.FindByID(id) + if err != nil { + return nil, err + } + + if !svc.prm.CanReadRole(role) { + return nil, errors.New("Not allowed to read role") + } + return role, nil } func (svc *role) Find(filter *types.RoleFilter) ([]*types.Role, error) { - // @todo: permission check to return only roles that current user has access to - return svc.role.Find(filter) + roles, err := svc.role.Find(filter) + if err != nil { + return nil, err + } + + ret := []*types.Role{} + for _, role := range roles { + if svc.prm.CanReadRole(role) { + ret = append(ret, role) + } + } + return ret, nil } func (svc *role) Create(mod *types.Role) (*types.Role, error) { - // @todo: permission check if current user can add/edit role - + if !svc.prm.CanCreateRole() { + return nil, errors.New("Not allowed to create role") + } return svc.role.Create(mod) } func (svc *role) Update(mod *types.Role) (t *types.Role, err error) { - // @todo: permission check if current user can add/edit role + if !svc.prm.CanUpdateRole(mod) { + return nil, errors.New("Not allowed to update role") + } + // @todo: make sure archived & deleted entries can not be edited return t, svc.db.Transaction(func() (err error) { @@ -91,7 +120,11 @@ func (svc *role) Update(mod *types.Role) (t *types.Role, err error) { func (svc *role) Delete(id uint64) error { // @todo: make history unavailable // @todo: notify users that role has been removed (remove from web UI) - // @todo: permissions check if current user can remove role + + rl := &types.Role{ID: id} + if !svc.prm.CanDeleteRole(rl) { + return errors.New("Not allowed to delete role") + } return svc.role.DeleteByID(id) } @@ -120,18 +153,27 @@ func (svc *role) Move(id, targetOrganisationID uint64) error { } func (svc *role) MemberList(roleID uint64) ([]*types.RoleMember, error) { - // @todo: permission check if current user can read role members + rl := &types.Role{ID: roleID} + if !svc.prm.CanManageRoleMembers(rl) { + return nil, errors.New("Not allowed to manage role members") + } return svc.role.MemberFindByRoleID(roleID) } -func (svc *role) MemberAdd(id, userID uint64) error { - // @todo: permission check if current user can add user in to a role - return svc.role.MemberAddByID(id, userID) +func (svc *role) MemberAdd(roleID, userID uint64) error { + rl := &types.Role{ID: roleID} + if !svc.prm.CanManageRoleMembers(rl) { + return errors.New("Not allowed to manage role members") + } + return svc.role.MemberAddByID(roleID, userID) } -func (svc *role) MemberRemove(id, userID uint64) error { - // @todo: permission check if current user can remove user from a role - return svc.role.MemberRemoveByID(id, userID) +func (svc *role) MemberRemove(roleID, userID uint64) error { + rl := &types.Role{ID: roleID} + if !svc.prm.CanManageRoleMembers(rl) { + return errors.New("Not allowed to manage role members") + } + return svc.role.MemberRemoveByID(roleID, userID) } var _ RoleService = &role{} diff --git a/system/service/service.go b/system/service/service.go index f34e1d6a4..421586f2e 100644 --- a/system/service/service.go +++ b/system/service/service.go @@ -18,6 +18,7 @@ var ( DefaultRules RulesService DefaultOrganisation OrganisationService DefaultApplication ApplicationService + DefaultPermissions PermissionsService ) func Init() { @@ -28,5 +29,6 @@ func Init() { DefaultRules = Rules() DefaultOrganisation = Organisation() DefaultApplication = Application() + DefaultPermissions = Permissions() }) }