Implement actionlog for applications
This commit is contained in:
parent
e8b81396ef
commit
84bb75e6f7
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza-server/pkg/permissions"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rh"
|
||||
@ -21,6 +22,8 @@ type (
|
||||
ac applicationAccessController
|
||||
eventbus eventDispatcher
|
||||
|
||||
actionlog actionlog.Recorder
|
||||
|
||||
application repository.ApplicationRepository
|
||||
}
|
||||
|
||||
@ -64,71 +67,111 @@ func (svc *application) With(ctx context.Context) ApplicationService {
|
||||
ac: svc.ac,
|
||||
eventbus: svc.eventbus,
|
||||
|
||||
actionlog: DefaultActionlog,
|
||||
|
||||
application: repository.Application(ctx, db),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *application) FindByID(ID uint64) (app *types.Application, err error) {
|
||||
if ID == 0 {
|
||||
return nil, ErrInvalidID.withStack()
|
||||
}
|
||||
var (
|
||||
aaProps = &applicationActionProps{application: &types.Application{ID: ID}}
|
||||
)
|
||||
|
||||
if app, err = svc.application.FindByID(ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = svc.db.Transaction(func() error {
|
||||
if ID == 0 {
|
||||
return ApplicationErrInvalidID()
|
||||
}
|
||||
|
||||
if !svc.ac.CanReadApplication(svc.ctx, app) {
|
||||
return nil, ErrNoPermissions.withStack()
|
||||
}
|
||||
if app, err = svc.application.FindByID(ID); err != nil {
|
||||
return ApplicationErrInvalidID().Wrap(err)
|
||||
}
|
||||
|
||||
return app, nil
|
||||
aaProps.setApplication(app)
|
||||
|
||||
if !svc.ac.CanReadApplication(svc.ctx, app) {
|
||||
return ApplicationErrNotAllowedToRead()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return app, svc.recordAction(svc.ctx, aaProps, ApplicationActionLookup, err)
|
||||
}
|
||||
|
||||
func (svc *application) Find(f types.ApplicationFilter) (types.ApplicationSet, types.ApplicationFilter, error) {
|
||||
f.IsReadable = svc.ac.FilterReadableApplications(svc.ctx)
|
||||
func (svc *application) Find(filter types.ApplicationFilter) (aa types.ApplicationSet, f types.ApplicationFilter, err error) {
|
||||
var (
|
||||
aaProps = &applicationActionProps{filter: &filter}
|
||||
)
|
||||
|
||||
if f.Deleted > rh.FilterStateExcluded {
|
||||
// If list with deleted applications is requested
|
||||
// user must have access permissions to system (ie: is admin)
|
||||
//
|
||||
// not the best solution but ATM it allows us to have at least
|
||||
// some kind of control over who can see deleted applications
|
||||
if !svc.ac.CanAccess(svc.ctx) {
|
||||
return nil, f, ErrNoPermissions.withStack()
|
||||
err = svc.db.Transaction(func() error {
|
||||
filter.IsReadable = svc.ac.FilterReadableApplications(svc.ctx)
|
||||
|
||||
if filter.Deleted > rh.FilterStateExcluded {
|
||||
// If list with deleted applications is requested
|
||||
// user must have access permissions to system (ie: is admin)
|
||||
//
|
||||
// not the best solution but ATM it allows us to have at least
|
||||
// some kind of control over who can see deleted applications
|
||||
if !svc.ac.CanAccess(svc.ctx) {
|
||||
return ApplicationErrNotAllowedToListApplications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return svc.application.Find(f)
|
||||
aa, f, err = svc.application.Find(filter)
|
||||
return err
|
||||
})
|
||||
|
||||
return aa, f, svc.recordAction(svc.ctx, aaProps, ApplicationActionSearch, err)
|
||||
}
|
||||
|
||||
func (svc *application) Create(new *types.Application) (app *types.Application, err error) {
|
||||
if !svc.ac.CanCreateApplication(svc.ctx) {
|
||||
return nil, ErrNoPermissions.withStack()
|
||||
}
|
||||
var (
|
||||
aaProps = &applicationActionProps{new: new}
|
||||
)
|
||||
|
||||
if err = svc.eventbus.WaitFor(svc.ctx, event.ApplicationBeforeCreate(new, nil)); err != nil {
|
||||
return
|
||||
}
|
||||
err = svc.db.Transaction(func() (err error) {
|
||||
if !svc.ac.CanCreateApplication(svc.ctx) {
|
||||
return ApplicationErrNotAllowedToCreate()
|
||||
}
|
||||
|
||||
if app, err = svc.application.Create(new); err != nil {
|
||||
return
|
||||
}
|
||||
if err = svc.eventbus.WaitFor(svc.ctx, event.ApplicationBeforeCreate(new, nil)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer svc.eventbus.Dispatch(svc.ctx, event.ApplicationAfterCreate(new, nil))
|
||||
return
|
||||
if app, err = svc.application.Create(new); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
aaProps.setApplication(app)
|
||||
|
||||
defer svc.eventbus.Dispatch(svc.ctx, event.ApplicationAfterCreate(new, nil))
|
||||
return nil
|
||||
})
|
||||
|
||||
return app, svc.recordAction(svc.ctx, aaProps, ApplicationActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc *application) Update(upd *types.Application) (app *types.Application, err error) {
|
||||
if !svc.ac.CanUpdateApplication(svc.ctx, upd) {
|
||||
return nil, ErrNoPermissions.withStack()
|
||||
}
|
||||
var (
|
||||
aaProps = &applicationActionProps{update: upd}
|
||||
)
|
||||
|
||||
err = svc.db.Transaction(func() (err error) {
|
||||
if upd.ID == 0 {
|
||||
return ApplicationErrInvalidID()
|
||||
}
|
||||
|
||||
return app, svc.db.Transaction(func() (err error) {
|
||||
if app, err = svc.application.FindByID(upd.ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
aaProps.setApplication(app)
|
||||
|
||||
if !svc.ac.CanUpdateApplication(svc.ctx, app) {
|
||||
return ApplicationErrNotAllowedToUpdate()
|
||||
}
|
||||
|
||||
if err = svc.eventbus.WaitFor(svc.ctx, event.ApplicationBeforeUpdate(upd, app)); err != nil {
|
||||
return
|
||||
}
|
||||
@ -145,38 +188,76 @@ func (svc *application) Update(upd *types.Application) (app *types.Application,
|
||||
defer svc.eventbus.Dispatch(svc.ctx, event.ApplicationAfterUpdate(upd, app))
|
||||
return nil
|
||||
})
|
||||
|
||||
return app, svc.recordAction(svc.ctx, aaProps, ApplicationActionUpdate, err)
|
||||
}
|
||||
|
||||
func (svc *application) Delete(ID uint64) (err error) {
|
||||
var (
|
||||
app *types.Application
|
||||
aaProps = &applicationActionProps{}
|
||||
app *types.Application
|
||||
)
|
||||
|
||||
if app, err = svc.application.FindByID(ID); err != nil {
|
||||
return
|
||||
}
|
||||
err = svc.db.Transaction(func() (err error) {
|
||||
if ID == 0 {
|
||||
return ApplicationErrInvalidID()
|
||||
}
|
||||
|
||||
if !svc.ac.CanDeleteApplication(svc.ctx, app) {
|
||||
return ErrNoPermissions.withStack()
|
||||
}
|
||||
if err = svc.eventbus.WaitFor(svc.ctx, event.ApplicationBeforeDelete(nil, app)); err != nil {
|
||||
return
|
||||
}
|
||||
if app, err = svc.application.FindByID(ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.application.DeleteByID(ID); err != nil {
|
||||
return
|
||||
}
|
||||
if !svc.ac.CanDeleteApplication(svc.ctx, app) {
|
||||
return ApplicationErrNotAllowedToDelete()
|
||||
}
|
||||
|
||||
defer svc.eventbus.Dispatch(svc.ctx, event.ApplicationAfterDelete(nil, app))
|
||||
return
|
||||
if err = svc.eventbus.WaitFor(svc.ctx, event.ApplicationBeforeDelete(nil, app)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.application.DeleteByID(ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer svc.eventbus.Dispatch(svc.ctx, event.ApplicationAfterDelete(nil, app))
|
||||
return nil
|
||||
})
|
||||
|
||||
return svc.recordAction(svc.ctx, aaProps, ApplicationActionDelete, err)
|
||||
}
|
||||
|
||||
func (svc *application) Undelete(ID uint64) error {
|
||||
app := &types.Application{ID: ID}
|
||||
func (svc *application) Undelete(ID uint64) (err error) {
|
||||
var (
|
||||
aaProps = &applicationActionProps{}
|
||||
app *types.Application
|
||||
)
|
||||
|
||||
if !svc.ac.CanDeleteApplication(svc.ctx, app) {
|
||||
return ErrNoPermissions.withStack()
|
||||
}
|
||||
err = svc.db.Transaction(func() (err error) {
|
||||
if ID == 0 {
|
||||
return ApplicationErrInvalidID()
|
||||
}
|
||||
|
||||
return svc.application.UndeleteByID(ID)
|
||||
if app, err = svc.application.FindByID(ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !svc.ac.CanDeleteApplication(svc.ctx, app) {
|
||||
return ApplicationErrNotAllowedToUndelete()
|
||||
}
|
||||
|
||||
// @todo add event
|
||||
// if err = svc.eventbus.WaitFor(svc.ctx, event.ApplicationBeforeUndelete(nil, app)); err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
if err = svc.application.UndeleteByID(ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// @todo add event
|
||||
// defer svc.eventbus.Dispatch(svc.ctx, event.ApplicationAfterUndelete(nil, app))
|
||||
return nil
|
||||
})
|
||||
|
||||
return svc.recordAction(svc.ctx, aaProps, ApplicationActionDelete, err)
|
||||
}
|
||||
|
||||
783
system/service/application_actions.gen.go
Normal file
783
system/service/application_actions.gen.go
Normal file
@ -0,0 +1,783 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated from system/service/application_actions.yaml
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
applicationActionProps struct {
|
||||
application *types.Application
|
||||
new *types.Application
|
||||
update *types.Application
|
||||
filter *types.ApplicationFilter
|
||||
}
|
||||
|
||||
applicationAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *applicationActionProps
|
||||
}
|
||||
|
||||
applicationError struct {
|
||||
timestamp time.Time
|
||||
error string
|
||||
resource string
|
||||
action string
|
||||
message string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
wrap error
|
||||
|
||||
props *applicationActionProps
|
||||
}
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setApplication updates applicationActionProps's application
|
||||
//
|
||||
// Allows method chaining
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *applicationActionProps) setApplication(application *types.Application) *applicationActionProps {
|
||||
p.application = application
|
||||
return p
|
||||
}
|
||||
|
||||
// setNew updates applicationActionProps's new
|
||||
//
|
||||
// Allows method chaining
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *applicationActionProps) setNew(new *types.Application) *applicationActionProps {
|
||||
p.new = new
|
||||
return p
|
||||
}
|
||||
|
||||
// setUpdate updates applicationActionProps's update
|
||||
//
|
||||
// Allows method chaining
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *applicationActionProps) setUpdate(update *types.Application) *applicationActionProps {
|
||||
p.update = update
|
||||
return p
|
||||
}
|
||||
|
||||
// setFilter updates applicationActionProps's filter
|
||||
//
|
||||
// Allows method chaining
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *applicationActionProps) setFilter(filter *types.ApplicationFilter) *applicationActionProps {
|
||||
p.filter = filter
|
||||
return p
|
||||
}
|
||||
|
||||
// serialize converts applicationActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p applicationActionProps) serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
str = func(i interface{}) string { return fmt.Sprintf("%v", i) }
|
||||
)
|
||||
|
||||
if p.application != nil {
|
||||
m["application.name"] = str(p.application.Name)
|
||||
m["application.ID"] = str(p.application.ID)
|
||||
}
|
||||
if p.new != nil {
|
||||
m["new.name"] = str(p.new.Name)
|
||||
m["new.ID"] = str(p.new.ID)
|
||||
}
|
||||
if p.update != nil {
|
||||
m["update.name"] = str(p.update.Name)
|
||||
m["update.ID"] = str(p.update.ID)
|
||||
}
|
||||
if p.filter != nil {
|
||||
m["filter.query"] = str(p.filter.Query)
|
||||
m["filter.name"] = str(p.filter.Name)
|
||||
m["filter.deleted"] = str(p.filter.Deleted)
|
||||
m["filter.sort"] = str(p.filter.Sort)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p applicationActionProps) tr(in string, err error) string {
|
||||
var pairs = []string{"{err}"}
|
||||
|
||||
if err != nil {
|
||||
for {
|
||||
// Unwrap errors
|
||||
ue := errors.Unwrap(err)
|
||||
if ue == nil {
|
||||
break
|
||||
}
|
||||
|
||||
err = ue
|
||||
}
|
||||
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.application != nil {
|
||||
pairs = append(pairs, "{application}", fmt.Sprintf("%v", p.application.Name))
|
||||
pairs = append(pairs, "{application.name}", fmt.Sprintf("%v", p.application.Name))
|
||||
pairs = append(pairs, "{application.ID}", fmt.Sprintf("%v", p.application.ID))
|
||||
}
|
||||
|
||||
if p.new != nil {
|
||||
pairs = append(pairs, "{new}", fmt.Sprintf("%v", p.new.Name))
|
||||
pairs = append(pairs, "{new.name}", fmt.Sprintf("%v", p.new.Name))
|
||||
pairs = append(pairs, "{new.ID}", fmt.Sprintf("%v", p.new.ID))
|
||||
}
|
||||
|
||||
if p.update != nil {
|
||||
pairs = append(pairs, "{update}", fmt.Sprintf("%v", p.update.Name))
|
||||
pairs = append(pairs, "{update.name}", fmt.Sprintf("%v", p.update.Name))
|
||||
pairs = append(pairs, "{update.ID}", fmt.Sprintf("%v", p.update.ID))
|
||||
}
|
||||
|
||||
if p.filter != nil {
|
||||
pairs = append(pairs, "{filter}", fmt.Sprintf("%v", p.filter.Query))
|
||||
pairs = append(pairs, "{filter.query}", fmt.Sprintf("%v", p.filter.Query))
|
||||
pairs = append(pairs, "{filter.name}", fmt.Sprintf("%v", p.filter.Name))
|
||||
pairs = append(pairs, "{filter.deleted}", fmt.Sprintf("%v", p.filter.Deleted))
|
||||
pairs = append(pairs, "{filter.sort}", fmt.Sprintf("%v", p.filter.Sort))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (a *applicationAction) String() string {
|
||||
var props = &applicationActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.tr(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *applicationAction) LoggableAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Timestamp: e.timestamp,
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// It falls back to message if log is not set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (e *applicationError) String() string {
|
||||
var props = &applicationActionProps{}
|
||||
|
||||
if e.props != nil {
|
||||
props = e.props
|
||||
}
|
||||
|
||||
if e.wrap != nil && !strings.Contains(e.log, "{err}") {
|
||||
// Suffix error log with {err} to ensure
|
||||
// we log the cause for this error
|
||||
e.log += ": {err}"
|
||||
}
|
||||
|
||||
return props.tr(e.log, e.wrap)
|
||||
}
|
||||
|
||||
// Error satisfies
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (e *applicationError) Error() string {
|
||||
var props = &applicationActionProps{}
|
||||
|
||||
if e.props != nil {
|
||||
props = e.props
|
||||
}
|
||||
|
||||
return props.tr(e.message, e.wrap)
|
||||
}
|
||||
|
||||
// Is fn for error equality check
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (e *applicationError) Is(Resource error) bool {
|
||||
t, ok := Resource.(*applicationError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.resource == e.resource && t.error == e.error
|
||||
}
|
||||
|
||||
// Wrap wraps applicationError around another error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (e *applicationError) Wrap(err error) *applicationError {
|
||||
e.wrap = err
|
||||
return e
|
||||
}
|
||||
|
||||
// Unwrap returns wrapped error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (e *applicationError) Unwrap() error {
|
||||
return e.wrap
|
||||
}
|
||||
|
||||
func (e *applicationError) LoggableAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Timestamp: e.timestamp,
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Error: e.Error(),
|
||||
Meta: e.props.serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// ApplicationActionSearch returns "system:application.search" error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationActionSearch(props ...*applicationActionProps) *applicationAction {
|
||||
a := &applicationAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
action: "search",
|
||||
log: "searched for applications",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ApplicationActionLookup returns "system:application.lookup" error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationActionLookup(props ...*applicationActionProps) *applicationAction {
|
||||
a := &applicationAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
action: "lookup",
|
||||
log: "looked-up for a {application}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ApplicationActionCreate returns "system:application.create" error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationActionCreate(props ...*applicationActionProps) *applicationAction {
|
||||
a := &applicationAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
action: "create",
|
||||
log: "created {application}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ApplicationActionUpdate returns "system:application.update" error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationActionUpdate(props ...*applicationActionProps) *applicationAction {
|
||||
a := &applicationAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
action: "update",
|
||||
log: "updated {application}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ApplicationActionDelete returns "system:application.delete" error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationActionDelete(props ...*applicationActionProps) *applicationAction {
|
||||
a := &applicationAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
action: "delete",
|
||||
log: "deleted {application}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ApplicationActionUndelete returns "system:application.undelete" error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationActionUndelete(props ...*applicationActionProps) *applicationAction {
|
||||
a := &applicationAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
action: "undelete",
|
||||
log: "undeleted {application}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// ApplicationErrGeneric returns "system:application.generic" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrGeneric(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "generic",
|
||||
action: "error",
|
||||
message: "failed to complete request due to internal error",
|
||||
log: "{err}",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNonexistent returns "system:application.nonexistent" audit event as actionlog.Warning
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNonexistent(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "nonexistent",
|
||||
action: "error",
|
||||
message: "application does not exist",
|
||||
log: "application does not exist",
|
||||
severity: actionlog.Warning,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrInvalidID returns "system:application.invalidID" audit event as actionlog.Warning
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrInvalidID(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "invalidID",
|
||||
action: "error",
|
||||
message: "invalid ID",
|
||||
log: "invalid ID",
|
||||
severity: actionlog.Warning,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNotAllowedToRead returns "system:application.notAllowedToRead" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNotAllowedToRead(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "notAllowedToRead",
|
||||
action: "error",
|
||||
message: "not allowed to read application",
|
||||
log: "failed to read {application.name}; insufficient permissions",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNotAllowedToListApplications returns "system:application.notAllowedToListApplications" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNotAllowedToListApplications(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "notAllowedToListApplications",
|
||||
action: "error",
|
||||
message: "not allowed to list applications",
|
||||
log: "failed to list application; insufficient permissions",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNotAllowedToCreate returns "system:application.notAllowedToCreate" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNotAllowedToCreate(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "notAllowedToCreate",
|
||||
action: "error",
|
||||
message: "not allowed to create application",
|
||||
log: "failed to create application; insufficient permissions",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNotAllowedToUpdate returns "system:application.notAllowedToUpdate" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNotAllowedToUpdate(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "notAllowedToUpdate",
|
||||
action: "error",
|
||||
message: "not allowed to update application",
|
||||
log: "failed to update {application.name}; insufficient permissions",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNotAllowedToDelete returns "system:application.notAllowedToDelete" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNotAllowedToDelete(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "notAllowedToDelete",
|
||||
action: "error",
|
||||
message: "not allowed to delete application",
|
||||
log: "failed to delete {application.name}; insufficient permissions",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// ApplicationErrNotAllowedToUndelete returns "system:application.notAllowedToUndelete" audit event as actionlog.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func ApplicationErrNotAllowedToUndelete(props ...*applicationActionProps) *applicationError {
|
||||
var e = &applicationError{
|
||||
timestamp: time.Now(),
|
||||
resource: "system:application",
|
||||
error: "notAllowedToUndelete",
|
||||
action: "error",
|
||||
message: "not allowed to undelete application",
|
||||
log: "failed to undelete {application.name}; insufficient permissions",
|
||||
severity: actionlog.Error,
|
||||
props: func() *applicationActionProps {
|
||||
if len(props) > 0 {
|
||||
return props[0]
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
e.props = props[0]
|
||||
}
|
||||
|
||||
return e
|
||||
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// context is used to enrich audit log entry with current user info, request ID, IP address...
|
||||
// props are collected action/error properties
|
||||
// action (optional) fn will be used to construct applicationAction struct from given props (and error)
|
||||
// err is any error that occurred while action was happening
|
||||
//
|
||||
// Action has success and fail (error) state:
|
||||
// - when recorded without an error (4th param), action is recorded as successful.
|
||||
// - when an additional error is given (4th param), action is used to wrap
|
||||
// the additional error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (svc application) recordAction(ctx context.Context, props *applicationActionProps, action func(...*applicationActionProps) *applicationAction, err error) error {
|
||||
var (
|
||||
ok bool
|
||||
|
||||
// Return error
|
||||
retError *applicationError
|
||||
|
||||
// Recorder error
|
||||
recError *applicationError
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if retError, ok = err.(*applicationError); !ok {
|
||||
// got non-application error, wrap it with ApplicationErrGeneric
|
||||
retError = ApplicationErrGeneric(props).Wrap(err)
|
||||
|
||||
// copy action to returning and recording error
|
||||
retError.action = action().action
|
||||
|
||||
// we'll use ApplicationErrGeneric for recording too
|
||||
// because it can hold more info
|
||||
recError = retError
|
||||
} else if retError != nil {
|
||||
// copy action to returning and recording error
|
||||
retError.action = action().action
|
||||
// start with copy of return error for recording
|
||||
// this will be updated with tha root cause as we try and
|
||||
// unwrap the error
|
||||
recError = retError
|
||||
|
||||
// find the original recError for this error
|
||||
// for the purpose of logging
|
||||
var unwrappedError error = retError
|
||||
for {
|
||||
if unwrappedError = errors.Unwrap(unwrappedError); unwrappedError == nil {
|
||||
// nothing wrapped
|
||||
break
|
||||
}
|
||||
|
||||
// update recError ONLY of wrapped error is of type applicationError
|
||||
if unwrappedSinkError, ok := unwrappedError.(*applicationError); ok {
|
||||
recError = unwrappedSinkError
|
||||
}
|
||||
}
|
||||
|
||||
if retError.props == nil {
|
||||
// set props on returning error if empty
|
||||
retError.props = props
|
||||
}
|
||||
|
||||
if recError.props == nil {
|
||||
// set props on recording error if empty
|
||||
recError.props = props
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if svc.actionlog != nil {
|
||||
if retError != nil {
|
||||
// failed action, log error
|
||||
svc.actionlog.Record(ctx, recError)
|
||||
} else if action != nil {
|
||||
// successful
|
||||
svc.actionlog.Record(ctx, action(props))
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// retError not an interface and that WILL (!!) cause issues
|
||||
// with nil check (== nil) when it is not explicitly returned
|
||||
return nil
|
||||
}
|
||||
|
||||
return retError
|
||||
}
|
||||
81
system/service/application_actions.yaml
Normal file
81
system/service/application_actions.yaml
Normal file
@ -0,0 +1,81 @@
|
||||
# List of loggable service actions
|
||||
|
||||
resource: system:application
|
||||
service: application
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: info
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: error
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza-server/system/types
|
||||
|
||||
props:
|
||||
- name: application
|
||||
type: "*types.Application"
|
||||
fields: [ name, ID ]
|
||||
- name: new
|
||||
type: "*types.Application"
|
||||
fields: [ name, ID ]
|
||||
- name: update
|
||||
type: "*types.Application"
|
||||
fields: [ name, ID ]
|
||||
- name: filter
|
||||
type: "*types.ApplicationFilter"
|
||||
fields: [ query, name, deleted, sort ]
|
||||
|
||||
actions:
|
||||
- action: search
|
||||
log: "searched for applications"
|
||||
severity: info
|
||||
|
||||
- action: lookup
|
||||
log: "looked-up for a {application}"
|
||||
severity: info
|
||||
|
||||
- action: create
|
||||
log: "created {application}"
|
||||
|
||||
- action: update
|
||||
log: "updated {application}"
|
||||
|
||||
- action: delete
|
||||
log: "deleted {application}"
|
||||
|
||||
- action: undelete
|
||||
log: "undeleted {application}"
|
||||
|
||||
errors:
|
||||
- error: nonexistent
|
||||
message: "application does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: invalidID
|
||||
message: "invalid ID"
|
||||
severity: warning
|
||||
|
||||
- error: notAllowedToRead
|
||||
message: "not allowed to read application"
|
||||
log: "failed to read {application.name}; insufficient permissions"
|
||||
|
||||
- error: notAllowedToListApplications
|
||||
message: "not allowed to list applications"
|
||||
log: "failed to list application; insufficient permissions"
|
||||
|
||||
- error: notAllowedToCreate
|
||||
message: "not allowed to create application"
|
||||
log: "failed to create application; insufficient permissions"
|
||||
|
||||
- error: notAllowedToUpdate
|
||||
message: "not allowed to update application"
|
||||
log: "failed to update {application.name}; insufficient permissions"
|
||||
|
||||
- error: notAllowedToDelete
|
||||
message: "not allowed to delete application"
|
||||
log: "failed to delete {application.name}; insufficient permissions"
|
||||
|
||||
- error: notAllowedToUndelete
|
||||
message: "not allowed to undelete application"
|
||||
log: "failed to undelete {application.name}; insufficient permissions"
|
||||
@ -80,7 +80,7 @@ func TestApplicationCreateForbidden(t *testing.T) {
|
||||
FormData("name", "my-app").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("system.service.NoPermissions")).
|
||||
Assert(helpers.AssertError("not allowed to create application")).
|
||||
End()
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ func TestApplicationUpdateForbidden(t *testing.T) {
|
||||
FormData("name", "changed-name").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("system.service.NoPermissions")).
|
||||
Assert(helpers.AssertError("not allowed to update application")).
|
||||
End()
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ func TestApplicationDeleteForbidden(t *testing.T) {
|
||||
Delete(fmt.Sprintf("/application/%d", a.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("system.service.NoPermissions")).
|
||||
Assert(helpers.AssertError("not allowed to delete application")).
|
||||
End()
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user