3
0

Add access-control check for execution

This commit is contained in:
Denis Arh 2021-03-18 15:07:49 +01:00
parent 2e011f2427
commit 739e994145
4 changed files with 111 additions and 67 deletions

View File

@ -47,6 +47,7 @@ type (
triggerAccessController interface {
CanSearchTriggers(context.Context) bool
CanManageWorkflowTriggers(context.Context, *types.Workflow) bool
CanExecuteWorkflow(context.Context, *types.Workflow) bool
}
triggerEventTriggerHandler interface {
@ -510,7 +511,7 @@ func (svc *trigger) registerTriggers(wf *types.Workflow, runAs auth.Identifiable
).Wrap(wf.Issues)
}
} else {
handlerFn = makeWorkflowHandler(svc.session, t, wf, g, runAs)
handlerFn = makeWorkflowHandler(svc.ac, svc.session, t, wf, g, runAs)
}
ops = append(
@ -573,71 +574,6 @@ func (svc *trigger) unregisterTriggers(tt ...*types.Trigger) {
}
}
func makeWorkflowHandler(s *session, t *types.Trigger, wf *types.Workflow, g *wfexec.Graph, runAs auth.Identifiable) eventbus.HandlerFn {
return func(ctx context.Context, ev eventbus.Event) (err error) {
var (
// create session scope from predefined workflow scope and trigger input
scope = wf.Scope.Merge(t.Input)
evScope *expr.Vars
wait WaitFn
)
if enc, is := ev.(varsEncoder); is {
if evScope, err = enc.EncodeVars(); err != nil {
return
}
scope = scope.Merge(evScope)
}
_ = scope.AssignFieldValue("eventType", expr.Must(expr.NewString(ev.EventType())))
_ = scope.AssignFieldValue("resourceType", expr.Must(expr.NewString(ev.ResourceType())))
if runAs == nil {
// @todo can/should we get alternative identity from Event?
// for example:
// - use http auth header and get username
// - use from/to/replyTo and use that as an identifier
runAs = auth.GetIdentityFromContext(ctx)
}
wait, err = s.Start(g, runAs, types.SessionStartParams{
WorkflowID: wf.ID,
KeepFor: wf.KeepSessions,
Trace: wf.Trace,
Input: scope,
StepID: t.StepID,
EventType: t.EventType,
ResourceType: t.ResourceType,
})
if err != nil {
return err
}
if wf.CheckDeferred() {
// deferred workflow, return right away and keep the workflow session
// running without waiting for the execution
return nil
}
// wait for the workflow to complete
// reuse scope for results
// this will be decoded back to event properties
scope, _, err = wait(ctx)
if err != nil {
return
}
if dec, is := ev.(varsDecoder); is {
return dec.DecodeVars(scope)
}
return nil
}
}
func loadTrigger(ctx context.Context, s store.Storer, workflowID uint64) (res *types.Trigger, err error) {
if workflowID == 0 {
return nil, TriggerErrInvalidID()

View File

@ -51,6 +51,10 @@ type (
Grant(ctx context.Context, rr ...*rbac.Rule) error
}
workflowExecController interface {
CanExecuteWorkflow(context.Context, *types.Workflow) bool
}
workflowEventTriggerHandler interface {
Register(h eventbus.HandlerFn, ops ...eventbus.HandlerRegOp) uintptr
Unregister(ptrs ...uintptr)
@ -449,7 +453,7 @@ func (svc workflow) handleDelete(ctx context.Context, res *types.Workflow) (work
}
func (svc workflow) handleUndelete(ctx context.Context, res *types.Workflow) (workflowChanges, error) {
if !svc.ac.CanDeleteWorkflow(ctx, res) {
if !svc.ac.CanUndeleteWorkflow(ctx, res) {
return workflowUnchanged, WorkflowErrNotAllowedToUndelete()
}
@ -475,6 +479,74 @@ func (svc *workflow) Load(ctx context.Context) error {
return svc.triggers.registerWorkflows(ctx, wwf...)
}
func makeWorkflowHandler(ac workflowExecController, s *session, t *types.Trigger, wf *types.Workflow, g *wfexec.Graph, runAs intAuth.Identifiable) eventbus.HandlerFn {
return func(ctx context.Context, ev eventbus.Event) (err error) {
if !ac.(*accessControl).CanExecuteWorkflow(ctx, wf) {
return WorkflowErrNotAllowedToExecute()
}
var (
// create session scope from predefined workflow scope and trigger input
scope = wf.Scope.Merge(t.Input)
evScope *expr.Vars
wait WaitFn
)
if enc, is := ev.(varsEncoder); is {
if evScope, err = enc.EncodeVars(); err != nil {
return
}
scope = scope.Merge(evScope)
}
_ = scope.AssignFieldValue("eventType", expr.Must(expr.NewString(ev.EventType())))
_ = scope.AssignFieldValue("resourceType", expr.Must(expr.NewString(ev.ResourceType())))
if runAs == nil {
// @todo can/should we get alternative identity from Event?
// for example:
// - use http auth header and get username
// - use from/to/replyTo and use that as an identifier
runAs = intAuth.GetIdentityFromContext(ctx)
}
wait, err = s.Start(g, runAs, types.SessionStartParams{
WorkflowID: wf.ID,
KeepFor: wf.KeepSessions,
Trace: wf.Trace,
Input: scope,
StepID: t.StepID,
EventType: t.EventType,
ResourceType: t.ResourceType,
})
if err != nil {
return err
}
if wf.CheckDeferred() {
// deferred workflow, return right away and keep the workflow session
// running without waiting for the execution
return nil
}
// wait for the workflow to complete
// reuse scope for results
// this will be decoded back to event properties
scope, _, err = wait(ctx)
if err != nil {
return
}
if dec, is := ev.(varsDecoder); is {
return dec.DecodeVars(scope)
}
return nil
}
}
func loadWorkflow(ctx context.Context, s store.Storer, workflowID uint64) (res *types.Workflow, err error) {
if workflowID == 0 {
return nil, WorkflowErrInvalidID()

View File

@ -700,6 +700,38 @@ func WorkflowErrNotAllowedToUndelete(mm ...*workflowActionProps) *errors.Error {
return e
}
// WorkflowErrNotAllowedToExecute returns "automation:workflow.notAllowedToExecute" as *errors.Error
//
//
// This function is auto-generated.
//
func WorkflowErrNotAllowedToExecute(mm ...*workflowActionProps) *errors.Error {
var p = &workflowActionProps{}
if len(mm) > 0 {
p = mm[0]
}
var e = errors.New(
errors.KindInternal,
p.Format("not allowed to execute this workflow", nil),
errors.Meta("type", "notAllowedToExecute"),
errors.Meta("resource", "automation:workflow"),
// action log entry; no formatting, it will be applied inside recordAction fn.
errors.Meta(workflowLogMetaKey{}, "failed to execute {workflow}; insufficient permissions"),
errors.Meta(workflowPropsMetaKey{}, p),
errors.StackSkip(1),
)
if len(mm) > 0 {
}
return e
}
// WorkflowErrHandleNotUnique returns "automation:workflow.handleNotUnique" as *errors.Error
//
//

View File

@ -86,6 +86,10 @@ errors:
message: "not allowed to undelete this workflow"
log: "failed to undelete {workflow}; insufficient permissions"
- error: notAllowedToExecute
message: "not allowed to execute this workflow"
log: "failed to execute {workflow}; insufficient permissions"
- error: handleNotUnique
message: "workflow handle not unique"
log: "duplicate handle used for workflow ({workflow})"