3
0

Add RBAC functions for workflow for check&grant

This commit is contained in:
Denis Arh
2021-07-13 08:51:32 +02:00
parent 3eafe6c39d
commit 89ae50dbae
7 changed files with 162 additions and 13 deletions

View File

@@ -38,12 +38,13 @@ func (s session) Context() context.Context { return s.ctx }
var _ Session = &session{}
func ContextToSession(ctx context.Context) *session {
i := auth.GetIdentityFromContext(ctx)
s := &session{
return NewSession(ctx, auth.GetIdentityFromContext(ctx))
}
func NewSession(ctx context.Context, i auth.Identifiable) *session {
return &session{
id: i.Identity(),
rr: i.Roles(),
ctx: ctx,
}
return s
}

View File

@@ -230,6 +230,9 @@ func (s *Session) Result() *expr.Vars {
}
func (s *Session) Exec(ctx context.Context, step Step, scope *expr.Vars) error {
s.mux.RLock()
defer s.mux.RUnlock()
err := func() error {
if s.g.Len() == 0 {
return fmt.Errorf("refusing to execute without steps")
@@ -466,7 +469,9 @@ func (s *Session) worker(ctx context.Context) {
st.err,
)
// when the error handler is defined, the error was handled and should not kill the workflow
s.mux.Lock()
// when the err handler is defined, the error was handled and should not kill the workflow
if !st.errHandled {
// We need to force failed session status
// because it's not set early enough to pick it up with s.Status()
@@ -476,6 +481,8 @@ func (s *Session) worker(ctx context.Context) {
// to break worker loop
s.qErr <- st.err
}
s.mux.Unlock()
}
s.log.Debug(

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/system/types"
)
type (
@@ -13,9 +14,19 @@ type (
Grant(ctx context.Context, rules ...*rbac.Rule) (err error)
}
rbacUserService interface {
FindByAny(ctx context.Context, identifier interface{}) (*types.User, error)
}
rbacRoleService interface {
Membership(ctx context.Context, userID uint64) (types.RoleMemberSet, error)
}
rbacHandler struct {
reg rbacHandlerRegistry
svc rbacService
reg rbacHandlerRegistry
rbac rbacService
user rbacUserService
role rbacRoleService
}
auxResource struct{ res string }
@@ -27,10 +38,12 @@ func (r auxResource) RbacResource() string {
return r.res
}
func RbacHandler(reg rbacHandlerRegistry, svc rbacService) *rbacHandler {
func RbacHandler(reg rbacHandlerRegistry, rbac rbacService, user rbacUserService, role rbacRoleService) *rbacHandler {
h := &rbacHandler{
reg: reg,
svc: svc,
reg: reg,
rbac: rbac,
user: user,
role: role,
}
h.register()
@@ -56,16 +69,41 @@ func (h rbacHandler) grant(ctx context.Context, r *rbac.Rule) (err error) {
// no extra metadata hidden under it,
cmpRes := &auxResource{rbac.ResourceComponent(r.Resource)}
if !h.svc.Can(rbac.ContextToSession(ctx), "grant", cmpRes) {
if !h.rbac.Can(rbac.ContextToSession(ctx), "grant", cmpRes) {
return fmt.Errorf("not allowed to grant %s", r.String())
}
return h.svc.Grant(ctx, r)
return h.rbac.Grant(ctx, r)
}
func (h rbacHandler) check(ctx context.Context, args *rbacCheckArgs) (b *rbacCheckResults, err error) {
var secses rbac.Session
b = &rbacCheckResults{}
b.Can = h.svc.Can(rbac.ContextToSession(ctx), args.Operation, args.Resource)
if args.hasUser {
// user was explicitly set to rbac checking function
//
// load all roles that this user is member of
// and create new security session out of it
var (
mm types.RoleMemberSet
ids []uint64
)
mm, err = h.role.Membership(ctx, args.User.ID)
_ = mm.Walk(func(m *types.RoleMember) error {
ids = append(ids, m.RoleID)
return nil
})
args.User.SetRoles(ids...)
secses = rbac.NewSession(ctx, args.User)
} else {
secses = rbac.ContextToSession(ctx)
}
b.Can = h.rbac.Can(secses, args.Operation, args.Resource)
return
}

View File

@@ -202,6 +202,8 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, ws websock
automation.RbacHandler(
automationService.Registry(),
rbac.Global(),
DefaultUser,
DefaultRole,
)
return

View File

@@ -0,0 +1,34 @@
package workflows
import (
"context"
"testing"
"github.com/cortezaproject/corteza-server/automation/types"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/stretchr/testify/require"
)
func Test0002_rbac_fn(t *testing.T) {
var (
ctx = bypassRBAC(context.Background())
req = require.New(t)
)
loadScenario(ctx, t)
req.Len(rbac.Global().Rules(), 0)
var (
aux = struct {
CanCurrentRead string
CanOtherRead string
}{}
vars, _ = mustExecWorkflow(ctx, t, "check-and-grant", types.WorkflowExecParams{})
)
req.NoError(vars.Decode(&aux))
req.Equal("y", aux.CanCurrentRead)
req.Equal("n", aux.CanOtherRead)
req.Len(rbac.Global().Rules(), 1)
}

View File

@@ -39,6 +39,7 @@ func TestMain(m *testing.M) {
ctx := context.Background()
defApp = helpers.NewIntegrationTestApp(ctx, func(app *app.CortezaApp) (err error) {
//app.Opt.Workflow.ExecDebug = true
defStore = app.Store
eventbus.Set(eventBus)
return nil

View File

@@ -0,0 +1,66 @@
roles:
testers:
users:
tester:
email: tester@testing-samples.tld
workflows:
check-and-grant:
enabled: true
trace: true
triggers:
- enabled: true
stepID: 1
steps:
- stepID: 1
kind: function
ref: rolesLookup
arguments:
- { target: lookup, type: Handle, value: "testers" }
results:
- { target: res, expr: role }
- stepID: 2
kind: function
ref: rbacCheck
arguments:
- { target: resource, type: RbacResource, expr: "res" }
- { target: operation, type: String, value: "read" }
results:
- { target: canCurrentRead, type: String, expr: 'can ? "y":"n"' }
- stepID: 3
kind: function
ref: usersLookup
arguments:
- { target: lookup, type: Handle, value: "tester" }
results:
- { target: usr, expr: user }
- stepID: 4
kind: function
ref: rbacCheck
arguments:
- { target: resource, type: RbacResource, expr: "res" }
- { target: operation, type: String, value: "read" }
- { target: user, type: User, expr: "usr" }
results:
- { target: canOtherRead, type: String, expr: 'can ? "y":"n"' }
- stepID: 5
kind: function
ref: rbacAllow
arguments:
- { target: resource, type: RbacResource, expr: "res" }
- { target: operation, type: String, value: "read" }
- { target: role, type: Role, expr: "res" }
paths:
- { parentID: 1, childID: 2 }
- { parentID: 2, childID: 3 }
- { parentID: 3, childID: 4 }
- { parentID: 4, childID: 5 }