3
0

Add role membership workflow handlers

This commit is contained in:
Tomaž Jerman
2021-07-27 13:11:01 +02:00
parent 8ccaa76d87
commit 3f36a0f865
13 changed files with 1311 additions and 2 deletions

View File

@@ -28,6 +28,10 @@ type (
func (h rolesHandler) register() {
h.reg.AddFunctions(
h.Lookup(),
h.SearchMembers(),
h.EachMember(),
h.AddMember(),
h.RemoveMember(),
h.Search(),
h.Each(),
h.Create(),
@@ -137,6 +141,406 @@ func (h rolesHandler) Lookup() *atypes.Function {
}
}
type (
rolesSearchMembersArgs struct {
hasLookup bool
Lookup interface{}
lookupID uint64
lookupHandle string
lookupRes *types.Role
}
rolesSearchMembersResults struct {
Users []*types.User
Total uint64
}
)
func (a rolesSearchMembersArgs) GetLookup() (bool, uint64, string, *types.Role) {
return a.hasLookup, a.lookupID, a.lookupHandle, a.lookupRes
}
// SearchMembers function Role members search
//
// expects implementation of searchMembers function:
// func (h rolesHandler) searchMembers(ctx context.Context, args *rolesSearchMembersArgs) (results *rolesSearchMembersResults, err error) {
// return
// }
func (h rolesHandler) SearchMembers() *atypes.Function {
return &atypes.Function{
Ref: "rolesSearchMembers",
Kind: "function",
Labels: map[string]string{"users": "step,workflow"},
Meta: &atypes.FunctionMeta{
Short: "Role members search",
Description: "Find members for a specific roleby ID or handle",
},
Parameters: []*atypes.Param{
{
Name: "lookup",
Types: []string{"ID", "Handle", "Role"}, Required: true,
},
},
Results: []*atypes.Param{
{
Name: "users",
Types: []string{"User"},
IsArray: true,
},
{
Name: "total",
Types: []string{"UnsignedInteger"},
},
},
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
var (
args = &rolesSearchMembersArgs{
hasLookup: in.Has("lookup"),
}
)
if err = in.Decode(args); err != nil {
return
}
// Converting Lookup argument
if args.hasLookup {
aux := expr.Must(expr.Select(in, "lookup"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.lookupID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.lookupHandle = aux.Get().(string)
case h.reg.Type("Role").Type():
args.lookupRes = aux.Get().(*types.Role)
}
}
var results *rolesSearchMembersResults
if results, err = h.searchMembers(ctx, args); err != nil {
return
}
out = &expr.Vars{}
{
// converting results.Users (*types.User) to Array (of User)
var (
tval expr.TypedValue
tarr = make([]expr.TypedValue, len(results.Users))
)
for i := range results.Users {
if tarr[i], err = h.reg.Type("User").Cast(results.Users[i]); err != nil {
return
}
}
if tval, err = expr.NewArray(tarr); err != nil {
return
} else if err = expr.Assign(out, "users", tval); err != nil {
return
}
}
{
// converting results.Total (uint64) to UnsignedInteger
var (
tval expr.TypedValue
)
if tval, err = h.reg.Type("UnsignedInteger").Cast(results.Total); err != nil {
return
} else if err = expr.Assign(out, "total", tval); err != nil {
return
}
}
return
},
}
}
type (
rolesEachMemberArgs struct {
hasLookup bool
Lookup interface{}
lookupID uint64
lookupHandle string
lookupRes *types.Role
}
rolesEachMemberResults struct {
User *types.User
Total uint64
}
)
func (a rolesEachMemberArgs) GetLookup() (bool, uint64, string, *types.Role) {
return a.hasLookup, a.lookupID, a.lookupHandle, a.lookupRes
}
// EachMember function Iterate over role members
//
// expects implementation of eachMember function:
// func (h rolesHandler) eachMember(ctx context.Context, args *rolesEachMemberArgs) (results *rolesEachMemberResults, err error) {
// return
// }
func (h rolesHandler) EachMember() *atypes.Function {
return &atypes.Function{
Ref: "rolesEachMember",
Kind: "iterator",
Labels: map[string]string{"users": "step,workflow"},
Meta: &atypes.FunctionMeta{
Short: "Iterate over role members",
},
Parameters: []*atypes.Param{
{
Name: "lookup",
Types: []string{"ID", "Handle", "Role"}, Required: true,
},
},
Results: []*atypes.Param{
{
Name: "user",
Types: []string{"User"},
},
{
Name: "total",
Types: []string{"UnsignedInteger"},
},
},
Iterator: func(ctx context.Context, in *expr.Vars) (out wfexec.IteratorHandler, err error) {
var (
args = &rolesEachMemberArgs{
hasLookup: in.Has("lookup"),
}
)
if err = in.Decode(args); err != nil {
return
}
// Converting Lookup argument
if args.hasLookup {
aux := expr.Must(expr.Select(in, "lookup"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.lookupID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.lookupHandle = aux.Get().(string)
case h.reg.Type("Role").Type():
args.lookupRes = aux.Get().(*types.Role)
}
}
return h.eachMember(ctx, args)
},
}
}
type (
rolesAddMemberArgs struct {
hasRole bool
Role interface{}
roleID uint64
roleHandle string
roleRes *types.Role
hasUser bool
User interface{}
userID uint64
userHandle string
userEmail string
userRes *types.User
}
)
func (a rolesAddMemberArgs) GetRole() (bool, uint64, string, *types.Role) {
return a.hasRole, a.roleID, a.roleHandle, a.roleRes
}
func (a rolesAddMemberArgs) GetUser() (bool, uint64, string, string, *types.User) {
return a.hasUser, a.userID, a.userHandle, a.userEmail, a.userRes
}
// AddMember function Role membership add
//
// expects implementation of addMember function:
// func (h rolesHandler) addMember(ctx context.Context, args *rolesAddMemberArgs) (err error) {
// return
// }
func (h rolesHandler) AddMember() *atypes.Function {
return &atypes.Function{
Ref: "rolesAddMember",
Kind: "function",
Labels: map[string]string{"users": "step,workflow"},
Meta: &atypes.FunctionMeta{
Short: "Role membership add",
},
Parameters: []*atypes.Param{
{
Name: "role",
Types: []string{"ID", "Handle", "Role"}, Required: true,
},
{
Name: "user",
Types: []string{"ID", "Handle", "String", "User"}, Required: true,
},
},
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
var (
args = &rolesAddMemberArgs{
hasRole: in.Has("role"),
hasUser: in.Has("user"),
}
)
if err = in.Decode(args); err != nil {
return
}
// Converting Role argument
if args.hasRole {
aux := expr.Must(expr.Select(in, "role"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.roleID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.roleHandle = aux.Get().(string)
case h.reg.Type("Role").Type():
args.roleRes = aux.Get().(*types.Role)
}
}
// Converting User argument
if args.hasUser {
aux := expr.Must(expr.Select(in, "user"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.userID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.userHandle = aux.Get().(string)
case h.reg.Type("String").Type():
args.userEmail = aux.Get().(string)
case h.reg.Type("User").Type():
args.userRes = aux.Get().(*types.User)
}
}
return out, h.addMember(ctx, args)
},
}
}
type (
rolesRemoveMemberArgs struct {
hasRole bool
Role interface{}
roleID uint64
roleHandle string
roleRes *types.Role
hasUser bool
User interface{}
userID uint64
userHandle string
userEmail string
userRes *types.User
}
)
func (a rolesRemoveMemberArgs) GetRole() (bool, uint64, string, *types.Role) {
return a.hasRole, a.roleID, a.roleHandle, a.roleRes
}
func (a rolesRemoveMemberArgs) GetUser() (bool, uint64, string, string, *types.User) {
return a.hasUser, a.userID, a.userHandle, a.userEmail, a.userRes
}
// RemoveMember function Role membership remove
//
// expects implementation of removeMember function:
// func (h rolesHandler) removeMember(ctx context.Context, args *rolesRemoveMemberArgs) (err error) {
// return
// }
func (h rolesHandler) RemoveMember() *atypes.Function {
return &atypes.Function{
Ref: "rolesRemoveMember",
Kind: "function",
Labels: map[string]string{"users": "step,workflow"},
Meta: &atypes.FunctionMeta{
Short: "Role membership remove",
},
Parameters: []*atypes.Param{
{
Name: "role",
Types: []string{"ID", "Handle", "Role"}, Required: true,
},
{
Name: "user",
Types: []string{"ID", "Handle", "String", "User"}, Required: true,
},
},
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
var (
args = &rolesRemoveMemberArgs{
hasRole: in.Has("role"),
hasUser: in.Has("user"),
}
)
if err = in.Decode(args); err != nil {
return
}
// Converting Role argument
if args.hasRole {
aux := expr.Must(expr.Select(in, "role"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.roleID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.roleHandle = aux.Get().(string)
case h.reg.Type("Role").Type():
args.roleRes = aux.Get().(*types.Role)
}
}
// Converting User argument
if args.hasUser {
aux := expr.Must(expr.Select(in, "user"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.userID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.userHandle = aux.Get().(string)
case h.reg.Type("String").Type():
args.userEmail = aux.Get().(string)
case h.reg.Type("User").Type():
args.userRes = aux.Get().(*types.User)
}
}
return out, h.removeMember(ctx, args)
},
}
}
type (
rolesSearchArgs struct {
hasQuery bool

View File

@@ -2,6 +2,7 @@ package automation
import (
"context"
"errors"
"fmt"
. "github.com/cortezaproject/corteza-server/pkg/expr"
@@ -23,11 +24,17 @@ type (
Archive(ctx context.Context, id uint64) error
Unarchive(ctx context.Context, id uint64) error
Undelete(ctx context.Context, id uint64) error
Membership(ctx context.Context, userID uint64) (types.RoleMemberSet, error)
MemberList(ctx context.Context, roleID uint64) (types.RoleMemberSet, error)
MemberAdd(ctx context.Context, roleID, userID uint64) error
MemberRemove(ctx context.Context, roleID, userID uint64) error
}
rolesHandler struct {
reg rolesHandlerRegistry
rSvc roleService
uSvc userService
}
roleSetIterator struct {
@@ -41,10 +48,11 @@ type (
}
)
func RolesHandler(reg rolesHandlerRegistry, rSvc roleService) *rolesHandler {
func RolesHandler(reg rolesHandlerRegistry, rSvc roleService, uSvc userService) *rolesHandler {
h := &rolesHandler{
reg: reg,
rSvc: rSvc,
uSvc: uSvc,
}
h.register()
@@ -57,6 +65,143 @@ func (h rolesHandler) lookup(ctx context.Context, args *rolesLookupArgs) (result
return
}
func (h rolesHandler) searchMembers(ctx context.Context, args *rolesSearchMembersArgs) (results *rolesSearchMembersResults, err error) {
results = &rolesSearchMembersResults{}
rl, err := lookupRole(ctx, h.rSvc, args)
if err != nil {
return
}
if rl == nil {
return nil, errors.New("role not found")
}
// Get membership info
mm, err := h.rSvc.MemberList(ctx, rl.ID)
if err != nil {
return
}
if len(mm) == 0 {
results.Users = []*types.User{}
return
}
// Get actual users
uu := make([]uint64, len(mm))
for i, m := range mm {
uu[i] = m.UserID
}
results.Users, _, err = h.uSvc.Find(ctx, types.UserFilter{
UserID: uu,
})
results.Total = uint64(len(results.Users))
return
}
func (h rolesHandler) eachMember(ctx context.Context, args *rolesEachMemberArgs) (out wfexec.IteratorHandler, err error) {
var (
i = &userSetIterator{}
)
rl, err := lookupRole(ctx, h.rSvc, args)
if err != nil {
return
}
if rl == nil {
return nil, errors.New("role not found")
}
// Get membership info
mm, err := h.rSvc.MemberList(ctx, rl.ID)
if err != nil {
return
}
if len(mm) == 0 {
i.set = []*types.User{}
i.filter = types.UserFilter{}
return i, nil
}
// Get actual users
uu := make([]uint64, len(mm))
for i, m := range mm {
uu[i] = m.UserID
}
i.set, i.filter, err = h.uSvc.Find(ctx, types.UserFilter{
UserID: uu,
})
return i, err
}
func (h rolesHandler) addMember(ctx context.Context, args *rolesAddMemberArgs) (err error) {
role, err := lookupRole(ctx, h.rSvc, &rolesLookupArgs{
hasLookup: args.hasRole,
Lookup: args.Role,
lookupID: args.roleID,
lookupHandle: args.roleHandle,
lookupRes: args.roleRes,
})
if err != nil {
return
}
if role == nil {
return errors.New("role not found")
}
user, err := lookupUser(ctx, h.uSvc, &usersLookupArgs{
hasLookup: args.hasUser,
Lookup: args.User,
lookupID: args.userID,
lookupHandle: args.userHandle,
lookupEmail: args.userEmail,
lookupRes: args.userRes,
})
if err != nil {
return
}
if role == nil {
return errors.New("user not found")
}
return h.rSvc.MemberAdd(ctx, role.ID, user.ID)
}
func (h rolesHandler) removeMember(ctx context.Context, args *rolesRemoveMemberArgs) (err error) {
role, err := lookupRole(ctx, h.rSvc, &rolesLookupArgs{
hasLookup: args.hasRole,
Lookup: args.Role,
lookupID: args.roleID,
lookupHandle: args.roleHandle,
lookupRes: args.roleRes,
})
if err != nil {
return
}
if role == nil {
return errors.New("role not found")
}
user, err := lookupUser(ctx, h.uSvc, &usersLookupArgs{
hasLookup: args.hasUser,
Lookup: args.User,
lookupID: args.userID,
lookupHandle: args.userHandle,
lookupEmail: args.userEmail,
lookupRes: args.userRes,
})
if err != nil {
return
}
if role == nil {
return errors.New("user not found")
}
return h.rSvc.MemberRemove(ctx, role.ID, user.ID)
}
func (h rolesHandler) search(ctx context.Context, args *rolesSearchArgs) (results *rolesSearchResults, err error) {
results = &rolesSearchResults{}

View File

@@ -16,6 +16,8 @@ params:
rvRole: &rvRole
wf: Role
rvUser: &rvUser
wf: User
rvTotal: &rvTotal
wf: UnsignedInteger
@@ -23,6 +25,14 @@ params:
rvPageCursor: &rvPageCursor
wf: String
lookupUser: &lookupUser
required: true
types:
- { wf: ID }
- { wf: Handle }
- { wf: String, suffix: email }
- { wf: User, suffix: res }
filterParams: &filterParams
query:
types:
@@ -76,6 +86,50 @@ functions:
results:
role: *rvRole
searchMembers:
meta:
short: Role members search
description: Find members for a specific roleby ID or handle
labels:
<<: *labels
params:
lookup: *lookup
results:
users:
<<: *rvUser
isArray: true
total: *rvTotal
eachMember:
kind: iterator
meta:
short: Iterate over role members
labels:
<<: *labels
params:
lookup: *lookup
results:
user: *rvUser
total: *rvTotal
addMember:
meta:
short: Role membership add
labels:
<<: *labels
params:
role: *lookup
user: *lookupUser
removeMember:
meta:
short: Role membership remove
labels:
<<: *labels
params:
role: *lookup
user: *lookupUser
search:
meta:
short: Roles search

View File

@@ -28,6 +28,8 @@ type (
func (h usersHandler) register() {
h.reg.AddFunctions(
h.Lookup(),
h.SearchMembership(),
h.CheckMembership(),
h.Search(),
h.Each(),
h.Create(),
@@ -140,6 +142,263 @@ func (h usersHandler) Lookup() *atypes.Function {
}
}
type (
usersSearchMembershipArgs struct {
hasLookup bool
Lookup interface{}
lookupID uint64
lookupHandle string
lookupEmail string
lookupRes *types.User
}
usersSearchMembershipResults struct {
Roles []*types.Role
Total uint64
}
)
func (a usersSearchMembershipArgs) GetLookup() (bool, uint64, string, string, *types.User) {
return a.hasLookup, a.lookupID, a.lookupHandle, a.lookupEmail, a.lookupRes
}
// SearchMembership function User role search
//
// expects implementation of searchMembership function:
// func (h usersHandler) searchMembership(ctx context.Context, args *usersSearchMembershipArgs) (results *usersSearchMembershipResults, err error) {
// return
// }
func (h usersHandler) SearchMembership() *atypes.Function {
return &atypes.Function{
Ref: "usersSearchMembership",
Kind: "function",
Labels: map[string]string{"users": "step,workflow"},
Meta: &atypes.FunctionMeta{
Short: "User role search",
Description: "Search user role membership by ID, handle or string",
},
Parameters: []*atypes.Param{
{
Name: "lookup",
Types: []string{"ID", "Handle", "String", "User"}, Required: true,
},
},
Results: []*atypes.Param{
{
Name: "roles",
Types: []string{"Role"},
IsArray: true,
},
{
Name: "total",
Types: []string{"UnsignedInteger"},
},
},
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
var (
args = &usersSearchMembershipArgs{
hasLookup: in.Has("lookup"),
}
)
if err = in.Decode(args); err != nil {
return
}
// Converting Lookup argument
if args.hasLookup {
aux := expr.Must(expr.Select(in, "lookup"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.lookupID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.lookupHandle = aux.Get().(string)
case h.reg.Type("String").Type():
args.lookupEmail = aux.Get().(string)
case h.reg.Type("User").Type():
args.lookupRes = aux.Get().(*types.User)
}
}
var results *usersSearchMembershipResults
if results, err = h.searchMembership(ctx, args); err != nil {
return
}
out = &expr.Vars{}
{
// converting results.Roles (*types.Role) to Array (of Role)
var (
tval expr.TypedValue
tarr = make([]expr.TypedValue, len(results.Roles))
)
for i := range results.Roles {
if tarr[i], err = h.reg.Type("Role").Cast(results.Roles[i]); err != nil {
return
}
}
if tval, err = expr.NewArray(tarr); err != nil {
return
} else if err = expr.Assign(out, "roles", tval); err != nil {
return
}
}
{
// converting results.Total (uint64) to UnsignedInteger
var (
tval expr.TypedValue
)
if tval, err = h.reg.Type("UnsignedInteger").Cast(results.Total); err != nil {
return
} else if err = expr.Assign(out, "total", tval); err != nil {
return
}
}
return
},
}
}
type (
usersCheckMembershipArgs struct {
hasUser bool
User interface{}
userID uint64
userHandle string
userEmail string
userRes *types.User
hasRole bool
Role interface{}
roleID uint64
roleHandle string
roleRes *types.Role
}
usersCheckMembershipResults struct {
Member bool
}
)
func (a usersCheckMembershipArgs) GetUser() (bool, uint64, string, string, *types.User) {
return a.hasUser, a.userID, a.userHandle, a.userEmail, a.userRes
}
func (a usersCheckMembershipArgs) GetRole() (bool, uint64, string, *types.Role) {
return a.hasRole, a.roleID, a.roleHandle, a.roleRes
}
// CheckMembership function User membership check
//
// expects implementation of checkMembership function:
// func (h usersHandler) checkMembership(ctx context.Context, args *usersCheckMembershipArgs) (results *usersCheckMembershipResults, err error) {
// return
// }
func (h usersHandler) CheckMembership() *atypes.Function {
return &atypes.Function{
Ref: "usersCheckMembership",
Kind: "function",
Labels: map[string]string{"users": "step,workflow"},
Meta: &atypes.FunctionMeta{
Short: "User membership check",
Description: "Find user role membership by ID, handle or string",
},
Parameters: []*atypes.Param{
{
Name: "user",
Types: []string{"ID", "Handle", "String", "User"}, Required: true,
},
{
Name: "role",
Types: []string{"ID", "Handle", "Role"}, Required: true,
},
},
Results: []*atypes.Param{
{
Name: "member",
Types: []string{"Boolean"},
},
},
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
var (
args = &usersCheckMembershipArgs{
hasUser: in.Has("user"),
hasRole: in.Has("role"),
}
)
if err = in.Decode(args); err != nil {
return
}
// Converting User argument
if args.hasUser {
aux := expr.Must(expr.Select(in, "user"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.userID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.userHandle = aux.Get().(string)
case h.reg.Type("String").Type():
args.userEmail = aux.Get().(string)
case h.reg.Type("User").Type():
args.userRes = aux.Get().(*types.User)
}
}
// Converting Role argument
if args.hasRole {
aux := expr.Must(expr.Select(in, "role"))
switch aux.Type() {
case h.reg.Type("ID").Type():
args.roleID = aux.Get().(uint64)
case h.reg.Type("Handle").Type():
args.roleHandle = aux.Get().(string)
case h.reg.Type("Role").Type():
args.roleRes = aux.Get().(*types.Role)
}
}
var results *usersCheckMembershipResults
if results, err = h.checkMembership(ctx, args); err != nil {
return
}
out = &expr.Vars{}
{
// converting results.Member (bool) to Boolean
var (
tval expr.TypedValue
)
if tval, err = h.reg.Type("Boolean").Cast(results.Member); err != nil {
return
} else if err = expr.Assign(out, "member", tval); err != nil {
return
}
}
return
},
}
}
type (
usersSearchArgs struct {
hasQuery bool

View File

@@ -2,6 +2,7 @@ package automation
import (
"context"
"errors"
"fmt"
. "github.com/cortezaproject/corteza-server/pkg/expr"
@@ -29,6 +30,7 @@ type (
usersHandler struct {
reg usersHandlerRegistry
uSvc userService
rSvc roleService
}
userSetIterator struct {
@@ -42,10 +44,11 @@ type (
}
)
func UsersHandler(reg usersHandlerRegistry, uSvc userService) *usersHandler {
func UsersHandler(reg usersHandlerRegistry, uSvc userService, rSvc roleService) *usersHandler {
h := &usersHandler{
reg: reg,
uSvc: uSvc,
rSvc: rSvc,
}
h.register()
@@ -58,6 +61,98 @@ func (h usersHandler) lookup(ctx context.Context, args *usersLookupArgs) (result
return
}
func (h usersHandler) searchMembership(ctx context.Context, args *usersSearchMembershipArgs) (results *usersSearchMembershipResults, err error) {
results = &usersSearchMembershipResults{}
u, err := lookupUser(ctx, h.uSvc, args)
if err != nil {
return
}
if u == nil {
return nil, errors.New("user not found")
}
// Get the roles
mm, err := h.rSvc.Membership(ctx, u.ID)
if err != nil {
return
}
if len(mm) == 0 {
results.Roles = []*types.Role{}
return
}
rr := make([]uint64, len(mm))
for i, r := range mm {
rr[i] = r.RoleID
}
results.Roles, _, err = h.rSvc.Find(ctx, types.RoleFilter{
RoleID: rr,
})
results.Total = uint64(len(results.Roles))
return
}
func (h usersHandler) checkMembership(ctx context.Context, args *usersCheckMembershipArgs) (results *usersCheckMembershipResults, err error) {
results = &usersCheckMembershipResults{}
// Resolve user
u, err := lookupUser(ctx, h.uSvc, usersLookupArgs{
hasLookup: args.hasUser,
Lookup: args.User,
lookupID: args.userID,
lookupHandle: args.userHandle,
lookupEmail: args.userEmail,
lookupRes: args.userRes,
})
if err != nil {
return
}
if u == nil {
return nil, errors.New("user not found")
}
// Get user membershio
mm, err := h.rSvc.Membership(ctx, u.ID)
if err != nil {
return
}
if len(mm) == 0 {
return &usersCheckMembershipResults{
Member: false,
}, nil
}
// Resolve role
r, err := lookupRole(ctx, h.rSvc, rolesLookupArgs{
hasLookup: args.hasRole,
Lookup: args.Role,
lookupID: args.roleID,
lookupHandle: args.roleHandle,
lookupRes: args.roleRes,
})
if err != nil {
return
}
if r == nil {
return nil, errors.New("role not found")
}
// Check if there
for _, m := range mm {
if m.RoleID == r.ID {
return &usersCheckMembershipResults{
Member: true,
}, nil
}
}
return &usersCheckMembershipResults{
Member: false,
}, nil
}
func (h usersHandler) search(ctx context.Context, args *usersSearchArgs) (results *usersSearchResults, err error) {
results = &usersSearchResults{}

View File

@@ -10,6 +10,13 @@ snippets:
- { wf: String, suffix: email }
- { wf: User, suffix: res }
lookupRole: &lookupRole
required: true
types:
- { wf: ID, }
- { wf: Handle, }
- { wf: Role, suffix: res }
user: &user
types:
- { wf: User }
@@ -17,9 +24,15 @@ snippets:
rvUser: &rvUser
wf: User
rvRole: &rvRole
wf: Role
rvTotal: &rvTotal
wf: UnsignedInteger
rvInclusion: &rvInclusion
wf: Boolean
rvPageCursor: &rvPageCursor
wf: String
@@ -73,6 +86,32 @@ functions:
results:
user: *rvUser
searchMembership:
meta:
short: User role search
description: Search user role membership by ID, handle or string
params:
lookup: *lookup
labels:
<<: *labels
results:
roles:
<<: *rvRole
isArray: true
total: *rvTotal
checkMembership:
meta:
short: User membership check
description: Find user role membership by ID, handle or string
params:
user: *lookup
role: *lookupRole
labels:
<<: *labels
results:
member: *rvInclusion
search:
meta:
short: User search

View File

@@ -187,6 +187,7 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, ws websock
automation.UsersHandler(
automationService.Registry(),
DefaultUser,
DefaultRole,
)
automation.TemplatesHandler(
@@ -197,6 +198,7 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, ws websock
automation.RolesHandler(
automationService.Registry(),
DefaultRole,
DefaultUser,
)
automation.RbacHandler(

View File

@@ -0,0 +1,50 @@
package workflows
import (
"context"
"testing"
autTypes "github.com/cortezaproject/corteza-server/automation/types"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/stretchr/testify/require"
)
func Test0003_role_members(t *testing.T) {
var (
ctx = superUser(context.Background())
req = require.New(t)
)
req.NoError(defStore.TruncateRoleMembers(ctx))
req.NoError(defStore.TruncateUsers(ctx))
req.NoError(defStore.TruncateRoles(ctx))
loadScenario(ctx, t)
var (
aux = struct {
UsersPre_r1 sysTypes.UserSet
TotalPre_r1 uint64
UsersPre_r2 sysTypes.UserSet
TotalPre_r2 uint64
UsersPost_r1 sysTypes.UserSet
TotalPost_r1 uint64
UsersPost_r2 sysTypes.UserSet
TotalPost_r2 uint64
}{}
vars, _ = mustExecWorkflow(ctx, t, "role-members", autTypes.WorkflowExecParams{})
)
req.NoError(vars.Decode(&aux))
req.Len(aux.UsersPre_r1, 1)
req.Equal(uint64(1), aux.TotalPre_r1)
req.Len(aux.UsersPre_r2, 1)
req.Equal(uint64(1), aux.TotalPre_r2)
req.Len(aux.UsersPost_r1, 0)
req.Equal(uint64(0), aux.TotalPost_r1)
req.Len(aux.UsersPost_r2, 1)
req.Equal(uint64(1), aux.TotalPost_r2)
}

View File

@@ -0,0 +1,58 @@
package workflows
import (
"context"
"testing"
autTypes "github.com/cortezaproject/corteza-server/automation/types"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/stretchr/testify/require"
)
func Test0004_user_membership(t *testing.T) {
var (
ctx = superUser(context.Background())
req = require.New(t)
)
req.NoError(defStore.TruncateRoleMembers(ctx))
req.NoError(defStore.TruncateUsers(ctx))
req.NoError(defStore.TruncateRoles(ctx))
loadScenario(ctx, t)
var (
aux = struct {
RolesPre_u1 sysTypes.RoleSet
TotalPre_u1 uint64
RolesPre_u2 sysTypes.RoleSet
TotalPre_u2 uint64
MemberPre_u1 bool
MemberPre_u2 bool
RolesPost_u1 sysTypes.RoleSet
TotalPost_u1 uint64
RolesPost_u2 sysTypes.RoleSet
TotalPost_u2 uint64
MemberPost_u1 bool
MemberPost_u2 bool
}{}
vars, _ = mustExecWorkflow(ctx, t, "user-membership", autTypes.WorkflowExecParams{})
)
req.NoError(vars.Decode(&aux))
req.Len(aux.RolesPre_u1, 2)
req.Equal(uint64(2), aux.TotalPre_u1)
req.True(aux.MemberPre_u1)
req.Len(aux.RolesPre_u2, 0)
req.Equal(uint64(0), aux.TotalPre_u2)
req.False(aux.MemberPre_u2)
req.Len(aux.RolesPost_u1, 1)
req.Equal(uint64(1), aux.TotalPost_u1)
req.False(aux.MemberPost_u1)
req.Len(aux.RolesPost_u2, 0)
req.Equal(uint64(0), aux.TotalPost_u2)
req.False(aux.MemberPost_u2)
}

View File

@@ -0,0 +1,7 @@
users:
u1: u1@example.tld
u2: u2@example.tld
roles:
r1: Role1
r2: Role2

View File

@@ -0,0 +1,74 @@
workflows:
role-members:
enabled: true
trace: true
triggers:
- enabled: true
stepID: 10
steps:
# Adding membership
- stepID: 10
kind: function
ref: rolesAddMember
arguments:
- { target: role, type: Handle, value: r1 }
- { target: user, type: Handle, value: u1 }
- stepID: 11
kind: function
ref: rolesAddMember
arguments:
- { target: role, type: Handle, value: r2 }
- { target: user, type: Handle, value: u1 }
# Get current state
- stepID: 20
kind: function
ref: rolesSearchMembers
arguments:
- { target: lookup, type: Handle, value: r1 }
results:
- { target: usersPre_r1, expr: "users" }
- { target: totalPre_r1, expr: "total" }
- stepID: 21
kind: function
ref: rolesSearchMembers
arguments:
- { target: lookup, type: Handle, value: r2 }
results:
- { target: usersPre_r2, expr: "users" }
- { target: totalPre_r2, expr: "total" }
# Removing membership
- stepID: 30
kind: function
ref: rolesRemoveMember
arguments:
- { target: role, type: Handle, value: r1 }
- { target: user, type: Handle, value: u1 }
# Get final state
- stepID: 40
kind: function
ref: rolesSearchMembers
arguments:
- { target: lookup, type: Handle, value: r1 }
results:
- { target: usersPost_r1, expr: "users" }
- { target: totalPost_r1, expr: "total" }
- stepID: 41
kind: function
ref: rolesSearchMembers
arguments:
- { target: lookup, type: Handle, value: r2 }
results:
- { target: usersPost_r2, expr: "users" }
- { target: totalPost_r2, expr: "total" }
paths:
- { parentID: 10, childID: 11 }
- { parentID: 11, childID: 20 }
- { parentID: 20, childID: 21 }
- { parentID: 21, childID: 30 }
- { parentID: 30, childID: 40 }
- { parentID: 40, childID: 41 }

View File

@@ -0,0 +1,7 @@
users:
u1: u1@example.tld
u2: u2@example.tld
roles:
r1: Role1
r2: Role2

View File

@@ -0,0 +1,115 @@
workflows:
user-membership:
enabled: true
trace: true
triggers:
- enabled: true
stepID: 10
steps:
# Adding membership
- stepID: 10
kind: function
ref: rolesAddMember
arguments:
- { target: role, type: Handle, value: r1 }
- { target: user, type: Handle, value: u1 }
- stepID: 11
kind: function
ref: rolesAddMember
arguments:
- { target: role, type: Handle, value: r2 }
- { target: user, type: Handle, value: u1 }
# Get current state
- stepID: 20
kind: function
ref: usersSearchMembership
arguments:
- { target: lookup, type: Handle, value: u1 }
results:
- { target: rolesPre_u1, expr: "roles" }
- { target: totalPre_u1, expr: "total" }
- stepID: 21
kind: function
ref: usersSearchMembership
arguments:
- { target: lookup, type: Handle, value: u2 }
results:
- { target: rolesPre_u2, expr: "roles" }
- { target: totalPre_u2, expr: "total" }
- stepID: 22
kind: function
ref: usersCheckMembership
arguments:
- { target: user, type: Handle, value: u1 }
- { target: role, type: Handle, value: r1 }
results:
- { target: memberPre_u1, expr: "member" }
- stepID: 23
kind: function
ref: usersCheckMembership
arguments:
- { target: user, type: Handle, value: u2 }
- { target: role, type: Handle, value: r1 }
results:
- { target: memberPre_u2, expr: "member" }
# Removing membership
- stepID: 30
kind: function
ref: rolesRemoveMember
arguments:
- { target: role, type: Handle, value: r1 }
- { target: user, type: Handle, value: u1 }
# Get final state
- stepID: 40
kind: function
ref: usersSearchMembership
arguments:
- { target: lookup, type: Handle, value: u1 }
results:
- { target: rolesPost_u1, expr: "roles" }
- { target: totalPost_u1, expr: "total" }
- stepID: 41
kind: function
ref: usersSearchMembership
arguments:
- { target: lookup, type: Handle, value: u2 }
results:
- { target: rolesPost_u2, expr: "roles" }
- { target: totalPost_u2, expr: "total" }
- stepID: 42
kind: function
ref: usersCheckMembership
arguments:
- { target: user, type: Handle, value: u1 }
- { target: role, type: Handle, value: r1 }
results:
- { target: memberPost_u1, expr: "member" }
- stepID: 43
kind: function
ref: usersCheckMembership
arguments:
- { target: user, type: Handle, value: u2 }
- { target: role, type: Handle, value: r1 }
results:
- { target: memberPost_u2, expr: "member" }
paths:
- { parentID: 10, childID: 11 }
- { parentID: 11, childID: 20 }
- { parentID: 20, childID: 21 }
- { parentID: 21, childID: 22 }
- { parentID: 22, childID: 23 }
- { parentID: 23, childID: 30 }
- { parentID: 30, childID: 40 }
- { parentID: 40, childID: 41 }
- { parentID: 41, childID: 42 }
- { parentID: 42, childID: 43 }