From b9e102313d888801c63e4949d1cb05665a26eb2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Tue, 21 Mar 2023 11:58:41 +0100 Subject: [PATCH] Add access control and support for personal layouts --- .../automation/corredor_handler.gen.go | 1 - server/compose/model/models.gen.go | 8 + server/compose/page.cue | 7 + server/compose/page_layout.cue | 8 + server/compose/rest.yaml | 10 + server/compose/rest/page.go | 2 + server/compose/rest/request/page.go | 70 +++++ server/compose/service/access_control.gen.go | 70 ++++- server/compose/service/page.go | 10 + server/compose/service/page_layout.go | 105 +++++--- .../service/page_layout_actions.gen.go | 252 ++++++++++++++++++ .../compose/service/page_layout_actions.yaml | 46 ++-- server/compose/types/page.go | 6 + server/compose/types/parsers.go | 5 + server/store/adapters/rdbms/aux_types.gen.go | 4 + server/store/adapters/rdbms/queries.gen.go | 4 + .../system/service/attachment_actions.gen.go | 6 + server/system/service/user_actions.gen.go | 5 + 18 files changed, 549 insertions(+), 70 deletions(-) diff --git a/server/automation/automation/corredor_handler.gen.go b/server/automation/automation/corredor_handler.gen.go index bbb39e316..cd4b0b6eb 100644 --- a/server/automation/automation/corredor_handler.gen.go +++ b/server/automation/automation/corredor_handler.gen.go @@ -10,7 +10,6 @@ package automation import ( "context" - atypes "github.com/cortezaproject/corteza/server/automation/types" "github.com/cortezaproject/corteza/server/pkg/expr" "github.com/cortezaproject/corteza/server/pkg/wfexec" diff --git a/server/compose/model/models.gen.go b/server/compose/model/models.gen.go index adde7cf18..4722b1ceb 100644 --- a/server/compose/model/models.gen.go +++ b/server/compose/model/models.gen.go @@ -632,6 +632,14 @@ var Page = &dal.Model{ Store: &dal.CodecAlias{Ident: "rel_namespace"}, }, + &dal.Attribute{ + Ident: "Meta", + Type: &dal.TypeJSON{ + DefaultValue: "{}", + }, + Store: &dal.CodecAlias{Ident: "meta"}, + }, + &dal.Attribute{ Ident: "Config", Type: &dal.TypeJSON{ diff --git a/server/compose/page.cue b/server/compose/page.cue index 4c2122d35..b250d6a09 100644 --- a/server/compose/page.cue +++ b/server/compose/page.cue @@ -58,6 +58,10 @@ page: { } } + meta: { + goType: "types.PageMeta" + dal: { type: "JSON", defaultEmptyObject: true } + } config: { goType: "types.PageConfig" dal: { type: "JSON", defaultEmptyObject: true } @@ -160,6 +164,9 @@ page: { "read": {} "update": {} "delete": {} + + "page-layout.create": description: "Create page layout on namespace" + "page-layouts.search": description: "List, search or filter page layouts on namespace" } } diff --git a/server/compose/page_layout.cue b/server/compose/page_layout.cue index 373f3f316..2091132c2 100644 --- a/server/compose/page_layout.cue +++ b/server/compose/page_layout.cue @@ -97,6 +97,14 @@ pageLayout: { } } + rbac: { + operations: { + "read": {} + "update": {} + "delete": {} + } + } + locale: { extended: true diff --git a/server/compose/rest.yaml b/server/compose/rest.yaml index ccb324b46..6affe957d 100644 --- a/server/compose/rest.yaml +++ b/server/compose/rest.yaml @@ -345,6 +345,11 @@ endpoints: name: config required: false title: Config JSON + - type: "*types.PageMeta" + name: meta + required: false + title: Meta + parser: types.ParsePageMeta - name: read path: "/{pageID}" method: GET @@ -411,6 +416,11 @@ endpoints: name: config required: false title: Config JSON + - type: "*types.PageMeta" + name: meta + required: false + title: Meta + parser: types.ParsePageMeta - name: reorder method: POST title: Reorder pages diff --git a/server/compose/rest/page.go b/server/compose/rest/page.go index 1b28ebb0d..c90d32943 100644 --- a/server/compose/rest/page.go +++ b/server/compose/rest/page.go @@ -118,6 +118,7 @@ func (ctrl *Page) Create(ctx context.Context, r *request.PageCreate) (interface{ Visible: r.Visible, Weight: r.Weight, Labels: r.Labels, + Meta: r.Meta, } ) @@ -168,6 +169,7 @@ func (ctrl *Page) Update(ctx context.Context, r *request.PageUpdate) (interface{ Visible: r.Visible, Weight: r.Weight, Labels: r.Labels, + Meta: r.Meta, } ) diff --git a/server/compose/rest/request/page.go b/server/compose/rest/request/page.go index f8b3cdb48..63951e4f7 100644 --- a/server/compose/rest/request/page.go +++ b/server/compose/rest/request/page.go @@ -140,6 +140,11 @@ type ( // // Config JSON Config sqlxTypes.JSONText + + // Meta POST parameter + // + // Meta + Meta *types.PageMeta } PageRead struct { @@ -221,6 +226,11 @@ type ( // // Config JSON Config sqlxTypes.JSONText + + // Meta POST parameter + // + // Meta + Meta *types.PageMeta } PageReorder struct { @@ -514,6 +524,7 @@ func (r PageCreate) Auditable() map[string]interface{} { "visible": r.Visible, "blocks": r.Blocks, "config": r.Config, + "meta": r.Meta, } } @@ -572,6 +583,11 @@ func (r PageCreate) GetConfig() sqlxTypes.JSONText { return r.Config } +// Auditable returns all auditable/loggable parameters +func (r PageCreate) GetMeta() *types.PageMeta { + return r.Meta +} + // Fill processes request and fills internal variables func (r *PageCreate) Fill(req *http.Request) (err error) { @@ -667,6 +683,18 @@ func (r *PageCreate) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.MultipartForm.Value["meta[]"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } else if val, ok := req.MultipartForm.Value["meta"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } } } @@ -751,6 +779,18 @@ func (r *PageCreate) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["meta[]"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } else if val, ok := req.Form["meta"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } } { @@ -870,6 +910,7 @@ func (r PageUpdate) Auditable() map[string]interface{} { "visible": r.Visible, "blocks": r.Blocks, "config": r.Config, + "meta": r.Meta, } } @@ -933,6 +974,11 @@ func (r PageUpdate) GetConfig() sqlxTypes.JSONText { return r.Config } +// Auditable returns all auditable/loggable parameters +func (r PageUpdate) GetMeta() *types.PageMeta { + return r.Meta +} + // Fill processes request and fills internal variables func (r *PageUpdate) Fill(req *http.Request) (err error) { @@ -1028,6 +1074,18 @@ func (r *PageUpdate) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.MultipartForm.Value["meta[]"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } else if val, ok := req.MultipartForm.Value["meta"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } } } @@ -1112,6 +1170,18 @@ func (r *PageUpdate) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["meta[]"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } else if val, ok := req.Form["meta"]; ok { + r.Meta, err = types.ParsePageMeta(val) + if err != nil { + return err + } + } } { diff --git a/server/compose/service/access_control.gen.go b/server/compose/service/access_control.gen.go index df07f3d6c..f03ae753c 100644 --- a/server/compose/service/access_control.gen.go +++ b/server/compose/service/access_control.gen.go @@ -272,6 +272,31 @@ func (svc accessControl) List() (out []map[string]string) { "any": types.PageRbacResource(0, 0), "op": "delete", }, + { + "type": types.PageResourceType, + "any": types.PageRbacResource(0, 0), + "op": "page-layout.create", + }, + { + "type": types.PageResourceType, + "any": types.PageRbacResource(0, 0), + "op": "page-layouts.search", + }, + { + "type": types.PageLayoutResourceType, + "any": types.PageLayoutRbacResource(0, 0, 0), + "op": "read", + }, + { + "type": types.PageLayoutResourceType, + "any": types.PageLayoutRbacResource(0, 0, 0), + "op": "update", + }, + { + "type": types.PageLayoutResourceType, + "any": types.PageLayoutRbacResource(0, 0, 0), + "op": "delete", + }, { "type": types.RecordResourceType, "any": types.RecordRbacResource(0, 0, 0), @@ -592,6 +617,41 @@ func (svc accessControl) CanDeletePage(ctx context.Context, r *types.Page) bool return svc.can(ctx, "delete", r) } +// CanCreatePageLayoutOnPage checks if current user can create page layout on namespace +// +// This function is auto-generated +func (svc accessControl) CanCreatePageLayoutOnPage(ctx context.Context, r *types.Page) bool { + return svc.can(ctx, "page-layout.create", r) +} + +// CanSearchPageLayoutsOnPage checks if current user can list, search or filter page layouts on namespace +// +// This function is auto-generated +func (svc accessControl) CanSearchPageLayoutsOnPage(ctx context.Context, r *types.Page) bool { + return svc.can(ctx, "page-layouts.search", r) +} + +// CanReadPageLayout checks if current user can read +// +// This function is auto-generated +func (svc accessControl) CanReadPageLayout(ctx context.Context, r *types.PageLayout) bool { + return svc.can(ctx, "read", r) +} + +// CanUpdatePageLayout checks if current user can update +// +// This function is auto-generated +func (svc accessControl) CanUpdatePageLayout(ctx context.Context, r *types.PageLayout) bool { + return svc.can(ctx, "update", r) +} + +// CanDeletePageLayout checks if current user can delete +// +// This function is auto-generated +func (svc accessControl) CanDeletePageLayout(ctx context.Context, r *types.PageLayout) bool { + return svc.can(ctx, "delete", r) +} + // CanReadRecord checks if current user can read // // This function is auto-generated @@ -817,13 +877,19 @@ func rbacResourceOperations(r string) map[string]bool { "pages.search": true, } case types.PageResourceType: + return map[string]bool{ + "read": true, + "update": true, + "delete": true, + "page-layout.create": true, + "page-layouts.search": true, + } + case types.PageLayoutResourceType: return map[string]bool{ "read": true, "update": true, "delete": true, } - case types.PageLayoutResourceType: - return map[string]bool{} case types.RecordResourceType: return map[string]bool{ "read": true, diff --git a/server/compose/service/page.go b/server/compose/service/page.go index ac66196e6..f44afb255 100644 --- a/server/compose/service/page.go +++ b/server/compose/service/page.go @@ -622,6 +622,16 @@ func (svc page) handleUpdate(ctx context.Context, upd *types.Page) pageUpdateHan } } + if upd.Meta != nil { + if res.Meta.AllowPersonalLayouts != upd.Meta.AllowPersonalLayouts { + if res.Meta == nil { + res.Meta = &types.PageMeta{} + res.Meta.AllowPersonalLayouts = upd.Meta.AllowPersonalLayouts + changes |= pageChanged + } + } + } + if res.Title != upd.Title { res.Title = upd.Title changes |= pageChanged diff --git a/server/compose/service/page_layout.go b/server/compose/service/page_layout.go index ccdb887e5..5a749ffc1 100644 --- a/server/compose/service/page_layout.go +++ b/server/compose/service/page_layout.go @@ -37,12 +37,16 @@ type ( } pageLayoutAccessController interface { - CanManageResourceTranslations(ctx context.Context) bool + CanManageResourceTranslations(context.Context) bool - // @todo... + CanCreatePageLayoutOnPage(context.Context, *types.Page) bool + CanSearchPageLayoutsOnPage(context.Context, *types.Page) bool + CanReadPageLayout(context.Context, *types.PageLayout) bool + CanUpdatePageLayout(context.Context, *types.PageLayout) bool + CanDeletePageLayout(context.Context, *types.PageLayout) bool } - pageLayoutUpdateHandler func(ctx context.Context, ns *types.Namespace, c *types.PageLayout) (pageLayoutChanges, error) + pageLayoutUpdateHandler func(ctx context.Context, ns *types.Namespace, pg *types.Page, c *types.PageLayout) (pageLayoutChanges, error) pageLayoutChanges uint8 ) @@ -98,9 +102,9 @@ func (svc pageLayout) FindByPageLayoutID(ctx context.Context, namespaceID, pageL func checkPageLayout(ctx context.Context, ac pageLayoutAccessController) func(res *types.PageLayout) (bool, error) { return func(res *types.PageLayout) (bool, error) { - // if !ac.CanReadPageLayout(ctx, res) { - // return false, nil - // } + if !ac.CanReadPageLayout(ctx, res) { + return false, nil + } return true, nil } @@ -111,21 +115,22 @@ func (svc pageLayout) search(ctx context.Context, filter types.PageLayoutFilter) var ( aProps = &pageLayoutActionProps{filter: &filter} ns *types.Namespace + pg *types.Page ) // For each fetched item, store backend will check if it is valid or not filter.Check = checkPageLayout(ctx, svc.ac) err = func() error { - ns, err = loadNamespace(ctx, svc.store, filter.NamespaceID) + ns, pg, err = loadPageCombo(ctx, svc.store, ns.ID, filter.PageID) if err != nil { return err } aProps.setNamespace(ns) - // if !svc.ac.CanSearchPageLayoutsOnNamespace(ctx, ns) { - // return PageLayoutErrNotAllowedToSearch() - // } + if !svc.ac.CanSearchPageLayoutsOnPage(ctx, pg) { + return PageLayoutErrNotAllowedToSearch() + } if len(filter.Labels) > 0 { filter.LabeledIDs, err = label.Search( @@ -172,8 +177,9 @@ func (svc pageLayout) Find(ctx context.Context, filter types.PageLayoutFilter) ( func (svc pageLayout) Create(ctx context.Context, new *types.PageLayout) (*types.PageLayout, error) { var ( - ns *types.Namespace aProps = &pageLayoutActionProps{pageLayout: new} + ns *types.Namespace + pg *types.Page ) new.ID = 0 @@ -183,13 +189,16 @@ func (svc pageLayout) Create(ctx context.Context, new *types.PageLayout) (*types return PageLayoutErrInvalidID() } - if ns, err = loadNamespace(ctx, s, new.NamespaceID); err != nil { + if ns, pg, err = loadPageCombo(ctx, s, new.NamespaceID, new.PageID); err != nil { return err } - // if !svc.ac.CanCreatePageLayoutOnNamespace(ctx, ns) { - // return PageLayoutErrNotAllowedToCreate() - // } + // Allow users to manage their personal layouts regardless of RBAC (when enabled) + if !svc.ac.CanCreatePageLayoutOnPage(ctx, pg) { + if new.OwnedBy == 0 || !pg.Meta.AllowPersonalLayouts { + return PageLayoutErrNotAllowedToCreate() + } + } aProps.setNamespace(ns) @@ -247,9 +256,11 @@ func (svc pageLayout) Reorder(ctx context.Context, namespaceID, pageID uint64, p } _ = p - // if !svc.ac.CanUpdatePageLayout(ctx, p) { - // return PageLayoutErrNotAllowedToUpdate() - // } + // @note following the pattern of pages; should probably change this + // on both resources since it doesn't sound right. + if !svc.ac.CanCreatePageLayoutOnPage(ctx, p) { + return PageLayoutErrNotAllowedToUpdate() + } return store.ReorderComposePageLayouts(ctx, s, namespaceID, pageID, pageLayoutIDs) }) @@ -260,12 +271,12 @@ func (svc pageLayout) Reorder(ctx context.Context, namespaceID, pageID uint64, p func (svc pageLayout) Update(ctx context.Context, upd *types.PageLayout) (c *types.PageLayout, err error) { err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) { - ns, res, err := loadPageLayoutCombo(ctx, s, upd.NamespaceID, upd.PageID, upd.ID) + ns, pg, res, err := loadPageLayoutCombo(ctx, s, upd.NamespaceID, upd.PageID, upd.ID) if err != nil { return } - c, err = svc.updater(ctx, svc.store, ns, res, PageLayoutActionUpdate, svc.handleUpdate(ctx, upd)) + c, err = svc.updater(ctx, svc.store, ns, pg, res, PageLayoutActionUpdate, svc.handleUpdate(ctx, upd)) return }) @@ -275,34 +286,35 @@ func (svc pageLayout) Update(ctx context.Context, upd *types.PageLayout) (c *typ func (svc pageLayout) DeleteByID(ctx context.Context, namespaceID, pageID, pageLayoutID uint64) error { var ( ns *types.Namespace + pg *types.Page res *types.PageLayout ) return store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) { // simply delete the pageLayout and ignore the subpageLayouts - ns, res, err = loadPageLayoutCombo(ctx, s, namespaceID, pageID, pageLayoutID) + ns, pg, res, err = loadPageLayoutCombo(ctx, s, namespaceID, pageID, pageLayoutID) if err != nil { return } - _, err = svc.updater(ctx, svc.store, ns, res, PageLayoutActionDelete, svc.handleDelete) + _, err = svc.updater(ctx, svc.store, ns, pg, res, PageLayoutActionDelete, svc.handleDelete) return }) } func (svc pageLayout) UndeleteByID(ctx context.Context, namespaceID, pageID, pageLayoutID uint64) error { return store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) { - ns, res, err := loadPageLayoutCombo(ctx, s, namespaceID, pageID, pageLayoutID) + ns, pg, res, err := loadPageLayoutCombo(ctx, s, namespaceID, pageID, pageLayoutID) if err != nil { return } - _, err = svc.updater(ctx, svc.store, ns, res, PageLayoutActionUpdate, svc.handleUndelete) + _, err = svc.updater(ctx, svc.store, ns, pg, res, PageLayoutActionUpdate, svc.handleUndelete) return }) } -func (svc pageLayout) updater(ctx context.Context, s store.Storer, ns *types.Namespace, res *types.PageLayout, action func(...*pageLayoutActionProps) *pageLayoutAction, fn pageLayoutUpdateHandler) (*types.PageLayout, error) { +func (svc pageLayout) updater(ctx context.Context, s store.Storer, ns *types.Namespace, pg *types.Page, res *types.PageLayout, action func(...*pageLayoutActionProps) *pageLayoutAction, fn pageLayoutUpdateHandler) (*types.PageLayout, error) { var ( changes pageLayoutChanges old *types.PageLayout @@ -338,7 +350,7 @@ func (svc pageLayout) updater(ctx context.Context, s store.Storer, ns *types.Nam return } - if changes, err = fn(ctx, ns, res); err != nil { + if changes, err = fn(ctx, ns, pg, res); err != nil { return err } @@ -391,9 +403,9 @@ func (svc pageLayout) lookup(ctx context.Context, namespaceID uint64, lookup fun aProps.setPageLayout(p) - // if !svc.ac.CanReadPageLayout(ctx, p) { - // return PageLayoutErrNotAllowedToRead() - // } + if !svc.ac.CanReadPageLayout(ctx, p) { + return PageLayoutErrNotAllowedToRead() + } if err = label.Load(ctx, svc.store, p); err != nil { return err @@ -416,7 +428,7 @@ func (svc pageLayout) uniqueCheck(ctx context.Context, p *types.PageLayout) (err } func (svc pageLayout) handleUpdate(ctx context.Context, upd *types.PageLayout) pageLayoutUpdateHandler { - return func(ctx context.Context, ns *types.Namespace, res *types.PageLayout) (changes pageLayoutChanges, err error) { + return func(ctx context.Context, ns *types.Namespace, pg *types.Page, res *types.PageLayout) (changes pageLayoutChanges, err error) { if isStale(upd.UpdatedAt, res.UpdatedAt, res.CreatedAt) { return pageLayoutUnchanged, PageLayoutErrStaleData() } @@ -429,9 +441,12 @@ func (svc pageLayout) handleUpdate(ctx context.Context, upd *types.PageLayout) p return pageLayoutUnchanged, err } - // if !svc.ac.CanUpdatePageLayout(ctx, res) { - // return pageLayoutUnchanged, PageLayoutErrNotAllowedToUpdate() - // } + // Allow users to manage their personal layouts regardless of RBAC (when enabled) + if !svc.ac.CanUpdatePageLayout(ctx, res) { + if res.OwnedBy == 0 || !pg.Meta.AllowPersonalLayouts { + return pageLayoutUnchanged, PageLayoutErrNotAllowedToUpdate() + } + } // Get max blockID for later use blockID := uint64(0) @@ -517,10 +532,13 @@ func (svc pageLayout) handleUpdate(ctx context.Context, upd *types.PageLayout) p } } -func (svc pageLayout) handleDelete(ctx context.Context, ns *types.Namespace, m *types.PageLayout) (pageLayoutChanges, error) { - // if !svc.ac.CanDeletePageLayout(ctx, m) { - // return pageLayoutUnchanged, PageLayoutErrNotAllowedToDelete() - // } +func (svc pageLayout) handleDelete(ctx context.Context, ns *types.Namespace, pg *types.Page, m *types.PageLayout) (pageLayoutChanges, error) { + // Allow users to manage their personal layouts regardless of RBAC (when enabled) + if !svc.ac.CanDeletePageLayout(ctx, m) { + if m.OwnedBy == 0 || !pg.Meta.AllowPersonalLayouts { + return pageLayoutUnchanged, PageLayoutErrNotAllowedToDelete() + } + } if m.DeletedAt != nil { // pageLayout already deleted @@ -531,10 +549,13 @@ func (svc pageLayout) handleDelete(ctx context.Context, ns *types.Namespace, m * return pageLayoutChanged, nil } -func (svc pageLayout) handleUndelete(ctx context.Context, ns *types.Namespace, m *types.PageLayout) (pageLayoutChanges, error) { - // if !svc.ac.CanDeletePageLayout(ctx, m) { - // return pageLayoutUnchanged, PageLayoutErrNotAllowedToUndelete() - // } +func (svc pageLayout) handleUndelete(ctx context.Context, ns *types.Namespace, pg *types.Page, m *types.PageLayout) (pageLayoutChanges, error) { + // Allow users to manage their personal layouts regardless of RBAC (when enabled) + if !svc.ac.CanDeletePageLayout(ctx, m) { + if m.OwnedBy == 0 || !pg.Meta.AllowPersonalLayouts { + return pageLayoutUnchanged, PageLayoutErrNotAllowedToUndelete() + } + } if m.DeletedAt == nil { // pageLayout not deleted @@ -561,7 +582,7 @@ func (svc *pageLayout) UpdateConfig(ss *systemTypes.AppSettings) { func loadPageLayoutCombo(ctx context.Context, s interface { store.ComposePageLayouts store.ComposeNamespaces -}, namespaceID, pageID, pageLayoutID uint64) (ns *types.Namespace, c *types.PageLayout, err error) { +}, namespaceID, pageID, pageLayoutID uint64) (ns *types.Namespace, pg *types.Page, c *types.PageLayout, err error) { ns, err = loadNamespace(ctx, s, namespaceID) if err != nil { return diff --git a/server/compose/service/page_layout_actions.gen.go b/server/compose/service/page_layout_actions.gen.go index 8c2c200d4..0655eb3f8 100644 --- a/server/compose/service/page_layout_actions.gen.go +++ b/server/compose/service/page_layout_actions.gen.go @@ -707,6 +707,258 @@ func PageLayoutErrInvalidNamespaceID(mm ...*pageLayoutActionProps) *errors.Error return e } +// PageLayoutErrNotAllowedToRead returns "compose:page-layout.notAllowedToRead" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToRead(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to read this pageLayout", nil), + + errors.Meta("type", "notAllowedToRead"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not read {{pageLayout}}; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToRead"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// PageLayoutErrNotAllowedToSearch returns "compose:page-layout.notAllowedToSearch" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToSearch(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to search or list pageLayouts", nil), + + errors.Meta("type", "notAllowedToSearch"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not search pageLayouts; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToSearch"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// PageLayoutErrNotAllowedToListPageLayouts returns "compose:page-layout.notAllowedToListPageLayouts" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToListPageLayouts(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to list pageLayouts", nil), + + errors.Meta("type", "notAllowedToListPageLayouts"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not list pageLayouts; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToListPageLayouts"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// PageLayoutErrNotAllowedToCreate returns "compose:page-layout.notAllowedToCreate" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToCreate(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to create pageLayouts", nil), + + errors.Meta("type", "notAllowedToCreate"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not create pageLayouts; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToCreate"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// PageLayoutErrNotAllowedToUpdate returns "compose:page-layout.notAllowedToUpdate" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToUpdate(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to update this pageLayout", nil), + + errors.Meta("type", "notAllowedToUpdate"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not update {{pageLayout}}; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToUpdate"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// PageLayoutErrNotAllowedToDelete returns "compose:page-layout.notAllowedToDelete" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToDelete(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to delete this pageLayout", nil), + + errors.Meta("type", "notAllowedToDelete"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not delete {{pageLayout}}; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToDelete"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// PageLayoutErrNotAllowedToUndelete returns "compose:page-layout.notAllowedToUndelete" as *errors.Error +// +// +// This function is auto-generated. +// +func PageLayoutErrNotAllowedToUndelete(mm ...*pageLayoutActionProps) *errors.Error { + var p = &pageLayoutActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to undelete this pageLayout", nil), + + errors.Meta("type", "notAllowedToUndelete"), + errors.Meta("resource", "compose:page-layout"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(pageLayoutLogMetaKey{}, "could not undelete {{pageLayout}}; insufficient permissions"), + errors.Meta(pageLayoutPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "compose"), + errors.Meta(locale.ErrorMetaKey{}, "page-layout.errors.notAllowedToUndelete"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + // ********************************************************************************************************************* // ********************************************************************************************************************* diff --git a/server/compose/service/page_layout_actions.yaml b/server/compose/service/page_layout_actions.yaml index 3a01f23ca..f044b69ba 100644 --- a/server/compose/service/page_layout_actions.yaml +++ b/server/compose/service/page_layout_actions.yaml @@ -84,34 +84,30 @@ errors: message: "invalid or missing namespace ID" severity: warning - # - error: notAllowedToRead - # message: "not allowed to read this pageLayout" - # log: "could not read {{pageLayout}}; insufficient permissions" + - error: notAllowedToRead + message: "not allowed to read this pageLayout" + log: "could not read {{pageLayout}}; insufficient permissions" - # - error: notAllowedToSearch - # message: "not allowed to search or list pageLayouts" - # log: "could not search pageLayouts; insufficient permissions" + - error: notAllowedToSearch + message: "not allowed to search or list pageLayouts" + log: "could not search pageLayouts; insufficient permissions" - # - error: notAllowedToReadNamespace - # message: "not allowed to read this namespace" - # log: "could not read namespace {{namespace}}; insufficient permissions" + - error: notAllowedToListPageLayouts + message: "not allowed to list pageLayouts" + log: "could not list pageLayouts; insufficient permissions" - # - error: notAllowedToListPageLayouts - # message: "not allowed to list pageLayouts" - # log: "could not list pageLayouts; insufficient permissions" + - error: notAllowedToCreate + message: "not allowed to create pageLayouts" + log: "could not create pageLayouts; insufficient permissions" - # - error: notAllowedToCreate - # message: "not allowed to create pageLayouts" - # log: "could not create pageLayouts; insufficient permissions" + - error: notAllowedToUpdate + message: "not allowed to update this pageLayout" + log: "could not update {{pageLayout}}; insufficient permissions" - # - error: notAllowedToUpdate - # message: "not allowed to update this pageLayout" - # log: "could not update {{pageLayout}}; insufficient permissions" + - error: notAllowedToDelete + message: "not allowed to delete this pageLayout" + log: "could not delete {{pageLayout}}; insufficient permissions" - # - error: notAllowedToDelete - # message: "not allowed to delete this pageLayout" - # log: "could not delete {{pageLayout}}; insufficient permissions" - - # - error: notAllowedToUndelete - # message: "not allowed to undelete this pageLayout" - # log: "could not undelete {{pageLayout}}; insufficient permissions" + - error: notAllowedToUndelete + message: "not allowed to undelete this pageLayout" + log: "could not undelete {{pageLayout}}; insufficient permissions" diff --git a/server/compose/types/page.go b/server/compose/types/page.go index 4ce0c0198..4adc22f98 100644 --- a/server/compose/types/page.go +++ b/server/compose/types/page.go @@ -29,6 +29,8 @@ type ( Config PageConfig `json:"config"` Blocks PageBlocks `json:"blocks"` + Meta *PageMeta `json:"meta"` + Children PageSet `json:"children,omitempty"` Labels map[string]string `json:"labels,omitempty"` @@ -73,6 +75,10 @@ type ( Description string `json:"description,omitempty"` } + PageMeta struct { + AllowPersonalLayouts bool `json:"allowPersonalLayouts"` + } + PageBlockStyle struct { Variants map[string]string `json:"variants,omitempty"` Wrap map[string]string `json:"wrap,omitempty"` diff --git a/server/compose/types/parsers.go b/server/compose/types/parsers.go index 390056b77..98c9fabec 100644 --- a/server/compose/types/parsers.go +++ b/server/compose/types/parsers.go @@ -7,6 +7,11 @@ func ParsePageLayoutMeta(ss []string) (p *PageLayoutMeta, err error) { return p, parseStringsInput(ss, &p) } +func ParsePageMeta(ss []string) (p *PageMeta, err error) { + p = &PageMeta{} + return p, parseStringsInput(ss, &p) +} + func parseStringsInput(ss []string, p interface{}) (err error) { if len(ss) == 0 { return diff --git a/server/store/adapters/rdbms/aux_types.gen.go b/server/store/adapters/rdbms/aux_types.gen.go index bf32c306e..f300e134b 100644 --- a/server/store/adapters/rdbms/aux_types.gen.go +++ b/server/store/adapters/rdbms/aux_types.gen.go @@ -293,6 +293,7 @@ type ( SelfID uint64 `db:"self_id"` ModuleID uint64 `db:"module_id"` NamespaceID uint64 `db:"namespace_id"` + Meta composeType.PageMeta `db:"meta"` Config composeType.PageConfig `db:"config"` Blocks composeType.PageBlocks `db:"blocks"` Visible bool `db:"visible"` @@ -1619,6 +1620,7 @@ func (aux *auxComposePage) encode(res *composeType.Page) (_ error) { aux.SelfID = res.SelfID aux.ModuleID = res.ModuleID aux.NamespaceID = res.NamespaceID + aux.Meta = res.Meta aux.Config = res.Config aux.Blocks = res.Blocks aux.Visible = res.Visible @@ -1641,6 +1643,7 @@ func (aux auxComposePage) decode() (res *composeType.Page, _ error) { res.SelfID = aux.SelfID res.ModuleID = aux.ModuleID res.NamespaceID = aux.NamespaceID + res.Meta = aux.Meta res.Config = aux.Config res.Blocks = aux.Blocks res.Visible = aux.Visible @@ -1663,6 +1666,7 @@ func (aux *auxComposePage) scan(row scanner) error { &aux.SelfID, &aux.ModuleID, &aux.NamespaceID, + &aux.Meta, &aux.Config, &aux.Blocks, &aux.Visible, diff --git a/server/store/adapters/rdbms/queries.gen.go b/server/store/adapters/rdbms/queries.gen.go index 5bab0d2cd..abf8c6da9 100644 --- a/server/store/adapters/rdbms/queries.gen.go +++ b/server/store/adapters/rdbms/queries.gen.go @@ -1971,6 +1971,7 @@ var ( "self_id", "rel_module", "rel_namespace", + "meta", "config", "blocks", "visible", @@ -1994,6 +1995,7 @@ var ( "self_id": res.SelfID, "rel_module": res.ModuleID, "rel_namespace": res.NamespaceID, + "meta": res.Meta, "config": res.Config, "blocks": res.Blocks, "visible": res.Visible, @@ -2020,6 +2022,7 @@ var ( "self_id": res.SelfID, "rel_module": res.ModuleID, "rel_namespace": res.NamespaceID, + "meta": res.Meta, "config": res.Config, "blocks": res.Blocks, "visible": res.Visible, @@ -2044,6 +2047,7 @@ var ( "self_id": res.SelfID, "rel_module": res.ModuleID, "rel_namespace": res.NamespaceID, + "meta": res.Meta, "config": res.Config, "blocks": res.Blocks, "visible": res.Visible, diff --git a/server/system/service/attachment_actions.gen.go b/server/system/service/attachment_actions.gen.go index 9179ba631..f732f515f 100644 --- a/server/system/service/attachment_actions.gen.go +++ b/server/system/service/attachment_actions.gen.go @@ -642,7 +642,9 @@ func AttachmentErrFailedToProcessImage(mm ...*attachmentActionProps) *errors.Err // AttachmentErrInvalidAvatarFileType returns "system:attachment.invalidAvatarFileType" as *errors.Error // +// // This function is auto-generated. +// func AttachmentErrInvalidAvatarFileType(mm ...*attachmentActionProps) *errors.Error { var p = &attachmentActionProps{} if len(mm) > 0 { @@ -674,7 +676,9 @@ func AttachmentErrInvalidAvatarFileType(mm ...*attachmentActionProps) *errors.Er // AttachmentErrInvalidAvatarFileSize returns "system:attachment.invalidAvatarFileSize" as *errors.Error // +// // This function is auto-generated. +// func AttachmentErrInvalidAvatarFileSize(mm ...*attachmentActionProps) *errors.Error { var p = &attachmentActionProps{} if len(mm) > 0 { @@ -706,7 +710,9 @@ func AttachmentErrInvalidAvatarFileSize(mm ...*attachmentActionProps) *errors.Er // AttachmentErrInvalidInitialsLength returns "system:attachment.invalidInitialsLength" as *errors.Error // +// // This function is auto-generated. +// func AttachmentErrInvalidInitialsLength(mm ...*attachmentActionProps) *errors.Error { var p = &attachmentActionProps{} if len(mm) > 0 { diff --git a/server/system/service/user_actions.gen.go b/server/system/service/user_actions.gen.go index c7ab25539..c3a524d86 100644 --- a/server/system/service/user_actions.gen.go +++ b/server/system/service/user_actions.gen.go @@ -560,6 +560,7 @@ func UserActionDeleteAuthSessions(props ...*userActionProps) *userAction { // UserActionUploadAvatar returns "system:user.uploadAvatar" action // // This function is auto-generated. +// func UserActionUploadAvatar(props ...*userActionProps) *userAction { a := &userAction{ timestamp: time.Now(), @@ -579,6 +580,7 @@ func UserActionUploadAvatar(props ...*userActionProps) *userAction { // UserActionGenerateAvatar returns "system:user.generateAvatar" action // // This function is auto-generated. +// func UserActionGenerateAvatar(props ...*userActionProps) *userAction { a := &userAction{ timestamp: time.Now(), @@ -598,6 +600,7 @@ func UserActionGenerateAvatar(props ...*userActionProps) *userAction { // UserActionDeleteAvatar returns "system:user.deleteAvatar" action // // This function is auto-generated. +// func UserActionDeleteAvatar(props ...*userActionProps) *userAction { a := &userAction{ timestamp: time.Now(), @@ -1364,7 +1367,9 @@ func UserErrMaxUserLimitReached(mm ...*userActionProps) *errors.Error { // UserErrNotAllowedToDeleteAvatar returns "system:user.notAllowedToDeleteAvatar" as *errors.Error // +// // This function is auto-generated. +// func UserErrNotAllowedToDeleteAvatar(mm ...*userActionProps) *errors.Error { var p = &userActionProps{} if len(mm) > 0 {