Add trigger management access-control, disable direct trigger management

This commit is contained in:
Denis Arh
2019-08-25 09:54:16 +02:00
parent c671de3df7
commit eb1c1cf65f
6 changed files with 159 additions and 72 deletions
+1 -1
View File
@@ -114,7 +114,7 @@ func (svc accessControl) CanDeleteRecord(ctx context.Context, r *types.Module) b
}
func (svc accessControl) CanManageAutomationTriggersOnModule(ctx context.Context, r *types.Module) bool {
return svc.can(ctx, r, "automation-trigger.manage", permissions.Allowed)
return svc.can(ctx, r, "automation-trigger.manage")
}
func (svc accessControl) CanCreateChart(ctx context.Context, r *types.Namespace) bool {
+89 -26
View File
@@ -16,6 +16,7 @@ type (
logger *zap.Logger
scriptManager automationScriptManager
ns NamespaceService
mod ModuleService
ac automationScriptAccessController
}
@@ -37,6 +38,8 @@ type (
CanReadAutomationScript(context.Context, *automation.Script) bool
CanUpdateAutomationScript(context.Context, *automation.Script) bool
CanDeleteAutomationScript(context.Context, *automation.Script) bool
CanManageAutomationTriggersOnModule(context.Context, *types.Module) bool
}
automationScriptNamespaceFinder interface {
@@ -49,7 +52,8 @@ func AutomationScript(sm automationScriptManager) automationScript {
scriptManager: sm,
logger: DefaultLogger.Named("automation-script"),
ac: DefaultAccessControl,
ns: Namespace(),
mod: DefaultModule,
ns: DefaultNamespace,
}
return svc
@@ -78,9 +82,13 @@ func (svc automationScript) Find(ctx context.Context, namespaceID uint64, f auto
}
func (svc automationScript) Create(ctx context.Context, namespaceID uint64, mod *automation.Script) (err error) {
if ns, _, err := svc.loadCombo(ctx, namespaceID, 0); err != nil {
var ns *types.Namespace
if ns, _, err = svc.loadCombo(ctx, namespaceID, 0); err != nil {
return err
} else if !svc.ac.CanCreateAutomationScript(ctx, ns) {
}
if !svc.ac.CanCreateAutomationScript(ctx, ns) {
return ErrNoCreatePermissions.withStack()
}
@@ -90,35 +98,57 @@ func (svc automationScript) Create(ctx context.Context, namespaceID uint64, mod
}
}
err = mod.Triggers().Walk(func(t *automation.Trigger) error {
return svc.isValidTrigger(ctx, t, namespaceID)
})
if err != nil {
return
}
return svc.scriptManager.CreateScript(ctx, mod)
}
func (svc automationScript) Update(ctx context.Context, namespaceID uint64, mod *automation.Script) (err error) {
if _, s, err := svc.loadCombo(ctx, namespaceID, mod.ID); err != nil {
var s *automation.Script
if _, s, err = svc.loadCombo(ctx, namespaceID, mod.ID); err != nil {
return err
} else if !svc.ac.CanUpdateAutomationScript(ctx, s) {
return ErrNoCreatePermissions.withStack()
} else {
// Users need to have grant privileges to
// set script runner
if mod.RunAs != s.RunAs {
if !svc.ac.CanGrant(ctx) {
return ErrNoGrantPermissions
}
}
s.Name = mod.Name
s.SourceRef = mod.SourceRef
s.Source = mod.Source
s.Async = mod.Async
s.RunAs = mod.RunAs
s.RunInUA = mod.RunInUA
s.Timeout = mod.Timeout
s.Critical = mod.Critical
s.Enabled = mod.Enabled
return svc.scriptManager.UpdateScript(ctx, s)
}
if !svc.ac.CanUpdateAutomationScript(ctx, s) {
return ErrNoCreatePermissions.withStack()
}
// Users need to have grant privileges to
// set script runner
if mod.RunAs != s.RunAs {
if !svc.ac.CanGrant(ctx) {
return ErrNoGrantPermissions
}
}
s.Name = mod.Name
s.SourceRef = mod.SourceRef
s.Source = mod.Source
s.Async = mod.Async
s.RunAs = mod.RunAs
s.RunInUA = mod.RunInUA
s.Timeout = mod.Timeout
s.Critical = mod.Critical
s.Enabled = mod.Enabled
err = mod.Triggers().Walk(func(t *automation.Trigger) error {
return svc.isValidTrigger(ctx, t, namespaceID)
})
if err != nil {
return
}
s.AddTrigger(automation.STMS_UPDATE, mod.Triggers()...)
return svc.scriptManager.UpdateScript(ctx, s)
}
func (svc automationScript) Delete(ctx context.Context, namespaceID, scriptID uint64) (err error) {
@@ -155,3 +185,36 @@ func (svc automationScript) loadCombo(ctx context.Context, namespaceID, scriptID
return
}
func (svc automationScript) isValidTrigger(ctx context.Context, t *automation.Trigger, namespaceID uint64) error {
if t.Resource != "compose:record" {
// Accepting only compose:record resources
return automation.ErrAutomationTriggerInvalidResource
}
if t.IsDeferred() {
// @todo validate condition for deferred triggers
return nil
}
switch t.Event {
case "manual",
"beforeCreate", "beforeUpdate", "beforeDelete",
"afterCreate", "afterUpdate", "afterDelete":
var moduleID = t.Uint64Condition()
if t.Event != "manual" && moduleID == 0 {
return automation.ErrAutomationTriggerInvalidCondition
}
if m, err := svc.mod.With(ctx).FindByID(namespaceID, moduleID); err != nil {
return err
} else if !svc.ac.CanManageAutomationTriggersOnModule(ctx, m) {
return ErrNoTriggerManagementPermissions
}
default:
return automation.ErrAutomationTriggerInvalidEvent
}
return nil
}
+11 -10
View File
@@ -9,16 +9,17 @@ type (
)
const (
ErrInvalidID serviceError = "InvalidID"
ErrStaleData serviceError = "StaleData"
ErrNoGrantPermissions serviceError = "NoGrantPermissions"
ErrNoCreatePermissions serviceError = "NoCreatePermissions"
ErrNoReadPermissions serviceError = "NoReadPermissions"
ErrNoUpdatePermissions serviceError = "NoUpdatePermissions"
ErrNoDeletePermissions serviceError = "NoDeletePermissions"
ErrNamespaceRequired serviceError = "NamespaceRequired"
ErrModulePageExists serviceError = "ModulePageExists"
ErrNotImplemented serviceError = "NotImplemented"
ErrInvalidID serviceError = "InvalidID"
ErrStaleData serviceError = "StaleData"
ErrNoGrantPermissions serviceError = "NoGrantPermissions"
ErrNoCreatePermissions serviceError = "NoCreatePermissions"
ErrNoReadPermissions serviceError = "NoReadPermissions"
ErrNoUpdatePermissions serviceError = "NoUpdatePermissions"
ErrNoDeletePermissions serviceError = "NoDeletePermissions"
ErrNoTriggerManagementPermissions serviceError = "NoTriggerManagementPermissions"
ErrNamespaceRequired serviceError = "NamespaceRequired"
ErrModulePageExists serviceError = "ModulePageExists"
ErrNotImplemented serviceError = "NotImplemented"
)
func (e serviceError) Error() string {
+2 -3
View File
@@ -77,6 +77,8 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
permissions.Repository(db, "compose_permission_rules"))
DefaultAccessControl = AccessControl(DefaultPermissions)
DefaultNamespace = Namespace()
DefaultModule = Module()
{
systemClientConn, err := NewSystemGRPCClient(ctx, c.GRPCClientSystem, DefaultLogger)
@@ -128,10 +130,7 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
)
}
// Compose internals:
DefaultNamespace = Namespace()
DefaultRecord = Record()
DefaultModule = Module()
DefaultPage = Page()
DefaultChart = Chart()
DefaultNotification = Notification()
+40 -32
View File
@@ -4,7 +4,6 @@ import (
"context"
"github.com/pkg/errors"
"github.com/titpetric/factory/resputil"
"github.com/cortezaproject/corteza-server/compose/internal/service"
"github.com/cortezaproject/corteza-server/compose/rest/request"
@@ -78,22 +77,25 @@ func (ctrl AutomationTrigger) List(ctx context.Context, r *request.AutomationTri
}
func (ctrl AutomationTrigger) Create(ctx context.Context, r *request.AutomationTriggerCreate) (interface{}, error) {
s, _, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, 0)
if err != nil {
return nil, errors.Wrap(err, "can not create trigger")
}
// @todo trigger management is currently done through automation-script endpoints
return nil, errors.New("direct trigger management disabled")
var (
t = &automation.Trigger{
Event: r.Event,
Resource: r.Resource,
Condition: r.Condition,
ScriptID: s.ID,
Enabled: r.Enabled,
}
)
return ctrl.makePayload(ctx, t, ctrl.triggers.Create(ctx, s, t))
// s, _, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, 0)
// if err != nil {
// return nil, errors.Wrap(err, "can not create trigger")
// }
//
// var (
// t = &automation.Trigger{
// Event: r.Event,
// Resource: r.Resource,
// Condition: r.Condition,
// ScriptID: s.ID,
// Enabled: r.Enabled,
// }
// )
//
// return ctrl.makePayload(ctx, t, ctrl.triggers.Create(ctx, s, t))
}
func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTriggerRead) (interface{}, error) {
@@ -106,27 +108,33 @@ func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTri
}
func (ctrl AutomationTrigger) Update(ctx context.Context, r *request.AutomationTriggerUpdate) (interface{}, error) {
s, t, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, r.TriggerID)
if err != nil {
return nil, errors.Wrap(err, "can not update trigger")
}
// @todo trigger management is currently done through automation-script endpoints
return nil, errors.New("direct trigger management disabled")
t.Event = r.Event
t.Resource = r.Resource
t.Condition = r.Condition
t.ScriptID = r.ScriptID
t.Enabled = r.Enabled
return ctrl.makePayload(ctx, t, ctrl.triggers.Update(ctx, s, t))
// s, t, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, r.TriggerID)
// if err != nil {
// return nil, errors.Wrap(err, "can not update trigger")
// }
//
// t.Event = r.Event
// t.Resource = r.Resource
// t.Condition = r.Condition
// t.ScriptID = r.ScriptID
// t.Enabled = r.Enabled
//
// return ctrl.makePayload(ctx, t, ctrl.triggers.Update(ctx, s, t))
}
func (ctrl AutomationTrigger) Delete(ctx context.Context, r *request.AutomationTriggerDelete) (interface{}, error) {
trigger, err := ctrl.triggers.FindByID(ctx, r.TriggerID)
if err != nil {
return nil, errors.Wrap(err, "can not delete trigger")
}
// @todo trigger management is currently done through automation-script endpoints
return nil, errors.New("direct trigger management disabled")
return resputil.OK(), ctrl.triggers.Delete(ctx, trigger)
// trigger, err := ctrl.triggers.FindByID(ctx, r.TriggerID)
// if err != nil {
// return nil, errors.Wrap(err, "can not delete trigger")
// }
//
// return resputil.OK(), ctrl.triggers.Delete(ctx, trigger)
}
func (ctrl AutomationTrigger) loadCombo(ctx context.Context, namespaceID, scriptID, triggerID uint64) (s *automation.Script, t *automation.Trigger, err error) {
+16
View File
@@ -1,6 +1,8 @@
package automation
import (
"errors"
"strconv"
"time"
"github.com/cortezaproject/corteza-server/pkg/rh"
@@ -66,6 +68,12 @@ const (
EVENT_TYPE_DEFERRED = "deferred"
)
var (
ErrAutomationTriggerInvalidResource = errors.New("AutomationTriggerInvalidResource")
ErrAutomationTriggerInvalidCondition = errors.New("AutomationTriggerInvalidCondition")
ErrAutomationTriggerInvalidEvent = errors.New("AutomationTriggerInvalidEvent")
)
// IsValid checks if trigger is enabled and not deleted
func (t *Trigger) IsValid() bool {
return t != nil && t.Enabled && t.DeletedAt == nil
@@ -81,6 +89,14 @@ func (t Trigger) IsDeferred() bool {
return t.Event == EVENT_TYPE_DEFERRED
}
// Uint64Condition converts condition to uint64
//
// Errors are ignored
func (t Trigger) Uint64Condition() (o uint64) {
o, _ = strconv.ParseUint(t.Condition, 10, 64)
return
}
// HasMatch checks if any og the triggers in a set matches the given parameters
func (set TriggerSet) HasMatch(m Trigger, ff ...TriggerConditionChecker) bool {
withTriggers: