Add access-control check for execution
This commit is contained in:
parent
2e011f2427
commit
739e994145
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
//
|
||||
//
|
||||
|
||||
@ -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})"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user