Add access control and support for personal layouts
This commit is contained in:
committed by
Jože Fortun
parent
3a329511a4
commit
b9e102313d
@@ -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"
|
||||
|
||||
Generated
+8
@@ -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{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,14 @@ pageLayout: {
|
||||
}
|
||||
}
|
||||
|
||||
rbac: {
|
||||
operations: {
|
||||
"read": {}
|
||||
"update": {}
|
||||
"delete": {}
|
||||
}
|
||||
}
|
||||
|
||||
locale: {
|
||||
extended: true
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+68
-2
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+252
@@ -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
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
@@ -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,
|
||||
|
||||
+4
@@ -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,
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+5
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user