From c19ee84f5d629f7f50121c542e3ace5c62119d86 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Mon, 27 Dec 2021 15:12:58 +0100 Subject: [PATCH 1/7] Base CUE, def, schemas --- app/app.cue | 16 + codecov.cue | 1 + codegen/all.cue | 10 + .../templates/gocode/header-gentext.tpl | 5 + .../gocode/rbac/access_control.go.tpl | 146 ++-- .../assets/templates/gocode/rbac/types.go.tpl | 78 ++ codegen/rbac-access_control.cue | 60 ++ codegen/rbac-types.cue | 43 ++ codegen/schema/codegen.cue | 13 + codegen/tool/main.go | 74 ++ codegen/tool/templating.go | 78 ++ compose/chart.cue | 23 + compose/component.cue | 26 + compose/module-field.cue | 34 + compose/module.cue | 27 + compose/namespace.cue | 29 + compose/page.cue | 29 + compose/record.cue | 17 + compose/service/access_control.gen.go | 727 +++++++++--------- compose/types/rbac.gen.go | 289 ++++--- cue.mod/module.cue | 1 + cue.mods | 3 + cue.sums | 0 def/schema/component.cue | 27 + def/schema/locale.cue | 29 + def/schema/platform.cue | 17 + def/schema/rbac.cue | 61 ++ def/schema/resource.cue | 93 +++ def/schema/shared.cue | 14 + def/system.user.yaml | 1 + .../assets/templates/gocode/rbac/types.go.tpl | 99 --- system/apigw-route.cue | 15 + system/application.cue | 18 + system/auth-client.cue | 16 + system/component.cue | 56 ++ system/queue.cue | 18 + system/report.cue | 23 + system/role.cue | 14 + system/service/access_control.gen.go | 280 ++++--- system/template.cue | 16 + system/types/rbac.gen.go | 73 +- system/user.cue | 33 + 42 files changed, 1760 insertions(+), 872 deletions(-) create mode 100644 app/app.cue create mode 100644 codecov.cue create mode 100644 codegen/all.cue create mode 100644 codegen/assets/templates/gocode/header-gentext.tpl rename {pkg/codegen-v3 => codegen}/assets/templates/gocode/rbac/access_control.go.tpl (61%) create mode 100644 codegen/assets/templates/gocode/rbac/types.go.tpl create mode 100644 codegen/rbac-access_control.cue create mode 100644 codegen/rbac-types.cue create mode 100644 codegen/schema/codegen.cue create mode 100644 codegen/tool/main.go create mode 100644 codegen/tool/templating.go create mode 100644 compose/chart.cue create mode 100644 compose/component.cue create mode 100644 compose/module-field.cue create mode 100644 compose/module.cue create mode 100644 compose/namespace.cue create mode 100644 compose/page.cue create mode 100644 compose/record.cue create mode 100644 cue.mod/module.cue create mode 100644 cue.mods create mode 100644 cue.sums create mode 100644 def/schema/component.cue create mode 100644 def/schema/locale.cue create mode 100644 def/schema/platform.cue create mode 100644 def/schema/rbac.cue create mode 100644 def/schema/resource.cue create mode 100644 def/schema/shared.cue delete mode 100644 pkg/codegen-v3/assets/templates/gocode/rbac/types.go.tpl create mode 100644 system/apigw-route.cue create mode 100644 system/application.cue create mode 100644 system/auth-client.cue create mode 100644 system/component.cue create mode 100644 system/queue.cue create mode 100644 system/report.cue create mode 100644 system/role.cue create mode 100644 system/template.cue create mode 100644 system/user.cue diff --git a/app/app.cue b/app/app.cue new file mode 100644 index 000000000..24d0e9e75 --- /dev/null +++ b/app/app.cue @@ -0,0 +1,16 @@ +package app + +import ( + "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/system" + "github.com/cortezaproject/corteza-server/compose" +) + +corteza: schema.#platform & { + ident: "corteza" + + components: [ + system.component, + compose.component, + ] +} diff --git a/codecov.cue b/codecov.cue new file mode 100644 index 000000000..4d3bb4e5a --- /dev/null +++ b/codecov.cue @@ -0,0 +1 @@ +ignore: ["vendor/.*"] diff --git a/codegen/all.cue b/codegen/all.cue new file mode 100644 index 000000000..198b29124 --- /dev/null +++ b/codegen/all.cue @@ -0,0 +1,10 @@ +package codegen + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +all: [...schema.#codegen] & + rbacAccessControl + + rbacTypes + + [] // placeholder diff --git a/codegen/assets/templates/gocode/header-gentext.tpl b/codegen/assets/templates/gocode/header-gentext.tpl new file mode 100644 index 000000000..323ae0bbc --- /dev/null +++ b/codegen/assets/templates/gocode/header-gentext.tpl @@ -0,0 +1,5 @@ +// This file is auto-generated. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// diff --git a/pkg/codegen-v3/assets/templates/gocode/rbac/access_control.go.tpl b/codegen/assets/templates/gocode/rbac/access_control.go.tpl similarity index 61% rename from pkg/codegen-v3/assets/templates/gocode/rbac/access_control.go.tpl rename to codegen/assets/templates/gocode/rbac/access_control.go.tpl index c05566008..e26ac69cc 100644 --- a/pkg/codegen-v3/assets/templates/gocode/rbac/access_control.go.tpl +++ b/codegen/assets/templates/gocode/rbac/access_control.go.tpl @@ -1,7 +1,6 @@ -package {{ .Package }} +package {{ .package }} -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} +{{ template "gocode/header-gentext.tpl" }} import ( "fmt" @@ -10,8 +9,8 @@ import ( "context" "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/cortezaproject/corteza-server/pkg/actionlog" -{{- range .Imports }} - {{ . }} +{{- range .imports }} + "{{ . }}" {{- end }} ) @@ -26,7 +25,6 @@ type ( FindRulesByRoleID(roleID uint64) (rr rbac.RuleSet) CloneRulesByRoleID(ctx context.Context, fromRoleID uint64, toRoleID ...uint64) error } - } ) @@ -56,17 +54,13 @@ func (svc accessControl) Effective(ctx context.Context, rr ... rbac.Resource) (e func (svc accessControl) List() (out []map[string]string) { def := []map[string]string{ - {{- range .Def }} - {{- $Resource := .Resource }} - {{- $RbacResource := .RBAC.Resource }} - {{- range .RBAC.Operations }} + {{- range .operations }} { - "type": types.{{ coalesce $Resource }}ResourceType, - "any": types.{{ coalesce $Resource }}RbacResource({{ range $RbacResource.References }}0,{{ end }}), - "op": {{ printf "%q" .Operation }}, + "type": {{ .const }}, + "any": {{ .ctor }}, + "op": {{ printf "%q" .op }}, }, {{- end }} - {{- end }} } func(svc interface{}) { @@ -143,42 +137,24 @@ func (svc accessControl) CloneRulesByRoleID(ctx context.Context, fromRoleID uint return svc.rbac.CloneRulesByRoleID(ctx, fromRoleID, toRoleID...) } -{{- range .Def }} - {{ $GoType := printf "types.%s" (.Resource) }} - - {{ if .IsComponentResource }} - - {{- range .RBAC.Operations }} - // {{ export .CanFnName }} checks if current user can {{ lower .Description }} - // - // This function is auto-generated - func (svc accessControl) {{ export .CanFnName }}(ctx context.Context) bool { - return svc.can(ctx, {{ printf "%q" .Operation }}, &types.Component{}) - } - {{- end }} - - {{ else }} - {{- range .RBAC.Operations }} - // {{ export .CanFnName }} checks if current user can {{ lower .Description }} - // - // This function is auto-generated - func (svc accessControl) {{ export .CanFnName }}(ctx context.Context, r * {{ $GoType }}) bool { - return svc.can(ctx, {{ printf "%q" .Operation }}, r) - } - {{- end }} - -{{ end }} +{{- range .operations }} + // {{ .checkFuncName }} checks if current user can {{ lower .description }} + // + // This function is auto-generated + func (svc accessControl) {{ .checkFuncName }}(ctx context.Context{{ if not .component }}, r *{{ .goType }}{{ end }}) bool { + {{- if .component }}r := &{{ .goType }}{}{{ end }} + return svc.can(ctx, {{ printf "%q" .op }}, r) + } {{- end }} - // rbacResourceValidator validates known component's resource by routing it to the appropriate validator // // This function is auto-generated func rbacResourceValidator(r string, oo ...string) error { switch rbac.ResourceType(r) { - {{- range .Def }} - case types.{{ coalesce .Resource }}ResourceType: - return rbac{{ .Resource }}ResourceValidator(r, oo...) + {{- range .validation }} + case {{ .const }}: + return {{ .funcName }}(r, oo...) {{- end }} } @@ -190,11 +166,11 @@ func rbacResourceValidator(r string, oo ...string) error { // This function is auto-generated func rbacResourceOperations(r string) map[string]bool { switch rbac.ResourceType(r) { - {{- range .Def }} - case types.{{ coalesce .Resource }}ResourceType: + {{- range .validation }} + case {{ .const }}: return map[string]bool{ - {{- range .RBAC.Operations }} - {{ printf "%q" .Operation }}: true, + {{- range .operations }} + {{ printf "%q" . }}: true, {{- end }} } {{- end }} @@ -203,57 +179,53 @@ func rbacResourceOperations(r string) map[string]bool { return nil } -{{- range .Def }} +{{- range .validation }} -{{ $Resource := .Resource }} -{{ $GoType := printf "types.%s" (.Resource) }} - -// rbac{{ .Resource }}ResourceValidator checks validity of rbac resource and operations +// {{ .funcName }} checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated -func rbac{{ .Resource }}ResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for {{ .Component }}{{ if not .IsComponentResource }} {{ .Resource }}{{end }} resource", o) - } - } - - if !strings.HasPrefix(r, {{ $GoType }}ResourceType) { +func {{ .funcName }}(r string, oo ...string) error { + if !strings.HasPrefix(r, {{ .const }}) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for {{ .label }} resource", o) + } + } -{{ if .RBAC.Resource.References }} - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len({{ $GoType }}ResourceType):], sep), sep) - prc = []string{ - {{- range .RBAC.Resource.References }} - {{ printf "%q" .Field }}, + {{ if .references }} + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len({{ .const }}):], sep), sep) + prc = []string{ + {{- range .references }} + {{ printf "%q" . }}, + {{- end }} + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for {{ .label }} resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } {{- end }} - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for {{ .Resource }}", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } -{{- end }} return nil } {{- end }} diff --git a/codegen/assets/templates/gocode/rbac/types.go.tpl b/codegen/assets/templates/gocode/rbac/types.go.tpl new file mode 100644 index 000000000..fc1e294eb --- /dev/null +++ b/codegen/assets/templates/gocode/rbac/types.go.tpl @@ -0,0 +1,78 @@ +package {{ .package }} + +{{ template "gocode/header-gentext.tpl" }} + +import ( + "fmt" + "strconv" +) + +type ( + // Component struct serves as a virtual resource type for the {{ .cmpIdent }} component + // + // This struct is auto-generated + Component struct {} +) + +var ( + {{/* + making sure that generated code does not break + when these packages are not used + */}} + _ = fmt.Printf + _ = strconv.FormatUint +) + +const ( +{{- range .types }} + {{ .const }} = {{ printf "%q" .type }} +{{- end }} +) + + +{{- range .types }} + +// RbacResource returns string representation of RBAC resource for {{ .goType }} by calling {{ .resFunc }} fn +// +// RBAC resource is in the {{ .type }}/... format +// +// This function is auto-generated +func (r {{ .goType }}) RbacResource() string { + return {{ .resFunc }}({{ if not .component }}{{ range .references }}r.{{ . }},{{ end }}{{ end }}) +} + +// {{ .resFunc }} returns string representation of RBAC resource for {{ .goType }} +// +// RBAC resource is in the {{ .type }}/{{- if .references }}...{{ end }} format +// +// This function is auto-generated +func {{ .resFunc }}({{ if not .component }}{{ range .references }}{{ . }} uint64,{{ end }}{{ end }}) string { + {{- if .references }} + cpts := []interface{{"{}"}}{{"{"}}{{ .goType }}ResourceType{{"}"}} + {{- range .references }} + if {{ . }} != 0 { + cpts = append(cpts, strconv.FormatUint({{ . }}, 10)) + } else { + cpts = append(cpts, "*") + } + + {{ end }} + return fmt.Sprintf({{ .tplFunc }}(), cpts...) + {{- else }} + return {{ .goType }}ResourceType + "/" + {{- end }} + +} + +func {{ .tplFunc }}() string { + {{- if .references }} + return "%s + {{- range .references }}/%s{{- end }}" + + {{- else }} + return "%s" + {{- end }} +} + +{{- end }} + diff --git a/codegen/rbac-access_control.cue b/codegen/rbac-access_control.cue new file mode 100644 index 000000000..49e777ba1 --- /dev/null +++ b/codegen/rbac-access_control.cue @@ -0,0 +1,60 @@ +package codegen + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" + "github.com/cortezaproject/corteza-server/app" +) + +rbacAccessControl: + [...schema.#codegen] & + [ + for cmp in app.corteza.components { + template: "gocode/rbac/access_control.go.tpl" + output: "\(cmp.ident)/service/access_control.gen.go" + payload: { + imports: [ + "github.com/cortezaproject/corteza-server/\(cmp.ident)/types", + ] + package: "service" + + // All possible RBAC operations on component and resources + // flattened + operations: [ + for res in cmp.resources for op in res.rbac.operations { + "op": op.handle + "const": "types.\(res.expIdent)ResourceType" + "ctor": "types.\(res.expIdent)RbacResource(\(len(res.rbac.resource.references)*"0,"))" + "goType": res.goType + "description": op.description + "checkFuncName": op.checkFuncName + }, + for op in cmp.rbac.operations { + "op": op.handle + "const": "types.ComponentResourceType" + "ctor": "types.ComponentRbacResource()" + "goType": "types.Component" + "description": op.description + "checkFuncName": op.checkFuncName + "component": true + }, + ] + + // Operation/resource validators, grouped by resource + validation: [ + for res in cmp.resources { + "label": res.ident + "const": "types.\(res.expIdent)ResourceType" + "funcName": "rbac\(res.expIdent)ResourceValidator" + "references": res.rbac.resource.references + "operations": [ for op in res.rbac.operations {op.handle}] + }, + { + "label": "\(cmp.ident) component" + "const": "types.ComponentResourceType" + "funcName": "rbacComponentResourceValidator" + "operations": [ for op in cmp.rbac.operations {op.handle}] + }, + ] + } + }, + ] diff --git a/codegen/rbac-types.cue b/codegen/rbac-types.cue new file mode 100644 index 000000000..1f5ecc5ef --- /dev/null +++ b/codegen/rbac-types.cue @@ -0,0 +1,43 @@ +package codegen + +import ( + "github.com/cortezaproject/corteza-server/app" + "github.com/cortezaproject/corteza-server/codegen/schema" + "strings" +) + +rbacTypes: + [...schema.#codegen] & + [ + for cmp in app.corteza.components { + template: "gocode/rbac/types.go.tpl" + output: "\(cmp.ident)/types/rbac.gen.go" + payload: { + package: "types" + + cmpIdent: cmp.ident + // Operation/resource validators, grouped by resource + types: [ + for res in cmp.resources { + "const": "\(res.expIdent)ResourceType" + "type": res.rbac.resource.type + "resFunc": "\(res.expIdent)RbacResource" + "tplFunc": "\(res.expIdent)RbacResourceTpl" + "attFunc": "\(res.expIdent)RbacAttributes" + "goType": res.expIdent + + "references": [ for field in res.rbac.resource.references { strings.ToTitle(field) } ] + }, + { + "const": "ComponentResourceType" + "type": cmp.rbac.resource.type + "resFunc": "ComponentRbacResource" + "tplFunc": "ComponentRbacResourceTpl" + "attFunc": "ComponentRbacAttributes" + "goType": "Component" + "component": true + }, + ] + } + }, + ] diff --git a/codegen/schema/codegen.cue b/codegen/schema/codegen.cue new file mode 100644 index 000000000..36a9467fd --- /dev/null +++ b/codegen/schema/codegen.cue @@ -0,0 +1,13 @@ +package schema + +#codegen: { + template: string + output: string + + syntax: "go" + if output =~ "\\.adoc" { + syntax: "adoc" + } + + payload: _ +} diff --git a/codegen/tool/main.go b/codegen/tool/main.go new file mode 100644 index 000000000..8141f7159 --- /dev/null +++ b/codegen/tool/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "text/template" + + "github.com/cortezaproject/corteza-server/pkg/cli" +) + +type ( + task struct { + Template string `json:"template"` + Output string `json:"output"` + Syntax string `json:"syntax"` + Payload interface{} `json:"payload"` + } +) + +var ( + verbose bool + showHelp bool + tplRootPath string +) + +func init() { + flag.BoolVar(&showHelp, "h", false, "show help") + flag.BoolVar(&verbose, "v", false, "be verbose") + flag.StringVar(&tplRootPath, "p", "codegen/assets/templates", "location of the template files") + flag.Parse() +} + +// Takes JSON input with codegen tasks and definitions and generates files +func main() { + if showHelp { + flag.PrintDefaults() + os.Exit(0) + } + + var ( + input = json.NewDecoder(os.Stdin) + tasks = make([]*task, 0) + tpl *template.Template + err error + ) + + print("Waiting for stdin ...\n") + if err = input.Decode(&tasks); err != nil { + cli.HandleError(fmt.Errorf("failed to decode input from standard input: %v", err)) + } + + if tpl, err = LoadTemplates(BaseTemplate(), tplRootPath); err != nil { + cli.HandleError(fmt.Errorf("failed to load templates: %v", err)) + } + + for _, j := range tasks { + switch j.Syntax { + case "go": + print(fmt.Sprintf("generating %s (from %s) ...", j.Output, j.Template)) + if err = GoTemplate(j.Output, tpl.Lookup(j.Template), j.Payload); err != nil { + cli.HandleError(fmt.Errorf("failed to write template: %v", err)) + } + print("done\n") + } + } +} + +func print(msg string) { + if verbose { + _, _ = fmt.Fprint(os.Stderr, msg) + } +} diff --git a/codegen/tool/templating.go b/codegen/tool/templating.go new file mode 100644 index 000000000..e6822f752 --- /dev/null +++ b/codegen/tool/templating.go @@ -0,0 +1,78 @@ +package main + +import ( + "bytes" + "fmt" + "go/format" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/Masterminds/sprig" +) + +func BaseTemplate() *template.Template { + return template.New(""). + Funcs(sprig.TxtFuncMap()) +} + +func LoadTemplates(rTpl *template.Template, rootDir string) (*template.Template, error) { + cleanRoot := filepath.Clean(rootDir) + pfx := len(cleanRoot) + 1 + + return rTpl, filepath.Walk(cleanRoot, func(path string, info os.FileInfo, err error) error { + if info.IsDir() || !strings.HasSuffix(path, ".tpl") || err != nil { + return err + } + + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + name := path[pfx:] + rTpl, err = rTpl.New(name).Parse(string(b)) + + return err + }) +} + +func GoTemplate(dst string, tpl *template.Template, payload interface{}) (err error) { + var output io.WriteCloser + buf := bytes.Buffer{} + + if tpl == nil { + return fmt.Errorf("could not find template for %s", dst) + } + + if err := tpl.Execute(&buf, payload); err != nil { + return err + } + + fmtsrc, err := format.Source(buf.Bytes()) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%s fmt warn: %v\n", dst, err) + + err = nil + fmtsrc = buf.Bytes() + } + + if dst == "" || dst == "-" { + output = os.Stdout + } else { + if output, err = os.Create(dst); err != nil { + return err + } + + defer output.Close() + } + + if _, err = output.Write(fmtsrc); err != nil { + return err + } + + return nil +} diff --git a/compose/chart.cue b/compose/chart.cue new file mode 100644 index 000000000..2435dc156 --- /dev/null +++ b/compose/chart.cue @@ -0,0 +1,23 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +chart: schema.#resource & { + rbac: { + resource: references: [ "namespaceID", "ID"] + + operations: { + "read": {} + "update": {} + "delete": {} + } + } + + // locale: + // resource: + // references: [ namespace, ID ] + // keys: + // - name +} diff --git a/compose/component.cue b/compose/component.cue new file mode 100644 index 000000000..931accdcf --- /dev/null +++ b/compose/component.cue @@ -0,0 +1,26 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +component: schema.#component & { + ident: "compose" + + resources: { + "namespace": namespace + "module": module + "module-field": moduleField + "record": record + "page": page + "chart": chart + } + + rbac: operations: { + "settings.read": description: "Read settings" + "settings.manage": description: "Manage settings" + "namespace.create": description: "Create namespace" + "namespaces.search": description: "List, search or filter namespaces" + "resource-translations.manage": description: "List, search, create, or update resource translations" + } +} diff --git a/compose/module-field.cue b/compose/module-field.cue new file mode 100644 index 000000000..0e7f174eb --- /dev/null +++ b/compose/module-field.cue @@ -0,0 +1,34 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +moduleField: schema.#resource & { + rbac: { + resource: references: [ "namespaceID", "moduleID", "ID"] + + operations: { + "recod.value.read": description: "Read field value on records" + "recod.value.update": description: "Update field value on records" + } + } + + //locale: + // resource: + // references: [ namespace, module, ID ] + // + // skipSvc: true + // keys: + // - label + // - { name: descriptionView, path: meta.description.view, custom: true, customHandler: descriptionView } + // - { name: descriptionEdit, path: meta.description.edit, custom: true, customHandler: descriptionEdit } + // - { name: hintView, path: meta.hint.view, custom: true, customHandler: hintView } + // - { name: hintEdit, path: meta.hint.edit, custom: true, customHandler: hintEdit } + // - { name: validatorError, path: "expression.validator.{{validatorID}}.error", custom: true, customHandler: validatorError } + // - { name: optionsOptionTexts, + // path: "meta.options.{{value}}.text", + // custom: true, + // customHandler: optionsOptionTexts + // } +} diff --git a/compose/module.cue b/compose/module.cue new file mode 100644 index 000000000..c1b0409f6 --- /dev/null +++ b/compose/module.cue @@ -0,0 +1,27 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +module: schema.#resource & { + rbac: { + resource: references: [ "namespaceID", "ID"] + + operations: { + "read": {} + "update": {} + "delete": {} + "record.create": description: "Create record" + "records.search": description: "List, search or filter records" + } + } + + //locale: + // resource: + // references: [ namespace, ID ] + // + // extended: true + // keys: + // - name +} diff --git a/compose/namespace.cue b/compose/namespace.cue new file mode 100644 index 000000000..4ecf6d130 --- /dev/null +++ b/compose/namespace.cue @@ -0,0 +1,29 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +namespace: schema.#resource & { + rbac: { + operations: { + "read": {} + "update": {} + "delete": {} + "manage": description: "Access to namespace admin panel" + "module.create": description: "Create module on namespace" + "modules.search": description: "List, search or filter module on namespace" + "chart.create": description: "Create chart on namespace" + "charts.search": description: "List, search or filter chart on namespace" + "page.create": description: "Create page on namespace" + "pages.search": description: "List, search or filter pages on namespace" + } + } + + // + //locale: + // keys: + // - name + // - { path: subtitle, field: "Meta.Subtitle" } + // - { path: description, field: "Meta.Description" } +} diff --git a/compose/page.cue b/compose/page.cue new file mode 100644 index 000000000..04ffa49a4 --- /dev/null +++ b/compose/page.cue @@ -0,0 +1,29 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +page: schema.#resource & { + rbac: { + resource: references: [ "namespaceID", "ID"] + + operations: { + "read": {} + "update": {} + "delete": {} + } + } + + //locale: + // resource: + // references: [ namespace, ID ] + // + // extended: true + // keys: + // - title + // - description + // - { name: blockTitle, path: "pageBlock.{{blockID}}.title", custom: true } + // - { name: blockDescription, path: "pageBlock.{{blockID}}.description", custom: true } + // - { name: blockAutomationButtonlabel, path: "pageBlock.{{blockID}}.button.{{buttonID}}.label", custom: true } +} diff --git a/compose/record.cue b/compose/record.cue new file mode 100644 index 000000000..dfbfe11fe --- /dev/null +++ b/compose/record.cue @@ -0,0 +1,17 @@ +package compose + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +record: schema.#resource & { + rbac: { + resource: references: [ "namespaceID", "moduleID", "ID"] + + operations: { + "read": {} + "update": {} + "delete": {} + } + } +} diff --git a/compose/service/access_control.gen.go b/compose/service/access_control.gen.go index dc7b382e0..4f8173af1 100644 --- a/compose/service/access_control.gen.go +++ b/compose/service/access_control.gen.go @@ -6,15 +6,6 @@ package service // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.chart.yaml -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml -// - compose.record.yaml -// - compose.yaml - import ( "context" "fmt" @@ -63,56 +54,6 @@ func (svc accessControl) Effective(ctx context.Context, rr ...rbac.Resource) (ee func (svc accessControl) List() (out []map[string]string) { def := []map[string]string{ - { - "type": types.ChartResourceType, - "any": types.ChartRbacResource(0, 0), - "op": "read", - }, - { - "type": types.ChartResourceType, - "any": types.ChartRbacResource(0, 0), - "op": "update", - }, - { - "type": types.ChartResourceType, - "any": types.ChartRbacResource(0, 0), - "op": "delete", - }, - { - "type": types.ModuleFieldResourceType, - "any": types.ModuleFieldRbacResource(0, 0, 0), - "op": "record.value.read", - }, - { - "type": types.ModuleFieldResourceType, - "any": types.ModuleFieldRbacResource(0, 0, 0), - "op": "record.value.update", - }, - { - "type": types.ModuleResourceType, - "any": types.ModuleRbacResource(0, 0), - "op": "read", - }, - { - "type": types.ModuleResourceType, - "any": types.ModuleRbacResource(0, 0), - "op": "update", - }, - { - "type": types.ModuleResourceType, - "any": types.ModuleRbacResource(0, 0), - "op": "delete", - }, - { - "type": types.ModuleResourceType, - "any": types.ModuleRbacResource(0, 0), - "op": "record.create", - }, - { - "type": types.ModuleResourceType, - "any": types.ModuleRbacResource(0, 0), - "op": "records.search", - }, { "type": types.NamespaceResourceType, "any": types.NamespaceRbacResource(0), @@ -163,6 +104,56 @@ func (svc accessControl) List() (out []map[string]string) { "any": types.NamespaceRbacResource(0), "op": "pages.search", }, + { + "type": types.ModuleResourceType, + "any": types.ModuleRbacResource(0, 0), + "op": "read", + }, + { + "type": types.ModuleResourceType, + "any": types.ModuleRbacResource(0, 0), + "op": "update", + }, + { + "type": types.ModuleResourceType, + "any": types.ModuleRbacResource(0, 0), + "op": "delete", + }, + { + "type": types.ModuleResourceType, + "any": types.ModuleRbacResource(0, 0), + "op": "record.create", + }, + { + "type": types.ModuleResourceType, + "any": types.ModuleRbacResource(0, 0), + "op": "records.search", + }, + { + "type": types.ModuleFieldResourceType, + "any": types.ModuleFieldRbacResource(0, 0, 0), + "op": "recod.value.read", + }, + { + "type": types.ModuleFieldResourceType, + "any": types.ModuleFieldRbacResource(0, 0, 0), + "op": "recod.value.update", + }, + { + "type": types.RecordResourceType, + "any": types.RecordRbacResource(0, 0, 0), + "op": "read", + }, + { + "type": types.RecordResourceType, + "any": types.RecordRbacResource(0, 0, 0), + "op": "update", + }, + { + "type": types.RecordResourceType, + "any": types.RecordRbacResource(0, 0, 0), + "op": "delete", + }, { "type": types.PageResourceType, "any": types.PageRbacResource(0, 0), @@ -179,18 +170,18 @@ func (svc accessControl) List() (out []map[string]string) { "op": "delete", }, { - "type": types.RecordResourceType, - "any": types.RecordRbacResource(0, 0, 0), + "type": types.ChartResourceType, + "any": types.ChartRbacResource(0, 0), "op": "read", }, { - "type": types.RecordResourceType, - "any": types.RecordRbacResource(0, 0, 0), + "type": types.ChartResourceType, + "any": types.ChartRbacResource(0, 0), "op": "update", }, { - "type": types.RecordResourceType, - "any": types.RecordRbacResource(0, 0, 0), + "type": types.ChartResourceType, + "any": types.ChartRbacResource(0, 0), "op": "delete", }, { @@ -296,91 +287,21 @@ func (svc accessControl) CloneRulesByRoleID(ctx context.Context, fromRoleID uint return svc.rbac.CloneRulesByRoleID(ctx, fromRoleID, toRoleID...) } -// CanReadChart checks if current user can read chart -// -// This function is auto-generated -func (svc accessControl) CanReadChart(ctx context.Context, r *types.Chart) bool { - return svc.can(ctx, "read", r) -} - -// CanUpdateChart checks if current user can update chart -// -// This function is auto-generated -func (svc accessControl) CanUpdateChart(ctx context.Context, r *types.Chart) bool { - return svc.can(ctx, "update", r) -} - -// CanDeleteChart checks if current user can delete chart -// -// This function is auto-generated -func (svc accessControl) CanDeleteChart(ctx context.Context, r *types.Chart) bool { - return svc.can(ctx, "delete", r) -} - -// CanReadRecordValue checks if current user can read field value on records -// -// This function is auto-generated -func (svc accessControl) CanReadRecordValue(ctx context.Context, r *types.ModuleField) bool { - return svc.can(ctx, "record.value.read", r) -} - -// CanUpdateRecordValue checks if current user can update field value on records -// -// This function is auto-generated -func (svc accessControl) CanUpdateRecordValue(ctx context.Context, r *types.ModuleField) bool { - return svc.can(ctx, "record.value.update", r) -} - -// CanReadModule checks if current user can read module -// -// This function is auto-generated -func (svc accessControl) CanReadModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "read", r) -} - -// CanUpdateModule checks if current user can update module -// -// This function is auto-generated -func (svc accessControl) CanUpdateModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "update", r) -} - -// CanDeleteModule checks if current user can delete module -// -// This function is auto-generated -func (svc accessControl) CanDeleteModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "delete", r) -} - -// CanCreateRecordOnModule checks if current user can create record -// -// This function is auto-generated -func (svc accessControl) CanCreateRecordOnModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "record.create", r) -} - -// CanSearchRecordsOnModule checks if current user can list, search or filter records -// -// This function is auto-generated -func (svc accessControl) CanSearchRecordsOnModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "records.search", r) -} - -// CanReadNamespace checks if current user can read namespace +// CanReadNamespace checks if current user can read corteza::compose:namespace // // This function is auto-generated func (svc accessControl) CanReadNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "read", r) } -// CanUpdateNamespace checks if current user can update namespace +// CanUpdateNamespace checks if current user can update corteza::compose:namespace // // This function is auto-generated func (svc accessControl) CanUpdateNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "update", r) } -// CanDeleteNamespace checks if current user can delete namespace +// CanDeleteNamespace checks if current user can delete corteza::compose:namespace // // This function is auto-generated func (svc accessControl) CanDeleteNamespace(ctx context.Context, r *types.Namespace) bool { @@ -394,130 +315,206 @@ func (svc accessControl) CanManageNamespace(ctx context.Context, r *types.Namesp return svc.can(ctx, "manage", r) } -// CanCreateModuleOnNamespace checks if current user can create module on namespace +// CanCreateModuleNamespace checks if current user can create module on namespace // // This function is auto-generated -func (svc accessControl) CanCreateModuleOnNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanCreateModuleNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "module.create", r) } -// CanSearchModulesOnNamespace checks if current user can list, search or filter module on namespace +// CanSearchModulesNamespace checks if current user can list, search or filter module on namespace // // This function is auto-generated -func (svc accessControl) CanSearchModulesOnNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanSearchModulesNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "modules.search", r) } -// CanCreateChartOnNamespace checks if current user can create chart on namespace +// CanCreateChartNamespace checks if current user can create chart on namespace // // This function is auto-generated -func (svc accessControl) CanCreateChartOnNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanCreateChartNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "chart.create", r) } -// CanSearchChartsOnNamespace checks if current user can list, search or filter chart on namespace +// CanSearchChartsNamespace checks if current user can list, search or filter chart on namespace // // This function is auto-generated -func (svc accessControl) CanSearchChartsOnNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanSearchChartsNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "charts.search", r) } -// CanCreatePageOnNamespace checks if current user can create page on namespace +// CanCreatePageNamespace checks if current user can create page on namespace // // This function is auto-generated -func (svc accessControl) CanCreatePageOnNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanCreatePageNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "page.create", r) } -// CanSearchPagesOnNamespace checks if current user can list, search or filter pages on namespace +// CanSearchPagesNamespace checks if current user can list, search or filter pages on namespace // // This function is auto-generated -func (svc accessControl) CanSearchPagesOnNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanSearchPagesNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "pages.search", r) } -// CanReadPage checks if current user can read page +// CanReadModule checks if current user can read corteza::compose:module // // This function is auto-generated -func (svc accessControl) CanReadPage(ctx context.Context, r *types.Page) bool { +func (svc accessControl) CanReadModule(ctx context.Context, r *types.Module) bool { return svc.can(ctx, "read", r) } -// CanUpdatePage checks if current user can update page +// CanUpdateModule checks if current user can update corteza::compose:module // // This function is auto-generated -func (svc accessControl) CanUpdatePage(ctx context.Context, r *types.Page) bool { +func (svc accessControl) CanUpdateModule(ctx context.Context, r *types.Module) bool { return svc.can(ctx, "update", r) } -// CanDeletePage checks if current user can delete page +// CanDeleteModule checks if current user can delete corteza::compose:module // // This function is auto-generated -func (svc accessControl) CanDeletePage(ctx context.Context, r *types.Page) bool { +func (svc accessControl) CanDeleteModule(ctx context.Context, r *types.Module) bool { return svc.can(ctx, "delete", r) } -// CanReadRecord checks if current user can read record +// CanCreateRecordModule checks if current user can create record +// +// This function is auto-generated +func (svc accessControl) CanCreateRecordModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "record.create", r) +} + +// CanSearchRecordsModule checks if current user can list, search or filter records +// +// This function is auto-generated +func (svc accessControl) CanSearchRecordsModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "records.search", r) +} + +// CanReadRecodValueModuleField checks if current user can read field value on records +// +// This function is auto-generated +func (svc accessControl) CanReadRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { + return svc.can(ctx, "recod.value.read", r) +} + +// CanUpdateRecodValueModuleField checks if current user can update field value on records +// +// This function is auto-generated +func (svc accessControl) CanUpdateRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { + return svc.can(ctx, "recod.value.update", r) +} + +// CanReadRecord checks if current user can read corteza::compose:record // // This function is auto-generated func (svc accessControl) CanReadRecord(ctx context.Context, r *types.Record) bool { return svc.can(ctx, "read", r) } -// CanUpdateRecord checks if current user can update record +// CanUpdateRecord checks if current user can update corteza::compose:record // // This function is auto-generated func (svc accessControl) CanUpdateRecord(ctx context.Context, r *types.Record) bool { return svc.can(ctx, "update", r) } -// CanDeleteRecord checks if current user can delete record +// CanDeleteRecord checks if current user can delete corteza::compose:record // // This function is auto-generated func (svc accessControl) CanDeleteRecord(ctx context.Context, r *types.Record) bool { return svc.can(ctx, "delete", r) } +// CanReadPage checks if current user can read corteza::compose:page +// +// This function is auto-generated +func (svc accessControl) CanReadPage(ctx context.Context, r *types.Page) bool { + return svc.can(ctx, "read", r) +} + +// CanUpdatePage checks if current user can update corteza::compose:page +// +// This function is auto-generated +func (svc accessControl) CanUpdatePage(ctx context.Context, r *types.Page) bool { + return svc.can(ctx, "update", r) +} + +// CanDeletePage checks if current user can delete corteza::compose:page +// +// This function is auto-generated +func (svc accessControl) CanDeletePage(ctx context.Context, r *types.Page) bool { + return svc.can(ctx, "delete", r) +} + +// CanReadChart checks if current user can read corteza::compose:chart +// +// This function is auto-generated +func (svc accessControl) CanReadChart(ctx context.Context, r *types.Chart) bool { + return svc.can(ctx, "read", r) +} + +// CanUpdateChart checks if current user can update corteza::compose:chart +// +// This function is auto-generated +func (svc accessControl) CanUpdateChart(ctx context.Context, r *types.Chart) bool { + return svc.can(ctx, "update", r) +} + +// CanDeleteChart checks if current user can delete corteza::compose:chart +// +// This function is auto-generated +func (svc accessControl) CanDeleteChart(ctx context.Context, r *types.Chart) bool { + return svc.can(ctx, "delete", r) +} + // CanGrant checks if current user can manage compose permissions // // This function is auto-generated func (svc accessControl) CanGrant(ctx context.Context) bool { - return svc.can(ctx, "grant", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "grant", r) } // CanReadSettings checks if current user can read settings // // This function is auto-generated func (svc accessControl) CanReadSettings(ctx context.Context) bool { - return svc.can(ctx, "settings.read", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "settings.read", r) } // CanManageSettings checks if current user can manage settings // // This function is auto-generated func (svc accessControl) CanManageSettings(ctx context.Context) bool { - return svc.can(ctx, "settings.manage", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "settings.manage", r) } // CanCreateNamespace checks if current user can create namespace // // This function is auto-generated func (svc accessControl) CanCreateNamespace(ctx context.Context) bool { - return svc.can(ctx, "namespace.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "namespace.create", r) } // CanSearchNamespaces checks if current user can list, search or filter namespaces // // This function is auto-generated func (svc accessControl) CanSearchNamespaces(ctx context.Context) bool { - return svc.can(ctx, "namespaces.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "namespaces.search", r) } // CanManageResourceTranslations checks if current user can list, search, create, or update resource translations // // This function is auto-generated func (svc accessControl) CanManageResourceTranslations(ctx context.Context) bool { - return svc.can(ctx, "resource-translations.manage", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "resource-translations.manage", r) } // rbacResourceValidator validates known component's resource by routing it to the appropriate validator @@ -525,18 +522,18 @@ func (svc accessControl) CanManageResourceTranslations(ctx context.Context) bool // This function is auto-generated func rbacResourceValidator(r string, oo ...string) error { switch rbac.ResourceType(r) { - case types.ChartResourceType: - return rbacChartResourceValidator(r, oo...) - case types.ModuleFieldResourceType: - return rbacModuleFieldResourceValidator(r, oo...) - case types.ModuleResourceType: - return rbacModuleResourceValidator(r, oo...) case types.NamespaceResourceType: return rbacNamespaceResourceValidator(r, oo...) - case types.PageResourceType: - return rbacPageResourceValidator(r, oo...) + case types.ModuleResourceType: + return rbacModuleResourceValidator(r, oo...) + case types.ModuleFieldResourceType: + return rbacModuleFieldResourceValidator(r, oo...) case types.RecordResourceType: return rbacRecordResourceValidator(r, oo...) + case types.PageResourceType: + return rbacPageResourceValidator(r, oo...) + case types.ChartResourceType: + return rbacChartResourceValidator(r, oo...) case types.ComponentResourceType: return rbacComponentResourceValidator(r, oo...) } @@ -549,25 +546,6 @@ func rbacResourceValidator(r string, oo ...string) error { // This function is auto-generated func rbacResourceOperations(r string) map[string]bool { switch rbac.ResourceType(r) { - case types.ChartResourceType: - return map[string]bool{ - "read": true, - "update": true, - "delete": true, - } - case types.ModuleFieldResourceType: - return map[string]bool{ - "record.value.read": true, - "record.value.update": true, - } - case types.ModuleResourceType: - return map[string]bool{ - "read": true, - "update": true, - "delete": true, - "record.create": true, - "records.search": true, - } case types.NamespaceResourceType: return map[string]bool{ "read": true, @@ -581,13 +559,32 @@ func rbacResourceOperations(r string) map[string]bool { "page.create": true, "pages.search": true, } + case types.ModuleResourceType: + return map[string]bool{ + "read": true, + "update": true, + "delete": true, + "record.create": true, + "records.search": true, + } + case types.ModuleFieldResourceType: + return map[string]bool{ + "recod.value.read": true, + "recod.value.update": true, + } + case types.RecordResourceType: + return map[string]bool{ + "read": true, + "update": true, + "delete": true, + } case types.PageResourceType: return map[string]bool{ "read": true, "update": true, "delete": true, } - case types.RecordResourceType: + case types.ChartResourceType: return map[string]bool{ "read": true, "update": true, @@ -607,27 +604,71 @@ func rbacResourceOperations(r string) map[string]bool { return nil } -// rbacChartResourceValidator checks validity of rbac resource and operations +// rbacNamespaceResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated -func rbacChartResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose Chart resource", o) - } - } - - if !strings.HasPrefix(r, types.ChartResourceType) { +func rbacNamespaceResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.NamespaceResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for namespace resource", o) + } + } + const sep = "/" var ( - pp = strings.Split(strings.Trim(r[len(types.ChartResourceType):], sep), sep) + pp = strings.Split(strings.Trim(r[len(types.NamespaceResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for namespace resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } + return nil +} + +// rbacModuleResourceValidator checks validity of RBAC resource and operations +// +// Can be called without operations to check for validity of resource string only +// +// This function is auto-generated +func rbacModuleResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.ModuleResourceType) { + // expecting resource to always include path + return fmt.Errorf("invalid resource type") + } + + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for module resource", o) + } + } + + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.ModuleResourceType):], sep), sep) prc = []string{ "namespaceID", "ID", @@ -641,7 +682,7 @@ func rbacChartResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Chart", i) + return fmt.Errorf("invalid path wildcard level (%d) for module resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -652,24 +693,24 @@ func rbacChartResourceValidator(r string, oo ...string) error { return nil } -// rbacModuleFieldResourceValidator checks validity of rbac resource and operations +// rbacModuleFieldResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacModuleFieldResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose ModuleField resource", o) - } - } - if !strings.HasPrefix(r, types.ModuleFieldResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for moduleField resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.ModuleFieldResourceType):], sep), sep) @@ -687,7 +728,7 @@ func rbacModuleFieldResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for ModuleField", i) + return fmt.Errorf("invalid path wildcard level (%d) for moduleField resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -698,158 +739,24 @@ func rbacModuleFieldResourceValidator(r string, oo ...string) error { return nil } -// rbacModuleResourceValidator checks validity of rbac resource and operations -// -// Can be called without operations to check for validity of resource string only -// -// This function is auto-generated -func rbacModuleResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose Module resource", o) - } - } - - if !strings.HasPrefix(r, types.ModuleResourceType) { - // expecting resource to always include path - return fmt.Errorf("invalid resource type") - } - - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.ModuleResourceType):], sep), sep) - prc = []string{ - "namespaceID", - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Module", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } - return nil -} - -// rbacNamespaceResourceValidator checks validity of rbac resource and operations -// -// Can be called without operations to check for validity of resource string only -// -// This function is auto-generated -func rbacNamespaceResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose Namespace resource", o) - } - } - - if !strings.HasPrefix(r, types.NamespaceResourceType) { - // expecting resource to always include path - return fmt.Errorf("invalid resource type") - } - - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.NamespaceResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Namespace", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } - return nil -} - -// rbacPageResourceValidator checks validity of rbac resource and operations -// -// Can be called without operations to check for validity of resource string only -// -// This function is auto-generated -func rbacPageResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose Page resource", o) - } - } - - if !strings.HasPrefix(r, types.PageResourceType) { - // expecting resource to always include path - return fmt.Errorf("invalid resource type") - } - - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.PageResourceType):], sep), sep) - prc = []string{ - "namespaceID", - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Page", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } - return nil -} - -// rbacRecordResourceValidator checks validity of rbac resource and operations +// rbacRecordResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacRecordResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose Record resource", o) - } - } - if !strings.HasPrefix(r, types.RecordResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for record resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.RecordResourceType):], sep), sep) @@ -867,7 +774,7 @@ func rbacRecordResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Record", i) + return fmt.Errorf("invalid path wildcard level (%d) for record resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -878,23 +785,113 @@ func rbacRecordResourceValidator(r string, oo ...string) error { return nil } -// rbacComponentResourceValidator checks validity of rbac resource and operations +// rbacPageResourceValidator checks validity of RBAC resource and operations +// +// Can be called without operations to check for validity of resource string only +// +// This function is auto-generated +func rbacPageResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.PageResourceType) { + // expecting resource to always include path + return fmt.Errorf("invalid resource type") + } + + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for page resource", o) + } + } + + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.PageResourceType):], sep), sep) + prc = []string{ + "namespaceID", + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for page resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } + return nil +} + +// rbacChartResourceValidator checks validity of RBAC resource and operations +// +// Can be called without operations to check for validity of resource string only +// +// This function is auto-generated +func rbacChartResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.ChartResourceType) { + // expecting resource to always include path + return fmt.Errorf("invalid resource type") + } + + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for chart resource", o) + } + } + + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.ChartResourceType):], sep), sep) + prc = []string{ + "namespaceID", + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for chart resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } + return nil +} + +// rbacComponentResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacComponentResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for compose resource", o) - } - } - if !strings.HasPrefix(r, types.ComponentResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for compose component resource", o) + } + } + return nil } diff --git a/compose/types/rbac.gen.go b/compose/types/rbac.gen.go index 6832ddc0b..5d0f02249 100644 --- a/compose/types/rbac.gen.go +++ b/compose/types/rbac.gen.go @@ -6,15 +6,6 @@ package types // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.chart.yaml -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml -// - compose.record.yaml -// - compose.yaml - import ( "fmt" "strconv" @@ -27,50 +18,84 @@ type ( Component struct{} ) +var ( + _ = fmt.Printf + _ = strconv.FormatUint +) + const ( - ChartResourceType = "corteza::compose:chart" - ModuleFieldResourceType = "corteza::compose:module-field" - ModuleResourceType = "corteza::compose:module" NamespaceResourceType = "corteza::compose:namespace" - PageResourceType = "corteza::compose:page" + ModuleResourceType = "corteza::compose:module" + ModuleFieldResourceType = "corteza::compose:module-field" RecordResourceType = "corteza::compose:record" + PageResourceType = "corteza::compose:page" + ChartResourceType = "corteza::compose:chart" ComponentResourceType = "corteza::compose" ) -// RbacResource returns string representation of RBAC resource for Chart by calling ChartRbacResource fn +// RbacResource returns string representation of RBAC resource for Namespace by calling NamespaceRbacResource fn // -// RBAC resource is in the corteza::compose:chart/... format +// RBAC resource is in the corteza::compose:namespace/... format // // This function is auto-generated -func (r Chart) RbacResource() string { - return ChartRbacResource(r.NamespaceID, r.ID) +func (r Namespace) RbacResource() string { + return NamespaceRbacResource(r.ID) } -// ChartRbacResource returns string representation of RBAC resource for Chart +// NamespaceRbacResource returns string representation of RBAC resource for Namespace // -// RBAC resource is in the corteza::compose:chart/... format +// RBAC resource is in the corteza::compose:namespace/... format // // This function is auto-generated -func ChartRbacResource(namespaceID uint64, id uint64) string { - cpts := []interface{}{ChartResourceType} - if namespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) +func NamespaceRbacResource(ID uint64) string { + cpts := []interface{}{NamespaceResourceType} + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + return fmt.Sprintf(NamespaceRbacResourceTpl(), cpts...) + +} + +func NamespaceRbacResourceTpl() string { + return "%s/%s" +} + +// RbacResource returns string representation of RBAC resource for Module by calling ModuleRbacResource fn +// +// RBAC resource is in the corteza::compose:module/... format +// +// This function is auto-generated +func (r Module) RbacResource() string { + return ModuleRbacResource(r.NamespaceID, r.ID) +} + +// ModuleRbacResource returns string representation of RBAC resource for Module +// +// RBAC resource is in the corteza::compose:module/... format +// +// This function is auto-generated +func ModuleRbacResource(NamespaceID uint64, ID uint64) string { + cpts := []interface{}{ModuleResourceType} + if NamespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) } else { cpts = append(cpts, "*") } - return fmt.Sprintf(ChartRbacResourceTpl(), cpts...) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(ModuleRbacResourceTpl(), cpts...) } -// @todo template -func ChartRbacResourceTpl() string { +func ModuleRbacResourceTpl() string { return "%s/%s/%s" } @@ -88,22 +113,22 @@ func (r ModuleField) RbacResource() string { // RBAC resource is in the corteza::compose:module-field/... format // // This function is auto-generated -func ModuleFieldRbacResource(namespaceID uint64, moduleID uint64, id uint64) string { +func ModuleFieldRbacResource(NamespaceID uint64, ModuleID uint64, ID uint64) string { cpts := []interface{}{ModuleFieldResourceType} - if namespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) + if NamespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) } else { cpts = append(cpts, "*") } - if moduleID != 0 { - cpts = append(cpts, strconv.FormatUint(moduleID, 10)) + if ModuleID != 0 { + cpts = append(cpts, strconv.FormatUint(ModuleID, 10)) } else { cpts = append(cpts, "*") } - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -112,116 +137,10 @@ func ModuleFieldRbacResource(namespaceID uint64, moduleID uint64, id uint64) str } -// @todo template func ModuleFieldRbacResourceTpl() string { return "%s/%s/%s/%s" } -// RbacResource returns string representation of RBAC resource for Module by calling ModuleRbacResource fn -// -// RBAC resource is in the corteza::compose:module/... format -// -// This function is auto-generated -func (r Module) RbacResource() string { - return ModuleRbacResource(r.NamespaceID, r.ID) -} - -// ModuleRbacResource returns string representation of RBAC resource for Module -// -// RBAC resource is in the corteza::compose:module/... format -// -// This function is auto-generated -func ModuleRbacResource(namespaceID uint64, id uint64) string { - cpts := []interface{}{ModuleResourceType} - if namespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) - } else { - cpts = append(cpts, "*") - } - - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(ModuleRbacResourceTpl(), cpts...) - -} - -// @todo template -func ModuleRbacResourceTpl() string { - return "%s/%s/%s" -} - -// RbacResource returns string representation of RBAC resource for Namespace by calling NamespaceRbacResource fn -// -// RBAC resource is in the corteza::compose:namespace/... format -// -// This function is auto-generated -func (r Namespace) RbacResource() string { - return NamespaceRbacResource(r.ID) -} - -// NamespaceRbacResource returns string representation of RBAC resource for Namespace -// -// RBAC resource is in the corteza::compose:namespace/... format -// -// This function is auto-generated -func NamespaceRbacResource(id uint64) string { - cpts := []interface{}{NamespaceResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(NamespaceRbacResourceTpl(), cpts...) - -} - -// @todo template -func NamespaceRbacResourceTpl() string { - return "%s/%s" -} - -// RbacResource returns string representation of RBAC resource for Page by calling PageRbacResource fn -// -// RBAC resource is in the corteza::compose:page/... format -// -// This function is auto-generated -func (r Page) RbacResource() string { - return PageRbacResource(r.NamespaceID, r.ID) -} - -// PageRbacResource returns string representation of RBAC resource for Page -// -// RBAC resource is in the corteza::compose:page/... format -// -// This function is auto-generated -func PageRbacResource(namespaceID uint64, id uint64) string { - cpts := []interface{}{PageResourceType} - if namespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) - } else { - cpts = append(cpts, "*") - } - - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(PageRbacResourceTpl(), cpts...) - -} - -// @todo template -func PageRbacResourceTpl() string { - return "%s/%s/%s" -} - // RbacResource returns string representation of RBAC resource for Record by calling RecordRbacResource fn // // RBAC resource is in the corteza::compose:record/... format @@ -236,22 +155,22 @@ func (r Record) RbacResource() string { // RBAC resource is in the corteza::compose:record/... format // // This function is auto-generated -func RecordRbacResource(namespaceID uint64, moduleID uint64, id uint64) string { +func RecordRbacResource(NamespaceID uint64, ModuleID uint64, ID uint64) string { cpts := []interface{}{RecordResourceType} - if namespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) + if NamespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) } else { cpts = append(cpts, "*") } - if moduleID != 0 { - cpts = append(cpts, strconv.FormatUint(moduleID, 10)) + if ModuleID != 0 { + cpts = append(cpts, strconv.FormatUint(ModuleID, 10)) } else { cpts = append(cpts, "*") } - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -260,11 +179,82 @@ func RecordRbacResource(namespaceID uint64, moduleID uint64, id uint64) string { } -// @todo template func RecordRbacResourceTpl() string { return "%s/%s/%s/%s" } +// RbacResource returns string representation of RBAC resource for Page by calling PageRbacResource fn +// +// RBAC resource is in the corteza::compose:page/... format +// +// This function is auto-generated +func (r Page) RbacResource() string { + return PageRbacResource(r.NamespaceID, r.ID) +} + +// PageRbacResource returns string representation of RBAC resource for Page +// +// RBAC resource is in the corteza::compose:page/... format +// +// This function is auto-generated +func PageRbacResource(NamespaceID uint64, ID uint64) string { + cpts := []interface{}{PageResourceType} + if NamespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) + } else { + cpts = append(cpts, "*") + } + + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(PageRbacResourceTpl(), cpts...) + +} + +func PageRbacResourceTpl() string { + return "%s/%s/%s" +} + +// RbacResource returns string representation of RBAC resource for Chart by calling ChartRbacResource fn +// +// RBAC resource is in the corteza::compose:chart/... format +// +// This function is auto-generated +func (r Chart) RbacResource() string { + return ChartRbacResource(r.NamespaceID, r.ID) +} + +// ChartRbacResource returns string representation of RBAC resource for Chart +// +// RBAC resource is in the corteza::compose:chart/... format +// +// This function is auto-generated +func ChartRbacResource(NamespaceID uint64, ID uint64) string { + cpts := []interface{}{ChartResourceType} + if NamespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) + } else { + cpts = append(cpts, "*") + } + + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(ChartRbacResourceTpl(), cpts...) + +} + +func ChartRbacResourceTpl() string { + return "%s/%s/%s" +} + // RbacResource returns string representation of RBAC resource for Component by calling ComponentRbacResource fn // // RBAC resource is in the corteza::compose/... format @@ -284,7 +274,6 @@ func ComponentRbacResource() string { } -// @todo template func ComponentRbacResourceTpl() string { return "%s" } diff --git a/cue.mod/module.cue b/cue.mod/module.cue new file mode 100644 index 000000000..921718396 --- /dev/null +++ b/cue.mod/module.cue @@ -0,0 +1 @@ +module: "github.com/cortezaproject/corteza-server" diff --git a/cue.mods b/cue.mods new file mode 100644 index 000000000..e3e715219 --- /dev/null +++ b/cue.mods @@ -0,0 +1,3 @@ +module github.com/cortezaproject/corteza-server + +cue v0.4.0 diff --git a/cue.sums b/cue.sums new file mode 100644 index 000000000..e69de29bb diff --git a/def/schema/component.cue b/def/schema/component.cue new file mode 100644 index 000000000..280cbbf98 --- /dev/null +++ b/def/schema/component.cue @@ -0,0 +1,27 @@ +package schema + +import ( + "strings" +) + +#component: { + ident: #baseHandle + expIdent: #expIdent | *strings.ToTitle(ident) + label: strings.ToTitle(ident) + platform: #baseHandle + + resources: { + [key=_]: {handle: key, "component": ident, "platform": platform} & #resource + } + + // All known RBAC operations for this component + rbac: #rbacComponent & { + resource: type: platform + "::" + ident + + operations: { + grant: { + description: "Manage \(ident) permissions" + } + } + } +} diff --git a/def/schema/locale.cue b/def/schema/locale.cue new file mode 100644 index 000000000..974a59aa8 --- /dev/null +++ b/def/schema/locale.cue @@ -0,0 +1,29 @@ +package schema + +import ( +// "strings" +) + + +#locale: { + // @todo we need a better name here! + skipSvc: bool | *false + + resource: { + // @todo merge with RBAC res-ref and move 2 levels lower. + references: [ ...string] | *["ID"] + } + + keys: { + [key=_]: #localeKey & { + name: key + } + } +} + +#localeKey: { + name: #handle + path: string | *(name) + custom?: true + customHandler?: string +} diff --git a/def/schema/platform.cue b/def/schema/platform.cue new file mode 100644 index 000000000..f4c489f8a --- /dev/null +++ b/def/schema/platform.cue @@ -0,0 +1,17 @@ +package schema + +#platform: { + ident: #baseHandle + + components: [...{platform: ident} & #component] + + // env-var definitions + // options: {} + + // + + // automation: { + // types: .... + // function .... + // } +} diff --git a/def/schema/rbac.cue b/def/schema/rbac.cue new file mode 100644 index 000000000..f0228c20d --- /dev/null +++ b/def/schema/rbac.cue @@ -0,0 +1,61 @@ +package schema + +import ( + "strings" +) + +#rbacComponent: { + resource: { + type: string + } + + operations: { + [key=_]: #rbacOperation & {handle: key} + } +} + +#rbacResource: { + resource: { + type: string + expIdent: #expIdent + references: [ ...string] | *["ID"] + } + + operations: { + [key=_]: #rbacOperation & { + handle: key + resourceExpIdent: resource.expIdent + description: string | *(strings.ToTitle(key) + " " + resource.type) + } + } +} + +#rbacOperation: { + handle: #handle + description: string + resourceExpIdent?: string + + _isComponent: resourceExpIdent == _|_ + + // Some string manipulation that will result in + // more pronouncable access-control check function name + + // When check function name is not explicitly defined we try + // to use resource and operation name and generate easy-to-read name + // + // + => Can + // + => CanOn + + _operation: strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1) + _opSplit: strings.Split(_operation, ".") + _opFlip: [_opSplit[len(_opSplit)-1]] + _opSplit[0:len(_opSplit)-1] + _opFinal: strings.Replace(strings.ToTitle(strings.Join(_opFlip, " ")), " ", "", -1) + + if _isComponent { + checkFuncName: #expIdent | *("Can" + _opFinal) + } + + if !_isComponent { + checkFuncName: #expIdent | *("Can" + _opFinal + resourceExpIdent) + } +} diff --git a/def/schema/resource.cue b/def/schema/resource.cue new file mode 100644 index 000000000..d290ce446 --- /dev/null +++ b/def/schema/resource.cue @@ -0,0 +1,93 @@ +package schema + +import ( + "strings" +) + +#resource: { + handle: #baseHandle | *"unknown-resource" + + _words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1) + + ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1)) + expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1) + platform: #baseHandle | *"unknown-platform" + component: string | *"unknown-component" + + // Fully qualified resource name + fqrn: string | *(platform + "::" + component + ":" + handle) + + goType: string | *("types." + expIdent) + + // fields: #Fields + // operations: #Operations + + // All known RBAC operations for this resource + rbac: #rbacResource & { + resource: { + type: fqrn + "expIdent": expIdent + } + } + + // List of known keys for resource translation + // locale?: { + // [Name=_]: { + // name: Name & #Handle + // path: string + // custom: bool | *false + // } + // } +} + +#fields: { + // Each field can be + [key=_]: #fields | *({name: key} & #field) +} + +#field: { + name: #expIdent + unique: bool | *false + + // Golang type (built-in or other) + type: string | *"string" + + // System fields, + system: bool | *false + + if name =~ "At$" { + type: string | *"*time.Time" + } +} + +//#Operations: { +// [Operation=_]: {operation: Operation} & #Operation +//} + +//#Operation: { +// name: #ExpIdent +// description: string +// can: string | false | *"\(name)" +//} + +idField: { + // Expecting ID field to allways have name ID + name: "ID" + unique: true + + // Service fields, + // @todo We might want to have a better name for this + // service: true + + // @todo someday we'll replace this with the "ID" type + type: "uint64" +} + +handleField: { + // Expecting ID field to allways have name ID + name: "handle" + unique: true + + // @todo someday we'll replace this with the "ID" type + type: "string" & #handle +} diff --git a/def/schema/shared.cue b/def/schema/shared.cue new file mode 100644 index 000000000..557d73ead --- /dev/null +++ b/def/schema/shared.cue @@ -0,0 +1,14 @@ +package schema + +// Resource definition identifier +#ident: =~"^[a-z][a-zA-Z0-9_]*$" + +// Exported identifier +#expIdent: =~"^[A-Z][a-zA-Z0-9]*$" + +// More liberal then identifier, allows underscores and dots +#handle: =~"^[A-Za-z][a-zA-Z0-9_\\-\\.]*[a-zA-Z0-9]+$" + + +// More liberal then identifier, allows underscores and dots +#baseHandle: =~"^[a-z][a-z0-9-]*[a-z0-9]+$" diff --git a/def/system.user.yaml b/def/system.user.yaml index 840fb1652..3241b5521 100644 --- a/def/system.user.yaml +++ b/def/system.user.yaml @@ -16,3 +16,4 @@ rbac: description: Unmask name impersonate: description: Impersonate user + diff --git a/pkg/codegen-v3/assets/templates/gocode/rbac/types.go.tpl b/pkg/codegen-v3/assets/templates/gocode/rbac/types.go.tpl deleted file mode 100644 index 5442cd6af..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/rbac/types.go.tpl +++ /dev/null @@ -1,99 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( - "fmt" - "strconv" -) - -type ( - // Component struct serves as a virtual resource type for the {{ .Component }} component - // - // This struct is auto-generated - Component struct {} -) - -const ( -{{- range .Def }} - {{ coalesce .Resource "Component" }}ResourceType = "{{ .RBAC.ResourceType }}" -{{- end }} -) - - -{{- range .Def }} -{{ $Resource := .Resource }} -{{ $GoType := printf "types.%s" .Resource }} - - -// RbacResource returns string representation of RBAC resource for {{ .Resource }} by calling {{ .Resource }}RbacResource fn -// -// RBAC resource is in the {{ .RBAC.ResourceType }}/... format -// -// This function is auto-generated -func (r {{ .Resource }}) RbacResource() string { - return {{ .Resource }}RbacResource({{ if .RBAC.Resource }}{{ range .RBAC.Resource.References }}r.{{ export .Field }},{{ end }}{{ end }}) -} - -// {{ .Resource }}RbacResource returns string representation of RBAC resource for {{ .Resource }} -// -// RBAC resource is in the {{ .RBAC.ResourceType }}/{{- if .RBAC.Resource.References }}...{{ end }} format -// -// This function is auto-generated -func {{ .Resource }}RbacResource({{ if .RBAC.Resource }}{{ range .RBAC.Resource.References }}{{ unexport .Field }} uint64,{{ end }}{{ end }}) string { - {{- if .RBAC.Resource.References }} - cpts := []interface{{"{}"}}{{"{"}}{{ .Resource }}ResourceType{{"}"}} - {{- range .RBAC.Resource.References }} - if {{ unexport .Field }} != 0 { - cpts = append(cpts, strconv.FormatUint({{ unexport .Field }}, 10)) - } else { - cpts = append(cpts, "*") - } - - {{ end }} - return fmt.Sprintf({{ .Resource }}RbacResourceTpl(), cpts...) - {{- else }} - return {{ .Resource }}ResourceType + "/" - {{- end }} - -} - -// @todo template -func {{ .Resource }}RbacResourceTpl() string { - {{- if .RBAC.Resource.References }} - return "%s - {{- range .RBAC.Resource.References }}/%s{{- end }}" - - {{- else }} - return "%s" - {{- end }} -} - -{{ if .RBAC.Resource.Attributes }} - // RbacAttributes returns resource attributes used for generating list of contextual roles - // - // This function is auto-generated - func (r {{ .Resource }}) RbacAttributes() map[string]interface{} { - return {{ unexport .Resource }}RbacAttributes(r) - } - - {{ if .RBAC.Resource.Attributes.Fields }} - // {{ .Resource }}RbacResource returns string representation of RBAC resource for {{ .Resource }} - // - // RBAC resource is in the {{ .RBAC.ResourceType }}/... format - // - // This function is auto-generated - func {{ unexport .Resource }}RbacAttributes(r {{ .Resource }}) map[string]interface{} { - return map[string]interface{}{ - {{- range .RBAC.Resource.Attributes.Fields }} - {{ printf "%q" . }}: r.{{ export . }}, - {{- end }} - } - } - {{- end }} -{{- end }} - - -{{- end }} - diff --git a/system/apigw-route.cue b/system/apigw-route.cue new file mode 100644 index 000000000..f22a4d78a --- /dev/null +++ b/system/apigw-route.cue @@ -0,0 +1,15 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +apigwRoute: schema.#resource & { + rbac: { + operations: { + read: description: "Read API Gateway route" + update: description: "Update API Gateway route" + delete: description: "Delete API Gateway route" + } + } +} diff --git a/system/application.cue b/system/application.cue new file mode 100644 index 000000000..6c023ca37 --- /dev/null +++ b/system/application.cue @@ -0,0 +1,18 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +application: schema.#resource & { + rbac: { + operations: { + read: + description: "Read application" + update: + description: "Update application" + delete: + description: "Delete application" + } + } +} diff --git a/system/auth-client.cue b/system/auth-client.cue new file mode 100644 index 000000000..c03e65eae --- /dev/null +++ b/system/auth-client.cue @@ -0,0 +1,16 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +authClient: schema.#resource & { + rbac: { + operations: { + read: description: "Read authorization client" + update: description: "Update authorization client" + delete: description: "Delete authorization client" + authorize: description: "Authorize authorization client" + } + } +} diff --git a/system/component.cue b/system/component.cue new file mode 100644 index 000000000..d7d9e2b07 --- /dev/null +++ b/system/component.cue @@ -0,0 +1,56 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +component: schema.#component & { + ident: "system" + + resources: { + "apigw-route": apigwRoute + "application": application + "auth-client": authClient + "queue": queue + "report": report + "role": role + "template": template + "user": user + } + + rbac: operations: { + "action-log.read": description: "Access to action log" + + "settings.read": description: "Read system settings" + "settings.manage": description: "Manage system settings" + "auth-client.create": description: "Create auth clients" + "auth-clients.search": description: "List, search or filter auth clients" + + "role.create": description: "Create roles" + "roles.search": description: "List, search or filter roles" + + "user.create": description: "Create users" + "users.search": description: "List, search or filter users" + + "application.create": description: "Create applications" + "applications.search": description: "List, search or filter auth clients" + "application.flag.self": description: "Manage private flags for applications" + "application.flag.global": description: "Manage global flags for applications" + + "template.create": description: "Create template" + "templates.search": description: "List, search or filter templates" + + "report.create": description: "Create report" + "reports.search": description: "List, search or filter reports" + + "reminder.assign": description: " Assign reminders" + + "queue.create": description: "Create messagebus queues" + "queues.search": description: "List, search or filter messagebus queues" + + "apigw-route.create": description: "Create API gateway route" + "apigw-routes.search": description: "List search or filter API gateway routes" + + "resource-translations.manage": description: "List, search, create, or update resource translations" + } +} diff --git a/system/queue.cue b/system/queue.cue new file mode 100644 index 000000000..22d02e93a --- /dev/null +++ b/system/queue.cue @@ -0,0 +1,18 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +queue: schema.#resource & { + rbac: { + operations: { + "render": description: "Render template" + "read": description: "Read queue" + "update": description: "Update queue" + "delete": description: "Delete queue" + "queue.read": description: "Read from queue" + "queue.write": description: "Write to queue" + } + } +} diff --git a/system/report.cue b/system/report.cue new file mode 100644 index 000000000..5eae70599 --- /dev/null +++ b/system/report.cue @@ -0,0 +1,23 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +report: schema.#resource & { + rbac: { + operations: { + read: description: "Read report" + update: description: "Update report" + delete: description: "Delete report" + run: description: "Run report" + } + } + // locale: + // extended: true + // keys: + // - { path: name, field: "Meta.Name" } + // - { path: description, field: "Meta.Description" } + // - { name: block title, path: "block.{{blockID}}.title", custom: true } + // - { name: block description, path: "block.{{blockID}}.description", custom: true } +} diff --git a/system/role.cue b/system/role.cue new file mode 100644 index 000000000..60afb074b --- /dev/null +++ b/system/role.cue @@ -0,0 +1,14 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +role: schema.#resource & { + rbac: { + operations: { + read: description: "Read role" + update: description: "Update role" + delete: description: "Delete role" + "members.manage": description: "Manage members" + }}} diff --git a/system/service/access_control.gen.go b/system/service/access_control.gen.go index 01b4925e0..4f9afe093 100644 --- a/system/service/access_control.gen.go +++ b/system/service/access_control.gen.go @@ -6,17 +6,6 @@ package service // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - system.apigw-route.yaml -// - system.application.yaml -// - system.auth-client.yaml -// - system.queue.yaml -// - system.report.yaml -// - system.role.yaml -// - system.template.yaml -// - system.user.yaml -// - system.yaml - import ( "context" "fmt" @@ -115,6 +104,11 @@ func (svc accessControl) List() (out []map[string]string) { "any": types.AuthClientRbacResource(0), "op": "authorize", }, + { + "type": types.QueueResourceType, + "any": types.QueueRbacResource(0), + "op": "render", + }, { "type": types.QueueResourceType, "any": types.QueueRbacResource(0), @@ -503,6 +497,13 @@ func (svc accessControl) CanAuthorizeAuthClient(ctx context.Context, r *types.Au return svc.can(ctx, "authorize", r) } +// CanRenderQueue checks if current user can render template +// +// This function is auto-generated +func (svc accessControl) CanRenderQueue(ctx context.Context, r *types.Queue) bool { + return svc.can(ctx, "render", r) +} + // CanReadQueue checks if current user can read queue // // This function is auto-generated @@ -524,17 +525,17 @@ func (svc accessControl) CanDeleteQueue(ctx context.Context, r *types.Queue) boo return svc.can(ctx, "delete", r) } -// CanReadQueueOnQueue checks if current user can read from queue +// CanReadQueueQueue checks if current user can read from queue // // This function is auto-generated -func (svc accessControl) CanReadQueueOnQueue(ctx context.Context, r *types.Queue) bool { +func (svc accessControl) CanReadQueueQueue(ctx context.Context, r *types.Queue) bool { return svc.can(ctx, "queue.read", r) } -// CanWriteQueueOnQueue checks if current user can write to queue +// CanWriteQueueQueue checks if current user can write to queue // // This function is auto-generated -func (svc accessControl) CanWriteQueueOnQueue(ctx context.Context, r *types.Queue) bool { +func (svc accessControl) CanWriteQueueQueue(ctx context.Context, r *types.Queue) bool { return svc.can(ctx, "queue.write", r) } @@ -587,10 +588,10 @@ func (svc accessControl) CanDeleteRole(ctx context.Context, r *types.Role) bool return svc.can(ctx, "delete", r) } -// CanManageMembersOnRole checks if current user can manage members +// CanManageMembersRole checks if current user can manage members // // This function is auto-generated -func (svc accessControl) CanManageMembersOnRole(ctx context.Context, r *types.Role) bool { +func (svc accessControl) CanManageMembersRole(ctx context.Context, r *types.Role) bool { return svc.can(ctx, "members.manage", r) } @@ -643,7 +644,7 @@ func (svc accessControl) CanDeleteUser(ctx context.Context, r *types.User) bool return svc.can(ctx, "delete", r) } -// CanSuspendUser checks if current user can suspemd user +// CanSuspendUser checks if current user can suspend user // // This function is auto-generated func (svc accessControl) CanSuspendUser(ctx context.Context, r *types.User) bool { @@ -657,17 +658,17 @@ func (svc accessControl) CanUnsuspendUser(ctx context.Context, r *types.User) bo return svc.can(ctx, "unsuspend", r) } -// CanUnmaskEmailOnUser checks if current user can unmask email +// CanUnmaskEmailUser checks if current user can unmask email // // This function is auto-generated -func (svc accessControl) CanUnmaskEmailOnUser(ctx context.Context, r *types.User) bool { +func (svc accessControl) CanUnmaskEmailUser(ctx context.Context, r *types.User) bool { return svc.can(ctx, "email.unmask", r) } -// CanUnmaskNameOnUser checks if current user can unmask name +// CanUnmaskNameUser checks if current user can unmask name // // This function is auto-generated -func (svc accessControl) CanUnmaskNameOnUser(ctx context.Context, r *types.User) bool { +func (svc accessControl) CanUnmaskNameUser(ctx context.Context, r *types.User) bool { return svc.can(ctx, "name.unmask", r) } @@ -682,168 +683,192 @@ func (svc accessControl) CanImpersonateUser(ctx context.Context, r *types.User) // // This function is auto-generated func (svc accessControl) CanGrant(ctx context.Context) bool { - return svc.can(ctx, "grant", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "grant", r) } // CanReadActionLog checks if current user can access to action log // // This function is auto-generated func (svc accessControl) CanReadActionLog(ctx context.Context) bool { - return svc.can(ctx, "action-log.read", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "action-log.read", r) } // CanReadSettings checks if current user can read system settings // // This function is auto-generated func (svc accessControl) CanReadSettings(ctx context.Context) bool { - return svc.can(ctx, "settings.read", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "settings.read", r) } // CanManageSettings checks if current user can manage system settings // // This function is auto-generated func (svc accessControl) CanManageSettings(ctx context.Context) bool { - return svc.can(ctx, "settings.manage", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "settings.manage", r) } // CanCreateAuthClient checks if current user can create auth clients // // This function is auto-generated func (svc accessControl) CanCreateAuthClient(ctx context.Context) bool { - return svc.can(ctx, "auth-client.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "auth-client.create", r) } // CanSearchAuthClients checks if current user can list, search or filter auth clients // // This function is auto-generated func (svc accessControl) CanSearchAuthClients(ctx context.Context) bool { - return svc.can(ctx, "auth-clients.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "auth-clients.search", r) } // CanCreateRole checks if current user can create roles // // This function is auto-generated func (svc accessControl) CanCreateRole(ctx context.Context) bool { - return svc.can(ctx, "role.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "role.create", r) } // CanSearchRoles checks if current user can list, search or filter roles // // This function is auto-generated func (svc accessControl) CanSearchRoles(ctx context.Context) bool { - return svc.can(ctx, "roles.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "roles.search", r) } // CanCreateUser checks if current user can create users // // This function is auto-generated func (svc accessControl) CanCreateUser(ctx context.Context) bool { - return svc.can(ctx, "user.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "user.create", r) } // CanSearchUsers checks if current user can list, search or filter users // // This function is auto-generated func (svc accessControl) CanSearchUsers(ctx context.Context) bool { - return svc.can(ctx, "users.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "users.search", r) } // CanCreateApplication checks if current user can create applications // // This function is auto-generated func (svc accessControl) CanCreateApplication(ctx context.Context) bool { - return svc.can(ctx, "application.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "application.create", r) } // CanSearchApplications checks if current user can list, search or filter auth clients // // This function is auto-generated func (svc accessControl) CanSearchApplications(ctx context.Context) bool { - return svc.can(ctx, "applications.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "applications.search", r) } // CanSelfApplicationFlag checks if current user can manage private flags for applications // // This function is auto-generated func (svc accessControl) CanSelfApplicationFlag(ctx context.Context) bool { - return svc.can(ctx, "application.flag.self", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "application.flag.self", r) } // CanGlobalApplicationFlag checks if current user can manage global flags for applications // // This function is auto-generated func (svc accessControl) CanGlobalApplicationFlag(ctx context.Context) bool { - return svc.can(ctx, "application.flag.global", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "application.flag.global", r) } // CanCreateTemplate checks if current user can create template // // This function is auto-generated func (svc accessControl) CanCreateTemplate(ctx context.Context) bool { - return svc.can(ctx, "template.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "template.create", r) } // CanSearchTemplates checks if current user can list, search or filter templates // // This function is auto-generated func (svc accessControl) CanSearchTemplates(ctx context.Context) bool { - return svc.can(ctx, "templates.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "templates.search", r) } // CanCreateReport checks if current user can create report // // This function is auto-generated func (svc accessControl) CanCreateReport(ctx context.Context) bool { - return svc.can(ctx, "report.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "report.create", r) } // CanSearchReports checks if current user can list, search or filter reports // // This function is auto-generated func (svc accessControl) CanSearchReports(ctx context.Context) bool { - return svc.can(ctx, "reports.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "reports.search", r) } -// CanAssignReminder checks if current user can assign reminders +// CanAssignReminder checks if current user can assign reminders // // This function is auto-generated func (svc accessControl) CanAssignReminder(ctx context.Context) bool { - return svc.can(ctx, "reminder.assign", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "reminder.assign", r) } // CanCreateQueue checks if current user can create messagebus queues // // This function is auto-generated func (svc accessControl) CanCreateQueue(ctx context.Context) bool { - return svc.can(ctx, "queue.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "queue.create", r) } // CanSearchQueues checks if current user can list, search or filter messagebus queues // // This function is auto-generated func (svc accessControl) CanSearchQueues(ctx context.Context) bool { - return svc.can(ctx, "queues.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "queues.search", r) } // CanCreateApigwRoute checks if current user can create api gateway route // // This function is auto-generated func (svc accessControl) CanCreateApigwRoute(ctx context.Context) bool { - return svc.can(ctx, "apigw-route.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "apigw-route.create", r) } // CanSearchApigwRoutes checks if current user can list search or filter api gateway routes // // This function is auto-generated func (svc accessControl) CanSearchApigwRoutes(ctx context.Context) bool { - return svc.can(ctx, "apigw-routes.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "apigw-routes.search", r) } // CanManageResourceTranslations checks if current user can list, search, create, or update resource translations // // This function is auto-generated func (svc accessControl) CanManageResourceTranslations(ctx context.Context) bool { - return svc.can(ctx, "resource-translations.manage", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "resource-translations.manage", r) } // rbacResourceValidator validates known component's resource by routing it to the appropriate validator @@ -900,6 +925,7 @@ func rbacResourceOperations(r string) map[string]bool { } case types.QueueResourceType: return map[string]bool{ + "render": true, "read": true, "update": true, "delete": true, @@ -970,24 +996,24 @@ func rbacResourceOperations(r string) map[string]bool { return nil } -// rbacApigwRouteResourceValidator checks validity of rbac resource and operations +// rbacApigwRouteResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacApigwRouteResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system ApigwRoute resource", o) - } - } - if !strings.HasPrefix(r, types.ApigwRouteResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for apigwRoute resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.ApigwRouteResourceType):], sep), sep) @@ -1003,7 +1029,7 @@ func rbacApigwRouteResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for ApigwRoute", i) + return fmt.Errorf("invalid path wildcard level (%d) for apigwRoute resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1014,24 +1040,24 @@ func rbacApigwRouteResourceValidator(r string, oo ...string) error { return nil } -// rbacApplicationResourceValidator checks validity of rbac resource and operations +// rbacApplicationResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacApplicationResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system Application resource", o) - } - } - if !strings.HasPrefix(r, types.ApplicationResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for application resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.ApplicationResourceType):], sep), sep) @@ -1047,7 +1073,7 @@ func rbacApplicationResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Application", i) + return fmt.Errorf("invalid path wildcard level (%d) for application resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1058,24 +1084,24 @@ func rbacApplicationResourceValidator(r string, oo ...string) error { return nil } -// rbacAuthClientResourceValidator checks validity of rbac resource and operations +// rbacAuthClientResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacAuthClientResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system AuthClient resource", o) - } - } - if !strings.HasPrefix(r, types.AuthClientResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for authClient resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.AuthClientResourceType):], sep), sep) @@ -1091,7 +1117,7 @@ func rbacAuthClientResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for AuthClient", i) + return fmt.Errorf("invalid path wildcard level (%d) for authClient resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1102,24 +1128,24 @@ func rbacAuthClientResourceValidator(r string, oo ...string) error { return nil } -// rbacQueueResourceValidator checks validity of rbac resource and operations +// rbacQueueResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacQueueResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system Queue resource", o) - } - } - if !strings.HasPrefix(r, types.QueueResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for queue resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.QueueResourceType):], sep), sep) @@ -1135,7 +1161,7 @@ func rbacQueueResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Queue", i) + return fmt.Errorf("invalid path wildcard level (%d) for queue resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1146,24 +1172,24 @@ func rbacQueueResourceValidator(r string, oo ...string) error { return nil } -// rbacReportResourceValidator checks validity of rbac resource and operations +// rbacReportResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacReportResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system Report resource", o) - } - } - if !strings.HasPrefix(r, types.ReportResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for report resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.ReportResourceType):], sep), sep) @@ -1179,7 +1205,7 @@ func rbacReportResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Report", i) + return fmt.Errorf("invalid path wildcard level (%d) for report resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1190,24 +1216,24 @@ func rbacReportResourceValidator(r string, oo ...string) error { return nil } -// rbacRoleResourceValidator checks validity of rbac resource and operations +// rbacRoleResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacRoleResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system Role resource", o) - } - } - if !strings.HasPrefix(r, types.RoleResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for role resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.RoleResourceType):], sep), sep) @@ -1223,7 +1249,7 @@ func rbacRoleResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Role", i) + return fmt.Errorf("invalid path wildcard level (%d) for role resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1234,24 +1260,24 @@ func rbacRoleResourceValidator(r string, oo ...string) error { return nil } -// rbacTemplateResourceValidator checks validity of rbac resource and operations +// rbacTemplateResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacTemplateResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system Template resource", o) - } - } - if !strings.HasPrefix(r, types.TemplateResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for template resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.TemplateResourceType):], sep), sep) @@ -1267,7 +1293,7 @@ func rbacTemplateResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Template", i) + return fmt.Errorf("invalid path wildcard level (%d) for template resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1278,24 +1304,24 @@ func rbacTemplateResourceValidator(r string, oo ...string) error { return nil } -// rbacUserResourceValidator checks validity of rbac resource and operations +// rbacUserResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacUserResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system User resource", o) - } - } - if !strings.HasPrefix(r, types.UserResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for user resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.UserResourceType):], sep), sep) @@ -1311,7 +1337,7 @@ func rbacUserResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for User", i) + return fmt.Errorf("invalid path wildcard level (%d) for user resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -1322,23 +1348,23 @@ func rbacUserResourceValidator(r string, oo ...string) error { return nil } -// rbacComponentResourceValidator checks validity of rbac resource and operations +// rbacComponentResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacComponentResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for system resource", o) - } - } - if !strings.HasPrefix(r, types.ComponentResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for system component resource", o) + } + } + return nil } diff --git a/system/template.cue b/system/template.cue new file mode 100644 index 000000000..5d4ed657c --- /dev/null +++ b/system/template.cue @@ -0,0 +1,16 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +template: schema.#resource & { + rbac: { + operations: { + read: description: "Read template" + update: description: "Update template" + delete: description: "Delete template" + render: description: "Render template" + } + } +} diff --git a/system/types/rbac.gen.go b/system/types/rbac.gen.go index ea32860c6..a0e8d625c 100644 --- a/system/types/rbac.gen.go +++ b/system/types/rbac.gen.go @@ -6,17 +6,6 @@ package types // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - system.apigw-route.yaml -// - system.application.yaml -// - system.auth-client.yaml -// - system.queue.yaml -// - system.report.yaml -// - system.role.yaml -// - system.template.yaml -// - system.user.yaml -// - system.yaml - import ( "fmt" "strconv" @@ -29,6 +18,11 @@ type ( Component struct{} ) +var ( + _ = fmt.Printf + _ = strconv.FormatUint +) + const ( ApigwRouteResourceType = "corteza::system:apigw-route" ApplicationResourceType = "corteza::system:application" @@ -55,10 +49,10 @@ func (r ApigwRoute) RbacResource() string { // RBAC resource is in the corteza::system:apigw-route/... format // // This function is auto-generated -func ApigwRouteRbacResource(id uint64) string { +func ApigwRouteRbacResource(ID uint64) string { cpts := []interface{}{ApigwRouteResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -67,7 +61,6 @@ func ApigwRouteRbacResource(id uint64) string { } -// @todo template func ApigwRouteRbacResourceTpl() string { return "%s/%s" } @@ -86,10 +79,10 @@ func (r Application) RbacResource() string { // RBAC resource is in the corteza::system:application/... format // // This function is auto-generated -func ApplicationRbacResource(id uint64) string { +func ApplicationRbacResource(ID uint64) string { cpts := []interface{}{ApplicationResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -98,7 +91,6 @@ func ApplicationRbacResource(id uint64) string { } -// @todo template func ApplicationRbacResourceTpl() string { return "%s/%s" } @@ -117,10 +109,10 @@ func (r AuthClient) RbacResource() string { // RBAC resource is in the corteza::system:auth-client/... format // // This function is auto-generated -func AuthClientRbacResource(id uint64) string { +func AuthClientRbacResource(ID uint64) string { cpts := []interface{}{AuthClientResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -129,7 +121,6 @@ func AuthClientRbacResource(id uint64) string { } -// @todo template func AuthClientRbacResourceTpl() string { return "%s/%s" } @@ -148,10 +139,10 @@ func (r Queue) RbacResource() string { // RBAC resource is in the corteza::system:queue/... format // // This function is auto-generated -func QueueRbacResource(id uint64) string { +func QueueRbacResource(ID uint64) string { cpts := []interface{}{QueueResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -160,7 +151,6 @@ func QueueRbacResource(id uint64) string { } -// @todo template func QueueRbacResourceTpl() string { return "%s/%s" } @@ -179,10 +169,10 @@ func (r Report) RbacResource() string { // RBAC resource is in the corteza::system:report/... format // // This function is auto-generated -func ReportRbacResource(id uint64) string { +func ReportRbacResource(ID uint64) string { cpts := []interface{}{ReportResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -191,7 +181,6 @@ func ReportRbacResource(id uint64) string { } -// @todo template func ReportRbacResourceTpl() string { return "%s/%s" } @@ -210,10 +199,10 @@ func (r Role) RbacResource() string { // RBAC resource is in the corteza::system:role/... format // // This function is auto-generated -func RoleRbacResource(id uint64) string { +func RoleRbacResource(ID uint64) string { cpts := []interface{}{RoleResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -222,7 +211,6 @@ func RoleRbacResource(id uint64) string { } -// @todo template func RoleRbacResourceTpl() string { return "%s/%s" } @@ -241,10 +229,10 @@ func (r Template) RbacResource() string { // RBAC resource is in the corteza::system:template/... format // // This function is auto-generated -func TemplateRbacResource(id uint64) string { +func TemplateRbacResource(ID uint64) string { cpts := []interface{}{TemplateResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -253,7 +241,6 @@ func TemplateRbacResource(id uint64) string { } -// @todo template func TemplateRbacResourceTpl() string { return "%s/%s" } @@ -272,10 +259,10 @@ func (r User) RbacResource() string { // RBAC resource is in the corteza::system:user/... format // // This function is auto-generated -func UserRbacResource(id uint64) string { +func UserRbacResource(ID uint64) string { cpts := []interface{}{UserResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) + if ID != 0 { + cpts = append(cpts, strconv.FormatUint(ID, 10)) } else { cpts = append(cpts, "*") } @@ -284,7 +271,6 @@ func UserRbacResource(id uint64) string { } -// @todo template func UserRbacResourceTpl() string { return "%s/%s" } @@ -308,7 +294,6 @@ func ComponentRbacResource() string { } -// @todo template func ComponentRbacResourceTpl() string { return "%s" } diff --git a/system/user.cue b/system/user.cue new file mode 100644 index 000000000..89b8a357a --- /dev/null +++ b/system/user.cue @@ -0,0 +1,33 @@ +package system + +import ( + "github.com/cortezaproject/corteza-server/def/schema" +) + +user: schema.#resource & { + // fields: { + // ID: schema.IdField + // handle: schema.HandleField + // email: { unique: true } + // kind: {} + // meta: { + // note: type: string + // sub: { + // sub: { "non-unique-string-named-sub": {} } + // } + // } + // } + + rbac: { + operations: { + "read": description: "Read user" + "update": description: "Update user" + "delete": description: "Delete user" + "suspend": description: "Suspend user" + "unsuspend": description: "Unsuspend user" + "email.unmask": description: "Unmask email" + "name.unmask": description: "Unmask name" + "impersonate": description: "Impersonate user" + } + } +} From 3568d0841e1abad2a770eae8d048740626330502 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Tue, 28 Dec 2021 21:36:59 +0100 Subject: [PATCH 2/7] Migrate codegen for locale to CUE --- codegen/all.cue | 14 +- .../templates/gocode/locale/service.go.tpl | 206 ++++++++ .../templates/gocode/locale/types.go.tpl | 120 +++++ .../gocode/rbac/access_control.go.tpl | 4 +- .../assets/templates/gocode/rbac/types.go.tpl | 8 +- codegen/locale-service.cue | 44 ++ codegen/locale-types.cue | 55 ++ codegen/rbac-access_control.cue | 60 ++- codegen/rbac-types.cue | 31 +- compose/chart.cue | 16 +- compose/component.cue | 8 +- compose/module-field.cue | 55 +- compose/module.cue | 13 +- compose/namespace.cue | 19 +- compose/page.cue | 37 +- compose/record.cue | 7 +- compose/service/access_control.gen.go | 496 +++++++++--------- compose/service/locale.gen.go | 27 +- compose/service/locale.go | 16 +- compose/types/locale.gen.go | 301 +++++------ compose/types/module_field.go | 48 +- compose/types/module_field_test.go | 2 +- compose/types/namespace.go | 1 - compose/types/page.go | 12 +- compose/types/rbac.gen.go | 147 +++--- def/schema/component.cue | 15 +- def/schema/locale.cue | 37 +- def/schema/platform.cue | 2 +- def/schema/rbac.cue | 27 +- def/schema/resource.cue | 116 ++-- def/schema/shared.cue | 23 +- .../templates/gocode/locale/types.go.tpl | 135 ----- system/component.cue | 2 +- system/service/access_control.gen.go | 256 ++------- system/types/rbac.gen.go | 136 ++--- 35 files changed, 1284 insertions(+), 1212 deletions(-) create mode 100644 codegen/assets/templates/gocode/locale/service.go.tpl create mode 100644 codegen/assets/templates/gocode/locale/types.go.tpl create mode 100644 codegen/locale-service.cue create mode 100644 codegen/locale-types.cue delete mode 100644 pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl diff --git a/codegen/all.cue b/codegen/all.cue index 198b29124..693e5570d 100644 --- a/codegen/all.cue +++ b/codegen/all.cue @@ -1,10 +1,16 @@ package codegen import ( - "github.com/cortezaproject/corteza-server/codegen/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) -all: [...schema.#codegen] & - rbacAccessControl + - rbacTypes + +// List of all codegen jobs for the entire platform +// +// How to run it? +// @todo when this gets into +// cue eval codegen/*.cue --out json -e platform | go run codegen/tool/*.go -v +platform: [...schema.#codegen] & + rbacAccessControl+ + rbacTypes+ + localeTypes+ [] // placeholder diff --git a/codegen/assets/templates/gocode/locale/service.go.tpl b/codegen/assets/templates/gocode/locale/service.go.tpl new file mode 100644 index 000000000..f18b25cdd --- /dev/null +++ b/codegen/assets/templates/gocode/locale/service.go.tpl @@ -0,0 +1,206 @@ +package {{ .package }} + +{{ template "gocode/header-gentext.tpl" }} + +import ( + "context" + +{{- range .imports }} + {{ . }} +{{- end }} + "github.com/cortezaproject/corteza-server/pkg/actionlog" + intAuth "github.com/cortezaproject/corteza-server/pkg/auth" + "github.com/cortezaproject/corteza-server/pkg/errors" + "github.com/cortezaproject/corteza-server/pkg/locale" + "github.com/cortezaproject/corteza-server/pkg/options" + "github.com/cortezaproject/corteza-server/store" + systemTypes "github.com/cortezaproject/corteza-server/system/types" + "golang.org/x/text/language" +) + +type ( + localeAccessControl interface { + CanManageResourceTranslations(ctx context.Context) bool + } + + resourceTranslationsManager struct { + actionlog actionlog.Recorder + locale locale.Resource + store store.Storer + ac localeAccessController + } + + localeAccessController interface { + CanManageResourceTranslations(context.Context) bool + } + + ResourceTranslationsManagerService interface { +{{- range .resources }} + {{ .expIdent }}(ctx context.Context, {{ range .references }}{{ . }} uint64, {{ end }}) (locale.ResourceTranslationSet, error) +{{- end }} + + Upsert(context.Context, locale.ResourceTranslationSet) error + Locale() locale.Resource + } +) + +var ErrNotAllowedToManageResourceTranslations = errors.Unauthorized("not allowed to manage resource translations") + +func ResourceTranslationsManager(ls locale.Resource) *resourceTranslationsManager { + return &resourceTranslationsManager{ + actionlog: DefaultActionlog, + store: DefaultStore, + ac: DefaultAccessControl, + locale: ls, + } +} + +func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.ResourceTranslationSet) (err error) { + // User is allowed to manage resource translations when: + // - managed resource translation strings are all for default language + // or + // - user is allowed to manage resource translations + if rr.ContainsForeign(svc.Locale().Default().Tag) { + if !svc.ac.CanManageResourceTranslations(ctx) { + return ErrNotAllowedToManageResourceTranslations + } + } + + for _, r := range rr { + r.Msg = locale.SanitizeMessage(r.Msg) + } + + // @todo validation + + me := intAuth.GetIdentityFromContext(ctx) + + // - group by resource + localeByRes := make(map[string]locale.ResourceTranslationSet) + for _, r := range rr { + localeByRes[r.Resource] = append(localeByRes[r.Resource], r) + } + + // - for each resource, fetch the current state + sysLocale := make(systemTypes.ResourceTranslationSet, 0, len(rr)) + for res, rr := range localeByRes { + current, _, err := store.SearchResourceTranslations(ctx, svc.store, systemTypes.ResourceTranslationFilter{ + Resource: res, + }) + if err != nil { + return err + } + + // get deltas and prepare upsert accordingly + aux := current.New(rr) + aux.Walk(func(cc *systemTypes.ResourceTranslation) error { + cc.ID = nextID() + cc.CreatedAt = *now() + cc.CreatedBy = me.Identity() + + return nil + }) + sysLocale = append(sysLocale, aux...) + + aux = current.Old(rr) + _ = aux.Walk(func(cc *systemTypes.ResourceTranslation) error { + cc.UpdatedAt = now() + cc.UpdatedBy = me.Identity() + return nil + }) + sysLocale = append(sysLocale, aux...) + } + + err = store.UpsertResourceTranslation(ctx, svc.store, sysLocale...) + if err != nil { + return err + } + + // Reload ALL resource translations + // @todo we could probably do this more selectively and refresh only updated resources? + _ = locale.Global().ReloadResourceTranslations(ctx) + + return nil +} + +func (svc resourceTranslationsManager) Locale() locale.Resource { + return svc.locale +} + +{{- range .resources }} + +func (svc resourceTranslationsManager) {{ .expIdent }}(ctx context.Context, {{ range .references }}{{ . }} uint64, {{ end }}) (locale.ResourceTranslationSet, error) { + var ( + err error + out locale.ResourceTranslationSet + res *types.{{ .expIdent }} + k types.LocaleKey + ) + + res, err = svc.load{{ .expIdent }}(ctx, svc.store, {{ range .references }}{{ . }}, {{ end }}) + if err != nil { + return nil, err + } + + for _, tag := range svc.locale.Tags() { + {{- range .keys}} + {{- if not .customHandler }} + k = types.{{ .struct }} + out = append(out, &locale.ResourceTranslation{ + Resource: res.ResourceTranslation(), + Lang: tag.String(), + Key: k.Path, + Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path), + }) + {{ end }} + {{- end}} + } + + {{ if .extended }} + tmp, err := svc.{{ .ident }}Extended(ctx, res) + return append(out, tmp...), err + {{- else }} + return out, nil + {{- end }} +} + +{{- end }} + +func updateTranslations(ctx context.Context, ac localeAccessControl, lsvc ResourceTranslationsManagerService, tt ...*locale.ResourceTranslation) error { + if lsvc == nil || lsvc.Locale() == nil || lsvc.Locale().Default() == nil { + // gracefully handle partial initializations + return nil + } + + var ( + // assuming options will not change after start + contentLang = lsvc.Locale().Default().Tag + ) + + if options.Locale().ResourceTranslationsEnabled { + contentLang = locale.GetContentLanguageFromContext(ctx) + // Resource translations enabled + if contentLang == language.Und { + // If no content-language meta (HTTP header) info was + // used, do not run update translations - we do not know + // what is the language that we're sending in + return nil + } + + if !lsvc.Locale().SupportedLang(contentLang) { + // unsupported language + return errors.InvalidData("unsupported language") + } + + if !ac.CanManageResourceTranslations(ctx) { + return errors.Unauthorized("not allowed to manage resource translations") + } + } + + locale.ResourceTranslationSet(tt).SetLanguage(contentLang) + if err := lsvc.Upsert(ctx, tt); err != nil { + return err + } + + return nil +} + diff --git a/codegen/assets/templates/gocode/locale/types.go.tpl b/codegen/assets/templates/gocode/locale/types.go.tpl new file mode 100644 index 000000000..28a957631 --- /dev/null +++ b/codegen/assets/templates/gocode/locale/types.go.tpl @@ -0,0 +1,120 @@ +package {{ .package }} + +{{ template "gocode/header-gentext.tpl" }} + +import ( + "fmt" + "strconv" + "github.com/cortezaproject/corteza-server/pkg/locale" +) + +type ( + LocaleKey struct { + Name string + Resource string + Path string + CustomHandler string + } +) + +// Types and stuff +const ( +{{- range .resources }} + {{ .const }} = "{{ .type }}" +{{- end }} +) + +var ( + // @todo can we remove LocaleKey struct for string constant? +{{- range .resources }} +{{- range .keys }} + {{ .struct }} = LocaleKey{ Path: {{ printf "%q" .path }} } +{{- end }} +{{- end }} +) + +{{- range .resources }} + +// ResourceTranslation returns string representation of Locale resource for {{ .expIdent }} by calling {{ .expIdent }}ResourceTranslation fn +// +// Locale resource is in "{{ .type }}/..." format +// +// This function is auto-generated +func (r {{ .expIdent }}) ResourceTranslation() string { + return {{ .expIdent }}ResourceTranslation({{ if .references }}{{ range .references }}r.{{ . }},{{ end }}{{ end }}) +} + +// {{ .expIdent }}ResourceTranslation returns string representation of Locale resource for {{ .expIdent }} +// +// Locale resource is in the {{ .type }}/{{- if .references }}...{{ end }} format +// +// This function is auto-generated +func {{ .expIdent }}ResourceTranslation({{ if .references }}{{ range .references }}{{ . }} uint64,{{ end }}{{ end }}) string { + {{- if .references }} + cpts := []interface{{"{}"}}{ + {{ .expIdent }}ResourceTranslationType, + {{- range .references }} + strconv.FormatUint({{ . }}, 10), + {{- end }} + } + + return fmt.Sprintf({{ .expIdent }}ResourceTranslationTpl(), cpts...) + {{- end }} +} + +func {{ .expIdent }}ResourceTranslationTpl() string { + {{- if .references }} + return "%s + {{- range .references }}/%s{{- end }}" + + {{- else }} + return "%s" + {{- end }} +} + +func (r *{{ .expIdent }}) DecodeTranslations(tt locale.ResourceTranslationIndex) { + var aux *locale.ResourceTranslation + + {{- range .keys }} + {{ if .decodeFunc }} + {{ if not .extended }} + r.{{ .decodeFunc }}(tt) + {{- end}} + {{ else }} + if aux = tt.FindByKey({{ .struct }}.Path); aux != nil { + r.{{ .fieldPath }} = aux.Msg + } + {{- end}} + {{- end}} + + {{- if .extended }} + r.decodeTranslations(tt) + {{- end }} +} + +func (r *{{ .expIdent }}) EncodeTranslations() (out locale.ResourceTranslationSet) { + out = locale.ResourceTranslationSet{} + {{- range .keys }} + {{ if .encodeFunc }} + {{ if not .extended }} + out = append(out, r.{{ .encodeFunc }}()...) + {{- end}} + {{ else }} + if r.{{ .fieldPath }} != "" { + out = append(out, &locale.ResourceTranslation{ + Resource: r.ResourceTranslation(), + Key: {{ .struct }}.Path, + Msg: locale.SanitizeMessage(r.{{ .fieldPath }}), + }) + } + {{- end}} + {{- end}} + + {{- if .extended }} + out = append(out, r.encodeTranslations()...) + {{- end }} + + return out +} + +{{- end }} diff --git a/codegen/assets/templates/gocode/rbac/access_control.go.tpl b/codegen/assets/templates/gocode/rbac/access_control.go.tpl index e26ac69cc..907532aa1 100644 --- a/codegen/assets/templates/gocode/rbac/access_control.go.tpl +++ b/codegen/assets/templates/gocode/rbac/access_control.go.tpl @@ -10,7 +10,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/cortezaproject/corteza-server/pkg/actionlog" {{- range .imports }} - "{{ . }}" + {{ . }} {{- end }} ) @@ -57,7 +57,7 @@ func (svc accessControl) List() (out []map[string]string) { {{- range .operations }} { "type": {{ .const }}, - "any": {{ .ctor }}, + "any": {{ .resFunc }}({{ range .references }}0,{{ end }}), "op": {{ printf "%q" .op }}, }, {{- end }} diff --git a/codegen/assets/templates/gocode/rbac/types.go.tpl b/codegen/assets/templates/gocode/rbac/types.go.tpl index fc1e294eb..760f0c04b 100644 --- a/codegen/assets/templates/gocode/rbac/types.go.tpl +++ b/codegen/assets/templates/gocode/rbac/types.go.tpl @@ -38,7 +38,7 @@ const ( // // This function is auto-generated func (r {{ .goType }}) RbacResource() string { - return {{ .resFunc }}({{ if not .component }}{{ range .references }}r.{{ . }},{{ end }}{{ end }}) + return {{ .resFunc }}({{ if not .component }}{{ range .references }}r.{{ .refField }},{{ end }}{{ end }}) } // {{ .resFunc }} returns string representation of RBAC resource for {{ .goType }} @@ -46,12 +46,12 @@ func (r {{ .goType }}) RbacResource() string { // RBAC resource is in the {{ .type }}/{{- if .references }}...{{ end }} format // // This function is auto-generated -func {{ .resFunc }}({{ if not .component }}{{ range .references }}{{ . }} uint64,{{ end }}{{ end }}) string { +func {{ .resFunc }}({{ if not .component }}{{ range .references }}{{ .param }} uint64,{{ end }}{{ end }}) string { {{- if .references }} cpts := []interface{{"{}"}}{{"{"}}{{ .goType }}ResourceType{{"}"}} {{- range .references }} - if {{ . }} != 0 { - cpts = append(cpts, strconv.FormatUint({{ . }}, 10)) + if {{ .param }} != 0 { + cpts = append(cpts, strconv.FormatUint({{ .param }}, 10)) } else { cpts = append(cpts, "*") } diff --git a/codegen/locale-service.cue b/codegen/locale-service.cue new file mode 100644 index 000000000..2807d4597 --- /dev/null +++ b/codegen/locale-service.cue @@ -0,0 +1,44 @@ +package codegen + +import ( + "github.com/cortezaproject/corteza-server/app" + "github.com/cortezaproject/corteza-server/codegen/schema" + "strings" +) + +localeService: + [...schema.#codegen] & + [ + // wrapped with additional for loop to trim out templates with empty types list + for tpl in [ + for cmp in app.corteza.components { + template: "gocode/locale/service.go.tpl" + output: "\(cmp.ident)/service/locale.gen.go" + payload: { + package: "service" + imports: [ + "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", + ] + + resources: [ + for res in cmp.resources if (res.locale != _|_) if (!res.locale.skipSvc) { + expIdent: res.expIdent + ident: res.ident + + references: [ for field in res.locale.resource.references {strings.ToTitle(field)}] + + extended: res.locale.extended + + keys: [ for key in res.locale.keys if key.handlerFunc == _|_ { + struct: key.struct + + extended: extended + customHandler: key.customHandler + if key.serviceFunc != _|_ {serviceFunc: key.serviceFunc} + }] + }, + ] + } + }, + // skip empty type lists + ] if len(tpl.payload.resources) > 0 {tpl}] diff --git a/codegen/locale-types.cue b/codegen/locale-types.cue new file mode 100644 index 000000000..ff0b7c993 --- /dev/null +++ b/codegen/locale-types.cue @@ -0,0 +1,55 @@ +package codegen + +import ( + "github.com/cortezaproject/corteza-server/app" + "github.com/cortezaproject/corteza-server/codegen/schema" + "strings" +) + +localeTypes: + [...schema.#codegen] & + [ + // wrapped with additional for loop to trim out templates with empty types list + for tpl in [ + for cmp in app.corteza.components { + template: "gocode/locale/types.go.tpl" + output: "\(cmp.ident)/types/locale.gen.go" + payload: { + package: "types" + + resources: [ + for res in cmp.resources if res.locale != _|_ { + expIdent: res.expIdent + const: res.locale.resource.const + type: res.locale.resource.type + + references: [ for field in res.locale.resource.references {strings.ToTitle(field)}] + + extended: res.locale.extended + + keys: [ for key in res.locale.keys if key.handlerFunc == _|_ { + struct: key.struct + field: strings.ToTitle(key.name) + + path: strings.Join([ for p in key.expandedPath { + if p.var {"{{\(p.part)}}"} + if !p.var {p.part} + }], ".") + + if !key.customHandler { + fieldPath: strings.Join([ for p in key.expandedPath { + strings.ToTitle(p.part) + }], ".") + } + + "extended": extended + if key.decodeFunc != _|_ {decodeFunc: key.decodeFunc} + if key.encodeFunc != _|_ {encodeFunc: key.encodeFunc} + + }] + }, + ] + } + }, + // skip empty type lists + ] if len(tpl.payload.resources) > 0 {tpl}] diff --git a/codegen/rbac-access_control.cue b/codegen/rbac-access_control.cue index 49e777ba1..46cec3af7 100644 --- a/codegen/rbac-access_control.cue +++ b/codegen/rbac-access_control.cue @@ -1,8 +1,8 @@ package codegen import ( - "github.com/cortezaproject/corteza-server/codegen/schema" - "github.com/cortezaproject/corteza-server/app" + "github.com/cortezaproject/corteza-server/codegen/schema" + "github.com/cortezaproject/corteza-server/app" ) rbacAccessControl: @@ -12,47 +12,53 @@ rbacAccessControl: template: "gocode/rbac/access_control.go.tpl" output: "\(cmp.ident)/service/access_control.gen.go" payload: { - imports: [ - "github.com/cortezaproject/corteza-server/\(cmp.ident)/types", - ] package: "service" + imports: [ + "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", + ] // All possible RBAC operations on component and resources // flattened operations: [ for res in cmp.resources for op in res.rbac.operations { - "op": op.handle - "const": "types.\(res.expIdent)ResourceType" - "ctor": "types.\(res.expIdent)RbacResource(\(len(res.rbac.resource.references)*"0,"))" - "goType": res.goType - "description": op.description - "checkFuncName": op.checkFuncName + "op": op.handle + const: "types.\(res.expIdent)ResourceType" + resFunc: "types.\(res.expIdent)RbacResource" + goType: "types.\(res.expIdent)" + description: op.description + checkFuncName: op.checkFuncName + + if len(res.parents) > 0 { + references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] + } }, for op in cmp.rbac.operations { - "op": op.handle - "const": "types.ComponentResourceType" - "ctor": "types.ComponentRbacResource()" - "goType": "types.Component" - "description": op.description - "checkFuncName": op.checkFuncName - "component": true + "op": op.handle + const: "types.ComponentResourceType" + resFunc: "types.ComponentRbacResource" + goType: "types.Component" + description: op.description + checkFuncName: op.checkFuncName + component: true }, ] // Operation/resource validators, grouped by resource validation: [ for res in cmp.resources { - "label": res.ident - "const": "types.\(res.expIdent)ResourceType" - "funcName": "rbac\(res.expIdent)ResourceValidator" - "references": res.rbac.resource.references - "operations": [ for op in res.rbac.operations {op.handle}] + label: res.ident + const: "types.\(res.expIdent)ResourceType" + funcName: "rbac\(res.expIdent)ResourceValidator" + if len(res.parents) > 0 { + references: [ for p in res.parents {p.refField}, "ID"] + } + operations: [ for op in res.rbac.operations {op.handle}] }, { - "label": "\(cmp.ident) component" - "const": "types.ComponentResourceType" - "funcName": "rbacComponentResourceValidator" - "operations": [ for op in cmp.rbac.operations {op.handle}] + label: "\(cmp.ident) component" + const: "types.ComponentResourceType" + funcName: "rbacComponentResourceValidator" + operations: [ for op in cmp.rbac.operations {op.handle}] }, ] } diff --git a/codegen/rbac-types.cue b/codegen/rbac-types.cue index 1f5ecc5ef..6982f2342 100644 --- a/codegen/rbac-types.cue +++ b/codegen/rbac-types.cue @@ -3,7 +3,6 @@ package codegen import ( "github.com/cortezaproject/corteza-server/app" "github.com/cortezaproject/corteza-server/codegen/schema" - "strings" ) rbacTypes: @@ -19,23 +18,25 @@ rbacTypes: // Operation/resource validators, grouped by resource types: [ for res in cmp.resources { - "const": "\(res.expIdent)ResourceType" - "type": res.rbac.resource.type - "resFunc": "\(res.expIdent)RbacResource" - "tplFunc": "\(res.expIdent)RbacResourceTpl" - "attFunc": "\(res.expIdent)RbacAttributes" - "goType": res.expIdent + const: "\(res.expIdent)ResourceType" + type: res.fqrn + resFunc: "\(res.expIdent)RbacResource" + tplFunc: "\(res.expIdent)RbacResourceTpl" + attFunc: "\(res.expIdent)RbacAttributes" + goType: res.expIdent - "references": [ for field in res.rbac.resource.references { strings.ToTitle(field) } ] + if len(res.parents) > 0 { + references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] + } }, { - "const": "ComponentResourceType" - "type": cmp.rbac.resource.type - "resFunc": "ComponentRbacResource" - "tplFunc": "ComponentRbacResourceTpl" - "attFunc": "ComponentRbacAttributes" - "goType": "Component" - "component": true + const: "ComponentResourceType" + type: cmp.fqrn + resFunc: "ComponentRbacResource" + tplFunc: "ComponentRbacResourceTpl" + attFunc: "ComponentRbacAttributes" + goType: "Component" + component: true }, ] } diff --git a/compose/chart.cue b/compose/chart.cue index 2435dc156..25e87c424 100644 --- a/compose/chart.cue +++ b/compose/chart.cue @@ -5,19 +5,15 @@ import ( ) chart: schema.#resource & { - rbac: { - resource: references: [ "namespaceID", "ID"] + parents: [ + {handle: "namespace"}, + ] + rbac: { operations: { "read": {} - "update": {} - "delete": {} + "update": {} + "delete": {} } } - - // locale: - // resource: - // references: [ namespace, ID ] - // keys: - // - name } diff --git a/compose/component.cue b/compose/component.cue index 931accdcf..0136b3b78 100644 --- a/compose/component.cue +++ b/compose/component.cue @@ -5,15 +5,15 @@ import ( ) component: schema.#component & { - ident: "compose" + handle: "compose" resources: { - "namespace": namespace + "chart": chart "module": module "module-field": moduleField - "record": record + "namespace": namespace "page": page - "chart": chart + "record": record } rbac: operations: { diff --git a/compose/module-field.cue b/compose/module-field.cue index 0e7f174eb..57bb11a78 100644 --- a/compose/module-field.cue +++ b/compose/module-field.cue @@ -5,30 +5,47 @@ import ( ) moduleField: schema.#resource & { - rbac: { - resource: references: [ "namespaceID", "moduleID", "ID"] + parents: [ + {handle: "namespace"}, + {handle: "module"}, + ] + rbac: { operations: { "recod.value.read": description: "Read field value on records" "recod.value.update": description: "Update field value on records" } } - //locale: - // resource: - // references: [ namespace, module, ID ] - // - // skipSvc: true - // keys: - // - label - // - { name: descriptionView, path: meta.description.view, custom: true, customHandler: descriptionView } - // - { name: descriptionEdit, path: meta.description.edit, custom: true, customHandler: descriptionEdit } - // - { name: hintView, path: meta.hint.view, custom: true, customHandler: hintView } - // - { name: hintEdit, path: meta.hint.edit, custom: true, customHandler: hintEdit } - // - { name: validatorError, path: "expression.validator.{{validatorID}}.error", custom: true, customHandler: validatorError } - // - { name: optionsOptionTexts, - // path: "meta.options.{{value}}.text", - // custom: true, - // customHandler: optionsOptionTexts - // } + locale: { + skipSvc: true + + keys: { + label: {} + descriptionView: { + path: ["meta", "description", "view"] + customHandler: true + } + descriptionEdit: { + path: ["meta", "description", "edit"] + customHandler: true + } + hintView: { + path: ["meta", "hint", "view"] + customHandler: true + } + hintEdit: { + path: ["meta", "hint", "edit"] + customHandler: true + } + validatorError: { + path: ["expression", "validator", {part: "validatorID", var: true}, "error"] + customHandler: true + } + optionsOptionTexts: { + path: ["meta", "options", {part: "value", var: true}, "text"] + customHandler: true + } + } + } } diff --git a/compose/module.cue b/compose/module.cue index c1b0409f6..a08099d1d 100644 --- a/compose/module.cue +++ b/compose/module.cue @@ -5,9 +5,12 @@ import ( ) module: schema.#resource & { - rbac: { - resource: references: [ "namespaceID", "ID"] + handle: "module" + parents: [ + {handle: "namespace"}, + ] + rbac: { operations: { "read": {} "update": {} @@ -17,6 +20,12 @@ module: schema.#resource & { } } + locale: { + keys: { + "name": {} + } + } + //locale: // resource: // references: [ namespace, ID ] diff --git a/compose/namespace.cue b/compose/namespace.cue index 4ecf6d130..434c2dcf5 100644 --- a/compose/namespace.cue +++ b/compose/namespace.cue @@ -20,10 +20,17 @@ namespace: schema.#resource & { } } - // - //locale: - // keys: - // - name - // - { path: subtitle, field: "Meta.Subtitle" } - // - { path: description, field: "Meta.Description" } + locale: { + resource: references: [ "ID"] + + keys: { + name: {} + metaSubtitle: { + path: ["meta", "subtitle"] + } + metaDescription: { + path: ["meta", "description"] + } + } + } } diff --git a/compose/page.cue b/compose/page.cue index 04ffa49a4..dfe088f48 100644 --- a/compose/page.cue +++ b/compose/page.cue @@ -5,9 +5,11 @@ import ( ) page: schema.#resource & { - rbac: { - resource: references: [ "namespaceID", "ID"] + parents: [ + {handle: "namespace"}, + ] + rbac: { operations: { "read": {} "update": {} @@ -15,15 +17,24 @@ page: schema.#resource & { } } - //locale: - // resource: - // references: [ namespace, ID ] - // - // extended: true - // keys: - // - title - // - description - // - { name: blockTitle, path: "pageBlock.{{blockID}}.title", custom: true } - // - { name: blockDescription, path: "pageBlock.{{blockID}}.description", custom: true } - // - { name: blockAutomationButtonlabel, path: "pageBlock.{{blockID}}.button.{{buttonID}}.label", custom: true } + locale: { + extended: true + + keys: { + title: {} + description: {} + blockTitle: { + path: ["pageBlock", {part: "blockID", var: true}, "title"] + customHandler: true + } + blockDescription: { + path: ["pageBlock", {part: "blockID", var: true}, "description"] + customHandler: true + } + blockAutomationButtonLabel: { + path: ["pageBlock", {part: "blockID", var: true}, "button", {part: "buttonID", var: true}, "label"] + customHandler: true + } + } + } } diff --git a/compose/record.cue b/compose/record.cue index dfbfe11fe..dd10bc163 100644 --- a/compose/record.cue +++ b/compose/record.cue @@ -5,9 +5,12 @@ import ( ) record: schema.#resource & { - rbac: { - resource: references: [ "namespaceID", "moduleID", "ID"] + parents: [ + {handle: "namespace"}, + {handle: "module"}, + ] + rbac: { operations: { "read": {} "update": {} diff --git a/compose/service/access_control.gen.go b/compose/service/access_control.gen.go index 4f8173af1..70eac31ea 100644 --- a/compose/service/access_control.gen.go +++ b/compose/service/access_control.gen.go @@ -9,11 +9,12 @@ package service import ( "context" "fmt" + "strings" + "github.com/cortezaproject/corteza-server/compose/types" "github.com/cortezaproject/corteza-server/pkg/actionlog" "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/spf13/cast" - "strings" ) type ( @@ -55,55 +56,20 @@ func (svc accessControl) Effective(ctx context.Context, rr ...rbac.Resource) (ee func (svc accessControl) List() (out []map[string]string) { def := []map[string]string{ { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), + "type": types.ChartResourceType, + "any": types.ChartRbacResource(0, 0), "op": "read", }, { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), + "type": types.ChartResourceType, + "any": types.ChartRbacResource(0, 0), "op": "update", }, { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), + "type": types.ChartResourceType, + "any": types.ChartRbacResource(0, 0), "op": "delete", }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "manage", - }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "module.create", - }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "modules.search", - }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "chart.create", - }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "charts.search", - }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "page.create", - }, - { - "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(0), - "op": "pages.search", - }, { "type": types.ModuleResourceType, "any": types.ModuleRbacResource(0, 0), @@ -140,20 +106,55 @@ func (svc accessControl) List() (out []map[string]string) { "op": "recod.value.update", }, { - "type": types.RecordResourceType, - "any": types.RecordRbacResource(0, 0, 0), + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), "op": "read", }, { - "type": types.RecordResourceType, - "any": types.RecordRbacResource(0, 0, 0), + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), "op": "update", }, { - "type": types.RecordResourceType, - "any": types.RecordRbacResource(0, 0, 0), + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), "op": "delete", }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "manage", + }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "module.create", + }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "modules.search", + }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "chart.create", + }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "charts.search", + }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "page.create", + }, + { + "type": types.NamespaceResourceType, + "any": types.NamespaceRbacResource(), + "op": "pages.search", + }, { "type": types.PageResourceType, "any": types.PageRbacResource(0, 0), @@ -170,18 +171,18 @@ func (svc accessControl) List() (out []map[string]string) { "op": "delete", }, { - "type": types.ChartResourceType, - "any": types.ChartRbacResource(0, 0), + "type": types.RecordResourceType, + "any": types.RecordRbacResource(0, 0, 0), "op": "read", }, { - "type": types.ChartResourceType, - "any": types.ChartRbacResource(0, 0), + "type": types.RecordResourceType, + "any": types.RecordRbacResource(0, 0, 0), "op": "update", }, { - "type": types.ChartResourceType, - "any": types.ChartRbacResource(0, 0), + "type": types.RecordResourceType, + "any": types.RecordRbacResource(0, 0, 0), "op": "delete", }, { @@ -287,21 +288,91 @@ func (svc accessControl) CloneRulesByRoleID(ctx context.Context, fromRoleID uint return svc.rbac.CloneRulesByRoleID(ctx, fromRoleID, toRoleID...) } -// CanReadNamespace checks if current user can read corteza::compose:namespace +// CanReadChart checks if current user can read +// +// This function is auto-generated +func (svc accessControl) CanReadChart(ctx context.Context, r *types.Chart) bool { + return svc.can(ctx, "read", r) +} + +// CanUpdateChart checks if current user can update +// +// This function is auto-generated +func (svc accessControl) CanUpdateChart(ctx context.Context, r *types.Chart) bool { + return svc.can(ctx, "update", r) +} + +// CanDeleteChart checks if current user can delete +// +// This function is auto-generated +func (svc accessControl) CanDeleteChart(ctx context.Context, r *types.Chart) bool { + return svc.can(ctx, "delete", r) +} + +// CanReadModule checks if current user can read +// +// This function is auto-generated +func (svc accessControl) CanReadModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "read", r) +} + +// CanUpdateModule checks if current user can update +// +// This function is auto-generated +func (svc accessControl) CanUpdateModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "update", r) +} + +// CanDeleteModule checks if current user can delete +// +// This function is auto-generated +func (svc accessControl) CanDeleteModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "delete", r) +} + +// CanCreateRecordModule checks if current user can create record +// +// This function is auto-generated +func (svc accessControl) CanCreateRecordModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "record.create", r) +} + +// CanSearchRecordsModule checks if current user can list, search or filter records +// +// This function is auto-generated +func (svc accessControl) CanSearchRecordsModule(ctx context.Context, r *types.Module) bool { + return svc.can(ctx, "records.search", r) +} + +// CanReadRecodValueModuleField checks if current user can read field value on records +// +// This function is auto-generated +func (svc accessControl) CanReadRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { + return svc.can(ctx, "recod.value.read", r) +} + +// CanUpdateRecodValueModuleField checks if current user can update field value on records +// +// This function is auto-generated +func (svc accessControl) CanUpdateRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { + return svc.can(ctx, "recod.value.update", r) +} + +// CanReadNamespace checks if current user can read // // This function is auto-generated func (svc accessControl) CanReadNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "read", r) } -// CanUpdateNamespace checks if current user can update corteza::compose:namespace +// CanUpdateNamespace checks if current user can update // // This function is auto-generated func (svc accessControl) CanUpdateNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "update", r) } -// CanDeleteNamespace checks if current user can delete corteza::compose:namespace +// CanDeleteNamespace checks if current user can delete // // This function is auto-generated func (svc accessControl) CanDeleteNamespace(ctx context.Context, r *types.Namespace) bool { @@ -357,115 +428,45 @@ func (svc accessControl) CanSearchPagesNamespace(ctx context.Context, r *types.N return svc.can(ctx, "pages.search", r) } -// CanReadModule checks if current user can read corteza::compose:module -// -// This function is auto-generated -func (svc accessControl) CanReadModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "read", r) -} - -// CanUpdateModule checks if current user can update corteza::compose:module -// -// This function is auto-generated -func (svc accessControl) CanUpdateModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "update", r) -} - -// CanDeleteModule checks if current user can delete corteza::compose:module -// -// This function is auto-generated -func (svc accessControl) CanDeleteModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "delete", r) -} - -// CanCreateRecordModule checks if current user can create record -// -// This function is auto-generated -func (svc accessControl) CanCreateRecordModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "record.create", r) -} - -// CanSearchRecordsModule checks if current user can list, search or filter records -// -// This function is auto-generated -func (svc accessControl) CanSearchRecordsModule(ctx context.Context, r *types.Module) bool { - return svc.can(ctx, "records.search", r) -} - -// CanReadRecodValueModuleField checks if current user can read field value on records -// -// This function is auto-generated -func (svc accessControl) CanReadRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { - return svc.can(ctx, "recod.value.read", r) -} - -// CanUpdateRecodValueModuleField checks if current user can update field value on records -// -// This function is auto-generated -func (svc accessControl) CanUpdateRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { - return svc.can(ctx, "recod.value.update", r) -} - -// CanReadRecord checks if current user can read corteza::compose:record -// -// This function is auto-generated -func (svc accessControl) CanReadRecord(ctx context.Context, r *types.Record) bool { - return svc.can(ctx, "read", r) -} - -// CanUpdateRecord checks if current user can update corteza::compose:record -// -// This function is auto-generated -func (svc accessControl) CanUpdateRecord(ctx context.Context, r *types.Record) bool { - return svc.can(ctx, "update", r) -} - -// CanDeleteRecord checks if current user can delete corteza::compose:record -// -// This function is auto-generated -func (svc accessControl) CanDeleteRecord(ctx context.Context, r *types.Record) bool { - return svc.can(ctx, "delete", r) -} - -// CanReadPage checks if current user can read corteza::compose:page +// CanReadPage checks if current user can read // // This function is auto-generated func (svc accessControl) CanReadPage(ctx context.Context, r *types.Page) bool { return svc.can(ctx, "read", r) } -// CanUpdatePage checks if current user can update corteza::compose:page +// CanUpdatePage checks if current user can update // // This function is auto-generated func (svc accessControl) CanUpdatePage(ctx context.Context, r *types.Page) bool { return svc.can(ctx, "update", r) } -// CanDeletePage checks if current user can delete corteza::compose:page +// CanDeletePage checks if current user can delete // // This function is auto-generated func (svc accessControl) CanDeletePage(ctx context.Context, r *types.Page) bool { return svc.can(ctx, "delete", r) } -// CanReadChart checks if current user can read corteza::compose:chart +// CanReadRecord checks if current user can read // // This function is auto-generated -func (svc accessControl) CanReadChart(ctx context.Context, r *types.Chart) bool { +func (svc accessControl) CanReadRecord(ctx context.Context, r *types.Record) bool { return svc.can(ctx, "read", r) } -// CanUpdateChart checks if current user can update corteza::compose:chart +// CanUpdateRecord checks if current user can update // // This function is auto-generated -func (svc accessControl) CanUpdateChart(ctx context.Context, r *types.Chart) bool { +func (svc accessControl) CanUpdateRecord(ctx context.Context, r *types.Record) bool { return svc.can(ctx, "update", r) } -// CanDeleteChart checks if current user can delete corteza::compose:chart +// CanDeleteRecord checks if current user can delete // // This function is auto-generated -func (svc accessControl) CanDeleteChart(ctx context.Context, r *types.Chart) bool { +func (svc accessControl) CanDeleteRecord(ctx context.Context, r *types.Record) bool { return svc.can(ctx, "delete", r) } @@ -522,18 +523,18 @@ func (svc accessControl) CanManageResourceTranslations(ctx context.Context) bool // This function is auto-generated func rbacResourceValidator(r string, oo ...string) error { switch rbac.ResourceType(r) { - case types.NamespaceResourceType: - return rbacNamespaceResourceValidator(r, oo...) + case types.ChartResourceType: + return rbacChartResourceValidator(r, oo...) case types.ModuleResourceType: return rbacModuleResourceValidator(r, oo...) case types.ModuleFieldResourceType: return rbacModuleFieldResourceValidator(r, oo...) - case types.RecordResourceType: - return rbacRecordResourceValidator(r, oo...) + case types.NamespaceResourceType: + return rbacNamespaceResourceValidator(r, oo...) case types.PageResourceType: return rbacPageResourceValidator(r, oo...) - case types.ChartResourceType: - return rbacChartResourceValidator(r, oo...) + case types.RecordResourceType: + return rbacRecordResourceValidator(r, oo...) case types.ComponentResourceType: return rbacComponentResourceValidator(r, oo...) } @@ -546,18 +547,11 @@ func rbacResourceValidator(r string, oo ...string) error { // This function is auto-generated func rbacResourceOperations(r string) map[string]bool { switch rbac.ResourceType(r) { - case types.NamespaceResourceType: + case types.ChartResourceType: return map[string]bool{ - "read": true, - "update": true, - "delete": true, - "manage": true, - "module.create": true, - "modules.search": true, - "chart.create": true, - "charts.search": true, - "page.create": true, - "pages.search": true, + "read": true, + "update": true, + "delete": true, } case types.ModuleResourceType: return map[string]bool{ @@ -572,11 +566,18 @@ func rbacResourceOperations(r string) map[string]bool { "recod.value.read": true, "recod.value.update": true, } - case types.RecordResourceType: + case types.NamespaceResourceType: return map[string]bool{ - "read": true, - "update": true, - "delete": true, + "read": true, + "update": true, + "delete": true, + "manage": true, + "module.create": true, + "modules.search": true, + "chart.create": true, + "charts.search": true, + "page.create": true, + "pages.search": true, } case types.PageResourceType: return map[string]bool{ @@ -584,7 +585,7 @@ func rbacResourceOperations(r string) map[string]bool { "update": true, "delete": true, } - case types.ChartResourceType: + case types.RecordResourceType: return map[string]bool{ "read": true, "update": true, @@ -604,13 +605,13 @@ func rbacResourceOperations(r string) map[string]bool { return nil } -// rbacNamespaceResourceValidator checks validity of RBAC resource and operations +// rbacChartResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated -func rbacNamespaceResourceValidator(r string, oo ...string) error { - if !strings.HasPrefix(r, types.NamespaceResourceType) { +func rbacChartResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.ChartResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } @@ -618,14 +619,15 @@ func rbacNamespaceResourceValidator(r string, oo ...string) error { defOps := rbacResourceOperations(r) for _, o := range oo { if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for namespace resource", o) + return fmt.Errorf("invalid operation '%s' for chart resource", o) } } const sep = "/" var ( - pp = strings.Split(strings.Trim(r[len(types.NamespaceResourceType):], sep), sep) + pp = strings.Split(strings.Trim(r[len(types.ChartResourceType):], sep), sep) prc = []string{ + "NamespaceID", "ID", } ) @@ -637,7 +639,7 @@ func rbacNamespaceResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for namespace resource", i) + return fmt.Errorf("invalid path wildcard level (%d) for chart resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -670,7 +672,7 @@ func rbacModuleResourceValidator(r string, oo ...string) error { var ( pp = strings.Split(strings.Trim(r[len(types.ModuleResourceType):], sep), sep) prc = []string{ - "namespaceID", + "NamespaceID", "ID", } ) @@ -715,8 +717,8 @@ func rbacModuleFieldResourceValidator(r string, oo ...string) error { var ( pp = strings.Split(strings.Trim(r[len(types.ModuleFieldResourceType):], sep), sep) prc = []string{ - "namespaceID", - "moduleID", + "NamespaceID", + "ModuleID", "ID", } ) @@ -739,6 +741,72 @@ func rbacModuleFieldResourceValidator(r string, oo ...string) error { return nil } +// rbacNamespaceResourceValidator checks validity of RBAC resource and operations +// +// Can be called without operations to check for validity of resource string only +// +// This function is auto-generated +func rbacNamespaceResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.NamespaceResourceType) { + // expecting resource to always include path + return fmt.Errorf("invalid resource type") + } + + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for namespace resource", o) + } + } + + return nil +} + +// rbacPageResourceValidator checks validity of RBAC resource and operations +// +// Can be called without operations to check for validity of resource string only +// +// This function is auto-generated +func rbacPageResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.PageResourceType) { + // expecting resource to always include path + return fmt.Errorf("invalid resource type") + } + + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for page resource", o) + } + } + + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.PageResourceType):], sep), sep) + prc = []string{ + "NamespaceID", + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for page resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } + return nil +} + // rbacRecordResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only @@ -761,8 +829,8 @@ func rbacRecordResourceValidator(r string, oo ...string) error { var ( pp = strings.Split(strings.Trim(r[len(types.RecordResourceType):], sep), sep) prc = []string{ - "namespaceID", - "moduleID", + "NamespaceID", + "ModuleID", "ID", } ) @@ -785,96 +853,6 @@ func rbacRecordResourceValidator(r string, oo ...string) error { return nil } -// rbacPageResourceValidator checks validity of RBAC resource and operations -// -// Can be called without operations to check for validity of resource string only -// -// This function is auto-generated -func rbacPageResourceValidator(r string, oo ...string) error { - if !strings.HasPrefix(r, types.PageResourceType) { - // expecting resource to always include path - return fmt.Errorf("invalid resource type") - } - - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for page resource", o) - } - } - - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.PageResourceType):], sep), sep) - prc = []string{ - "namespaceID", - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for page resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } - return nil -} - -// rbacChartResourceValidator checks validity of RBAC resource and operations -// -// Can be called without operations to check for validity of resource string only -// -// This function is auto-generated -func rbacChartResourceValidator(r string, oo ...string) error { - if !strings.HasPrefix(r, types.ChartResourceType) { - // expecting resource to always include path - return fmt.Errorf("invalid resource type") - } - - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for chart resource", o) - } - } - - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.ChartResourceType):], sep), sep) - prc = []string{ - "namespaceID", - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for chart resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } - return nil -} - // rbacComponentResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only diff --git a/compose/service/locale.gen.go b/compose/service/locale.gen.go index d2428001e..00e3a1936 100644 --- a/compose/service/locale.gen.go +++ b/compose/service/locale.gen.go @@ -6,11 +6,6 @@ package service // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml - import ( "context" "github.com/cortezaproject/corteza-server/compose/types" @@ -41,9 +36,9 @@ type ( } ResourceTranslationsManagerService interface { - Module(ctx context.Context, namespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) + Module(ctx context.Context, NamespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) Namespace(ctx context.Context, ID uint64) (locale.ResourceTranslationSet, error) - Page(ctx context.Context, namespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) + Page(ctx context.Context, NamespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) Upsert(context.Context, locale.ResourceTranslationSet) error Locale() locale.Resource @@ -108,7 +103,7 @@ func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.Res sysLocale = append(sysLocale, aux...) aux = current.Old(rr) - aux.Walk(func(cc *systemTypes.ResourceTranslation) error { + _ = aux.Walk(func(cc *systemTypes.ResourceTranslation) error { cc.UpdatedAt = now() cc.UpdatedBy = me.Identity() return nil @@ -132,7 +127,7 @@ func (svc resourceTranslationsManager) Locale() locale.Resource { return svc.locale } -func (svc resourceTranslationsManager) Module(ctx context.Context, namespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) { +func (svc resourceTranslationsManager) Module(ctx context.Context, NamespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) { var ( err error out locale.ResourceTranslationSet @@ -140,7 +135,7 @@ func (svc resourceTranslationsManager) Module(ctx context.Context, namespaceID u k types.LocaleKey ) - res, err = svc.loadModule(ctx, svc.store, namespaceID, ID) + res, err = svc.loadModule(ctx, svc.store, NamespaceID, ID) if err != nil { return nil, err } @@ -156,8 +151,7 @@ func (svc resourceTranslationsManager) Module(ctx context.Context, namespaceID u } - tmp, err := svc.moduleExtended(ctx, res) - return append(out, tmp...), err + return out, nil } func (svc resourceTranslationsManager) Namespace(ctx context.Context, ID uint64) (locale.ResourceTranslationSet, error) { @@ -182,7 +176,7 @@ func (svc resourceTranslationsManager) Namespace(ctx context.Context, ID uint64) Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path), }) - k = types.LocaleKeyNamespaceSubtitle + k = types.LocaleKeyNamespaceMetaSubtitle out = append(out, &locale.ResourceTranslation{ Resource: res.ResourceTranslation(), Lang: tag.String(), @@ -190,7 +184,7 @@ func (svc resourceTranslationsManager) Namespace(ctx context.Context, ID uint64) Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path), }) - k = types.LocaleKeyNamespaceDescription + k = types.LocaleKeyNamespaceMetaDescription out = append(out, &locale.ResourceTranslation{ Resource: res.ResourceTranslation(), Lang: tag.String(), @@ -199,10 +193,11 @@ func (svc resourceTranslationsManager) Namespace(ctx context.Context, ID uint64) }) } + return out, nil } -func (svc resourceTranslationsManager) Page(ctx context.Context, namespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) { +func (svc resourceTranslationsManager) Page(ctx context.Context, NamespaceID uint64, ID uint64) (locale.ResourceTranslationSet, error) { var ( err error out locale.ResourceTranslationSet @@ -210,7 +205,7 @@ func (svc resourceTranslationsManager) Page(ctx context.Context, namespaceID uin k types.LocaleKey ) - res, err = svc.loadPage(ctx, svc.store, namespaceID, ID) + res, err = svc.loadPage(ctx, svc.store, NamespaceID, ID) if err != nil { return nil, err } diff --git a/compose/service/locale.go b/compose/service/locale.go index 60a0c6364..d34c5bc9a 100644 --- a/compose/service/locale.go +++ b/compose/service/locale.go @@ -28,14 +28,14 @@ func (svc resourceTranslationsManager) moduleExtended(ctx context.Context, res * Msg: svc.locale.TResourceFor(tag, f.ResourceTranslation(), k.Path), }) - k = types.LocaleKeyModuleFieldDescriptionView + k = types.LocaleKeyModuleFieldMetaDescriptionView out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), Lang: tag.String(), Key: k.Path, Msg: svc.locale.TResourceFor(tag, f.ResourceTranslation(), k.Path), }) - k = types.LocaleKeyModuleFieldDescriptionEdit + k = types.LocaleKeyModuleFieldMetaDescriptionEdit out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), Lang: tag.String(), @@ -43,14 +43,14 @@ func (svc resourceTranslationsManager) moduleExtended(ctx context.Context, res * Msg: svc.locale.TResourceFor(tag, f.ResourceTranslation(), k.Path), }) - k = types.LocaleKeyModuleFieldHintView + k = types.LocaleKeyModuleFieldMetaHintView out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), Lang: tag.String(), Key: k.Path, Msg: svc.locale.TResourceFor(tag, f.ResourceTranslation(), k.Path), }) - k = types.LocaleKeyModuleFieldHintEdit + k = types.LocaleKeyModuleFieldMetaHintEdit out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), Lang: tag.String(), @@ -86,7 +86,7 @@ func (svc resourceTranslationsManager) moduleFieldExpressionsHandler(ctx context "{{validatorID}}", strconv.FormatUint(vContentID, 10), ) - tKey := rpl.Replace(types.LocaleKeyModuleFieldValidatorError.Path) + tKey := rpl.Replace(types.LocaleKeyModuleFieldExpressionValidatorValidatorIDError.Path) out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), @@ -128,7 +128,7 @@ func (svc resourceTranslationsManager) moduleFieldOptionsHandler(ctx context.Con } } - trKey := strings.NewReplacer("{{value}}", value).Replace(types.LocaleKeyModuleFieldOptionsOptionTexts.Path) + trKey := strings.NewReplacer("{{value}}", value).Replace(types.LocaleKeyModuleFieldMetaOptionsValueText.Path) out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), @@ -154,7 +154,7 @@ func (svc resourceTranslationsManager) pageExtended(ctx context.Context, res *ty ) // base stuff - k = types.LocaleKeyPageBlockTitle + k = types.LocaleKeyPagePageBlockBlockIDTitle out = append(out, &locale.ResourceTranslation{ Resource: res.ResourceTranslation(), Lang: tag.String(), @@ -162,7 +162,7 @@ func (svc resourceTranslationsManager) pageExtended(ctx context.Context, res *ty Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), rpl.Replace(k.Path)), }) - k = types.LocaleKeyPageBlockDescription + k = types.LocaleKeyPagePageBlockBlockIDDescription out = append(out, &locale.ResourceTranslation{ Resource: res.ResourceTranslation(), Lang: tag.String(), diff --git a/compose/types/locale.gen.go b/compose/types/locale.gen.go index c180e9788..d267f6df4 100644 --- a/compose/types/locale.gen.go +++ b/compose/types/locale.gen.go @@ -6,12 +6,6 @@ package types // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml - import ( "fmt" "github.com/cortezaproject/corteza-server/pkg/locale" @@ -29,167 +23,39 @@ type ( // Types and stuff const ( - ModuleFieldResourceTranslationType = "compose:module-field" ModuleResourceTranslationType = "compose:module" + ModuleFieldResourceTranslationType = "compose:module-field" NamespaceResourceTranslationType = "compose:namespace" PageResourceTranslationType = "compose:page" ) var ( - LocaleKeyModuleFieldLabel = LocaleKey{ - Name: "label", - Resource: ModuleFieldResourceTranslationType, - Path: "label", - } - LocaleKeyModuleFieldDescriptionView = LocaleKey{ - Name: "descriptionView", - Resource: ModuleFieldResourceTranslationType, - Path: "meta.description.view", - CustomHandler: "descriptionView", - } - LocaleKeyModuleFieldDescriptionEdit = LocaleKey{ - Name: "descriptionEdit", - Resource: ModuleFieldResourceTranslationType, - Path: "meta.description.edit", - CustomHandler: "descriptionEdit", - } - LocaleKeyModuleFieldHintView = LocaleKey{ - Name: "hintView", - Resource: ModuleFieldResourceTranslationType, - Path: "meta.hint.view", - CustomHandler: "hintView", - } - LocaleKeyModuleFieldHintEdit = LocaleKey{ - Name: "hintEdit", - Resource: ModuleFieldResourceTranslationType, - Path: "meta.hint.edit", - CustomHandler: "hintEdit", - } - LocaleKeyModuleFieldValidatorError = LocaleKey{ - Name: "validatorError", - Resource: ModuleFieldResourceTranslationType, - Path: "expression.validator.{{validatorID}}.error", - CustomHandler: "validatorError", - } - LocaleKeyModuleFieldOptionsOptionTexts = LocaleKey{ - Name: "optionsOptionTexts", - Resource: ModuleFieldResourceTranslationType, - Path: "meta.options.{{value}}.text", - CustomHandler: "optionsOptionTexts", - } - LocaleKeyModuleName = LocaleKey{ - Name: "name", - Resource: ModuleResourceTranslationType, - Path: "name", - } - LocaleKeyNamespaceName = LocaleKey{ - Name: "name", - Resource: NamespaceResourceTranslationType, - Path: "name", - } - LocaleKeyNamespaceSubtitle = LocaleKey{ - Name: "subtitle", - Resource: NamespaceResourceTranslationType, - Path: "subtitle", - } - LocaleKeyNamespaceDescription = LocaleKey{ - Name: "description", - Resource: NamespaceResourceTranslationType, - Path: "description", - } - LocaleKeyPageTitle = LocaleKey{ - Name: "title", - Resource: PageResourceTranslationType, - Path: "title", - } - LocaleKeyPageDescription = LocaleKey{ - Name: "description", - Resource: PageResourceTranslationType, - Path: "description", - } - LocaleKeyPageBlockTitle = LocaleKey{ - Name: "blockTitle", - Resource: PageResourceTranslationType, - Path: "pageBlock.{{blockID}}.title", - } - LocaleKeyPageBlockDescription = LocaleKey{ - Name: "blockDescription", - Resource: PageResourceTranslationType, - Path: "pageBlock.{{blockID}}.description", - } - LocaleKeyPageBlockAutomationButtonlabel = LocaleKey{ - Name: "blockAutomationButtonlabel", - Resource: PageResourceTranslationType, - Path: "pageBlock.{{blockID}}.button.{{buttonID}}.label", - } + // @todo can we remove LocaleKey struct for string constant? + LocaleKeyModuleName = LocaleKey{Path: "name"} + LocaleKeyModuleFieldLabel = LocaleKey{Path: "label"} + LocaleKeyModuleFieldMetaDescriptionView = LocaleKey{Path: "meta.description.view"} + LocaleKeyModuleFieldMetaDescriptionEdit = LocaleKey{Path: "meta.description.edit"} + LocaleKeyModuleFieldMetaHintView = LocaleKey{Path: "meta.hint.view"} + LocaleKeyModuleFieldMetaHintEdit = LocaleKey{Path: "meta.hint.edit"} + LocaleKeyModuleFieldExpressionValidatorValidatorIDError = LocaleKey{Path: "expression.validator.{{validatorID}}.error"} + LocaleKeyModuleFieldMetaOptionsValueText = LocaleKey{Path: "meta.options.{{value}}.text"} + LocaleKeyNamespaceName = LocaleKey{Path: "name"} + LocaleKeyNamespaceMetaSubtitle = LocaleKey{Path: "meta.subtitle"} + LocaleKeyNamespaceMetaDescription = LocaleKey{Path: "meta.description"} + LocaleKeyPageTitle = LocaleKey{Path: "title"} + LocaleKeyPageDescription = LocaleKey{Path: "description"} + LocaleKeyPagePageBlockBlockIDTitle = LocaleKey{Path: "pageBlock.{{blockID}}.title"} + LocaleKeyPagePageBlockBlockIDDescription = LocaleKey{Path: "pageBlock.{{blockID}}.description"} + LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel = LocaleKey{Path: "pageBlock.{{blockID}}.button.{{buttonID}}.label"} ) -// ResourceTranslation returns string representation of Locale resource for ModuleField by calling ModuleFieldResourceTranslation fn -// -// Locale resource is in "compose:module-field/..." format -// -// This function is auto-generated -func (r ModuleField) ResourceTranslation() string { - return ModuleFieldResourceTranslation(r.NamespaceID, r.ModuleID, r.ID) -} - -// ModuleFieldResourceTranslation returns string representation of Locale resource for ModuleField -// -// Locale resource is in the compose:module-field/... format -// -// This function is auto-generated -func ModuleFieldResourceTranslation(namespaceID uint64, moduleID uint64, id uint64) string { - cpts := []interface{}{ModuleFieldResourceTranslationType} - cpts = append(cpts, strconv.FormatUint(namespaceID, 10), strconv.FormatUint(moduleID, 10), strconv.FormatUint(id, 10)) - - return fmt.Sprintf(ModuleFieldResourceTranslationTpl(), cpts...) -} - -// @todo template -func ModuleFieldResourceTranslationTpl() string { - return "%s/%s/%s/%s" -} - -func (r *ModuleField) DecodeTranslations(tt locale.ResourceTranslationIndex) { - var aux *locale.ResourceTranslation - if aux = tt.FindByKey(LocaleKeyModuleFieldLabel.Path); aux != nil { - r.Label = aux.Msg - } - r.decodeTranslationsDescriptionView(tt) - r.decodeTranslationsDescriptionEdit(tt) - r.decodeTranslationsHintView(tt) - r.decodeTranslationsHintEdit(tt) - r.decodeTranslationsValidatorError(tt) - r.decodeTranslationsOptionsOptionTexts(tt) -} - -func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) { - out = locale.ResourceTranslationSet{} - if r.Label != "" { - out = append(out, &locale.ResourceTranslation{ - Resource: r.ResourceTranslation(), - Key: LocaleKeyModuleFieldLabel.Path, - Msg: locale.SanitizeMessage(r.Label), - }) - } - - out = append(out, r.encodeTranslationsDescriptionView()...) - out = append(out, r.encodeTranslationsDescriptionEdit()...) - out = append(out, r.encodeTranslationsHintView()...) - out = append(out, r.encodeTranslationsHintEdit()...) - out = append(out, r.encodeTranslationsValidatorError()...) - out = append(out, r.encodeTranslationsOptionsOptionTexts()...) - - return out -} - // ResourceTranslation returns string representation of Locale resource for Module by calling ModuleResourceTranslation fn // // Locale resource is in "compose:module/..." format // // This function is auto-generated func (r Module) ResourceTranslation() string { - return ModuleResourceTranslation(r.NamespaceID, r.ID) + return ModuleResourceTranslation(r.ID) } // ModuleResourceTranslation returns string representation of Locale resource for Module @@ -197,29 +63,30 @@ func (r Module) ResourceTranslation() string { // Locale resource is in the compose:module/... format // // This function is auto-generated -func ModuleResourceTranslation(namespaceID uint64, id uint64) string { - cpts := []interface{}{ModuleResourceTranslationType} - cpts = append(cpts, strconv.FormatUint(namespaceID, 10), strconv.FormatUint(id, 10)) +func ModuleResourceTranslation(ID uint64) string { + cpts := []interface{}{ + ModuleResourceTranslationType, + strconv.FormatUint(ID, 10), + } return fmt.Sprintf(ModuleResourceTranslationTpl(), cpts...) } -// @todo template func ModuleResourceTranslationTpl() string { - return "%s/%s/%s" + return "%s/%s" } func (r *Module) DecodeTranslations(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation + if aux = tt.FindByKey(LocaleKeyModuleName.Path); aux != nil { r.Name = aux.Msg } - - r.decodeTranslations(tt) } func (r *Module) EncodeTranslations() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} + if r.Name != "" { out = append(out, &locale.ResourceTranslation{ Resource: r.ResourceTranslation(), @@ -228,7 +95,79 @@ func (r *Module) EncodeTranslations() (out locale.ResourceTranslationSet) { }) } - out = append(out, r.encodeTranslations()...) + return out +} + +// ResourceTranslation returns string representation of Locale resource for ModuleField by calling ModuleFieldResourceTranslation fn +// +// Locale resource is in "compose:module-field/..." format +// +// This function is auto-generated +func (r ModuleField) ResourceTranslation() string { + return ModuleFieldResourceTranslation(r.ID) +} + +// ModuleFieldResourceTranslation returns string representation of Locale resource for ModuleField +// +// Locale resource is in the compose:module-field/... format +// +// This function is auto-generated +func ModuleFieldResourceTranslation(ID uint64) string { + cpts := []interface{}{ + ModuleFieldResourceTranslationType, + strconv.FormatUint(ID, 10), + } + + return fmt.Sprintf(ModuleFieldResourceTranslationTpl(), cpts...) +} + +func ModuleFieldResourceTranslationTpl() string { + return "%s/%s" +} + +func (r *ModuleField) DecodeTranslations(tt locale.ResourceTranslationIndex) { + var aux *locale.ResourceTranslation + + if aux = tt.FindByKey(LocaleKeyModuleFieldLabel.Path); aux != nil { + r.Label = aux.Msg + } + + r.decodeTranslationsMetaDescriptionView(tt) + + r.decodeTranslationsMetaDescriptionEdit(tt) + + r.decodeTranslationsMetaHintView(tt) + + r.decodeTranslationsMetaHintEdit(tt) + + r.decodeTranslationsExpressionValidatorValidatorIDError(tt) + + r.decodeTranslationsMetaOptionsValueText(tt) + +} + +func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) { + out = locale.ResourceTranslationSet{} + + if r.Label != "" { + out = append(out, &locale.ResourceTranslation{ + Resource: r.ResourceTranslation(), + Key: LocaleKeyModuleFieldLabel.Path, + Msg: locale.SanitizeMessage(r.Label), + }) + } + + out = append(out, r.encodeTranslationsMetaDescriptionView()...) + + out = append(out, r.encodeTranslationsMetaDescriptionEdit()...) + + out = append(out, r.encodeTranslationsMetaHintView()...) + + out = append(out, r.encodeTranslationsMetaHintEdit()...) + + out = append(out, r.encodeTranslationsExpressionValidatorValidatorIDError()...) + + out = append(out, r.encodeTranslationsMetaOptionsValueText()...) return out } @@ -247,33 +186,38 @@ func (r Namespace) ResourceTranslation() string { // Locale resource is in the compose:namespace/... format // // This function is auto-generated -func NamespaceResourceTranslation(id uint64) string { - cpts := []interface{}{NamespaceResourceTranslationType} - cpts = append(cpts, strconv.FormatUint(id, 10)) +func NamespaceResourceTranslation(ID uint64) string { + cpts := []interface{}{ + NamespaceResourceTranslationType, + strconv.FormatUint(ID, 10), + } return fmt.Sprintf(NamespaceResourceTranslationTpl(), cpts...) } -// @todo template func NamespaceResourceTranslationTpl() string { return "%s/%s" } func (r *Namespace) DecodeTranslations(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation + if aux = tt.FindByKey(LocaleKeyNamespaceName.Path); aux != nil { r.Name = aux.Msg } - if aux = tt.FindByKey(LocaleKeyNamespaceSubtitle.Path); aux != nil { + + if aux = tt.FindByKey(LocaleKeyNamespaceMetaSubtitle.Path); aux != nil { r.Meta.Subtitle = aux.Msg } - if aux = tt.FindByKey(LocaleKeyNamespaceDescription.Path); aux != nil { + + if aux = tt.FindByKey(LocaleKeyNamespaceMetaDescription.Path); aux != nil { r.Meta.Description = aux.Msg } } func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} + if r.Name != "" { out = append(out, &locale.ResourceTranslation{ Resource: r.ResourceTranslation(), @@ -281,17 +225,19 @@ func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) { Msg: locale.SanitizeMessage(r.Name), }) } + if r.Meta.Subtitle != "" { out = append(out, &locale.ResourceTranslation{ Resource: r.ResourceTranslation(), - Key: LocaleKeyNamespaceSubtitle.Path, + Key: LocaleKeyNamespaceMetaSubtitle.Path, Msg: locale.SanitizeMessage(r.Meta.Subtitle), }) } + if r.Meta.Description != "" { out = append(out, &locale.ResourceTranslation{ Resource: r.ResourceTranslation(), - Key: LocaleKeyNamespaceDescription.Path, + Key: LocaleKeyNamespaceMetaDescription.Path, Msg: locale.SanitizeMessage(r.Meta.Description), }) } @@ -305,7 +251,7 @@ func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) { // // This function is auto-generated func (r Page) ResourceTranslation() string { - return PageResourceTranslation(r.NamespaceID, r.ID) + return PageResourceTranslation(r.ID) } // PageResourceTranslation returns string representation of Locale resource for Page @@ -313,23 +259,26 @@ func (r Page) ResourceTranslation() string { // Locale resource is in the compose:page/... format // // This function is auto-generated -func PageResourceTranslation(namespaceID uint64, id uint64) string { - cpts := []interface{}{PageResourceTranslationType} - cpts = append(cpts, strconv.FormatUint(namespaceID, 10), strconv.FormatUint(id, 10)) +func PageResourceTranslation(ID uint64) string { + cpts := []interface{}{ + PageResourceTranslationType, + strconv.FormatUint(ID, 10), + } return fmt.Sprintf(PageResourceTranslationTpl(), cpts...) } -// @todo template func PageResourceTranslationTpl() string { - return "%s/%s/%s" + return "%s/%s" } func (r *Page) DecodeTranslations(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation + if aux = tt.FindByKey(LocaleKeyPageTitle.Path); aux != nil { r.Title = aux.Msg } + if aux = tt.FindByKey(LocaleKeyPageDescription.Path); aux != nil { r.Description = aux.Msg } @@ -339,6 +288,7 @@ func (r *Page) DecodeTranslations(tt locale.ResourceTranslationIndex) { func (r *Page) EncodeTranslations() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} + if r.Title != "" { out = append(out, &locale.ResourceTranslation{ Resource: r.ResourceTranslation(), @@ -346,6 +296,7 @@ func (r *Page) EncodeTranslations() (out locale.ResourceTranslationSet) { Msg: locale.SanitizeMessage(r.Title), }) } + if r.Description != "" { out = append(out, &locale.ResourceTranslation{ Resource: r.ResourceTranslation(), diff --git a/compose/types/module_field.go b/compose/types/module_field.go index cc3bc30d9..cfd5ef329 100644 --- a/compose/types/module_field.go +++ b/compose/types/module_field.go @@ -56,7 +56,7 @@ var ( _ sort.Interface = &ModuleFieldSet{} ) -func (f *ModuleField) decodeTranslationsValidatorError(tt locale.ResourceTranslationIndex) { +func (f *ModuleField) decodeTranslationsExpressionValidatorValidatorIDError(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation for i, e := range f.Expressions.Validators { @@ -65,40 +65,40 @@ func (f *ModuleField) decodeTranslationsValidatorError(tt locale.ResourceTransla "{{validatorID}}", strconv.FormatUint(validatorID, 10), ) - if aux = tt.FindByKey(rpl.Replace(LocaleKeyModuleFieldValidatorError.Path)); aux != nil { + if aux = tt.FindByKey(rpl.Replace(LocaleKeyModuleFieldExpressionValidatorValidatorIDError.Path)); aux != nil { f.Expressions.Validators[i].Error = aux.Msg } } } -func (f *ModuleField) decodeTranslationsDescriptionView(tt locale.ResourceTranslationIndex) { +func (f *ModuleField) decodeTranslationsMetaDescriptionView(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation - if aux = tt.FindByKey(LocaleKeyModuleFieldDescriptionView.Path); aux != nil { + if aux = tt.FindByKey(LocaleKeyModuleFieldMetaDescriptionView.Path); aux != nil { f.setOptionKey(aux.Msg, "description", "edit") } } -func (f *ModuleField) decodeTranslationsDescriptionEdit(tt locale.ResourceTranslationIndex) { +func (f *ModuleField) decodeTranslationsMetaDescriptionEdit(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation - if aux = tt.FindByKey(LocaleKeyModuleFieldDescriptionEdit.Path); aux != nil { + if aux = tt.FindByKey(LocaleKeyModuleFieldMetaDescriptionEdit.Path); aux != nil { f.setOptionKey(aux.Msg, "description", "view") } } -func (f *ModuleField) decodeTranslationsHintView(tt locale.ResourceTranslationIndex) { +func (f *ModuleField) decodeTranslationsMetaHintView(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation - if aux = tt.FindByKey(LocaleKeyModuleFieldHintView.Path); aux != nil { + if aux = tt.FindByKey(LocaleKeyModuleFieldMetaHintView.Path); aux != nil { f.setOptionKey(aux.Msg, "hint", "edit") } } -func (f *ModuleField) decodeTranslationsHintEdit(tt locale.ResourceTranslationIndex) { +func (f *ModuleField) decodeTranslationsMetaHintEdit(tt locale.ResourceTranslationIndex) { var aux *locale.ResourceTranslation - if aux = tt.FindByKey(LocaleKeyModuleFieldHintEdit.Path); aux != nil { + if aux = tt.FindByKey(LocaleKeyModuleFieldMetaHintEdit.Path); aux != nil { f.setOptionKey(aux.Msg, "hint", "view") } } @@ -106,7 +106,7 @@ func (f *ModuleField) decodeTranslationsHintEdit(tt locale.ResourceTranslationIn // Decodes translations and modifies options // // Why "options-option-texts"? Because we're translating option txts under options key-value -func (f *ModuleField) decodeTranslationsOptionsOptionTexts(tt locale.ResourceTranslationIndex) { +func (f *ModuleField) decodeTranslationsMetaOptionsValueText(tt locale.ResourceTranslationIndex) { var ( tr *locale.ResourceTranslation ) @@ -145,7 +145,7 @@ func (f *ModuleField) decodeTranslationsOptionsOptionTexts(tt locale.ResourceTra // find the translation for that value // and update the option (effectively overwriting // the original text value (in case of map option) - trKey := strings.NewReplacer("{{value}}", outOpt["value"]).Replace(LocaleKeyModuleFieldOptionsOptionTexts.Path) + trKey := strings.NewReplacer("{{value}}", outOpt["value"]).Replace(LocaleKeyModuleFieldMetaOptionsValueText.Path) if tr = tt.FindByKey(trKey); tr != nil { outOpt["text"] = tr.Msg } @@ -155,7 +155,7 @@ func (f *ModuleField) decodeTranslationsOptionsOptionTexts(tt locale.ResourceTra } } -func (m *ModuleField) encodeTranslationsValidatorError() (out locale.ResourceTranslationSet) { +func (m *ModuleField) encodeTranslationsExpressionValidatorValidatorIDError() (out locale.ResourceTranslationSet) { out = make(locale.ResourceTranslationSet, 0, 3) // Module field expressions @@ -167,7 +167,7 @@ func (m *ModuleField) encodeTranslationsValidatorError() (out locale.ResourceTra out = append(out, &locale.ResourceTranslation{ Resource: m.ResourceTranslation(), - Key: rpl.Replace(LocaleKeyModuleFieldValidatorError.Path), + Key: rpl.Replace(LocaleKeyModuleFieldExpressionValidatorValidatorIDError.Path), Msg: e.Error, }) } @@ -175,53 +175,53 @@ func (m *ModuleField) encodeTranslationsValidatorError() (out locale.ResourceTra return } -func (f *ModuleField) encodeTranslationsDescriptionView() (out locale.ResourceTranslationSet) { +func (f *ModuleField) encodeTranslationsMetaDescriptionView() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} if v := f.getOptionKey("description", "edit"); v != nil { aux := cast.ToString(v) out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), - Key: LocaleKeyModuleFieldDescriptionView.Path, + Key: LocaleKeyModuleFieldMetaDescriptionView.Path, Msg: aux, }) } return out } -func (f *ModuleField) encodeTranslationsDescriptionEdit() (out locale.ResourceTranslationSet) { +func (f *ModuleField) encodeTranslationsMetaDescriptionEdit() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} if v := f.getOptionKey("description", "view"); v != nil { aux := cast.ToString(v) out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), - Key: LocaleKeyModuleFieldDescriptionEdit.Path, + Key: LocaleKeyModuleFieldMetaDescriptionEdit.Path, Msg: aux, }) } return out } -func (f *ModuleField) encodeTranslationsHintView() (out locale.ResourceTranslationSet) { +func (f *ModuleField) encodeTranslationsMetaHintView() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} if v := f.getOptionKey("hint", "edit"); v != nil { aux := cast.ToString(v) out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), - Key: LocaleKeyModuleFieldHintView.Path, + Key: LocaleKeyModuleFieldMetaHintView.Path, Msg: aux, }) } return out } -func (f *ModuleField) encodeTranslationsHintEdit() (out locale.ResourceTranslationSet) { +func (f *ModuleField) encodeTranslationsMetaHintEdit() (out locale.ResourceTranslationSet) { out = locale.ResourceTranslationSet{} if v := f.getOptionKey("hint", "view"); v != nil { aux := cast.ToString(v) out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), - Key: LocaleKeyModuleFieldHintEdit.Path, + Key: LocaleKeyModuleFieldMetaHintEdit.Path, Msg: aux, }) } @@ -229,7 +229,7 @@ func (f *ModuleField) encodeTranslationsHintEdit() (out locale.ResourceTranslati } // extracts option texts and converts (encodes) them to translations -func (f *ModuleField) encodeTranslationsOptionsOptionTexts() (out locale.ResourceTranslationSet) { +func (f *ModuleField) encodeTranslationsMetaOptionsValueText() (out locale.ResourceTranslationSet) { out = make(locale.ResourceTranslationSet, 0, 3) optsUnknown, has := f.Options["options"] @@ -252,7 +252,7 @@ func (f *ModuleField) encodeTranslationsOptionsOptionTexts() (out locale.Resourc out = append(out, &locale.ResourceTranslation{ Resource: f.ResourceTranslation(), Key: strings.NewReplacer("{{value}}", value). - Replace(LocaleKeyModuleFieldOptionsOptionTexts.Path), + Replace(LocaleKeyModuleFieldMetaOptionsValueText.Path), Msg: text, }) } diff --git a/compose/types/module_field_test.go b/compose/types/module_field_test.go index 8746de13e..1ddc15af0 100644 --- a/compose/types/module_field_test.go +++ b/compose/types/module_field_test.go @@ -68,7 +68,7 @@ func TestModuleField_decodeTranslationsOptionsOptionTexts(t *testing.T) { f = &ModuleField{Options: tt.opts} ) - f.decodeTranslationsOptionsOptionTexts(rti) + f.decodeTranslationsMetaOptionsValueText(rti) if tt.out != nil { req.Equal(tt.out, f.Options["options"]) diff --git a/compose/types/namespace.go b/compose/types/namespace.go index 40a04f989..c27ac56ef 100644 --- a/compose/types/namespace.go +++ b/compose/types/namespace.go @@ -6,7 +6,6 @@ import ( "time" "github.com/cortezaproject/corteza-server/pkg/filter" - "github.com/pkg/errors" ) diff --git a/compose/types/page.go b/compose/types/page.go index 034effc1e..9808f955f 100644 --- a/compose/types/page.go +++ b/compose/types/page.go @@ -124,10 +124,10 @@ func (p *Page) decodeTranslations(tt locale.ResourceTranslationIndex) { ) // - generic page block stuff - if aux = tt.FindByKey(rpl.Replace(LocaleKeyPageBlockTitle.Path)); aux != nil { + if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDTitle.Path)); aux != nil { p.Blocks[i].Title = aux.Msg } - if aux = tt.FindByKey(rpl.Replace(LocaleKeyPageBlockDescription.Path)); aux != nil { + if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDDescription.Path)); aux != nil { p.Blocks[i].Description = aux.Msg } @@ -148,7 +148,7 @@ func (p *Page) decodeTranslations(tt locale.ResourceTranslationIndex) { "{{buttonID}}", strconv.FormatUint(buttonID, 10), ) - if aux = tt.FindByKey(rpl.Replace(LocaleKeyPageBlockAutomationButtonlabel.Path)); aux != nil { + if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel.Path)); aux != nil { btn["label"] = aux.Msg } } @@ -169,13 +169,13 @@ func (p *Page) encodeTranslations() (out locale.ResourceTranslationSet) { // - generic page block stuff out = append(out, &locale.ResourceTranslation{ Resource: p.ResourceTranslation(), - Key: rpl.Replace(LocaleKeyPageBlockTitle.Path), + Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDTitle.Path), Msg: block.Title, }) out = append(out, &locale.ResourceTranslation{ Resource: p.ResourceTranslation(), - Key: rpl.Replace(LocaleKeyPageBlockDescription.Path), + Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDDescription.Path), Msg: block.Description, }) @@ -202,7 +202,7 @@ func (p *Page) encodeTranslations() (out locale.ResourceTranslationSet) { out = append(out, &locale.ResourceTranslation{ Resource: p.ResourceTranslation(), - Key: rpl.Replace(LocaleKeyPageBlockAutomationButtonlabel.Path), + Key: rpl.Replace(LocaleKeyPagePageBlockBlockIDButtonButtonIDLabel.Path), Msg: btn["label"].(string), }) } diff --git a/compose/types/rbac.gen.go b/compose/types/rbac.gen.go index 5d0f02249..98f15e2a4 100644 --- a/compose/types/rbac.gen.go +++ b/compose/types/rbac.gen.go @@ -24,43 +24,49 @@ var ( ) const ( - NamespaceResourceType = "corteza::compose:namespace" + ChartResourceType = "corteza::compose:chart" ModuleResourceType = "corteza::compose:module" ModuleFieldResourceType = "corteza::compose:module-field" - RecordResourceType = "corteza::compose:record" + NamespaceResourceType = "corteza::compose:namespace" PageResourceType = "corteza::compose:page" - ChartResourceType = "corteza::compose:chart" + RecordResourceType = "corteza::compose:record" ComponentResourceType = "corteza::compose" ) -// RbacResource returns string representation of RBAC resource for Namespace by calling NamespaceRbacResource fn +// RbacResource returns string representation of RBAC resource for Chart by calling ChartRbacResource fn // -// RBAC resource is in the corteza::compose:namespace/... format +// RBAC resource is in the corteza::compose:chart/... format // // This function is auto-generated -func (r Namespace) RbacResource() string { - return NamespaceRbacResource(r.ID) +func (r Chart) RbacResource() string { + return ChartRbacResource(r.NamespaceID, r.ID) } -// NamespaceRbacResource returns string representation of RBAC resource for Namespace +// ChartRbacResource returns string representation of RBAC resource for Chart // -// RBAC resource is in the corteza::compose:namespace/... format +// RBAC resource is in the corteza::compose:chart/... format // // This function is auto-generated -func NamespaceRbacResource(ID uint64) string { - cpts := []interface{}{NamespaceResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) +func ChartRbacResource(namespaceID uint64, id uint64) string { + cpts := []interface{}{ChartResourceType} + if namespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) } else { cpts = append(cpts, "*") } - return fmt.Sprintf(NamespaceRbacResourceTpl(), cpts...) + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(ChartRbacResourceTpl(), cpts...) } -func NamespaceRbacResourceTpl() string { - return "%s/%s" +func ChartRbacResourceTpl() string { + return "%s/%s/%s" } // RbacResource returns string representation of RBAC resource for Module by calling ModuleRbacResource fn @@ -77,16 +83,16 @@ func (r Module) RbacResource() string { // RBAC resource is in the corteza::compose:module/... format // // This function is auto-generated -func ModuleRbacResource(NamespaceID uint64, ID uint64) string { +func ModuleRbacResource(namespaceID uint64, id uint64) string { cpts := []interface{}{ModuleResourceType} - if NamespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) + if namespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) } else { cpts = append(cpts, "*") } - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) } else { cpts = append(cpts, "*") } @@ -113,22 +119,22 @@ func (r ModuleField) RbacResource() string { // RBAC resource is in the corteza::compose:module-field/... format // // This function is auto-generated -func ModuleFieldRbacResource(NamespaceID uint64, ModuleID uint64, ID uint64) string { +func ModuleFieldRbacResource(namespaceID uint64, moduleID uint64, id uint64) string { cpts := []interface{}{ModuleFieldResourceType} - if NamespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) + if namespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) } else { cpts = append(cpts, "*") } - if ModuleID != 0 { - cpts = append(cpts, strconv.FormatUint(ModuleID, 10)) + if moduleID != 0 { + cpts = append(cpts, strconv.FormatUint(moduleID, 10)) } else { cpts = append(cpts, "*") } - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) } else { cpts = append(cpts, "*") } @@ -141,46 +147,27 @@ func ModuleFieldRbacResourceTpl() string { return "%s/%s/%s/%s" } -// RbacResource returns string representation of RBAC resource for Record by calling RecordRbacResource fn +// RbacResource returns string representation of RBAC resource for Namespace by calling NamespaceRbacResource fn // -// RBAC resource is in the corteza::compose:record/... format +// RBAC resource is in the corteza::compose:namespace/... format // // This function is auto-generated -func (r Record) RbacResource() string { - return RecordRbacResource(r.NamespaceID, r.ModuleID, r.ID) +func (r Namespace) RbacResource() string { + return NamespaceRbacResource() } -// RecordRbacResource returns string representation of RBAC resource for Record +// NamespaceRbacResource returns string representation of RBAC resource for Namespace // -// RBAC resource is in the corteza::compose:record/... format +// RBAC resource is in the corteza::compose:namespace/ format // // This function is auto-generated -func RecordRbacResource(NamespaceID uint64, ModuleID uint64, ID uint64) string { - cpts := []interface{}{RecordResourceType} - if NamespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) - } else { - cpts = append(cpts, "*") - } - - if ModuleID != 0 { - cpts = append(cpts, strconv.FormatUint(ModuleID, 10)) - } else { - cpts = append(cpts, "*") - } - - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(RecordRbacResourceTpl(), cpts...) +func NamespaceRbacResource() string { + return NamespaceResourceType + "/" } -func RecordRbacResourceTpl() string { - return "%s/%s/%s/%s" +func NamespaceRbacResourceTpl() string { + return "%s" } // RbacResource returns string representation of RBAC resource for Page by calling PageRbacResource fn @@ -197,16 +184,16 @@ func (r Page) RbacResource() string { // RBAC resource is in the corteza::compose:page/... format // // This function is auto-generated -func PageRbacResource(NamespaceID uint64, ID uint64) string { +func PageRbacResource(namespaceID uint64, id uint64) string { cpts := []interface{}{PageResourceType} - if NamespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) + if namespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) } else { cpts = append(cpts, "*") } - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) } else { cpts = append(cpts, "*") } @@ -219,40 +206,46 @@ func PageRbacResourceTpl() string { return "%s/%s/%s" } -// RbacResource returns string representation of RBAC resource for Chart by calling ChartRbacResource fn +// RbacResource returns string representation of RBAC resource for Record by calling RecordRbacResource fn // -// RBAC resource is in the corteza::compose:chart/... format +// RBAC resource is in the corteza::compose:record/... format // // This function is auto-generated -func (r Chart) RbacResource() string { - return ChartRbacResource(r.NamespaceID, r.ID) +func (r Record) RbacResource() string { + return RecordRbacResource(r.NamespaceID, r.ModuleID, r.ID) } -// ChartRbacResource returns string representation of RBAC resource for Chart +// RecordRbacResource returns string representation of RBAC resource for Record // -// RBAC resource is in the corteza::compose:chart/... format +// RBAC resource is in the corteza::compose:record/... format // // This function is auto-generated -func ChartRbacResource(NamespaceID uint64, ID uint64) string { - cpts := []interface{}{ChartResourceType} - if NamespaceID != 0 { - cpts = append(cpts, strconv.FormatUint(NamespaceID, 10)) +func RecordRbacResource(namespaceID uint64, moduleID uint64, id uint64) string { + cpts := []interface{}{RecordResourceType} + if namespaceID != 0 { + cpts = append(cpts, strconv.FormatUint(namespaceID, 10)) } else { cpts = append(cpts, "*") } - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) + if moduleID != 0 { + cpts = append(cpts, strconv.FormatUint(moduleID, 10)) } else { cpts = append(cpts, "*") } - return fmt.Sprintf(ChartRbacResourceTpl(), cpts...) + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(RecordRbacResourceTpl(), cpts...) } -func ChartRbacResourceTpl() string { - return "%s/%s/%s" +func RecordRbacResourceTpl() string { + return "%s/%s/%s/%s" } // RbacResource returns string representation of RBAC resource for Component by calling ComponentRbacResource fn diff --git a/def/schema/component.cue b/def/schema/component.cue index 280cbbf98..6e85fe61f 100644 --- a/def/schema/component.cue +++ b/def/schema/component.cue @@ -4,23 +4,24 @@ import ( "strings" ) -#component: { - ident: #baseHandle - expIdent: #expIdent | *strings.ToTitle(ident) +#component: #_base & { + // copy field values from #_base + handle: handle, ident: ident, expIdent: expIdent + label: strings.ToTitle(ident) platform: #baseHandle resources: { - [key=_]: {handle: key, "component": ident, "platform": platform} & #resource + [key=_]: {"handle": key, "component": handle, "platform": platform} & #resource } + fqrn: platform + "::" + handle + // All known RBAC operations for this component rbac: #rbacComponent & { - resource: type: platform + "::" + ident - operations: { grant: { - description: "Manage \(ident) permissions" + description: "Manage \(handle) permissions" } } } diff --git a/def/schema/locale.cue b/def/schema/locale.cue index 974a59aa8..24ece587c 100644 --- a/def/schema/locale.cue +++ b/def/schema/locale.cue @@ -1,29 +1,58 @@ package schema import ( -// "strings" + "strings" + "list" ) #locale: { + resourceExpIdent: #expIdent + // @todo we need a better name here! skipSvc: bool | *false + extended: bool | *false + resource: { // @todo merge with RBAC res-ref and move 2 levels lower. references: [ ...string] | *["ID"] + type: string + const: string | *("\(resourceExpIdent)ResourceTranslationType") } keys: { [key=_]: #localeKey & { name: key + _resourceExpIdent: resourceExpIdent } } } #localeKey: { name: #handle - path: string | *(name) - custom?: true - customHandler?: string + _resourceExpIdent: #expIdent + + path: [...(#ident | { part: #ident, var: bool | *false })] | *([name]) + + expandedPath: [for p in path { + if (p & { "p": #ident }) != _|_ { p, var: p.var } + if (p & string) != _|_ { "part": p, var: false } + }] + + + _suffix: strings.Join([for p in expandedPath { strings.ToTitle(p.part) }], "") + + struct: string | *("LocaleKey" + _resourceExpIdent + _suffix) + + // As soon as we use vars in the path, + // custom handler must be present + _hasVars: list.Contains([for p in path { p.var | false }], true) + customHandler: bool | *_hasVars + + if customHandler { + decodeFunc: string | *("decodeTranslations" + _suffix) + encodeFunc: string | *("encodeTranslations" + _suffix) + serviceFunc: string | *("handle" + _resourceExpIdent + _suffix) + } } diff --git a/def/schema/platform.cue b/def/schema/platform.cue index f4c489f8a..f0901df4f 100644 --- a/def/schema/platform.cue +++ b/def/schema/platform.cue @@ -1,7 +1,7 @@ package schema #platform: { - ident: #baseHandle + ident: #baseHandle | *"corteza" components: [...{platform: ident} & #component] diff --git a/def/schema/rbac.cue b/def/schema/rbac.cue index f0228c20d..6d85a3146 100644 --- a/def/schema/rbac.cue +++ b/def/schema/rbac.cue @@ -10,32 +10,27 @@ import ( } operations: { - [key=_]: #rbacOperation & {handle: key} + [key=_]: #rbacOperation & { + handle: key + } } } #rbacResource: { - resource: { - type: string - expIdent: #expIdent - references: [ ...string] | *["ID"] - } + resourceExpIdent: #expIdent operations: { [key=_]: #rbacOperation & { handle: key - resourceExpIdent: resource.expIdent - description: string | *(strings.ToTitle(key) + " " + resource.type) + _resourceExpIdent: resourceExpIdent } } } #rbacOperation: { - handle: #handle - description: string - resourceExpIdent?: string - - _isComponent: resourceExpIdent == _|_ + handle: #handle + description: string | *handle + _resourceExpIdent?: string // Some string manipulation that will result in // more pronouncable access-control check function name @@ -51,11 +46,11 @@ import ( _opFlip: [_opSplit[len(_opSplit)-1]] + _opSplit[0:len(_opSplit)-1] _opFinal: strings.Replace(strings.ToTitle(strings.Join(_opFlip, " ")), " ", "", -1) - if _isComponent { + if _resourceExpIdent == _|_ { checkFuncName: #expIdent | *("Can" + _opFinal) } - if !_isComponent { - checkFuncName: #expIdent | *("Can" + _opFinal + resourceExpIdent) + if _resourceExpIdent != _|_ { + checkFuncName: #expIdent | *("Can" + _opFinal + _resourceExpIdent) } } diff --git a/def/schema/resource.cue b/def/schema/resource.cue index d290ce446..5a95e77a0 100644 --- a/def/schema/resource.cue +++ b/def/schema/resource.cue @@ -1,32 +1,38 @@ package schema -import ( - "strings" -) +#resource: #_base & { + // copy field values from #_base + handle: handle, ident: ident, expIdent: expIdent -#resource: { - handle: #baseHandle | *"unknown-resource" + component: #baseHandle | *"component" + platform: #baseHandle | *"corteza" - _words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1) - - ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1)) - expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1) - platform: #baseHandle | *"unknown-platform" - component: string | *"unknown-component" // Fully qualified resource name fqrn: string | *(platform + "::" + component + ":" + handle) - goType: string | *("types." + expIdent) - // fields: #Fields // operations: #Operations + // All parent resources + parents: [... #_base & { + // copy field values from #_base + handle: handle, ident: ident, expIdent: expIdent + + refField: #expIdent | *(expIdent + "ID") + param: #ident | *(ident + "ID") + }] + // All known RBAC operations for this resource rbac: #rbacResource & { + resourceExpIdent: expIdent + } + + locale?: #locale & { + resourceExpIdent: expIdent resource: { - type: fqrn - "expIdent": expIdent + // @todo can we merge this with RBAC type (FQRN?) + type: component + ":" + handle } } @@ -40,25 +46,25 @@ import ( // } } -#fields: { - // Each field can be - [key=_]: #fields | *({name: key} & #field) -} - -#field: { - name: #expIdent - unique: bool | *false - - // Golang type (built-in or other) - type: string | *"string" - - // System fields, - system: bool | *false - - if name =~ "At$" { - type: string | *"*time.Time" - } -} +//#fields: { +// // Each field can be +// [key=_]: #fields | *({name: key} & #field) +//} +// +//#field: { +// name: #expIdent +// unique: bool | *false +// +// // Golang type (built-in or other) +// type: string | *"string" +// +// // System fields, +// system: bool | *false +// +// if name =~ "At$" { +// type: string | *"*time.Time" +// } +//} //#Operations: { // [Operation=_]: {operation: Operation} & #Operation @@ -70,24 +76,24 @@ import ( // can: string | false | *"\(name)" //} -idField: { - // Expecting ID field to allways have name ID - name: "ID" - unique: true - - // Service fields, - // @todo We might want to have a better name for this - // service: true - - // @todo someday we'll replace this with the "ID" type - type: "uint64" -} - -handleField: { - // Expecting ID field to allways have name ID - name: "handle" - unique: true - - // @todo someday we'll replace this with the "ID" type - type: "string" & #handle -} +//idField: { +// // Expecting ID field to allways have name ID +// name: "ID" +// unique: true +// +// // Service fields, +// // @todo We might want to have a better name for this +// // service: true +// +// // @todo someday we'll replace this with the "ID" type +// type: "uint64" +//} +// +//handleField: { +// // Expecting ID field to allways have name ID +// name: "handle" +// unique: true +// +// // @todo someday we'll replace this with the "ID" type +// type: "string" & #handle +//} diff --git a/def/schema/shared.cue b/def/schema/shared.cue index 557d73ead..323d84df0 100644 --- a/def/schema/shared.cue +++ b/def/schema/shared.cue @@ -1,10 +1,14 @@ package schema -// Resource definition identifier +import ( + "strings" +) + +// Identifier #ident: =~"^[a-z][a-zA-Z0-9_]*$" // Exported identifier -#expIdent: =~"^[A-Z][a-zA-Z0-9]*$" +#expIdent: =~"^[A-Z][a-zA-Z0-9_]*$" // More liberal then identifier, allows underscores and dots #handle: =~"^[A-Za-z][a-zA-Z0-9_\\-\\.]*[a-zA-Z0-9]+$" @@ -12,3 +16,18 @@ package schema // More liberal then identifier, allows underscores and dots #baseHandle: =~"^[a-z][a-z0-9-]*[a-z0-9]+$" + +#_base: { + // lowercase dash-separated words + // used to build ident and exported identifiers + handle: #baseHandle | *"base" + _words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1) + + // lowercased (unexported, golang) identifier + ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1)) + + // upercased (exported, golang) identifier + expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1) + + ... +} diff --git a/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl b/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl deleted file mode 100644 index 05be93e59..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl +++ /dev/null @@ -1,135 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( - "fmt" - "strconv" - "github.com/cortezaproject/corteza-server/pkg/locale" -) - -type ( - LocaleKey struct { - Name string - Resource string - Path string - CustomHandler string - } -) - -// Types and stuff -const ( -{{- range .Def }} - {{ .Resource }}ResourceTranslationType = "{{ .Locale.ResourceType }}" -{{- end }} -) - -var ( -{{- range .Def }} -{{- $Resource := .Resource }} - {{- range .Locale.Keys}} - LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }} = LocaleKey{ - Name: "{{.Name}}", - Resource: {{ $Resource }}ResourceTranslationType, - Path: "{{.Path }}",{{ if .CustomHandler }} - CustomHandler: "{{ .CustomHandler }}", - {{- end }} - } - {{- end}} -{{- end }} -) - -{{- range .Def }} -{{ $Resource := .Resource }} -{{ $GoType := printf "types.%s" .Resource }} - - -// ResourceTranslation returns string representation of Locale resource for {{ .Resource }} by calling {{ .Resource }}ResourceTranslation fn -// -// Locale resource is in "{{ .Locale.ResourceType }}/..." format -// -// This function is auto-generated -func (r {{ .Resource }}) ResourceTranslation() string { - return {{ .Resource }}ResourceTranslation({{ if .Locale.Resource }}{{ range .Locale.Resource.References }}r.{{ export .Field }},{{ end }}{{ end }}) -} - -// {{ .Resource }}ResourceTranslation returns string representation of Locale resource for {{ .Resource }} -// -// Locale resource is in the {{ .Locale.ResourceType }}/{{- if .Locale.Resource.References }}...{{ end }} format -// -// This function is auto-generated -func {{ .Resource }}ResourceTranslation({{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ unexport .Field }} uint64,{{ end }}{{ end }}) string { - {{- if .Locale.Resource.References }} - cpts := []interface{{"{}"}}{{"{"}}{{ .Resource }}ResourceTranslationType{{"}"}} - cpts = append(cpts, {{range .Locale.Resource.References -}} - strconv.FormatUint({{ unexport .Field }}, 10), - {{- end }}) - - return fmt.Sprintf({{ .Resource }}ResourceTranslationTpl(), cpts...) - {{- end }} -} - -// @todo template -func {{ .Resource }}ResourceTranslationTpl() string { - {{- if .Locale.Resource.References }} - return "%s - {{- range .Locale.Resource.References }}/%s{{- end }}" - - {{- else }} - return "%s" - {{- end }} -} - -func (r *{{ .Resource }}) DecodeTranslations(tt locale.ResourceTranslationIndex) { - var aux *locale.ResourceTranslation - -{{- range .Locale.Keys}} -{{- if not .Custom }} - if aux = tt.FindByKey(LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }}.Path); aux != nil { - r.{{ .Field }} = aux.Msg - } -{{- end}} -{{- end}} - -{{- range .Locale.Keys}} -{{- if and .Custom .CustomHandler }} - r.decodeTranslations{{export .CustomHandler }}(tt) -{{- end}} -{{- end}} - -{{- if .Locale.Extended }} - - r.decodeTranslations(tt) -{{- end }} -} - -func (r *{{ .Resource }}) EncodeTranslations() (out locale.ResourceTranslationSet) { - out = locale.ResourceTranslationSet{} -{{- range .Locale.Keys}} -{{- if not .Custom }} - if r.{{ .Field }} != "" { - out = append(out, &locale.ResourceTranslation{ - Resource: r.ResourceTranslation(), - Key: LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }}.Path, - Msg: locale.SanitizeMessage(r.{{ .Field }}), - }) - } - -{{- end}} -{{- end}} - -{{range .Locale.Keys}} -{{- if and .Custom .CustomHandler }} - out = append(out, r.encodeTranslations{{export .CustomHandler}}()...) -{{- end}} -{{- end}} - -{{- if .Locale.Extended }} - out = append(out, r.encodeTranslations()...) -{{- end }} - - return out -} - -{{- end }} diff --git a/system/component.cue b/system/component.cue index d7d9e2b07..f50a08338 100644 --- a/system/component.cue +++ b/system/component.cue @@ -5,7 +5,7 @@ import ( ) component: schema.#component & { - ident: "system" + handle: "system" resources: { "apigw-route": apigwRoute diff --git a/system/service/access_control.gen.go b/system/service/access_control.gen.go index 4f9afe093..d5fc40d1e 100644 --- a/system/service/access_control.gen.go +++ b/system/service/access_control.gen.go @@ -56,182 +56,182 @@ func (svc accessControl) List() (out []map[string]string) { def := []map[string]string{ { "type": types.ApigwRouteResourceType, - "any": types.ApigwRouteRbacResource(0), + "any": types.ApigwRouteRbacResource(), "op": "read", }, { "type": types.ApigwRouteResourceType, - "any": types.ApigwRouteRbacResource(0), + "any": types.ApigwRouteRbacResource(), "op": "update", }, { "type": types.ApigwRouteResourceType, - "any": types.ApigwRouteRbacResource(0), + "any": types.ApigwRouteRbacResource(), "op": "delete", }, { "type": types.ApplicationResourceType, - "any": types.ApplicationRbacResource(0), + "any": types.ApplicationRbacResource(), "op": "read", }, { "type": types.ApplicationResourceType, - "any": types.ApplicationRbacResource(0), + "any": types.ApplicationRbacResource(), "op": "update", }, { "type": types.ApplicationResourceType, - "any": types.ApplicationRbacResource(0), + "any": types.ApplicationRbacResource(), "op": "delete", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(0), + "any": types.AuthClientRbacResource(), "op": "read", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(0), + "any": types.AuthClientRbacResource(), "op": "update", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(0), + "any": types.AuthClientRbacResource(), "op": "delete", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(0), + "any": types.AuthClientRbacResource(), "op": "authorize", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(0), + "any": types.QueueRbacResource(), "op": "render", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(0), + "any": types.QueueRbacResource(), "op": "read", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(0), + "any": types.QueueRbacResource(), "op": "update", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(0), + "any": types.QueueRbacResource(), "op": "delete", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(0), + "any": types.QueueRbacResource(), "op": "queue.read", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(0), + "any": types.QueueRbacResource(), "op": "queue.write", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(0), + "any": types.ReportRbacResource(), "op": "read", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(0), + "any": types.ReportRbacResource(), "op": "update", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(0), + "any": types.ReportRbacResource(), "op": "delete", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(0), + "any": types.ReportRbacResource(), "op": "run", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(0), + "any": types.RoleRbacResource(), "op": "read", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(0), + "any": types.RoleRbacResource(), "op": "update", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(0), + "any": types.RoleRbacResource(), "op": "delete", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(0), + "any": types.RoleRbacResource(), "op": "members.manage", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(0), + "any": types.TemplateRbacResource(), "op": "read", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(0), + "any": types.TemplateRbacResource(), "op": "update", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(0), + "any": types.TemplateRbacResource(), "op": "delete", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(0), + "any": types.TemplateRbacResource(), "op": "render", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "read", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "update", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "delete", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "suspend", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "unsuspend", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "email.unmask", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "name.unmask", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(0), + "any": types.UserRbacResource(), "op": "impersonate", }, { @@ -1014,29 +1014,6 @@ func rbacApigwRouteResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.ApigwRouteResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for apigwRoute resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1058,29 +1035,6 @@ func rbacApplicationResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.ApplicationResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for application resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1102,29 +1056,6 @@ func rbacAuthClientResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.AuthClientResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for authClient resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1146,29 +1077,6 @@ func rbacQueueResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.QueueResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for queue resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1190,29 +1098,6 @@ func rbacReportResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.ReportResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for report resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1234,29 +1119,6 @@ func rbacRoleResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.RoleResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for role resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1278,29 +1140,6 @@ func rbacTemplateResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.TemplateResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for template resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } @@ -1322,29 +1161,6 @@ func rbacUserResourceValidator(r string, oo ...string) error { } } - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.UserResourceType):], sep), sep) - prc = []string{ - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid path wildcard level (%d) for user resource", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } return nil } diff --git a/system/types/rbac.gen.go b/system/types/rbac.gen.go index a0e8d625c..ccb3413e5 100644 --- a/system/types/rbac.gen.go +++ b/system/types/rbac.gen.go @@ -41,28 +41,21 @@ const ( // // This function is auto-generated func (r ApigwRoute) RbacResource() string { - return ApigwRouteRbacResource(r.ID) + return ApigwRouteRbacResource() } // ApigwRouteRbacResource returns string representation of RBAC resource for ApigwRoute // -// RBAC resource is in the corteza::system:apigw-route/... format +// RBAC resource is in the corteza::system:apigw-route/ format // // This function is auto-generated -func ApigwRouteRbacResource(ID uint64) string { - cpts := []interface{}{ApigwRouteResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(ApigwRouteRbacResourceTpl(), cpts...) +func ApigwRouteRbacResource() string { + return ApigwRouteResourceType + "/" } func ApigwRouteRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for Application by calling ApplicationRbacResource fn @@ -71,28 +64,21 @@ func ApigwRouteRbacResourceTpl() string { // // This function is auto-generated func (r Application) RbacResource() string { - return ApplicationRbacResource(r.ID) + return ApplicationRbacResource() } // ApplicationRbacResource returns string representation of RBAC resource for Application // -// RBAC resource is in the corteza::system:application/... format +// RBAC resource is in the corteza::system:application/ format // // This function is auto-generated -func ApplicationRbacResource(ID uint64) string { - cpts := []interface{}{ApplicationResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(ApplicationRbacResourceTpl(), cpts...) +func ApplicationRbacResource() string { + return ApplicationResourceType + "/" } func ApplicationRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for AuthClient by calling AuthClientRbacResource fn @@ -101,28 +87,21 @@ func ApplicationRbacResourceTpl() string { // // This function is auto-generated func (r AuthClient) RbacResource() string { - return AuthClientRbacResource(r.ID) + return AuthClientRbacResource() } // AuthClientRbacResource returns string representation of RBAC resource for AuthClient // -// RBAC resource is in the corteza::system:auth-client/... format +// RBAC resource is in the corteza::system:auth-client/ format // // This function is auto-generated -func AuthClientRbacResource(ID uint64) string { - cpts := []interface{}{AuthClientResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(AuthClientRbacResourceTpl(), cpts...) +func AuthClientRbacResource() string { + return AuthClientResourceType + "/" } func AuthClientRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for Queue by calling QueueRbacResource fn @@ -131,28 +110,21 @@ func AuthClientRbacResourceTpl() string { // // This function is auto-generated func (r Queue) RbacResource() string { - return QueueRbacResource(r.ID) + return QueueRbacResource() } // QueueRbacResource returns string representation of RBAC resource for Queue // -// RBAC resource is in the corteza::system:queue/... format +// RBAC resource is in the corteza::system:queue/ format // // This function is auto-generated -func QueueRbacResource(ID uint64) string { - cpts := []interface{}{QueueResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(QueueRbacResourceTpl(), cpts...) +func QueueRbacResource() string { + return QueueResourceType + "/" } func QueueRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for Report by calling ReportRbacResource fn @@ -161,28 +133,21 @@ func QueueRbacResourceTpl() string { // // This function is auto-generated func (r Report) RbacResource() string { - return ReportRbacResource(r.ID) + return ReportRbacResource() } // ReportRbacResource returns string representation of RBAC resource for Report // -// RBAC resource is in the corteza::system:report/... format +// RBAC resource is in the corteza::system:report/ format // // This function is auto-generated -func ReportRbacResource(ID uint64) string { - cpts := []interface{}{ReportResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(ReportRbacResourceTpl(), cpts...) +func ReportRbacResource() string { + return ReportResourceType + "/" } func ReportRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for Role by calling RoleRbacResource fn @@ -191,28 +156,21 @@ func ReportRbacResourceTpl() string { // // This function is auto-generated func (r Role) RbacResource() string { - return RoleRbacResource(r.ID) + return RoleRbacResource() } // RoleRbacResource returns string representation of RBAC resource for Role // -// RBAC resource is in the corteza::system:role/... format +// RBAC resource is in the corteza::system:role/ format // // This function is auto-generated -func RoleRbacResource(ID uint64) string { - cpts := []interface{}{RoleResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(RoleRbacResourceTpl(), cpts...) +func RoleRbacResource() string { + return RoleResourceType + "/" } func RoleRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for Template by calling TemplateRbacResource fn @@ -221,28 +179,21 @@ func RoleRbacResourceTpl() string { // // This function is auto-generated func (r Template) RbacResource() string { - return TemplateRbacResource(r.ID) + return TemplateRbacResource() } // TemplateRbacResource returns string representation of RBAC resource for Template // -// RBAC resource is in the corteza::system:template/... format +// RBAC resource is in the corteza::system:template/ format // // This function is auto-generated -func TemplateRbacResource(ID uint64) string { - cpts := []interface{}{TemplateResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(TemplateRbacResourceTpl(), cpts...) +func TemplateRbacResource() string { + return TemplateResourceType + "/" } func TemplateRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for User by calling UserRbacResource fn @@ -251,28 +202,21 @@ func TemplateRbacResourceTpl() string { // // This function is auto-generated func (r User) RbacResource() string { - return UserRbacResource(r.ID) + return UserRbacResource() } // UserRbacResource returns string representation of RBAC resource for User // -// RBAC resource is in the corteza::system:user/... format +// RBAC resource is in the corteza::system:user/ format // // This function is auto-generated -func UserRbacResource(ID uint64) string { - cpts := []interface{}{UserResourceType} - if ID != 0 { - cpts = append(cpts, strconv.FormatUint(ID, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(UserRbacResourceTpl(), cpts...) +func UserRbacResource() string { + return UserResourceType + "/" } func UserRbacResourceTpl() string { - return "%s/%s" + return "%s" } // RbacResource returns string representation of RBAC resource for Component by calling ComponentRbacResource fn From a437913f8553dc95e26197702541f4e5f8855705 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Wed, 29 Dec 2021 14:01:16 +0100 Subject: [PATCH 3/7] Migrate codegen for envoy to CUE --- codegen/all.cue | 1 + .../gocode/envoy/rbac-references.go.tpl | 32 +++ .../gocode/envoy/rbac-rules-parse.go.tpl | 69 ++++++ .../resource-resource_translation.go.tpl- | 31 +++ ...esource-resource_translation_parse.go.tpl- | 73 ++++++ ...ce-resource_translation_references.go.tpl- | 33 +++ codegen/envoy-rbac.cue | 72 ++++++ codegen/locale-service.cue | 2 +- compose/service/access_control.gen.go | 3 +- .../resource/rbac_references_compose.gen.go | 75 +++--- .../resource/rbac_references_system.gen.go | 11 - pkg/envoy/resource/rbac_rules_parse.gen.go | 223 +++++++----------- 12 files changed, 425 insertions(+), 200 deletions(-) create mode 100644 codegen/assets/templates/gocode/envoy/rbac-references.go.tpl create mode 100644 codegen/assets/templates/gocode/envoy/rbac-rules-parse.go.tpl create mode 100644 codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- create mode 100644 codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- create mode 100644 codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- create mode 100644 codegen/envoy-rbac.cue diff --git a/codegen/all.cue b/codegen/all.cue index 693e5570d..a08ad5b7e 100644 --- a/codegen/all.cue +++ b/codegen/all.cue @@ -13,4 +13,5 @@ platform: [...schema.#codegen] & rbacAccessControl+ rbacTypes+ localeTypes+ + envoyRBAC+ [] // placeholder diff --git a/codegen/assets/templates/gocode/envoy/rbac-references.go.tpl b/codegen/assets/templates/gocode/envoy/rbac-references.go.tpl new file mode 100644 index 000000000..a9e825edb --- /dev/null +++ b/codegen/assets/templates/gocode/envoy/rbac-references.go.tpl @@ -0,0 +1,32 @@ +package {{ .package }} + +{{ template "gocode/header-gentext.tpl" }} + +import ( +{{- range .imports }} + {{ . }} +{{- end }} +) + +{{- range .resources }} +// {{ .rbacRefFunc }} generates RBAC references +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func {{ .rbacRefFunc }}({{- range .references }}{{ .param }} string, {{- end }}) (res *Ref, pp []*Ref, err error) { + {{- range .references }} + {{- if eq .refField "ID" }} + if {{ .param }} != "*" { + res = &Ref{ResourceType: types.{{ .expIdent }}ResourceType, Identifiers: MakeIdentifiers({{ .param }})} + } + {{- else }} + if {{ .param }} != "*" { + pp = append(pp, &Ref{ResourceType: types.{{ .expIdent }}ResourceType, Identifiers: MakeIdentifiers({{ .param }})}) + } + {{- end }} + {{- end }} + + return +} +{{- end }} diff --git a/codegen/assets/templates/gocode/envoy/rbac-rules-parse.go.tpl b/codegen/assets/templates/gocode/envoy/rbac-rules-parse.go.tpl new file mode 100644 index 000000000..6746acdcb --- /dev/null +++ b/codegen/assets/templates/gocode/envoy/rbac-rules-parse.go.tpl @@ -0,0 +1,69 @@ +package {{ .package }} + +{{ template "gocode/header-gentext.tpl" }} + +import ( + "fmt" + "strings" +{{- range .imports }} + {{ . }} +{{- end }} +) + + +// Parse generates resource setting logic for each resource +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func ParseRule(res string) (string, *Ref, []*Ref, error) { + if res == "" { + return "", nil, nil, fmt.Errorf("empty resource") + } + + sp := "/" + + res = strings.TrimSpace(res) + res = strings.TrimRight(res, sp) + rr := strings.Split(res, sp) + + // only service defined (corteza::system, corteza::compose, ...) + if len(rr) == 1 { + return res, nil, nil, nil + } + + // full thing + resourceType, path := rr[0], rr[1:] + + for p := 1; p < len(path); p++ { + if path[p] != "*" && path[p-1] == "*" { + return "", nil, nil, fmt.Errorf("invalid path wildcard combination for '%s'", res) + } + } + + + // make the resource provide the slice of parent resources we should nest under + switch resourceType { + {{- range .resources }} + case {{ .typeConst }}: + if len(path) != {{ len .references }} { + return "", nil, nil, fmt.Errorf("expecting {{ len .references }} reference components in path, got %d", len(path)) + } + {{- if gt (len .references) 0 }} + ref, pp, err := {{ .rbacRefFunc }}( + {{- range $i, $r := .references }} + path[{{ $i }}], + {{- end }} + ) + return resourceType, ref, pp, err + {{ else }} + + // Component resource, no path + return resourceType, nil, nil, nil + {{- end }} + {{- end}} + } + + // return unhandled resource as-is + return resourceType, nil, nil, nil +} diff --git a/codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- b/codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- new file mode 100644 index 000000000..580d1df0c --- /dev/null +++ b/codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- @@ -0,0 +1,31 @@ +package {{ .Package }} + +{{ template "header-gentext.tpl" }} +{{ template "header-definitions.tpl" . }} + +import ( + systemTypes "github.com/cortezaproject/corteza-server/system/types" +{{- range .Imports }} + {{ . }} +{{- end }} +) + +{{- range .Def }} +{{ $Component := .Component }} +{{ $Resource := .Resource }} +{{ $GoType := printf "types.%s" .Resource }} +func (r *{{if not (eq $Component "system")}}{{export $Component}}{{ end }}{{$Resource}}) EncodeTranslations() ([]*ResourceTranslation, error) { + out := make([]*ResourceTranslation, 0, 10) + + rr := r.Res.EncodeTranslations() + rr.SetLanguage(defaultLanguage) + res, ref, pp := r.ResourceTranslationParts() + out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...)) +{{ if .Locale.Extended }} + tmp, err := r.encodeTranslations() + return append(out, tmp...), err +{{ else }} + return out, nil +{{- end }} +} +{{- end }} diff --git a/codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- b/codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- new file mode 100644 index 000000000..a630a7aeb --- /dev/null +++ b/codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- @@ -0,0 +1,73 @@ +package {{ .Package }} + +{{ template "header-gentext.tpl" }} +{{ template "header-definitions.tpl" . }} + +import ( + "fmt" + "strings" +{{- range .Imports }} + {{ . }} +{{- end }} +) + + +// Parse generates resource setting logic for each resource +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) { + if res == "" { + return "", nil, nil, fmt.Errorf("empty resource") + } + + sp := "/" + + if strings.Index(res, "corteza::") == 0 { + res = res[9:] + } + + res = strings.TrimSpace(res) + res = strings.TrimRight(res, sp) + rr := strings.Split(res, sp) + + // only service defined (corteza::system, corteza::compose, ...) + if len(rr) == 1 { + return "", nil, nil, fmt.Errorf("only service defined: %s", res) + } + + // full thing + resourceType, path := rr[0], rr[1:] + for p := 1; p < len(path); p++ { + if path[p] == "*" { + return "", nil, nil, fmt.Errorf("path wildcard not allowed for locale resources: '%s'", res) + } + } + + // make the resource provide the slice of parent resources we should nest under + switch resourceType { + {{- range .Def }} + case {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType: + if len(path) != {{ len .Locale.Resource.References }} { + return "", nil, nil, fmt.Errorf("expecting {{ len .Locale.Resource.References }} reference components in path, got %d", len(path)) + } + {{- if gt (len .Locale.Resource.References) 0 }} + ref, pp, err := {{ export .Component .Resource }}ResourceTranslationReferences( + {{- range $i, $r := .Locale.Resource.References }} + // {{ unexport $r.Resource }} + path[{{ $i }}], + {{ end }} + ) + return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType, ref, pp, err + {{ else }} + + // Component resource, no path + return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType, nil, nil, nil + {{- end }} + {{- end}} + } + + // return unhandled resource as-is + return resourceType, nil, nil, nil +} diff --git a/codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- b/codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- new file mode 100644 index 000000000..065516b5b --- /dev/null +++ b/codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- @@ -0,0 +1,33 @@ +package {{ .Package }} + +{{ template "header-gentext.tpl" }} +{{ template "header-definitions.tpl" . }} + +import ( +{{- range .Imports }} + {{ . }} +{{- end }} +) + +{{- range .Def }} +{{- if .Locale }} +{{- if gt (len .Locale.Resource.References) 0 }} +// {{ export .Component .Resource }}ResourceTranslationReferences generates Locale references +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func {{ export .Component .Resource }}ResourceTranslationReferences({{- range .Locale.Resource.References }}{{ unexport .Resource }} string, {{- end }}) (res *Ref, pp []*Ref, err error) { + {{- range .Locale.Resource.References }} + {{- if eq .Field "ID" }} + res = &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})} + {{- else }} + pp = append(pp, &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})}) + {{- end }} + {{- end }} + + return +} +{{- end }} +{{- end }} +{{- end }} diff --git a/codegen/envoy-rbac.cue b/codegen/envoy-rbac.cue new file mode 100644 index 000000000..132d3937e --- /dev/null +++ b/codegen/envoy-rbac.cue @@ -0,0 +1,72 @@ +package codegen + +import ( + "github.com/cortezaproject/corteza-server/app" + "github.com/cortezaproject/corteza-server/codegen/schema" + // "strings" +) + +envoyRBAC: + [...schema.#codegen] & + [ + for cmp in app.corteza.components { + template: "gocode/envoy/rbac-references.go.tpl" + output: "pkg/envoy/resource/rbac_references_\(cmp.ident).gen.go" + payload: { + package: "resource" + imports: [ + "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", + ] + + // cmpIdent: cmp.ident + // Operation/resource validators, grouped by resource + resources: [ + for res in cmp.resources { + rbacRefFunc: "\(cmp.expIdent)\(res.expIdent)RbacReferences" + references: [ + for p in res.parents {p}, + {param: res.ident, refField: "ID", expIdent: res.expIdent}, + ] + }, + ] + } + }, + ]+ + [ + { + template: "gocode/envoy/rbac-rules-parse.go.tpl" + output: "pkg/envoy/resource/rbac_rules_parse.gen.go" + payload: { + package: "resource" + imports: [ + for cmp in app.corteza.components { + "\(cmp.ident)Types \"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"" + }, + ] + + // cmpIdent: cmp.ident + // Operation/resource validators, grouped by resource + resources: [ + for cmp in app.corteza.components for res in cmp.resources { + importAlias: "\(cmp.ident)Types" + expIdent: res.expIdent + + typeConst: "\(importAlias).\(expIdent)ResourceType" + rbacRefFunc: "\(cmp.expIdent)\(res.expIdent)RbacReferences" + references: [ + for p in res.parents {p}, + {param: res.ident, refField: "ID", expIdent: res.expIdent}, + ] + }, + + for cmp in app.corteza.components { + importAlias: "\(cmp.ident)Types" + expIdent: cmp.expIdent + + typeConst: "\(importAlias).ComponentResourceType" + references: [] + }, + ] + } + }, + ] diff --git a/codegen/locale-service.cue b/codegen/locale-service.cue index 2807d4597..00bd0bd93 100644 --- a/codegen/locale-service.cue +++ b/codegen/locale-service.cue @@ -32,7 +32,7 @@ localeService: keys: [ for key in res.locale.keys if key.handlerFunc == _|_ { struct: key.struct - extended: extended + "extended": extended customHandler: key.customHandler if key.serviceFunc != _|_ {serviceFunc: key.serviceFunc} }] diff --git a/compose/service/access_control.gen.go b/compose/service/access_control.gen.go index 70eac31ea..ae387c225 100644 --- a/compose/service/access_control.gen.go +++ b/compose/service/access_control.gen.go @@ -9,12 +9,11 @@ package service import ( "context" "fmt" - "strings" - "github.com/cortezaproject/corteza-server/compose/types" "github.com/cortezaproject/corteza-server/pkg/actionlog" "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/spf13/cast" + "strings" ) type ( diff --git a/pkg/envoy/resource/rbac_references_compose.gen.go b/pkg/envoy/resource/rbac_references_compose.gen.go index 9b72bfccc..34c706985 100644 --- a/pkg/envoy/resource/rbac_references_compose.gen.go +++ b/pkg/envoy/resource/rbac_references_compose.gen.go @@ -6,15 +6,6 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.chart.yaml -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml -// - compose.record.yaml -// - compose.yaml - import ( "github.com/cortezaproject/corteza-server/compose/types" ) @@ -24,9 +15,9 @@ import ( // Resources with "envoy: false" are skipped // // This function is auto-generated -func ComposeChartRbacReferences(namespace string, chart string) (res *Ref, pp []*Ref, err error) { - if namespace != "*" { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) +func ComposeChartRbacReferences(namespaceID string, chart string) (res *Ref, pp []*Ref, err error) { + if namespaceID != "*" { + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) } if chart != "*" { res = &Ref{ResourceType: types.ChartResourceType, Identifiers: MakeIdentifiers(chart)} @@ -35,33 +26,14 @@ func ComposeChartRbacReferences(namespace string, chart string) (res *Ref, pp [] return } -// ComposeModuleFieldRbacReferences generates RBAC references -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func ComposeModuleFieldRbacReferences(namespace string, module string, moduleField string) (res *Ref, pp []*Ref, err error) { - if namespace != "*" { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) - } - if module != "*" { - pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(module)}) - } - if moduleField != "*" { - res = &Ref{ResourceType: types.ModuleFieldResourceType, Identifiers: MakeIdentifiers(moduleField)} - } - - return -} - // ComposeModuleRbacReferences generates RBAC references // // Resources with "envoy: false" are skipped // // This function is auto-generated -func ComposeModuleRbacReferences(namespace string, module string) (res *Ref, pp []*Ref, err error) { - if namespace != "*" { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) +func ComposeModuleRbacReferences(namespaceID string, module string) (res *Ref, pp []*Ref, err error) { + if namespaceID != "*" { + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) } if module != "*" { res = &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(module)} @@ -70,6 +42,25 @@ func ComposeModuleRbacReferences(namespace string, module string) (res *Ref, pp return } +// ComposeModuleFieldRbacReferences generates RBAC references +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func ComposeModuleFieldRbacReferences(namespaceID string, moduleID string, moduleField string) (res *Ref, pp []*Ref, err error) { + if namespaceID != "*" { + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) + } + if moduleID != "*" { + pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(moduleID)}) + } + if moduleField != "*" { + res = &Ref{ResourceType: types.ModuleFieldResourceType, Identifiers: MakeIdentifiers(moduleField)} + } + + return +} + // ComposeNamespaceRbacReferences generates RBAC references // // Resources with "envoy: false" are skipped @@ -88,9 +79,9 @@ func ComposeNamespaceRbacReferences(namespace string) (res *Ref, pp []*Ref, err // Resources with "envoy: false" are skipped // // This function is auto-generated -func ComposePageRbacReferences(namespace string, page string) (res *Ref, pp []*Ref, err error) { - if namespace != "*" { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) +func ComposePageRbacReferences(namespaceID string, page string) (res *Ref, pp []*Ref, err error) { + if namespaceID != "*" { + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) } if page != "*" { res = &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(page)} @@ -104,12 +95,12 @@ func ComposePageRbacReferences(namespace string, page string) (res *Ref, pp []*R // Resources with "envoy: false" are skipped // // This function is auto-generated -func ComposeRecordRbacReferences(namespace string, module string, record string) (res *Ref, pp []*Ref, err error) { - if namespace != "*" { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) +func ComposeRecordRbacReferences(namespaceID string, moduleID string, record string) (res *Ref, pp []*Ref, err error) { + if namespaceID != "*" { + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) } - if module != "*" { - pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(module)}) + if moduleID != "*" { + pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(moduleID)}) } if record != "*" { res = &Ref{ResourceType: types.RecordResourceType, Identifiers: MakeIdentifiers(record)} diff --git a/pkg/envoy/resource/rbac_references_system.gen.go b/pkg/envoy/resource/rbac_references_system.gen.go index 4e953ee6d..4fb241d91 100644 --- a/pkg/envoy/resource/rbac_references_system.gen.go +++ b/pkg/envoy/resource/rbac_references_system.gen.go @@ -6,17 +6,6 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - system.apigw-route.yaml -// - system.application.yaml -// - system.auth-client.yaml -// - system.queue.yaml -// - system.report.yaml -// - system.role.yaml -// - system.template.yaml -// - system.user.yaml -// - system.yaml - import ( "github.com/cortezaproject/corteza-server/system/types" ) diff --git a/pkg/envoy/resource/rbac_rules_parse.gen.go b/pkg/envoy/resource/rbac_rules_parse.gen.go index 98423de17..09e5bdbbd 100644 --- a/pkg/envoy/resource/rbac_rules_parse.gen.go +++ b/pkg/envoy/resource/rbac_rules_parse.gen.go @@ -6,32 +6,12 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - automation.workflow.yaml -// - automation.yaml -// - compose.chart.yaml -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml -// - compose.record.yaml -// - compose.yaml -// - system.apigw-route.yaml -// - system.application.yaml -// - system.auth-client.yaml -// - system.queue.yaml -// - system.report.yaml -// - system.role.yaml -// - system.template.yaml -// - system.user.yaml -// - system.yaml - import ( "fmt" - automationTypes "github.com/cortezaproject/corteza-server/automation/types" + "strings" + composeTypes "github.com/cortezaproject/corteza-server/compose/types" systemTypes "github.com/cortezaproject/corteza-server/system/types" - "strings" ) // Parse generates resource setting logic for each resource @@ -66,190 +46,138 @@ func ParseRule(res string) (string, *Ref, []*Ref, error) { // make the resource provide the slice of parent resources we should nest under switch resourceType { - case automationTypes.WorkflowResourceType: - if len(path) != 1 { - return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) - } - ref, pp, err := AutomationWorkflowRbacReferences( - // workflow - path[0], - ) - return automationTypes.WorkflowResourceType, ref, pp, err - - case automationTypes.ComponentResourceType: - if len(path) != 0 { - return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) - } - - // Component resource, no path - return automationTypes.ComponentResourceType, nil, nil, nil - case composeTypes.ChartResourceType: - if len(path) != 2 { - return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposeChartRbacReferences( - // namespace - path[0], - - // chart - path[1], - ) - return composeTypes.ChartResourceType, ref, pp, err - - case composeTypes.ModuleFieldResourceType: - if len(path) != 3 { - return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposeModuleFieldRbacReferences( - // namespace - path[0], - - // module - path[1], - - // moduleField - path[2], - ) - return composeTypes.ModuleFieldResourceType, ref, pp, err - - case composeTypes.ModuleResourceType: - if len(path) != 2 { - return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposeModuleRbacReferences( - // namespace - path[0], - - // module - path[1], - ) - return composeTypes.ModuleResourceType, ref, pp, err - - case composeTypes.NamespaceResourceType: - if len(path) != 1 { - return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposeNamespaceRbacReferences( - // namespace - path[0], - ) - return composeTypes.NamespaceResourceType, ref, pp, err - - case composeTypes.PageResourceType: - if len(path) != 2 { - return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposePageRbacReferences( - // namespace - path[0], - - // page - path[1], - ) - return composeTypes.PageResourceType, ref, pp, err - - case composeTypes.RecordResourceType: - if len(path) != 3 { - return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposeRecordRbacReferences( - // namespace - path[0], - - // module - path[1], - - // record - path[2], - ) - return composeTypes.RecordResourceType, ref, pp, err - - case composeTypes.ComponentResourceType: - if len(path) != 0 { - return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) - } - - // Component resource, no path - return composeTypes.ComponentResourceType, nil, nil, nil case systemTypes.ApigwRouteResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemApigwRouteRbacReferences( - // apigwRoute path[0], ) - return systemTypes.ApigwRouteResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.ApplicationResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemApplicationRbacReferences( - // application path[0], ) - return systemTypes.ApplicationResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.AuthClientResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemAuthClientRbacReferences( - // authClient path[0], ) - return systemTypes.AuthClientResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.QueueResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemQueueRbacReferences( - // queue path[0], ) - return systemTypes.QueueResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.ReportResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemReportRbacReferences( - // report path[0], ) - return systemTypes.ReportResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.RoleResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemRoleRbacReferences( - // role path[0], ) - return systemTypes.RoleResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.TemplateResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemTemplateRbacReferences( - // template path[0], ) - return systemTypes.TemplateResourceType, ref, pp, err + return resourceType, ref, pp, err case systemTypes.UserResourceType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := SystemUserRbacReferences( - // user path[0], ) - return systemTypes.UserResourceType, ref, pp, err + return resourceType, ref, pp, err + + case composeTypes.ChartResourceType: + if len(path) != 2 { + return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposeChartRbacReferences( + path[0], + path[1], + ) + return resourceType, ref, pp, err + + case composeTypes.ModuleResourceType: + if len(path) != 2 { + return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposeModuleRbacReferences( + path[0], + path[1], + ) + return resourceType, ref, pp, err + + case composeTypes.ModuleFieldResourceType: + if len(path) != 3 { + return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposeModuleFieldRbacReferences( + path[0], + path[1], + path[2], + ) + return resourceType, ref, pp, err + + case composeTypes.NamespaceResourceType: + if len(path) != 1 { + return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposeNamespaceRbacReferences( + path[0], + ) + return resourceType, ref, pp, err + + case composeTypes.PageResourceType: + if len(path) != 2 { + return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposePageRbacReferences( + path[0], + path[1], + ) + return resourceType, ref, pp, err + + case composeTypes.RecordResourceType: + if len(path) != 3 { + return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposeRecordRbacReferences( + path[0], + path[1], + path[2], + ) + return resourceType, ref, pp, err case systemTypes.ComponentResourceType: if len(path) != 0 { @@ -257,7 +185,14 @@ func ParseRule(res string) (string, *Ref, []*Ref, error) { } // Component resource, no path - return systemTypes.ComponentResourceType, nil, nil, nil + return resourceType, nil, nil, nil + case composeTypes.ComponentResourceType: + if len(path) != 0 { + return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) + } + + // Component resource, no path + return resourceType, nil, nil, nil } // return unhandled resource as-is From 52209c476b34ab12d94bb324459da9a762adb47d Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Thu, 30 Dec 2021 14:59:16 +0100 Subject: [PATCH 4/7] Port all v3 codegen to cue-based codegen --- ....tpl => rbac_references_$component.go.tpl} | 0 ...s-parse.go.tpl => rbac_rules_parse.go.tpl} | 0 ...ce-resource_translation_references.go.tpl- | 33 --- ...on.go.tpl- => resource_translation.go.tpl} | 18 +- ...tpl- => resource_translation_parse.go.tpl} | 30 ++- ...e_translation_references_$component.go.tpl | 24 ++ ...rvice.go.tpl => $component_service.go.tpl} | 0 .../{types.go.tpl => $component_types.go.tpl} | 0 ...o.tpl => $component_access_control.go.tpl} | 0 .../{types.go.tpl => $component_types.go.tpl} | 0 codegen/envoy-rbac.cue | 78 ++++++- codegen/locale-service.cue | 2 +- codegen/locale-types.cue | 2 +- codegen/rbac-access_control.cue | 2 +- codegen/rbac-types.cue | 2 +- pkg/codegen-v3/README.md | 12 - .../envoy/resource-rbac_references.go.tpl | 35 --- .../envoy/resource-rbac_rules_parse.go.tpl | 71 ------ .../resource-resource_translation.go.tpl | 31 --- ...resource-resource_translation_parse.go.tpl | 73 ------ ...rce-resource_translation_references.go.tpl | 33 --- .../templates/gocode/header-definitions.tpl | 4 - .../templates/gocode/header-gentext.tpl | 5 - .../templates/gocode/locale/service.go.tpl | 216 ------------------ pkg/codegen-v3/internal/def/doc.go | 91 -------- pkg/codegen-v3/internal/def/locale.go | 127 ---------- pkg/codegen-v3/internal/def/rbac.go | 165 ------------- pkg/codegen-v3/internal/gen/envoy.go | 168 -------------- pkg/codegen-v3/internal/gen/gen.go | 67 ------ pkg/codegen-v3/internal/gen/locale.go | 70 ------ pkg/codegen-v3/internal/gen/rbac.go | 69 ------ pkg/codegen-v3/internal/tpl/templating.go | 126 ---------- pkg/codegen-v3/loader.go | 78 ------- pkg/envoy/resource/rbac_rules_parse.gen.go | 3 +- .../resource/resource_translation.gen.go | 31 +-- .../resource_translation_parse.gen.go | 43 ++-- ...urce_translation_references_compose.gen.go | 42 ++-- ...ource_translation_references_system.gen.go | 25 -- 38 files changed, 155 insertions(+), 1621 deletions(-) rename codegen/assets/templates/gocode/envoy/{rbac-references.go.tpl => rbac_references_$component.go.tpl} (100%) rename codegen/assets/templates/gocode/envoy/{rbac-rules-parse.go.tpl => rbac_rules_parse.go.tpl} (100%) delete mode 100644 codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- rename codegen/assets/templates/gocode/envoy/{resource-resource_translation.go.tpl- => resource_translation.go.tpl} (50%) rename codegen/assets/templates/gocode/envoy/{resource-resource_translation_parse.go.tpl- => resource_translation_parse.go.tpl} (54%) create mode 100644 codegen/assets/templates/gocode/envoy/resource_translation_references_$component.go.tpl rename codegen/assets/templates/gocode/locale/{service.go.tpl => $component_service.go.tpl} (100%) rename codegen/assets/templates/gocode/locale/{types.go.tpl => $component_types.go.tpl} (100%) rename codegen/assets/templates/gocode/rbac/{access_control.go.tpl => $component_access_control.go.tpl} (100%) rename codegen/assets/templates/gocode/rbac/{types.go.tpl => $component_types.go.tpl} (100%) delete mode 100644 pkg/codegen-v3/README.md delete mode 100644 pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_references.go.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_rules_parse.go.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation.go.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/header-definitions.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/header-gentext.tpl delete mode 100644 pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl delete mode 100644 pkg/codegen-v3/internal/def/doc.go delete mode 100644 pkg/codegen-v3/internal/def/locale.go delete mode 100644 pkg/codegen-v3/internal/def/rbac.go delete mode 100644 pkg/codegen-v3/internal/gen/envoy.go delete mode 100644 pkg/codegen-v3/internal/gen/gen.go delete mode 100644 pkg/codegen-v3/internal/gen/locale.go delete mode 100644 pkg/codegen-v3/internal/gen/rbac.go delete mode 100644 pkg/codegen-v3/internal/tpl/templating.go delete mode 100644 pkg/codegen-v3/loader.go delete mode 100644 pkg/envoy/resource/resource_translation_references_system.gen.go diff --git a/codegen/assets/templates/gocode/envoy/rbac-references.go.tpl b/codegen/assets/templates/gocode/envoy/rbac_references_$component.go.tpl similarity index 100% rename from codegen/assets/templates/gocode/envoy/rbac-references.go.tpl rename to codegen/assets/templates/gocode/envoy/rbac_references_$component.go.tpl diff --git a/codegen/assets/templates/gocode/envoy/rbac-rules-parse.go.tpl b/codegen/assets/templates/gocode/envoy/rbac_rules_parse.go.tpl similarity index 100% rename from codegen/assets/templates/gocode/envoy/rbac-rules-parse.go.tpl rename to codegen/assets/templates/gocode/envoy/rbac_rules_parse.go.tpl diff --git a/codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- b/codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- deleted file mode 100644 index 065516b5b..000000000 --- a/codegen/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl- +++ /dev/null @@ -1,33 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( -{{- range .Imports }} - {{ . }} -{{- end }} -) - -{{- range .Def }} -{{- if .Locale }} -{{- if gt (len .Locale.Resource.References) 0 }} -// {{ export .Component .Resource }}ResourceTranslationReferences generates Locale references -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func {{ export .Component .Resource }}ResourceTranslationReferences({{- range .Locale.Resource.References }}{{ unexport .Resource }} string, {{- end }}) (res *Ref, pp []*Ref, err error) { - {{- range .Locale.Resource.References }} - {{- if eq .Field "ID" }} - res = &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})} - {{- else }} - pp = append(pp, &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})}) - {{- end }} - {{- end }} - - return -} -{{- end }} -{{- end }} -{{- end }} diff --git a/codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- b/codegen/assets/templates/gocode/envoy/resource_translation.go.tpl similarity index 50% rename from codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- rename to codegen/assets/templates/gocode/envoy/resource_translation.go.tpl index 580d1df0c..721d3462b 100644 --- a/codegen/assets/templates/gocode/envoy/resource-resource_translation.go.tpl- +++ b/codegen/assets/templates/gocode/envoy/resource_translation.go.tpl @@ -1,27 +1,20 @@ -package {{ .Package }} +package {{ .package }} -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} +{{ template "gocode/header-gentext.tpl" }} import ( systemTypes "github.com/cortezaproject/corteza-server/system/types" -{{- range .Imports }} - {{ . }} -{{- end }} ) -{{- range .Def }} -{{ $Component := .Component }} -{{ $Resource := .Resource }} -{{ $GoType := printf "types.%s" .Resource }} -func (r *{{if not (eq $Component "system")}}{{export $Component}}{{ end }}{{$Resource}}) EncodeTranslations() ([]*ResourceTranslation, error) { +{{- range .resources }} +func (r *{{ .expIdent }}) EncodeTranslations() ([]*ResourceTranslation, error) { out := make([]*ResourceTranslation, 0, 10) rr := r.Res.EncodeTranslations() rr.SetLanguage(defaultLanguage) res, ref, pp := r.ResourceTranslationParts() out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...)) -{{ if .Locale.Extended }} +{{ if .extended }} tmp, err := r.encodeTranslations() return append(out, tmp...), err {{ else }} @@ -29,3 +22,4 @@ func (r *{{if not (eq $Component "system")}}{{export $Component}}{{ end }}{{$Res {{- end }} } {{- end }} + diff --git a/codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- b/codegen/assets/templates/gocode/envoy/resource_translation_parse.go.tpl similarity index 54% rename from codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- rename to codegen/assets/templates/gocode/envoy/resource_translation_parse.go.tpl index a630a7aeb..a1c8b366c 100644 --- a/codegen/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl- +++ b/codegen/assets/templates/gocode/envoy/resource_translation_parse.go.tpl @@ -1,18 +1,17 @@ -package {{ .Package }} +package {{ .package }} -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} +{{ template "gocode/header-gentext.tpl" }} import ( "fmt" "strings" -{{- range .Imports }} +{{- range .imports }} {{ . }} {{- end }} ) -// Parse generates resource setting logic for each resource +// ParseResourceTranslation generates resource setting logic for each resource // // Resources with "envoy: false" are skipped // @@ -47,25 +46,22 @@ func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) { // make the resource provide the slice of parent resources we should nest under switch resourceType { - {{- range .Def }} - case {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType: - if len(path) != {{ len .Locale.Resource.References }} { - return "", nil, nil, fmt.Errorf("expecting {{ len .Locale.Resource.References }} reference components in path, got %d", len(path)) + {{- range .resources }} + case {{ .typeConst }}: + if len(path) != {{ len .references }} { + return "", nil, nil, fmt.Errorf("expecting {{ len .references }} reference components in path, got %d", len(path)) } - {{- if gt (len .Locale.Resource.References) 0 }} - ref, pp, err := {{ export .Component .Resource }}ResourceTranslationReferences( - {{- range $i, $r := .Locale.Resource.References }} - // {{ unexport $r.Resource }} + ref, pp, err := {{ .resTrRefFunc }}( + {{- range $i, $r := .references }} path[{{ $i }}], - {{ end }} + {{- end }} ) - return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType, ref, pp, err + return {{ .typeConst }}, ref, pp, err {{ else }} // Component resource, no path - return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType, nil, nil, nil + return {{ .typeConst }}, nil, nil, nil {{- end }} - {{- end}} } // return unhandled resource as-is diff --git a/codegen/assets/templates/gocode/envoy/resource_translation_references_$component.go.tpl b/codegen/assets/templates/gocode/envoy/resource_translation_references_$component.go.tpl new file mode 100644 index 000000000..c6e8cda79 --- /dev/null +++ b/codegen/assets/templates/gocode/envoy/resource_translation_references_$component.go.tpl @@ -0,0 +1,24 @@ +package {{ .package }} + +{{ template "gocode/header-gentext.tpl" }} + +import ( +{{- range .imports }} + {{ . }} +{{- end }} +) + +{{- range .resources }} +// {{ .resTrRefFunc }} generates Locale references +// +// This function is auto-generated +func {{ .resTrRefFunc }}({{- range .references }}{{ .param }} string, {{- end }} self string) (res *Ref, pp []*Ref, err error) { + res = &Ref{ResourceType: types.{{ .expIdent }}ResourceType, Identifiers: MakeIdentifiers(self)} + + {{- range .references }} + pp = append(pp, &Ref{ResourceType: types.{{ .expIdent }}ResourceType, Identifiers: MakeIdentifiers({{ .param }})}) + {{- end }} + + return +} +{{- end }} diff --git a/codegen/assets/templates/gocode/locale/service.go.tpl b/codegen/assets/templates/gocode/locale/$component_service.go.tpl similarity index 100% rename from codegen/assets/templates/gocode/locale/service.go.tpl rename to codegen/assets/templates/gocode/locale/$component_service.go.tpl diff --git a/codegen/assets/templates/gocode/locale/types.go.tpl b/codegen/assets/templates/gocode/locale/$component_types.go.tpl similarity index 100% rename from codegen/assets/templates/gocode/locale/types.go.tpl rename to codegen/assets/templates/gocode/locale/$component_types.go.tpl diff --git a/codegen/assets/templates/gocode/rbac/access_control.go.tpl b/codegen/assets/templates/gocode/rbac/$component_access_control.go.tpl similarity index 100% rename from codegen/assets/templates/gocode/rbac/access_control.go.tpl rename to codegen/assets/templates/gocode/rbac/$component_access_control.go.tpl diff --git a/codegen/assets/templates/gocode/rbac/types.go.tpl b/codegen/assets/templates/gocode/rbac/$component_types.go.tpl similarity index 100% rename from codegen/assets/templates/gocode/rbac/types.go.tpl rename to codegen/assets/templates/gocode/rbac/$component_types.go.tpl diff --git a/codegen/envoy-rbac.cue b/codegen/envoy-rbac.cue index 132d3937e..fc5f1fb49 100644 --- a/codegen/envoy-rbac.cue +++ b/codegen/envoy-rbac.cue @@ -10,7 +10,7 @@ envoyRBAC: [...schema.#codegen] & [ for cmp in app.corteza.components { - template: "gocode/envoy/rbac-references.go.tpl" + template: "gocode/envoy/rbac_references_$component.go.tpl" output: "pkg/envoy/resource/rbac_references_\(cmp.ident).gen.go" payload: { package: "resource" @@ -18,8 +18,6 @@ envoyRBAC: "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", ] - // cmpIdent: cmp.ident - // Operation/resource validators, grouped by resource resources: [ for res in cmp.resources { rbacRefFunc: "\(cmp.expIdent)\(res.expIdent)RbacReferences" @@ -34,7 +32,7 @@ envoyRBAC: ]+ [ { - template: "gocode/envoy/rbac-rules-parse.go.tpl" + template: "gocode/envoy/rbac_rules_parse.go.tpl" output: "pkg/envoy/resource/rbac_rules_parse.gen.go" payload: { package: "resource" @@ -44,14 +42,12 @@ envoyRBAC: }, ] - // cmpIdent: cmp.ident - // Operation/resource validators, grouped by resource resources: [ for cmp in app.corteza.components for res in cmp.resources { importAlias: "\(cmp.ident)Types" expIdent: res.expIdent - typeConst: "\(importAlias).\(expIdent)ResourceType" + typeConst: "\(importAlias).\(expIdent)ResourceType" rbacRefFunc: "\(cmp.expIdent)\(res.expIdent)RbacReferences" references: [ for p in res.parents {p}, @@ -69,4 +65,70 @@ envoyRBAC: ] } }, - ] + ]+ + [ + { + template: "gocode/envoy/resource_translation.go.tpl" + output: "pkg/envoy/resource/resource_translation.gen.go" + payload: { + package: "resource" + resources: [ + for cmp in app.corteza.components for res in cmp.resources if res.locale != _|_ { + expIdent: "\(cmp.expIdent)\(res.expIdent)" + extended: res.locale.extended + }, + ] + } + }, + ]+ + [ + { + template: "gocode/envoy/resource_translation_parse.go.tpl" + output: "pkg/envoy/resource/resource_translation_parse.gen.go" + payload: { + package: "resource" + imports: [ + for cmp in app.corteza.components { + "\(cmp.ident)Types \"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"" + }, + ] + + resources: [ + for cmp in app.corteza.components for res in cmp.resources if res.locale != _|_ { + importAlias: "\(cmp.ident)Types" + typeConst: "\(importAlias).\(res.expIdent)ResourceTranslationType" + resTrRefFunc: "\(cmp.expIdent)\(res.expIdent)ResourceTranslationReferences" + references: [ + for p in res.parents {p}, + {param: res.ident, refField: "ID", expIdent: res.expIdent}, + ] + }, + ] + } + }, + ]+ + [ + // wrapped with additional for loop to trim out templates with empty types list + for tpl in [ + for cmp in app.corteza.components { + template: "gocode/envoy/resource_translation_references_$component.go.tpl" + output: "pkg/envoy/resource/resource_translation_references_\(cmp.ident).gen.go" + payload: { + package: "resource" + imports: [ + "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", + ] + + resources: [ + for res in cmp.resources if res.locale != _|_ { + resTrRefFunc: "\(cmp.expIdent)\(res.expIdent)ResourceTranslationReferences" + expIdent: res.expIdent + references: [ + for p in res.parents {p}, + ] + }, + ] + } + }, + ] if len(tpl.payload.resources) > 0 {tpl}]+ + [] diff --git a/codegen/locale-service.cue b/codegen/locale-service.cue index 00bd0bd93..3f8810b70 100644 --- a/codegen/locale-service.cue +++ b/codegen/locale-service.cue @@ -12,7 +12,7 @@ localeService: // wrapped with additional for loop to trim out templates with empty types list for tpl in [ for cmp in app.corteza.components { - template: "gocode/locale/service.go.tpl" + template: "gocode/locale/$component_service.go.tpl" output: "\(cmp.ident)/service/locale.gen.go" payload: { package: "service" diff --git a/codegen/locale-types.cue b/codegen/locale-types.cue index ff0b7c993..5cf6fcace 100644 --- a/codegen/locale-types.cue +++ b/codegen/locale-types.cue @@ -12,7 +12,7 @@ localeTypes: // wrapped with additional for loop to trim out templates with empty types list for tpl in [ for cmp in app.corteza.components { - template: "gocode/locale/types.go.tpl" + template: "gocode/locale/$component_types.go.tpl" output: "\(cmp.ident)/types/locale.gen.go" payload: { package: "types" diff --git a/codegen/rbac-access_control.cue b/codegen/rbac-access_control.cue index 46cec3af7..82d1151ee 100644 --- a/codegen/rbac-access_control.cue +++ b/codegen/rbac-access_control.cue @@ -9,7 +9,7 @@ rbacAccessControl: [...schema.#codegen] & [ for cmp in app.corteza.components { - template: "gocode/rbac/access_control.go.tpl" + template: "gocode/rbac/$component_access_control.go.tpl" output: "\(cmp.ident)/service/access_control.gen.go" payload: { package: "service" diff --git a/codegen/rbac-types.cue b/codegen/rbac-types.cue index 6982f2342..ee1ae8875 100644 --- a/codegen/rbac-types.cue +++ b/codegen/rbac-types.cue @@ -9,7 +9,7 @@ rbacTypes: [...schema.#codegen] & [ for cmp in app.corteza.components { - template: "gocode/rbac/types.go.tpl" + template: "gocode/rbac/$component_types.go.tpl" output: "\(cmp.ident)/types/rbac.gen.go" payload: { package: "types" diff --git a/pkg/codegen-v3/README.md b/pkg/codegen-v3/README.md deleted file mode 100644 index a7d2c4b0c..000000000 --- a/pkg/codegen-v3/README.md +++ /dev/null @@ -1,12 +0,0 @@ -This is 3rd generation of corteza code generator -(1st was written in PHP, apologies 2nd is active and in use) - -Goal for the 3rd generation is: - - improved definition structure that addresses all elements that are used for code generation (types, store, rbac) - - unify most if not all definitions (stuff in YAML files) into what you can find in /def/... - - JSON schema validation for YAML files - -How to run it (temp): -``` -go run pkg/codegen-v3/*.go def -``` diff --git a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_references.go.tpl b/pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_references.go.tpl deleted file mode 100644 index 37a4051ef..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_references.go.tpl +++ /dev/null @@ -1,35 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( -{{- range .Imports }} - {{ . }} -{{- end }} -) - -{{- range .Def }} -{{- if gt (len .RBAC.Resource.References) 0 }} -// {{ export .Component .Resource }}RbacReferences generates RBAC references -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func {{ export .Component .Resource }}RbacReferences({{- range .RBAC.Resource.References }}{{ unexport .Resource }} string, {{- end }}) (res *Ref, pp []*Ref, err error) { - {{- range .RBAC.Resource.References }} - {{- if eq .Field "ID" }} - if {{ unexport .Resource }} != "*" { - res = &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})} - } - {{- else }} - if {{ unexport .Resource }} != "*" { - pp = append(pp, &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})}) - } - {{- end }} - {{- end }} - - return -} -{{- end }} -{{- end }} diff --git a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_rules_parse.go.tpl b/pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_rules_parse.go.tpl deleted file mode 100644 index 97efa93c5..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-rbac_rules_parse.go.tpl +++ /dev/null @@ -1,71 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( - "fmt" - "strings" -{{- range .Imports }} - {{ . }} -{{- end }} -) - - -// Parse generates resource setting logic for each resource -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func ParseRule(res string) (string, *Ref, []*Ref, error) { - if res == "" { - return "", nil, nil, fmt.Errorf("empty resource") - } - - sp := "/" - - res = strings.TrimSpace(res) - res = strings.TrimRight(res, sp) - rr := strings.Split(res, sp) - - // only service defined (corteza::system, corteza::compose, ...) - if len(rr) == 1 { - return res, nil, nil, nil - } - - // full thing - resourceType, path := rr[0], rr[1:] - - for p := 1; p < len(path); p++ { - if path[p] != "*" && path[p-1] == "*" { - return "", nil, nil, fmt.Errorf("invalid path wildcard combination for '%s'", res) - } - } - - - // make the resource provide the slice of parent resources we should nest under - switch resourceType { - {{- range .Def }} - case {{ unexport .Component "types" }}.{{ export .Resource }}ResourceType: - if len(path) != {{ len .RBAC.Resource.References }} { - return "", nil, nil, fmt.Errorf("expecting {{ len .RBAC.Resource.References }} reference components in path, got %d", len(path)) - } - {{- if gt (len .RBAC.Resource.References) 0 }} - ref, pp, err := {{ export .Component .Resource }}RbacReferences( - {{- range $i, $r := .RBAC.Resource.References }} - // {{ unexport $r.Resource }} - path[{{ $i }}], - {{ end }} - ) - return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceType, ref, pp, err - {{ else }} - - // Component resource, no path - return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceType, nil, nil, nil - {{- end }} - {{- end}} - } - - // return unhandled resource as-is - return resourceType, nil, nil, nil -} diff --git a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation.go.tpl b/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation.go.tpl deleted file mode 100644 index 580d1df0c..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation.go.tpl +++ /dev/null @@ -1,31 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( - systemTypes "github.com/cortezaproject/corteza-server/system/types" -{{- range .Imports }} - {{ . }} -{{- end }} -) - -{{- range .Def }} -{{ $Component := .Component }} -{{ $Resource := .Resource }} -{{ $GoType := printf "types.%s" .Resource }} -func (r *{{if not (eq $Component "system")}}{{export $Component}}{{ end }}{{$Resource}}) EncodeTranslations() ([]*ResourceTranslation, error) { - out := make([]*ResourceTranslation, 0, 10) - - rr := r.Res.EncodeTranslations() - rr.SetLanguage(defaultLanguage) - res, ref, pp := r.ResourceTranslationParts() - out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...)) -{{ if .Locale.Extended }} - tmp, err := r.encodeTranslations() - return append(out, tmp...), err -{{ else }} - return out, nil -{{- end }} -} -{{- end }} diff --git a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl b/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl deleted file mode 100644 index a630a7aeb..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_parse.go.tpl +++ /dev/null @@ -1,73 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( - "fmt" - "strings" -{{- range .Imports }} - {{ . }} -{{- end }} -) - - -// Parse generates resource setting logic for each resource -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) { - if res == "" { - return "", nil, nil, fmt.Errorf("empty resource") - } - - sp := "/" - - if strings.Index(res, "corteza::") == 0 { - res = res[9:] - } - - res = strings.TrimSpace(res) - res = strings.TrimRight(res, sp) - rr := strings.Split(res, sp) - - // only service defined (corteza::system, corteza::compose, ...) - if len(rr) == 1 { - return "", nil, nil, fmt.Errorf("only service defined: %s", res) - } - - // full thing - resourceType, path := rr[0], rr[1:] - for p := 1; p < len(path); p++ { - if path[p] == "*" { - return "", nil, nil, fmt.Errorf("path wildcard not allowed for locale resources: '%s'", res) - } - } - - // make the resource provide the slice of parent resources we should nest under - switch resourceType { - {{- range .Def }} - case {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType: - if len(path) != {{ len .Locale.Resource.References }} { - return "", nil, nil, fmt.Errorf("expecting {{ len .Locale.Resource.References }} reference components in path, got %d", len(path)) - } - {{- if gt (len .Locale.Resource.References) 0 }} - ref, pp, err := {{ export .Component .Resource }}ResourceTranslationReferences( - {{- range $i, $r := .Locale.Resource.References }} - // {{ unexport $r.Resource }} - path[{{ $i }}], - {{ end }} - ) - return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType, ref, pp, err - {{ else }} - - // Component resource, no path - return {{ unexport .Component "types" }}.{{ export .Resource }}ResourceTranslationType, nil, nil, nil - {{- end }} - {{- end}} - } - - // return unhandled resource as-is - return resourceType, nil, nil, nil -} diff --git a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl b/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl deleted file mode 100644 index 065516b5b..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/envoy/resource-resource_translation_references.go.tpl +++ /dev/null @@ -1,33 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( -{{- range .Imports }} - {{ . }} -{{- end }} -) - -{{- range .Def }} -{{- if .Locale }} -{{- if gt (len .Locale.Resource.References) 0 }} -// {{ export .Component .Resource }}ResourceTranslationReferences generates Locale references -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func {{ export .Component .Resource }}ResourceTranslationReferences({{- range .Locale.Resource.References }}{{ unexport .Resource }} string, {{- end }}) (res *Ref, pp []*Ref, err error) { - {{- range .Locale.Resource.References }} - {{- if eq .Field "ID" }} - res = &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})} - {{- else }} - pp = append(pp, &Ref{ResourceType: types.{{ export .Resource }}ResourceType, Identifiers: MakeIdentifiers({{ unexport .Resource }})}) - {{- end }} - {{- end }} - - return -} -{{- end }} -{{- end }} -{{- end }} diff --git a/pkg/codegen-v3/assets/templates/gocode/header-definitions.tpl b/pkg/codegen-v3/assets/templates/gocode/header-definitions.tpl deleted file mode 100644 index 6ac55a5b7..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/header-definitions.tpl +++ /dev/null @@ -1,4 +0,0 @@ -// Definitions file that controls how this file is generated: -{{- range .Def }} -// - {{ .Source }} -{{- end }} diff --git a/pkg/codegen-v3/assets/templates/gocode/header-gentext.tpl b/pkg/codegen-v3/assets/templates/gocode/header-gentext.tpl deleted file mode 100644 index 323ae0bbc..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/header-gentext.tpl +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// diff --git a/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl b/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl deleted file mode 100644 index 20366955b..000000000 --- a/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl +++ /dev/null @@ -1,216 +0,0 @@ -package {{ .Package }} - -{{ template "header-gentext.tpl" }} -{{ template "header-definitions.tpl" . }} - -import ( - "context" - -{{- range .Imports }} - {{ . }} -{{- end }} - "github.com/cortezaproject/corteza-server/pkg/actionlog" - intAuth "github.com/cortezaproject/corteza-server/pkg/auth" - "github.com/cortezaproject/corteza-server/pkg/errors" - "github.com/cortezaproject/corteza-server/pkg/locale" - "github.com/cortezaproject/corteza-server/pkg/options" - "github.com/cortezaproject/corteza-server/store" - systemTypes "github.com/cortezaproject/corteza-server/system/types" - "golang.org/x/text/language" -) - -type ( - localeAccessControl interface { - CanManageResourceTranslations(ctx context.Context) bool - } - - resourceTranslationsManager struct { - actionlog actionlog.Recorder - locale locale.Resource - store store.Storer - ac localeAccessController - } - - localeAccessController interface { - CanManageResourceTranslations(context.Context) bool - } - - ResourceTranslationsManagerService interface { -{{- range .Def }} - {{ .Resource }}(ctx context.Context, {{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ .Field }} uint64, {{ end }}{{ end }}) (locale.ResourceTranslationSet, error) -{{- end }} - - Upsert(context.Context, locale.ResourceTranslationSet) error - Locale() locale.Resource - } -) - -var ErrNotAllowedToManageResourceTranslations = errors.Unauthorized("not allowed to manage resource translations") - -func ResourceTranslationsManager(ls locale.Resource) *resourceTranslationsManager { - return &resourceTranslationsManager{ - actionlog: DefaultActionlog, - store: DefaultStore, - ac: DefaultAccessControl, - locale: ls, - } -} - -func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.ResourceTranslationSet) (err error) { - // User is allowed to manage resource translations when: - // - managed resource translation strings are all for default language - // or - // - user is allowed to manage resource translations - if rr.ContainsForeign(svc.Locale().Default().Tag) { - if !svc.ac.CanManageResourceTranslations(ctx) { - return ErrNotAllowedToManageResourceTranslations - } - } - - for _, r := range rr { - r.Msg = locale.SanitizeMessage(r.Msg) - } - - // @todo validation - - me := intAuth.GetIdentityFromContext(ctx) - - // - group by resource - localeByRes := make(map[string]locale.ResourceTranslationSet) - for _, r := range rr { - localeByRes[r.Resource] = append(localeByRes[r.Resource], r) - } - - // - for each resource, fetch the current state - sysLocale := make(systemTypes.ResourceTranslationSet, 0, len(rr)) - for res, rr := range localeByRes { - current, _, err := store.SearchResourceTranslations(ctx, svc.store, systemTypes.ResourceTranslationFilter{ - Resource: res, - }) - if err != nil { - return err - } - - // get deltas and prepare upsert accordingly - aux := current.New(rr) - aux.Walk(func(cc *systemTypes.ResourceTranslation) error { - cc.ID = nextID() - cc.CreatedAt = *now() - cc.CreatedBy = me.Identity() - - return nil - }) - sysLocale = append(sysLocale, aux...) - - aux = current.Old(rr) - aux.Walk(func(cc *systemTypes.ResourceTranslation) error { - cc.UpdatedAt = now() - cc.UpdatedBy = me.Identity() - return nil - }) - sysLocale = append(sysLocale, aux...) - } - - err = store.UpsertResourceTranslation(ctx, svc.store, sysLocale...) - if err != nil { - return err - } - - // Reload ALL resource translations - // @todo we could probably do this more selectively and refresh only updated resources? - _ = locale.Global().ReloadResourceTranslations(ctx) - - return nil -} - -func (svc resourceTranslationsManager) Locale() locale.Resource { - return svc.locale -} - -{{- range .Def }} -{{ $Resource := .Resource }} -{{ $GoType := printf "types.%s" .Resource }} - -func (svc resourceTranslationsManager) {{ .Resource }}(ctx context.Context, {{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ .Field }} uint64, {{ end }}{{ end }}) (locale.ResourceTranslationSet, error) { - var ( - err error - out locale.ResourceTranslationSet - res *{{ $GoType }} - k types.LocaleKey - ) - - res, err = svc.load{{$Resource}}(ctx, svc.store, {{ if .Locale.Resource }}{{ range .Locale.Resource.References }}{{ .Field }}, {{ end }}{{ end }}) - if err != nil { - return nil, err - } - - for _, tag := range svc.locale.Tags() { -{{- range .Locale.Keys}} - {{- if not .Custom }} - k = types.LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }} - out = append(out, &locale.ResourceTranslation{ - Resource: res.ResourceTranslation(), - Lang: tag.String(), - Key: k.Path, - Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path), - }) - {{ end }} -{{- end}} - -{{- range .Locale.Keys}} - {{- if and .Custom .CustomHandler }} - auxResSet, err = svc.{{unexport $Resource}}{{export .CustomHandler}}Handler(ctx, tag, res, k.Path) - {{- end}} -{{- end}} - } - -{{- if .Locale.Extended }} - - tmp, err := svc.{{unexport $Resource}}Extended(ctx, res) - return append(out, tmp...), err - {{- else }} - return out, nil -{{- end }} -} - -{{- end }} - -func updateTranslations(ctx context.Context, ac localeAccessControl, lsvc ResourceTranslationsManagerService, tt ...*locale.ResourceTranslation) error { - if lsvc == nil || lsvc.Locale() == nil || lsvc.Locale().Default() == nil { - // gracefully handle partial initializations - return nil - } - - var ( - // assuming options will not change after start - contentLang = lsvc.Locale().Default().Tag - ) - - if options.Locale().ResourceTranslationsEnabled { - contentLang = locale.GetContentLanguageFromContext(ctx) - // Resource translations enabled - if contentLang == language.Und { - // If no content-language meta (HTTP header) info was - // used, do not run update translations - we do not know - // what is the language that we're sending in - return nil - } - - if !lsvc.Locale().SupportedLang(contentLang) { - // unsupported language - return errors.InvalidData("unsupported language") - } - - if !ac.CanManageResourceTranslations(ctx) { - return errors.Unauthorized("not allowed to manage resource translations") - } - } - - locale.ResourceTranslationSet(tt).SetLanguage(contentLang) - if err := lsvc.Upsert(ctx, tt); err != nil { - return err - } - - return nil -} - diff --git a/pkg/codegen-v3/internal/def/doc.go b/pkg/codegen-v3/internal/def/doc.go deleted file mode 100644 index b47b1058e..000000000 --- a/pkg/codegen-v3/internal/def/doc.go +++ /dev/null @@ -1,91 +0,0 @@ -package def - -import ( - "fmt" - "strings" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" - "github.com/cortezaproject/corteza-server/pkg/y7s" - "gopkg.in/yaml.v3" -) - -type ( - Document struct { - Skip bool `yaml:"(skip)"` - Imports []string - Component string - IsComponentResource bool `yaml:"-"` - Resource string - Source string - RBAC *rbac - Locale *locale - Envoy bool `yaml:"envoy"` - } -) - -func (set *rbacOperations) UnmarshalYAML(n *yaml.Node) error { - return y7s.Each(n, func(k *yaml.Node, v *yaml.Node) (err error) { - def := rbacOperation{} - if k != nil { - def.Operation = k.Value - } - - *set = append(*set, &def) - return v.Decode(&def) - }) -} - -// Preproc preprocesses the document and sets defaults -func (doc *Document) Proc(filename string) error { - doc.Source = filename - - // filename parts - fp := strings.Split(filename, ".") - // trim extension - fp = fp[:len(fp)-1] - if len(fp) > 0 && doc.Component == "" { - // set component from the 1st part - // component is system, compose, ... - doc.Component = fp[0] - } - - if len(fp) > 1 && doc.Resource == "" { - // if there are more parts, set resource - // resource is user, module, record, workflow, ... - doc.Resource = fp[1] - } - - if strings.ToLower(doc.Resource) == "component" { - return fmt.Errorf("can not use 'component' as a resource name, this is done automatically") - } else if doc.Resource == "" { - doc.Resource = "component" - doc.IsComponentResource = true - } - - doc.Imports = normalizeImport(doc.Imports...) - - if err := doc.RBAC.proc(doc.Component, doc.Resource); err != nil { - return err - } - - if err := doc.Locale.proc(doc.Component, doc.Resource); err != nil { - return err - } - - doc.Resource = tpl.Export(doc.Resource) - - return nil -} - -func normalizeImport(ii ...string) []string { - for i := range ii { - if strings.Contains(ii[i], " ") { - p := strings.SplitN(ii[i], " ", 2) - ii[i] = fmt.Sprintf(`%s "%s"`, p[0], strings.Trim(p[1], `"`)) - } else { - ii[i] = fmt.Sprintf(`"%s"`, strings.Trim(ii[i], `"'`+"`")) - } - } - - return ii -} diff --git a/pkg/codegen-v3/internal/def/locale.go b/pkg/codegen-v3/internal/def/locale.go deleted file mode 100644 index f173158c2..000000000 --- a/pkg/codegen-v3/internal/def/locale.go +++ /dev/null @@ -1,127 +0,0 @@ -package def - -import ( - "fmt" - "strings" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" - "github.com/cortezaproject/corteza-server/pkg/y7s" - "gopkg.in/yaml.v3" -) - -type ( - locale struct { - ResourceType string `yaml:"resourceType"` - Resource *resourceTranslation - Extended bool - SkipSvc bool `yaml:"skipSvc"` - Keys localeKeys - } - - resourceTranslation struct { - References []*resourceTranslationRef - } - - resourceTranslationRef struct { - Field string - ResourceType string - Resource string - Component string - - custom bool - } - - localeKeys []*localeKey - - localeKey struct { - Name string - Path string - Custom bool - CustomHandler string `yaml:"customHandler"` - Description string - Field string - } -) - -func (r *locale) proc(component, resource string) error { - if r == nil { - return nil - } - - if r.ResourceType == "" { - if strings.ToLower(resource) == "component" { - r.ResourceType = component - } else { - r.ResourceType = fmt.Sprintf("%s:%s", component, resource) - } - } - - if r.Resource == nil { - r.Resource = &resourceTranslation{References: []*resourceTranslationRef{{Field: "ID"}}} - } - - // check types of each referenced component - // and self-references (field==ID) with own resource type - for _, rc := range r.Resource.References { - if !rc.custom { - if rc.Field == "ID" { - rc.ResourceType = r.ResourceType - rc.Component = component - rc.Resource = resource - } else { - rc.ResourceType = fmt.Sprintf("%s:%s", component, rc.Field) - rc.Component = component - rc.Resource = rc.Field - rc.Field = rc.Field + "ID" - } - } - } - - // assure missing key field paths - for _, k := range r.Keys { - k.Custom = k.Custom || k.CustomHandler != "" - - // Guess the key path - if k.Field == "" { - k.Field = tpl.Export(k.Path) - } - - // Guess the name - if k.Name == "" { - k.Name = k.Path - } - } - - return nil -} - -func (op *localeKey) UnmarshalYAML(n *yaml.Node) error { - type auxType localeKey - - switch { - case y7s.IsKind(n, yaml.ScalarNode): - return y7s.DecodeScalar(n, "locale key", &op.Path) - - case y7s.IsKind(n, yaml.MappingNode): - var aux = (*auxType)(op) - return n.Decode(aux) - } - - return y7s.NodeErr(n, "unsupported formatting") -} - -func (op *resourceTranslationRef) UnmarshalYAML(n *yaml.Node) error { - if y7s.IsKind(n, yaml.ScalarNode) { - op.Field = n.Value - if n.Value != "ID" { - op.ResourceType = n.Value - } - - return nil - } - - type auxType resourceTranslationRef - var aux = (*auxType)(op) - aux.custom = true - return n.Decode(aux) -} diff --git a/pkg/codegen-v3/internal/def/rbac.go b/pkg/codegen-v3/internal/def/rbac.go deleted file mode 100644 index 245ecfa7f..000000000 --- a/pkg/codegen-v3/internal/def/rbac.go +++ /dev/null @@ -1,165 +0,0 @@ -package def - -import ( - "fmt" - "strings" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" - "github.com/cortezaproject/corteza-server/pkg/y7s" - "gopkg.in/yaml.v3" -) - -type ( - rbac struct { - // fully qualified resource name - ResourceType string `yaml:"resourceType"` - Resource *rbacResource - Operations rbacOperations - } - - rbacResource struct { - References []*rbacResourceRef - Attributes *rbacAttributes - } - - rbacResourceRef struct { - Field string - ResourceType string - Resource string - Component string - - custom bool - } - - rbacOperations []*rbacOperation - - rbacOperation struct { - Operation string - CanFnName string `yaml:"canFnName"` - Description string - } - - rbacAttributes struct { - Fields []string `yaml:"-"` - } -) - -func (r *rbac) proc(component, resource string) error { - const ( - defaultNS = "corteza" - nsDelimiter = "::" - ) - - if r.ResourceType == "" { - if strings.ToLower(resource) == "component" { - r.ResourceType = component - } else { - r.ResourceType = fmt.Sprintf("%s:%s", component, resource) - } - - r.ResourceType = defaultNS + nsDelimiter + r.ResourceType - } - - if !strings.Contains(r.ResourceType, nsDelimiter) { - return fmt.Errorf("no namespace prefix found (e.g.: 'corteza::') in resource type") - } - - for _, op := range r.Operations { - // Generate all check name - if op.CanFnName == "" { - op.CanFnName = RbacOperationCanFnName(resource, op.Operation) - } - } - - if r.Resource == nil { - r.Resource = &rbacResource{References: []*rbacResourceRef{{Field: "ID"}}} - } - - // check types of each referenced component - // and prefix non-custom components with corteza:: - // and self-references (field==ID) with own resource type - for _, rc := range r.Resource.References { - if !rc.custom { - if rc.Field == "ID" { - rc.ResourceType = r.ResourceType - rc.Component = component - rc.Resource = resource - } else { - rc.ResourceType = defaultNS + nsDelimiter + fmt.Sprintf("%s:%s", component, rc.Field) - rc.Component = component - rc.Resource = rc.Field - rc.Field = rc.Field + "ID" - } - } - } - - return nil -} - -func (op *rbacOperation) UnmarshalYAML(n *yaml.Node) error { - if y7s.IsKind(n, yaml.ScalarNode) { - // @todo handle disabled operations - // the idea is that when service operations are defined we implicitly define - // RBAC operations. Here, we'll be able to remove implicitly defined operation - return nil - } - - type auxType rbacOperation - var aux = (*auxType)(op) - return n.Decode(aux) -} - -func (op *rbacResourceRef) UnmarshalYAML(n *yaml.Node) error { - if y7s.IsKind(n, yaml.ScalarNode) { - op.Field = n.Value - if n.Value != "ID" { - op.ResourceType = n.Value - // @todo expand resource & component - } - - return nil - } - - type auxType rbacResourceRef - var aux = (*auxType)(op) - aux.custom = true - return n.Decode(aux) -} - -func (a *rbacAttributes) UnmarshalYAML(n *yaml.Node) error { - if y7s.IsKind(n, yaml.ScalarNode) { - return nil - } - - // if not scalar, assume we will get list of fields - a.Fields = make([]string, 0) - return n.Decode(&a.Fields) -} - -func RbacOperationCanFnName(res, op string) string { - // when check function name is not explicitly defined we try - // to use resource and operation name and generate easy-to-read name - // - // + => Can - // + => CanOn - - if strings.ToLower(res) == "component" { - res = "" - } - - if strings.Contains(op, ".") { - parts := strings.Split(op, ".") - l := len(parts) - - parts = append(parts[l-1:], parts[:l-1]...) - - if res != "" { - // Only append "on" if there is resource - parts = append(parts, "on") - } - - op = tpl.Export(parts...) - } - - return tpl.Export("can", op, res) -} diff --git a/pkg/codegen-v3/internal/gen/envoy.go b/pkg/codegen-v3/internal/gen/envoy.go deleted file mode 100644 index 48fa357e5..000000000 --- a/pkg/codegen-v3/internal/gen/envoy.go +++ /dev/null @@ -1,168 +0,0 @@ -package gen - -import ( - "fmt" - "text/template" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/def" - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" - "github.com/cortezaproject/corteza-server/pkg/slice" -) - -func Envoy(t *template.Template, dd []*def.Document) error { - return List{ - "resource rbac parse": envoyResourceRbacUnmarshal, - "resource rbac references": envoyResourceRbacReferences, - - "resource translation": envoyResourceTranslation, - "resource translation parse": envoyResourceTranslationUnmarshal, - "resource translation references": envoyResourceTranslationReferences, - }.Generate(t, dd) -} - -// EnvoyResourceRbacUnmarshal envoy rbac unmarshal -// /service/rbac.gen.go -// -// Contains all RBAC related definitions -func envoyResourceRbacUnmarshal(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "envoy/resource-rbac_rules_parse.go.tpl" - outputPathTpl = "pkg/envoy/resource/rbac_rules_parse.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Envoy }) - - // build list of component type imports - ctImports := make([]string, 0) - for _, d := range dd { - imp := d.Component + "Types " + cImport(d.Component, "types") - if !slice.HasString(ctImports, imp) { - ctImports = append(ctImports, imp) - } - } - - w := tpl.Wrap{ - Package: "resource", - Def: dd, - Imports: append(collectImports(dd...), ctImports...), - } - - err = tpl.GoTemplate(outputPathTpl, t.Lookup(templateName), w) - if err != nil { - return - } - - return -} - -// EnvoyResourceRbacReferences generates one rbac definition file per component -// /service/rbac.gen.go -// -// Contains all RBAC related definitions -func envoyResourceRbacReferences(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "envoy/resource-rbac_references.go.tpl" - outputPathTpl = "pkg/envoy/resource/rbac_references_%s.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Envoy }) - - for component, perComponent := range partByComponent(dd) { - - w := tpl.Wrap{ - Package: "resource", - Component: component, - Def: perComponent, - Imports: append(collectImports(perComponent...), cImport(component, "types")), - } - - err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w) - if err != nil { - return - } - } - - return -} - -func envoyResourceTranslation(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "envoy/resource-resource_translation.go.tpl" - outputPathTpl = "pkg/envoy/resource/resource_translation.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Envoy && d.Locale != nil }) - - // build list of component type imports - ctImports := make([]string, 0) - - w := tpl.Wrap{ - Package: "resource", - Def: dd, - Imports: append(collectImports(dd...), ctImports...), - } - - err = tpl.GoTemplate(outputPathTpl, t.Lookup(templateName), w) - if err != nil { - return - } - - return -} - -func envoyResourceTranslationUnmarshal(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "envoy/resource-resource_translation_parse.go.tpl" - outputPathTpl = "pkg/envoy/resource/resource_translation_parse.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Envoy && d.Locale != nil }) - - // build list of component type imports - ctImports := make([]string, 0) - for _, d := range dd { - imp := d.Component + "Types " + cImport(d.Component, "types") - if !slice.HasString(ctImports, imp) { - ctImports = append(ctImports, imp) - } - } - - w := tpl.Wrap{ - Package: "resource", - Def: dd, - Imports: append(collectImports(dd...), ctImports...), - } - - err = tpl.GoTemplate(outputPathTpl, t.Lookup(templateName), w) - if err != nil { - return - } - - return -} - -func envoyResourceTranslationReferences(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "envoy/resource-resource_translation_references.go.tpl" - outputPathTpl = "pkg/envoy/resource/resource_translation_references_%s.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Envoy && d.Locale != nil }) - - for component, perComponent := range partByComponent(dd) { - - w := tpl.Wrap{ - Package: "resource", - Component: component, - Def: perComponent, - Imports: append(collectImports(perComponent...), cImport(component, "types")), - } - - err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w) - if err != nil { - return - } - } - - return -} diff --git a/pkg/codegen-v3/internal/gen/gen.go b/pkg/codegen-v3/internal/gen/gen.go deleted file mode 100644 index 977b88f26..000000000 --- a/pkg/codegen-v3/internal/gen/gen.go +++ /dev/null @@ -1,67 +0,0 @@ -package gen - -import ( - "fmt" - "text/template" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/def" -) - -type ( - List map[string]func(*template.Template, []*def.Document) error -) - -func (gg List) Generate(tpls *template.Template, dd []*def.Document) (err error) { - for l, g := range gg { - if err = g(tpls, dd); err != nil { - return fmt.Errorf("codegen for %s failed: %w", l, err) - } - } - - return -} - -func filter(dd []*def.Document, check func(*def.Document) bool) []*def.Document { - aux := make([]*def.Document, 0, len(dd)) - for _, d := range dd { - if !check(d) { - continue - } - - aux = append(aux, d) - } - return aux -} - -func partByComponent(dd []*def.Document) map[string][]*def.Document { - var ( - parted = make(map[string][]*def.Document) - ) - - for _, d := range dd { - parted[d.Component] = append(parted[d.Component], d) - } - - return parted -} - -func collectImports(dd ...*def.Document) []string { - mm := make(map[string]bool) - for _, d := range dd { - for _, i := range d.Imports { - mm[i] = true - } - } - - ii := make([]string, 0, len(mm)) - for i := range mm { - ii = append(ii, i) - } - - return ii -} - -// component import -func cImport(c, s string) string { - return fmt.Sprintf(`"github.com/cortezaproject/corteza-server/%s/%s"`, c, s) -} diff --git a/pkg/codegen-v3/internal/gen/locale.go b/pkg/codegen-v3/internal/gen/locale.go deleted file mode 100644 index 2f52ba3fd..000000000 --- a/pkg/codegen-v3/internal/gen/locale.go +++ /dev/null @@ -1,70 +0,0 @@ -package gen - -import ( - "fmt" - "text/template" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/def" - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" -) - -func Locale(t *template.Template, dd []*def.Document) error { - return List{ - "types": localeTypes, - "services": localeServices, - }.Generate(t, dd) -} - -func localeTypes(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "locale/types.go.tpl" - outputPathTpl = "%s/types/locale.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Locale != nil }) - - for component, perComponent := range partByComponent(dd) { - w := tpl.Wrap{ - Package: "types", - Component: component, - Def: perComponent, - } - - err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w) - if err != nil { - return - } - } - - return -} - -func localeServices(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "locale/service.go.tpl" - outputPathTpl = "%s/service/locale.gen.go" - ) - - dd = filter(dd, func(d *def.Document) bool { return d.Locale != nil && !d.Locale.SkipSvc }) - - for component, perComponent := range partByComponent(dd) { - w := tpl.Wrap{ - Package: "service", - Component: component, - Def: perComponent, - Imports: append(collectImports(perComponent...), cImport(component, "types")), - } - - err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w) - if err != nil { - return - } - } - - for _, d := range dd { - if d.Locale != nil { - } - } - - return -} diff --git a/pkg/codegen-v3/internal/gen/rbac.go b/pkg/codegen-v3/internal/gen/rbac.go deleted file mode 100644 index f99a978e2..000000000 --- a/pkg/codegen-v3/internal/gen/rbac.go +++ /dev/null @@ -1,69 +0,0 @@ -package gen - -import ( - "fmt" - "text/template" - - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/def" - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" -) - -func RBAC(t *template.Template, dd []*def.Document) error { - return List{ - "type": rbacTypes, - "service access control": rbacAccessControlService, - }.Generate(t, dd) -} - -// RbacTypes generates rbac definitions (one per component) -// /service/rbac.gen.go -// -// Contains all RBAC related definitions -func rbacTypes(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "rbac/types.go.tpl" - outputPathTpl = "%s/types/rbac.gen.go" - ) - - for component, perComponent := range partByComponent(dd) { - w := tpl.Wrap{ - Package: "types", - Component: component, - Def: perComponent, - } - - err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w) - if err != nil { - return - } - } - - return -} - -// RbacAccessControlService generates access control functions (one file per component) -// /service/rbac.gen.go -// -// Contains all RBAC related definitions -func rbacAccessControlService(t *template.Template, dd []*def.Document) (err error) { - const ( - templateName = "rbac/access_control.go.tpl" - outputPathTpl = "%s/service/access_control.gen.go" - ) - - for component, perComponent := range partByComponent(dd) { - w := tpl.Wrap{ - Package: "service", - Component: component, - Def: perComponent, - Imports: append(collectImports(perComponent...), cImport(component, "types")), - } - - err = tpl.GoTemplate(fmt.Sprintf(outputPathTpl, component), t.Lookup(templateName), w) - if err != nil { - return - } - } - - return -} diff --git a/pkg/codegen-v3/internal/tpl/templating.go b/pkg/codegen-v3/internal/tpl/templating.go deleted file mode 100644 index 119abe40b..000000000 --- a/pkg/codegen-v3/internal/tpl/templating.go +++ /dev/null @@ -1,126 +0,0 @@ -package tpl - -import ( - "bytes" - "fmt" - "go/format" - "io" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "strings" - "text/template" - - "github.com/Masterminds/sprig" -) - -type ( - Wrap struct { - Package string - - // will be set when grouping definitions by component - Component string - - Imports []string - Def interface{} - } -) - -var nonIdentChars = regexp.MustCompile(`[\s\\/\-.]+`) - -func Export(pp ...string) (out string) { - for _, p := range pp { - if len(p) > 1 { - p = strings.ToUpper(p[:1]) + p[1:] - } - - if ss := nonIdentChars.Split(p, -1); len(ss) > 1 { - p = Export(ss...) - } - - out = out + p - } - - return out -} - -func Unexport(pp ...string) (out string) { - out = Export(pp...) - if len(out) == 0 { - return - } - - if out == "ID" { - return "id" - } - - return strings.ToLower(out[:1]) + out[1:] -} - -func BaseTemplate() *template.Template { - return template.New(""). - Funcs(sprig.TxtFuncMap()). - Funcs(map[string]interface{}{ - "export": Export, - "unexport": Unexport, - }) -} - -func LoadTemplates(rTpl *template.Template, rootDir string) (*template.Template, error) { - cleanRoot := filepath.Clean(rootDir) - pfx := len(cleanRoot) + 1 - - return rTpl, filepath.Walk(cleanRoot, func(path string, info os.FileInfo, err error) error { - if info.IsDir() || !strings.HasSuffix(path, ".tpl") || err != nil { - return err - } - - b, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - name := path[pfx:] - rTpl, err = rTpl.New(name).Parse(string(b)) - - return err - }) -} - -func GoTemplate(dst string, tpl *template.Template, payload Wrap) (err error) { - var output io.WriteCloser - buf := bytes.Buffer{} - - if tpl == nil { - return fmt.Errorf("could not find template for %s", dst) - } - - if err := tpl.Execute(&buf, payload); err != nil { - return err - } - - fmtsrc, err := format.Source(buf.Bytes()) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%s fmt warn: %v\n", dst, err) - - err = nil - fmtsrc = buf.Bytes() - } - - if dst == "" || dst == "-" { - output = os.Stdout - } else { - if output, err = os.Create(dst); err != nil { - return err - } - - defer output.Close() - } - - if _, err = output.Write(fmtsrc); err != nil { - return err - } - - return nil -} diff --git a/pkg/codegen-v3/loader.go b/pkg/codegen-v3/loader.go deleted file mode 100644 index 1dc8f46ed..000000000 --- a/pkg/codegen-v3/loader.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/cortezaproject/corteza-server/pkg/cli" - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/def" - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/gen" - "github.com/cortezaproject/corteza-server/pkg/codegen-v3/internal/tpl" - "github.com/davecgh/go-spew/spew" - "gopkg.in/yaml.v3" -) - -var _ = spew.Dump - -func main() { - dd, err := loadDefinitions(os.Args[1]) - cli.HandleError(err) - - tpls, err := tpl.LoadTemplates(tpl.BaseTemplate(), "./pkg/codegen-v3/assets/templates/gocode") - if err != nil { - cli.HandleError(fmt.Errorf("could not load templates: %w", err)) - } - - cli.HandleError(gen.List{ - "RBAC": gen.RBAC, - "Envoy": gen.Envoy, - "Locale": gen.Locale, - }.Generate(tpls, dd)) -} - -func loadDefinition(r io.Reader) (*def.Document, error) { - doc := &def.Document{ - Envoy: true, - } - - return doc, yaml.NewDecoder(r).Decode(doc) -} - -func loadDefinitions(path string) (dd []*def.Document, err error) { - var ( - fh *os.File - doc *def.Document - files []string - ) - - files, err = filepath.Glob(path + "/*.yaml") - if err != nil { - return nil, fmt.Errorf("could not load ddefinitions form path '%s': %w", path, err) - } - - for _, file := range files { - fh, err = os.Open(file) - if err != nil { - return nil, fmt.Errorf("could not load definiton file '%s': %w", file, err) - } - - doc, err = loadDefinition(fh) - if err != nil { - return nil, fmt.Errorf("could not load definiton from '%s': %w", file, err) - } - - if doc.Skip { - continue - } - - if err = doc.Proc(filepath.Base(file)); err != nil { - return nil, fmt.Errorf("failed to preprocess definitions from '%s': %w", file, err) - } - - dd = append(dd, doc) - } - - return -} diff --git a/pkg/envoy/resource/rbac_rules_parse.gen.go b/pkg/envoy/resource/rbac_rules_parse.gen.go index 09e5bdbbd..f5c9d7a04 100644 --- a/pkg/envoy/resource/rbac_rules_parse.gen.go +++ b/pkg/envoy/resource/rbac_rules_parse.gen.go @@ -8,10 +8,9 @@ package resource import ( "fmt" - "strings" - composeTypes "github.com/cortezaproject/corteza-server/compose/types" systemTypes "github.com/cortezaproject/corteza-server/system/types" + "strings" ) // Parse generates resource setting logic for each resource diff --git a/pkg/envoy/resource/resource_translation.gen.go b/pkg/envoy/resource/resource_translation.gen.go index b206aae87..0613308f2 100644 --- a/pkg/envoy/resource/resource_translation.gen.go +++ b/pkg/envoy/resource/resource_translation.gen.go @@ -6,16 +6,20 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml - import ( systemTypes "github.com/cortezaproject/corteza-server/system/types" ) +func (r *ComposeModule) EncodeTranslations() ([]*ResourceTranslation, error) { + out := make([]*ResourceTranslation, 0, 10) + + rr := r.Res.EncodeTranslations() + rr.SetLanguage(defaultLanguage) + res, ref, pp := r.ResourceTranslationParts() + out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...)) + + return out, nil +} func (r *ComposeModuleField) EncodeTranslations() ([]*ResourceTranslation, error) { out := make([]*ResourceTranslation, 0, 10) @@ -26,20 +30,6 @@ func (r *ComposeModuleField) EncodeTranslations() ([]*ResourceTranslation, error return out, nil } - -func (r *ComposeModule) EncodeTranslations() ([]*ResourceTranslation, error) { - out := make([]*ResourceTranslation, 0, 10) - - rr := r.Res.EncodeTranslations() - rr.SetLanguage(defaultLanguage) - res, ref, pp := r.ResourceTranslationParts() - out = append(out, NewResourceTranslation(systemTypes.FromLocale(rr), res, ref, pp...)) - - tmp, err := r.encodeTranslations() - return append(out, tmp...), err - -} - func (r *ComposeNamespace) EncodeTranslations() ([]*ResourceTranslation, error) { out := make([]*ResourceTranslation, 0, 10) @@ -50,7 +40,6 @@ func (r *ComposeNamespace) EncodeTranslations() ([]*ResourceTranslation, error) return out, nil } - func (r *ComposePage) EncodeTranslations() ([]*ResourceTranslation, error) { out := make([]*ResourceTranslation, 0, 10) diff --git a/pkg/envoy/resource/resource_translation_parse.gen.go b/pkg/envoy/resource/resource_translation_parse.gen.go index 522786c4a..d912071c3 100644 --- a/pkg/envoy/resource/resource_translation_parse.gen.go +++ b/pkg/envoy/resource/resource_translation_parse.gen.go @@ -6,19 +6,14 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml - import ( "fmt" composeTypes "github.com/cortezaproject/corteza-server/compose/types" + systemTypes "github.com/cortezaproject/corteza-server/system/types" "strings" ) -// Parse generates resource setting logic for each resource +// ParseResourceTranslation generates resource setting logic for each resource // // Resources with "envoy: false" are skipped // @@ -53,41 +48,32 @@ func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) { // make the resource provide the slice of parent resources we should nest under switch resourceType { - case composeTypes.ModuleFieldResourceTranslationType: - if len(path) != 3 { - return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path)) - } - ref, pp, err := ComposeModuleFieldResourceTranslationReferences( - // namespace - path[0], - - // module - path[1], - - // moduleField - path[2], - ) - return composeTypes.ModuleFieldResourceTranslationType, ref, pp, err - case composeTypes.ModuleResourceTranslationType: if len(path) != 2 { return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) } ref, pp, err := ComposeModuleResourceTranslationReferences( - // namespace path[0], - - // module path[1], ) return composeTypes.ModuleResourceTranslationType, ref, pp, err + case composeTypes.ModuleFieldResourceTranslationType: + if len(path) != 3 { + return "", nil, nil, fmt.Errorf("expecting 3 reference components in path, got %d", len(path)) + } + ref, pp, err := ComposeModuleFieldResourceTranslationReferences( + path[0], + path[1], + path[2], + ) + return composeTypes.ModuleFieldResourceTranslationType, ref, pp, err + case composeTypes.NamespaceResourceTranslationType: if len(path) != 1 { return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) } ref, pp, err := ComposeNamespaceResourceTranslationReferences( - // namespace path[0], ) return composeTypes.NamespaceResourceTranslationType, ref, pp, err @@ -97,10 +83,7 @@ func ParseResourceTranslation(res string) (string, *Ref, []*Ref, error) { return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) } ref, pp, err := ComposePageResourceTranslationReferences( - // namespace path[0], - - // page path[1], ) return composeTypes.PageResourceTranslationType, ref, pp, err diff --git a/pkg/envoy/resource/resource_translation_references_compose.gen.go b/pkg/envoy/resource/resource_translation_references_compose.gen.go index d5361607e..1df89c42e 100644 --- a/pkg/envoy/resource/resource_translation_references_compose.gen.go +++ b/pkg/envoy/resource/resource_translation_references_compose.gen.go @@ -6,60 +6,46 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - compose.module-field.yaml -// - compose.module.yaml -// - compose.namespace.yaml -// - compose.page.yaml - import ( "github.com/cortezaproject/corteza-server/compose/types" ) -// ComposeModuleFieldResourceTranslationReferences generates Locale references -// -// Resources with "envoy: false" are skipped +// ComposeModuleResourceTranslationReferences generates Locale references // // This function is auto-generated -func ComposeModuleFieldResourceTranslationReferences(namespace string, module string, moduleField string) (res *Ref, pp []*Ref, err error) { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) - pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(module)}) - res = &Ref{ResourceType: types.ModuleFieldResourceType, Identifiers: MakeIdentifiers(moduleField)} +func ComposeModuleResourceTranslationReferences(namespaceID string, self string) (res *Ref, pp []*Ref, err error) { + res = &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(self)} + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) return } -// ComposeModuleResourceTranslationReferences generates Locale references -// -// Resources with "envoy: false" are skipped +// ComposeModuleFieldResourceTranslationReferences generates Locale references // // This function is auto-generated -func ComposeModuleResourceTranslationReferences(namespace string, module string) (res *Ref, pp []*Ref, err error) { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) - res = &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(module)} +func ComposeModuleFieldResourceTranslationReferences(namespaceID string, moduleID string, self string) (res *Ref, pp []*Ref, err error) { + res = &Ref{ResourceType: types.ModuleFieldResourceType, Identifiers: MakeIdentifiers(self)} + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) + pp = append(pp, &Ref{ResourceType: types.ModuleResourceType, Identifiers: MakeIdentifiers(moduleID)}) return } // ComposeNamespaceResourceTranslationReferences generates Locale references // -// Resources with "envoy: false" are skipped -// // This function is auto-generated -func ComposeNamespaceResourceTranslationReferences(namespace string) (res *Ref, pp []*Ref, err error) { - res = &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)} +func ComposeNamespaceResourceTranslationReferences(self string) (res *Ref, pp []*Ref, err error) { + res = &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(self)} return } // ComposePageResourceTranslationReferences generates Locale references // -// Resources with "envoy: false" are skipped -// // This function is auto-generated -func ComposePageResourceTranslationReferences(namespace string, page string) (res *Ref, pp []*Ref, err error) { - pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespace)}) - res = &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(page)} +func ComposePageResourceTranslationReferences(namespaceID string, self string) (res *Ref, pp []*Ref, err error) { + res = &Ref{ResourceType: types.PageResourceType, Identifiers: MakeIdentifiers(self)} + pp = append(pp, &Ref{ResourceType: types.NamespaceResourceType, Identifiers: MakeIdentifiers(namespaceID)}) return } diff --git a/pkg/envoy/resource/resource_translation_references_system.gen.go b/pkg/envoy/resource/resource_translation_references_system.gen.go deleted file mode 100644 index c781bd5ed..000000000 --- a/pkg/envoy/resource/resource_translation_references_system.gen.go +++ /dev/null @@ -1,25 +0,0 @@ -package resource - -// This file is auto-generated. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// - -// Definitions file that controls how this file is generated: -// - system.report.yaml - -import ( - "github.com/cortezaproject/corteza-server/system/types" -) - -// SystemReportResourceTranslationReferences generates Locale references -// -// Resources with "envoy: false" are skipped -// -// This function is auto-generated -func SystemReportResourceTranslationReferences(report string) (res *Ref, pp []*Ref, err error) { - res = &Ref{ResourceType: types.ReportResourceType, Identifiers: MakeIdentifiers(report)} - - return -} From 1284371bb3950e61e41a044c62595cde3c080192 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Fri, 31 Dec 2021 10:38:03 +0100 Subject: [PATCH 5/7] Add cue tooling & codegen to makefile --- .github/workflows/snapshot.yml | 3 +- Makefile | 23 ++++++-- Makefile.inc | 9 +++ app/app.cue | 2 +- codegen/README.md | 81 +++++++++++++++++++++++++++ codegen/envoy-rbac.cue | 44 +++++++-------- codegen/{all.cue => platform.cue} | 0 {def => codegen}/schema/component.cue | 0 codegen/schema/locale.cue | 56 ++++++++++++++++++ {def => codegen}/schema/platform.cue | 0 {def => codegen}/schema/rbac.cue | 4 +- {def => codegen}/schema/resource.cue | 57 +++++++++---------- {def => codegen}/schema/shared.cue | 9 ++- codegen/tool/templating.go | 4 ++ compose/chart.cue | 2 +- compose/component.cue | 2 +- compose/module-field.cue | 2 +- compose/module.cue | 2 +- compose/namespace.cue | 2 +- compose/page.cue | 2 +- compose/record.cue | 2 +- def/README.md | 5 +- def/compose.chart.yaml | 17 ------ def/compose.module-field.yaml | 29 ---------- def/compose.module.yaml | 24 -------- def/compose.namespace.yaml | 31 ---------- def/compose.page.yaml | 23 -------- def/compose.record.yaml | 11 ---- def/compose.yaml | 19 ------- def/def-envoy.json | 7 --- def/def-rbac.json | 78 -------------------------- def/def.json | 26 --------- def/schema/locale.cue | 58 ------------------- def/system.apigw-route.yaml | 8 --- def/system.application.yaml | 8 --- def/system.auth-client.yaml | 10 ---- def/system.queue.yaml | 12 ---- def/system.report.yaml | 18 ------ def/system.role.yaml | 10 ---- def/system.template.yaml | 10 ---- def/system.user.yaml | 19 ------- def/system.yaml | 64 --------------------- pkg/codegen/README.md | 2 + system/apigw-route.cue | 2 +- system/application.cue | 2 +- system/auth-client.cue | 2 +- system/component.cue | 2 +- system/queue.cue | 2 +- system/report.cue | 2 +- system/role.cue | 2 +- system/template.cue | 2 +- system/user.cue | 2 +- 52 files changed, 250 insertions(+), 563 deletions(-) create mode 100644 codegen/README.md rename codegen/{all.cue => platform.cue} (100%) rename {def => codegen}/schema/component.cue (100%) create mode 100644 codegen/schema/locale.cue rename {def => codegen}/schema/platform.cue (100%) rename {def => codegen}/schema/rbac.cue (97%) rename {def => codegen}/schema/resource.cue (56%) rename {def => codegen}/schema/shared.cue (63%) delete mode 100644 def/compose.chart.yaml delete mode 100644 def/compose.module-field.yaml delete mode 100644 def/compose.module.yaml delete mode 100644 def/compose.namespace.yaml delete mode 100644 def/compose.page.yaml delete mode 100644 def/compose.record.yaml delete mode 100644 def/compose.yaml delete mode 100644 def/def-envoy.json delete mode 100644 def/def-rbac.json delete mode 100644 def/def.json delete mode 100644 def/schema/locale.cue delete mode 100644 def/system.apigw-route.yaml delete mode 100644 def/system.application.yaml delete mode 100644 def/system.auth-client.yaml delete mode 100644 def/system.queue.yaml delete mode 100644 def/system.report.yaml delete mode 100644 def/system.role.yaml delete mode 100644 def/system.template.yaml delete mode 100644 def/system.user.yaml delete mode 100644 def/system.yaml diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 515a85a41..2d331978e 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -27,7 +27,8 @@ jobs: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go- - - run: make test.all + - run: make test.codegen +# - run: make test.all test.codegen snapshot-docker: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 785e3ff0d..f26d1378c 100644 --- a/Makefile +++ b/Makefile @@ -132,14 +132,29 @@ watch.test.%: $(FSWATCH) watch.test: watch.test.unit -# codegen: $(PROTOGEN) -codegen: $(CODEGEN) +# See codegen/README.md for details +codegen: $(CUE) $(JSONTPLEXEC) + $(CUE) eval codegen/*.cue --out json -e platform | $(JSONTPLEXEC) -v + +test.codegen: $(CUE) $(JSONTPLEXEC) + $(CUE) eval codegen/*.cue --out json -e platform + +cue.fmt: $(CUE) + $(CUE) fmt -v codegen/*.cue + $(CUE) fmt -v codegen/schema/*.cue + $(CUE) fmt -v app/*.cue + $(CUE) fmt system/*.cue + $(CUE) fmt compose/*.cue + # $(CUE) fmt automation/*.cue + # $(CUE) fmt federation/*.cue + +codegen-legacy: $(CODEGEN) @ $(CODEGEN) -v -watch.codegen: $(CODEGEN) +watch.codegen-legacy: $(CODEGEN) @ $(CODEGEN) -w -v -clean.codegen: +clean.codegen-legacy: rm -f $(CODEGEN) provision: diff --git a/Makefile.inc b/Makefile.inc index d4a3870a2..e6e536561 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -19,7 +19,9 @@ PROTOGEN_GRPC = $(GOPATH)/bin/protoc-gen-go-grpc GIN = $(GOPATH)/bin/gin STATIK = $(GOPATH)/bin/statik MODOUTDATED = $(GOPATH)/bin/go-mod-outdated +CUE = $(GOPATH)/bin/cue CODEGEN = build/codegen +JSONTPLEXEC = build/json-tpl-exec PROTOC = /usr/local/bin/protoc FSWATCH = /usr/local/bin/fswatch @@ -60,6 +62,13 @@ $(MODOUTDATED): $(CODEGEN): $(GO) build -o $@ cmd/codegen/main.go +$(CUE): + $(GOINSTALL) cuelang.org/go/cmd/cue@latest + +$(JSONTPLEXEC): + $(GO) build -o $@ ./codegen/tool clean-tools: rm -f $(BINS) + +# diff --git a/app/app.cue b/app/app.cue index 24d0e9e75..37b1d1142 100644 --- a/app/app.cue +++ b/app/app.cue @@ -1,7 +1,7 @@ package app import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" "github.com/cortezaproject/corteza-server/system" "github.com/cortezaproject/corteza-server/compose" ) diff --git a/codegen/README.md b/codegen/README.md new file mode 100644 index 000000000..d941d6cac --- /dev/null +++ b/codegen/README.md @@ -0,0 +1,81 @@ +# Corteza codegen tools and definitions + +## History + +See [old codegen](../pkh/codegen/README.md). + +## Plans + +Right now, Corteza is migrating its old YAML definitions to CUE. +We are also simplifying all templates by moving as much data manipulation to Cue as possible. + +## Intro + +Codegen tools are based on [cuelang](https://cuelang.org/docs/tutorials/) and golang templates. + +**What you can find here:** + - [codegen definitions in `codegen/`](./) + - [templates in `codegen/asset/templates`](./asset/templates) + - [schemas in `codegen/schema`](./schema) + - [template exec tool in `codegen/tool`](./tool) + +Platform, component and resource definitions (.cue files) can be found in: + - `app` + - `automation` @todo + - `system` + - `compose` + - `federation` @todo + +## Running code generator + +When a definitions or templates are changed all outputs need to be regenerated + +This can be done with the following command +``` +make codegen +``` +Please note that + + +## How does it work? + +### High-level overview + +See [Makefile's `codegen-cue` task](../Makefile) + +1. evaluate codegen instructions (see [platform.cue](./platform.cue)) +2. output instructions as JSON +3. pipe JSON into [template exec tool](./tool) +4. process instructions, load and execute templates and write to output files. + +### Definition structure + +#### Codegen instructions + +Collection of `#codegen` structs with template + payload + output instructions. Template exec tool iterates over collection and creates output from each one. + +#### Platform + +Main entry point that combines all components + + - @todo options + + - @todo REST endpoints (unrelated to specific component) + +#### Component + +Defines component, it's behaviour, RBAC operations and resources + + - @todo REST endpoints (unrelated to specific resources) + +#### Resource + +Defines resource, it's behaviour, types, RBAC operations, translatable keys + + - @todo events + - @todo actions + - @todo errors + - @todo automation functions + - @todo expression types + - @todo REST endpoints + diff --git a/codegen/envoy-rbac.cue b/codegen/envoy-rbac.cue index fc5f1fb49..f7dd1aa9b 100644 --- a/codegen/envoy-rbac.cue +++ b/codegen/envoy-rbac.cue @@ -95,8 +95,8 @@ envoyRBAC: resources: [ for cmp in app.corteza.components for res in cmp.resources if res.locale != _|_ { - importAlias: "\(cmp.ident)Types" - typeConst: "\(importAlias).\(res.expIdent)ResourceTranslationType" + importAlias: "\(cmp.ident)Types" + typeConst: "\(importAlias).\(res.expIdent)ResourceTranslationType" resTrRefFunc: "\(cmp.expIdent)\(res.expIdent)ResourceTranslationReferences" references: [ for p in res.parents {p}, @@ -110,25 +110,25 @@ envoyRBAC: [ // wrapped with additional for loop to trim out templates with empty types list for tpl in [ - for cmp in app.corteza.components { - template: "gocode/envoy/resource_translation_references_$component.go.tpl" - output: "pkg/envoy/resource/resource_translation_references_\(cmp.ident).gen.go" - payload: { - package: "resource" - imports: [ - "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", - ] + for cmp in app.corteza.components { + template: "gocode/envoy/resource_translation_references_$component.go.tpl" + output: "pkg/envoy/resource/resource_translation_references_\(cmp.ident).gen.go" + payload: { + package: "resource" + imports: [ + "\"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"", + ] - resources: [ - for res in cmp.resources if res.locale != _|_ { - resTrRefFunc: "\(cmp.expIdent)\(res.expIdent)ResourceTranslationReferences" - expIdent: res.expIdent - references: [ - for p in res.parents {p}, - ] - }, - ] - } - }, - ] if len(tpl.payload.resources) > 0 {tpl}]+ + resources: [ + for res in cmp.resources if res.locale != _|_ { + resTrRefFunc: "\(cmp.expIdent)\(res.expIdent)ResourceTranslationReferences" + expIdent: res.expIdent + references: [ + for p in res.parents {p}, + ] + }, + ] + } + }, + ] if len(tpl.payload.resources) > 0 {tpl}]+ [] diff --git a/codegen/all.cue b/codegen/platform.cue similarity index 100% rename from codegen/all.cue rename to codegen/platform.cue diff --git a/def/schema/component.cue b/codegen/schema/component.cue similarity index 100% rename from def/schema/component.cue rename to codegen/schema/component.cue diff --git a/codegen/schema/locale.cue b/codegen/schema/locale.cue new file mode 100644 index 000000000..4908f6acb --- /dev/null +++ b/codegen/schema/locale.cue @@ -0,0 +1,56 @@ +package schema + +import ( + "strings" + "list" +) + +#locale: { + resourceExpIdent: #expIdent + + // @todo we need a better name here! + skipSvc: bool | *false + + extended: bool | *false + + resource: { + // @todo merge with RBAC res-ref and move 2 levels lower. + references: [ ...string] | *["ID"] + type: string + const: string | *("\(resourceExpIdent)ResourceTranslationType") + } + + keys: { + [key=_]: #localeKey & { + name: key + _resourceExpIdent: resourceExpIdent + } + } +} + +#localeKey: { + name: #handle + _resourceExpIdent: #expIdent + + path: [...(#ident | {part: #ident, var: bool | *false})] | *([name]) + + expandedPath: [ for p in path { + if (p & {"p": #ident}) != _|_ {p, var: p.var} + if (p & string) != _|_ {"part": p, var: false} + }] + + _suffix: strings.Join([ for p in expandedPath {strings.ToTitle(p.part)}], "") + + struct: string | *("LocaleKey" + _resourceExpIdent + _suffix) + + // As soon as we use vars in the path, + // custom handler must be present + _hasVars: list.Contains([ for p in path {p.var | false}], true) + customHandler: bool | *_hasVars + + if customHandler { + decodeFunc: string | *("decodeTranslations" + _suffix) + encodeFunc: string | *("encodeTranslations" + _suffix) + serviceFunc: string | *("handle" + _resourceExpIdent + _suffix) + } +} diff --git a/def/schema/platform.cue b/codegen/schema/platform.cue similarity index 100% rename from def/schema/platform.cue rename to codegen/schema/platform.cue diff --git a/def/schema/rbac.cue b/codegen/schema/rbac.cue similarity index 97% rename from def/schema/rbac.cue rename to codegen/schema/rbac.cue index 6d85a3146..d09f7976a 100644 --- a/def/schema/rbac.cue +++ b/codegen/schema/rbac.cue @@ -12,7 +12,7 @@ import ( operations: { [key=_]: #rbacOperation & { handle: key - } + } } } @@ -21,7 +21,7 @@ import ( operations: { [key=_]: #rbacOperation & { - handle: key + handle: key _resourceExpIdent: resourceExpIdent } } diff --git a/def/schema/resource.cue b/codegen/schema/resource.cue similarity index 56% rename from def/schema/resource.cue rename to codegen/schema/resource.cue index 5a95e77a0..fb4dccd6f 100644 --- a/def/schema/resource.cue +++ b/codegen/schema/resource.cue @@ -7,7 +7,6 @@ package schema component: #baseHandle | *"component" platform: #baseHandle | *"corteza" - // Fully qualified resource name fqrn: string | *(platform + "::" + component + ":" + handle) @@ -16,11 +15,11 @@ package schema // All parent resources parents: [... #_base & { - // copy field values from #_base - handle: handle, ident: ident, expIdent: expIdent + // copy field values from #_base + handle: handle, ident: ident, expIdent: expIdent refField: #expIdent | *(expIdent + "ID") - param: #ident | *(ident + "ID") + param: #ident | *(ident + "ID") }] // All known RBAC operations for this resource @@ -29,7 +28,7 @@ package schema } locale?: #locale & { - resourceExpIdent: expIdent + resourceExpIdent: expIdent resource: { // @todo can we merge this with RBAC type (FQRN?) type: component + ":" + handle @@ -47,23 +46,23 @@ package schema } //#fields: { -// // Each field can be -// [key=_]: #fields | *({name: key} & #field) +// // Each field can be +// [key=_]: #fields | *({name: key} & #field) //} // //#field: { -// name: #expIdent -// unique: bool | *false +// name: #expIdent +// unique: bool | *false // -// // Golang type (built-in or other) -// type: string | *"string" +// // Golang type (built-in or other) +// type: string | *"string" // -// // System fields, -// system: bool | *false +// // System fields, +// system: bool | *false // -// if name =~ "At$" { -// type: string | *"*time.Time" -// } +// if name =~ "At$" { +// type: string | *"*time.Time" +// } //} //#Operations: { @@ -77,23 +76,23 @@ package schema //} //idField: { -// // Expecting ID field to allways have name ID -// name: "ID" -// unique: true +// // Expecting ID field to allways have name ID +// name: "ID" +// unique: true // -// // Service fields, -// // @todo We might want to have a better name for this -// // service: true +// // Service fields, +// // @todo We might want to have a better name for this +// // service: true // -// // @todo someday we'll replace this with the "ID" type -// type: "uint64" +// // @todo someday we'll replace this with the "ID" type +// type: "uint64" //} // //handleField: { -// // Expecting ID field to allways have name ID -// name: "handle" -// unique: true +// // Expecting ID field to allways have name ID +// name: "handle" +// unique: true // -// // @todo someday we'll replace this with the "ID" type -// type: "string" & #handle +// // @todo someday we'll replace this with the "ID" type +// type: "string" & #handle //} diff --git a/def/schema/shared.cue b/codegen/schema/shared.cue similarity index 63% rename from def/schema/shared.cue rename to codegen/schema/shared.cue index 323d84df0..72b8a7a79 100644 --- a/def/schema/shared.cue +++ b/codegen/schema/shared.cue @@ -13,21 +13,20 @@ import ( // More liberal then identifier, allows underscores and dots #handle: =~"^[A-Za-z][a-zA-Z0-9_\\-\\.]*[a-zA-Z0-9]+$" - // More liberal then identifier, allows underscores and dots #baseHandle: =~"^[a-z][a-z0-9-]*[a-z0-9]+$" #_base: { // lowercase dash-separated words // used to build ident and exported identifiers - handle: #baseHandle | *"base" - _words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1) + handle: #baseHandle | *"base" + _words: strings.Replace(strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1), ".", " ", -1) // lowercased (unexported, golang) identifier - ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1)) + ident: #ident | *strings.ToCamel(strings.Replace(strings.ToTitle(_words), " ", "", -1)) // upercased (exported, golang) identifier - expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1) + expIdent: #expIdent | *strings.Replace(strings.ToTitle(_words), " ", "", -1) ... } diff --git a/codegen/tool/templating.go b/codegen/tool/templating.go index e6822f752..62aa0277e 100644 --- a/codegen/tool/templating.go +++ b/codegen/tool/templating.go @@ -24,6 +24,10 @@ func LoadTemplates(rTpl *template.Template, rootDir string) (*template.Template, pfx := len(cleanRoot) + 1 return rTpl, filepath.Walk(cleanRoot, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || !strings.HasSuffix(path, ".tpl") || err != nil { return err } diff --git a/compose/chart.cue b/compose/chart.cue index 25e87c424..2169c6aaf 100644 --- a/compose/chart.cue +++ b/compose/chart.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) chart: schema.#resource & { diff --git a/compose/component.cue b/compose/component.cue index 0136b3b78..beaad796a 100644 --- a/compose/component.cue +++ b/compose/component.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) component: schema.#component & { diff --git a/compose/module-field.cue b/compose/module-field.cue index 57bb11a78..62e6e6f99 100644 --- a/compose/module-field.cue +++ b/compose/module-field.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) moduleField: schema.#resource & { diff --git a/compose/module.cue b/compose/module.cue index a08099d1d..f628841d2 100644 --- a/compose/module.cue +++ b/compose/module.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) module: schema.#resource & { diff --git a/compose/namespace.cue b/compose/namespace.cue index 434c2dcf5..3a83c205a 100644 --- a/compose/namespace.cue +++ b/compose/namespace.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) namespace: schema.#resource & { diff --git a/compose/page.cue b/compose/page.cue index dfe088f48..21696d6ec 100644 --- a/compose/page.cue +++ b/compose/page.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) page: schema.#resource & { diff --git a/compose/record.cue b/compose/record.cue index dd10bc163..0d34cc3a5 100644 --- a/compose/record.cue +++ b/compose/record.cue @@ -1,7 +1,7 @@ package compose import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) record: schema.#resource & { diff --git a/def/README.md b/def/README.md index 1c63ca499..6733bffa2 100644 --- a/def/README.md +++ b/def/README.md @@ -1 +1,4 @@ -See `pkg/codegen-v3/README.md` +Obsolete and pending removal. + +All automation.* & federation.* yamls need to be converted +to cue and moved to automation/ diff --git a/def/compose.chart.yaml b/def/compose.chart.yaml deleted file mode 100644 index 19ec3882f..000000000 --- a/def/compose.chart.yaml +++ /dev/null @@ -1,17 +0,0 @@ -rbac: - resource: - references: [ namespace, ID ] - - operations: - read: - description: Read chart - update: - description: Update chart - delete: - description: Delete chart - -# locale: -# resource: -# references: [ namespace, ID ] -# keys: -# - name diff --git a/def/compose.module-field.yaml b/def/compose.module-field.yaml deleted file mode 100644 index b22fb9586..000000000 --- a/def/compose.module-field.yaml +++ /dev/null @@ -1,29 +0,0 @@ -rbac: - resource: - references: [ namespace, module, ID ] - - operations: - record.value.read: - canFnName: CanReadRecordValue - description: Read field value on records - record.value.update: - canFnName: CanUpdateRecordValue - description: Update field value on records - -locale: - resource: - references: [ namespace, module, ID ] - - skipSvc: true - keys: - - label - - { name: descriptionView, path: meta.description.view, custom: true, customHandler: descriptionView } - - { name: descriptionEdit, path: meta.description.edit, custom: true, customHandler: descriptionEdit } - - { name: hintView, path: meta.hint.view, custom: true, customHandler: hintView } - - { name: hintEdit, path: meta.hint.edit, custom: true, customHandler: hintEdit } - - { name: validatorError, path: "expression.validator.{{validatorID}}.error", custom: true, customHandler: validatorError } - - { name: optionsOptionTexts, - path: "meta.options.{{value}}.text", - custom: true, - customHandler: optionsOptionTexts - } diff --git a/def/compose.module.yaml b/def/compose.module.yaml deleted file mode 100644 index b3a8a7ffd..000000000 --- a/def/compose.module.yaml +++ /dev/null @@ -1,24 +0,0 @@ -rbac: - resource: - references: [ namespace, ID ] - - operations: - read: - description: Read module - update: - description: Update module - delete: - description: Delete module - - record.create: - description: Create record - records.search: - description: List, search or filter records - -locale: - resource: - references: [ namespace, ID ] - - extended: true - keys: - - name diff --git a/def/compose.namespace.yaml b/def/compose.namespace.yaml deleted file mode 100644 index fa0f355e8..000000000 --- a/def/compose.namespace.yaml +++ /dev/null @@ -1,31 +0,0 @@ -rbac: - operations: - read: - description: Read namespace - update: - description: Update namespace - delete: - description: Delete namespace - manage: - description: Access to namespace admin panel - - module.create: - description: Create module on namespace - modules.search: - description: List, search or filter module on namespace - - chart.create: - description: Create chart on namespace - charts.search: - description: List, search or filter chart on namespace - - page.create: - description: Create page on namespace - pages.search: - description: List, search or filter pages on namespace - -locale: - keys: - - name - - { path: subtitle, field: "Meta.Subtitle" } - - { path: description, field: "Meta.Description" } diff --git a/def/compose.page.yaml b/def/compose.page.yaml deleted file mode 100644 index 5affab111..000000000 --- a/def/compose.page.yaml +++ /dev/null @@ -1,23 +0,0 @@ -rbac: - resource: - references: [ namespace, ID ] - - operations: - read: - description: Read page - update: - description: Update page - delete: - description: Delete page - -locale: - resource: - references: [ namespace, ID ] - - extended: true - keys: - - title - - description - - { name: blockTitle, path: "pageBlock.{{blockID}}.title", custom: true } - - { name: blockDescription, path: "pageBlock.{{blockID}}.description", custom: true } - - { name: blockAutomationButtonlabel, path: "pageBlock.{{blockID}}.button.{{buttonID}}.label", custom: true } diff --git a/def/compose.record.yaml b/def/compose.record.yaml deleted file mode 100644 index a51137b0a..000000000 --- a/def/compose.record.yaml +++ /dev/null @@ -1,11 +0,0 @@ -rbac: - resource: - references: [ namespace, module, ID ] - - operations: - read: - description: Read record - update: - description: Update record - delete: - description: Delete record diff --git a/def/compose.yaml b/def/compose.yaml deleted file mode 100644 index 271a509f2..000000000 --- a/def/compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -rbac: - resource: { references: [] } - - operations: - grant: - description: Manage Compose permissions - - settings.read: - description: Read settings - settings.manage: - description: Manage settings - - namespace.create: - description: Create namespace - namespaces.search: - description: List, search or filter namespaces - - resource-translations.manage: - description: List, search, create, or update resource translations diff --git a/def/def-envoy.json b/def/def-envoy.json deleted file mode 100644 index ee59940d9..000000000 --- a/def/def-envoy.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "boolean", - "enum": [ "false" ], - "title": "Disable envoy support", - "description": "Enabled by default" -} diff --git a/def/def-rbac.json b/def/def-rbac.json deleted file mode 100644 index a6b3ab977..000000000 --- a/def/def-rbac.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "resource": { - "additionalProperties": false, - "type": "object", - "description": "RBAC resource definition", - "properties": { - "prefix": { - "type": "string", - "description": "Resource name", - "pattern": "^[a-z]+:?[a-zA-Z]+(:[a-zA-Z]+)?$" - }, - "references": { - "type": "array", - "title": "Reference components", - "description": "When not explicitly defined it fallbacks to one item array with 'ID'", - "items": { - "oneOf": [ - { - "type": "object", - "properties": { - "field": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - { - "type": "string" - } - ] - } - }, - "attributes": { - "anyOf": [ - { - "type": "boolean", - "enum": [ "true" ], - "title": "Custom implementation of resource attributes", - }, - { - "type": "array", - "title": "List of resource attributes", - "description": "Attributes are used for generating list of contextual roles" - } - ] - } - } - }, - "operations": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^([a-z]+(-[a-z]+)*)(\\.[a-z]+)*$": { - "anyOf": [ - { "type": "boolean", "enum": [ "false" ] }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "canFnName": { - "type": "string" - }, - "description": { - "type": "string" - } - } - } - ] - } - } - } - } -} diff --git a/def/def.json b/def/def.json deleted file mode 100644 index 273fda9f0..000000000 --- a/def/def.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$id": "https://schemas.cortezaproject.org/def.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "imports": { - "type": "array", - "title": "List of go packages to be imported when generating files", - "description": "Attributes are used for generating list of contextual roles" - }, - - "component": { - "type": "string", - "title": "Component of the definition", - "description": "By default, component is taken from the filename of the definition: from start of the filename to the first dot" - }, - "resource": { - "type": "string", - "title": "Resource of the definition", - "description": "By default, resource is taken from the filename of the definition: from the first dot to the end of the filename without extension" - }, - "rbac": { "$ref": "def-rbac.json" }, - "envoy": { "$ref": "def-envoy.json" } - }, - "additionalProperties": false -} diff --git a/def/schema/locale.cue b/def/schema/locale.cue deleted file mode 100644 index 24ece587c..000000000 --- a/def/schema/locale.cue +++ /dev/null @@ -1,58 +0,0 @@ -package schema - -import ( - "strings" - "list" -) - - -#locale: { - resourceExpIdent: #expIdent - - // @todo we need a better name here! - skipSvc: bool | *false - - extended: bool | *false - - resource: { - // @todo merge with RBAC res-ref and move 2 levels lower. - references: [ ...string] | *["ID"] - type: string - const: string | *("\(resourceExpIdent)ResourceTranslationType") - } - - keys: { - [key=_]: #localeKey & { - name: key - _resourceExpIdent: resourceExpIdent - } - } -} - -#localeKey: { - name: #handle - _resourceExpIdent: #expIdent - - path: [...(#ident | { part: #ident, var: bool | *false })] | *([name]) - - expandedPath: [for p in path { - if (p & { "p": #ident }) != _|_ { p, var: p.var } - if (p & string) != _|_ { "part": p, var: false } - }] - - - _suffix: strings.Join([for p in expandedPath { strings.ToTitle(p.part) }], "") - - struct: string | *("LocaleKey" + _resourceExpIdent + _suffix) - - // As soon as we use vars in the path, - // custom handler must be present - _hasVars: list.Contains([for p in path { p.var | false }], true) - customHandler: bool | *_hasVars - - if customHandler { - decodeFunc: string | *("decodeTranslations" + _suffix) - encodeFunc: string | *("encodeTranslations" + _suffix) - serviceFunc: string | *("handle" + _resourceExpIdent + _suffix) - } -} diff --git a/def/system.apigw-route.yaml b/def/system.apigw-route.yaml deleted file mode 100644 index 873dd5973..000000000 --- a/def/system.apigw-route.yaml +++ /dev/null @@ -1,8 +0,0 @@ -rbac: - operations: - read: - description: Read API Gateway route - update: - description: Update API Gateway route - delete: - description: Delete API Gateway route diff --git a/def/system.application.yaml b/def/system.application.yaml deleted file mode 100644 index 9566786a5..000000000 --- a/def/system.application.yaml +++ /dev/null @@ -1,8 +0,0 @@ -rbac: - operations: - read: - description: Read application - update: - description: Update application - delete: - description: Delete application diff --git a/def/system.auth-client.yaml b/def/system.auth-client.yaml deleted file mode 100644 index b656cba51..000000000 --- a/def/system.auth-client.yaml +++ /dev/null @@ -1,10 +0,0 @@ -rbac: - operations: - read: - description: Read authorization client - update: - description: Update authorization client - delete: - description: Delete authorization client - authorize: - description: Authorize authorization client diff --git a/def/system.queue.yaml b/def/system.queue.yaml deleted file mode 100644 index fdd31bec3..000000000 --- a/def/system.queue.yaml +++ /dev/null @@ -1,12 +0,0 @@ -rbac: - operations: - read: - description: Read queue - update: - description: Update queue - delete: - description: Delete queue - queue.read: - description: Read from queue - queue.write: - description: Write to queue diff --git a/def/system.report.yaml b/def/system.report.yaml deleted file mode 100644 index af5accba6..000000000 --- a/def/system.report.yaml +++ /dev/null @@ -1,18 +0,0 @@ -rbac: - operations: - read: - description: Read report - update: - description: Update report - delete: - description: Delete report - run: - description: Run report - -# locale: -# extended: true -# keys: -# - { path: name, field: "Meta.Name" } -# - { path: description, field: "Meta.Description" } -# - { name: block title, path: "block.{{blockID}}.title", custom: true } -# - { name: block description, path: "block.{{blockID}}.description", custom: true } diff --git a/def/system.role.yaml b/def/system.role.yaml deleted file mode 100644 index f262d2f50..000000000 --- a/def/system.role.yaml +++ /dev/null @@ -1,10 +0,0 @@ -rbac: - operations: - read: - description: Read role - update: - description: Update role - delete: - description: Delete role - members.manage: - description: Manage members diff --git a/def/system.template.yaml b/def/system.template.yaml deleted file mode 100644 index 0e0f857f8..000000000 --- a/def/system.template.yaml +++ /dev/null @@ -1,10 +0,0 @@ -rbac: - operations: - read: - description: Read template - update: - description: Update template - delete: - description: Delete template - render: - description: Render template diff --git a/def/system.user.yaml b/def/system.user.yaml deleted file mode 100644 index 3241b5521..000000000 --- a/def/system.user.yaml +++ /dev/null @@ -1,19 +0,0 @@ -rbac: - operations: - read: - description: Read user - update: - description: Update user - delete: - description: Delete user - suspend: - description: Suspemd user - unsuspend: - description: Unsuspend user - email.unmask: - description: Unmask email - name.unmask: - description: Unmask name - impersonate: - description: Impersonate user - diff --git a/def/system.yaml b/def/system.yaml deleted file mode 100644 index b757e5c36..000000000 --- a/def/system.yaml +++ /dev/null @@ -1,64 +0,0 @@ -rbac: - resource: { references: [] } - - operations: - grant: - description: Manage system permissions - - action-log.read: - description: Access to action log - - settings.read: - description: Read system settings - settings.manage: - description: Manage system settings - - auth-client.create: - description: Create auth clients - auth-clients.search: - description: List, search or filter auth clients - - role.create: - description: Create roles - roles.search: - description: List, search or filter roles - - user.create: - description: Create users - users.search: - description: List, search or filter users - - application.create: - description: Create applications - applications.search: - description: List, search or filter auth clients - application.flag.self: - description: Manage private flags for applications - application.flag.global: - description: Manage global flags for applications - - template.create: - description: Create template - templates.search: - description: List, search or filter templates - - report.create: - description: Create report - reports.search: - description: List, search or filter reports - - reminder.assign: - description: Assign reminders - - queue.create: - description: Create messagebus queues - queues.search: - description: List, search or filter messagebus queues - - apigw-route.create: - description: Create API gateway route - apigw-routes.search: - description: List search or filter API gateway routes - - resource-translations.manage: - description: List, search, create, or update resource translations diff --git a/pkg/codegen/README.md b/pkg/codegen/README.md index 61f880733..4bc784076 100644 --- a/pkg/codegen/README.md +++ b/pkg/codegen/README.md @@ -1,5 +1,7 @@ # Code generation package +See [latest codegen version](../../codegen/README.md). + It consumes YAMLs from all known locations and provides them to templates diff --git a/system/apigw-route.cue b/system/apigw-route.cue index f22a4d78a..14f14edb4 100644 --- a/system/apigw-route.cue +++ b/system/apigw-route.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) apigwRoute: schema.#resource & { diff --git a/system/application.cue b/system/application.cue index 6c023ca37..8c5196cc5 100644 --- a/system/application.cue +++ b/system/application.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) application: schema.#resource & { diff --git a/system/auth-client.cue b/system/auth-client.cue index c03e65eae..eaa2853d0 100644 --- a/system/auth-client.cue +++ b/system/auth-client.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) authClient: schema.#resource & { diff --git a/system/component.cue b/system/component.cue index f50a08338..ac462a7d8 100644 --- a/system/component.cue +++ b/system/component.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) component: schema.#component & { diff --git a/system/queue.cue b/system/queue.cue index 22d02e93a..f85aaf2c1 100644 --- a/system/queue.cue +++ b/system/queue.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) queue: schema.#resource & { diff --git a/system/report.cue b/system/report.cue index 5eae70599..53fbbad8f 100644 --- a/system/report.cue +++ b/system/report.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) report: schema.#resource & { diff --git a/system/role.cue b/system/role.cue index 60afb074b..f2d8de63c 100644 --- a/system/role.cue +++ b/system/role.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) role: schema.#resource & { diff --git a/system/template.cue b/system/template.cue index 5d4ed657c..c6f0e460b 100644 --- a/system/template.cue +++ b/system/template.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) template: schema.#resource & { diff --git a/system/user.cue b/system/user.cue index 89b8a357a..4614eb4c0 100644 --- a/system/user.cue +++ b/system/user.cue @@ -1,7 +1,7 @@ package system import ( - "github.com/cortezaproject/corteza-server/def/schema" + "github.com/cortezaproject/corteza-server/codegen/schema" ) user: schema.#resource & { From 74d6eabf94d8820edd4262e2a455dbd40780c402 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Fri, 31 Dec 2021 17:25:14 +0100 Subject: [PATCH 6/7] Various template & def fixes --- .github/workflows/snapshot.yml | 3 +- .../gocode/locale/$component_types.go.tpl | 6 +- codegen/envoy-rbac.cue | 2 +- codegen/locale-service.cue | 3 +- codegen/locale-types.cue | 2 +- codegen/rbac-access_control.cue | 8 +- codegen/rbac-types.cue | 4 +- codegen/schema/locale.cue | 2 - codegen/schema/rbac.cue | 11 +- compose/module-field.cue | 4 +- compose/rest/module.go | 8 +- compose/service/access_control.gen.go | 95 +++--- compose/service/record.go | 14 +- compose/types/locale.gen.go | 22 +- compose/types/rbac.gen.go | 17 +- .../resource_translation_parse.gen.go | 1 - pkg/envoy/store/encoder.go | 4 +- system/service/access_control.gen.go | 276 +++++++++++++++--- system/types/rbac.gen.go | 136 ++++++--- 19 files changed, 443 insertions(+), 175 deletions(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 2d331978e..ab009f526 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -27,8 +27,7 @@ jobs: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-go- - - run: make test.codegen -# - run: make test.all test.codegen + - run: make test.codegen test.all snapshot-docker: runs-on: ubuntu-latest diff --git a/codegen/assets/templates/gocode/locale/$component_types.go.tpl b/codegen/assets/templates/gocode/locale/$component_types.go.tpl index 28a957631..7bdbc0655 100644 --- a/codegen/assets/templates/gocode/locale/$component_types.go.tpl +++ b/codegen/assets/templates/gocode/locale/$component_types.go.tpl @@ -41,7 +41,7 @@ var ( // // This function is auto-generated func (r {{ .expIdent }}) ResourceTranslation() string { - return {{ .expIdent }}ResourceTranslation({{ if .references }}{{ range .references }}r.{{ . }},{{ end }}{{ end }}) + return {{ .expIdent }}ResourceTranslation({{ range .references }}r.{{ .refField }},{{ end }}) } // {{ .expIdent }}ResourceTranslation returns string representation of Locale resource for {{ .expIdent }} @@ -49,12 +49,12 @@ func (r {{ .expIdent }}) ResourceTranslation() string { // Locale resource is in the {{ .type }}/{{- if .references }}...{{ end }} format // // This function is auto-generated -func {{ .expIdent }}ResourceTranslation({{ if .references }}{{ range .references }}{{ . }} uint64,{{ end }}{{ end }}) string { +func {{ .expIdent }}ResourceTranslation({{ range .references }}{{ .refField }} uint64,{{ end }}) string { {{- if .references }} cpts := []interface{{"{}"}}{ {{ .expIdent }}ResourceTranslationType, {{- range .references }} - strconv.FormatUint({{ . }}, 10), + strconv.FormatUint({{ .refField }}, 10), {{- end }} } diff --git a/codegen/envoy-rbac.cue b/codegen/envoy-rbac.cue index f7dd1aa9b..e90892400 100644 --- a/codegen/envoy-rbac.cue +++ b/codegen/envoy-rbac.cue @@ -88,7 +88,7 @@ envoyRBAC: payload: { package: "resource" imports: [ - for cmp in app.corteza.components { + for cmp in app.corteza.components for res in cmp.resources if res.locale != _|_ { "\(cmp.ident)Types \"github.com/cortezaproject/corteza-server/\(cmp.ident)/types\"" }, ] diff --git a/codegen/locale-service.cue b/codegen/locale-service.cue index 3f8810b70..8a25a331e 100644 --- a/codegen/locale-service.cue +++ b/codegen/locale-service.cue @@ -3,7 +3,6 @@ package codegen import ( "github.com/cortezaproject/corteza-server/app" "github.com/cortezaproject/corteza-server/codegen/schema" - "strings" ) localeService: @@ -25,7 +24,7 @@ localeService: expIdent: res.expIdent ident: res.ident - references: [ for field in res.locale.resource.references {strings.ToTitle(field)}] + references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] extended: res.locale.extended diff --git a/codegen/locale-types.cue b/codegen/locale-types.cue index 5cf6fcace..a9e6b8e69 100644 --- a/codegen/locale-types.cue +++ b/codegen/locale-types.cue @@ -23,7 +23,7 @@ localeTypes: const: res.locale.resource.const type: res.locale.resource.type - references: [ for field in res.locale.resource.references {strings.ToTitle(field)}] + references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] extended: res.locale.extended diff --git a/codegen/rbac-access_control.cue b/codegen/rbac-access_control.cue index 82d1151ee..5dd4f810e 100644 --- a/codegen/rbac-access_control.cue +++ b/codegen/rbac-access_control.cue @@ -28,9 +28,7 @@ rbacAccessControl: description: op.description checkFuncName: op.checkFuncName - if len(res.parents) > 0 { - references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] - } + references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] }, for op in cmp.rbac.operations { "op": op.handle @@ -49,9 +47,7 @@ rbacAccessControl: label: res.ident const: "types.\(res.expIdent)ResourceType" funcName: "rbac\(res.expIdent)ResourceValidator" - if len(res.parents) > 0 { - references: [ for p in res.parents {p.refField}, "ID"] - } + references: [ for p in res.parents {p.refField}, "ID"] operations: [ for op in res.rbac.operations {op.handle}] }, { diff --git a/codegen/rbac-types.cue b/codegen/rbac-types.cue index ee1ae8875..a374e65df 100644 --- a/codegen/rbac-types.cue +++ b/codegen/rbac-types.cue @@ -25,9 +25,7 @@ rbacTypes: attFunc: "\(res.expIdent)RbacAttributes" goType: res.expIdent - if len(res.parents) > 0 { - references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] - } + references: [ for p in res.parents {p}, {param: "id", refField: "ID"}] }, { const: "ComponentResourceType" diff --git a/codegen/schema/locale.cue b/codegen/schema/locale.cue index 4908f6acb..69261b822 100644 --- a/codegen/schema/locale.cue +++ b/codegen/schema/locale.cue @@ -14,8 +14,6 @@ import ( extended: bool | *false resource: { - // @todo merge with RBAC res-ref and move 2 levels lower. - references: [ ...string] | *["ID"] type: string const: string | *("\(resourceExpIdent)ResourceTranslationType") } diff --git a/codegen/schema/rbac.cue b/codegen/schema/rbac.cue index d09f7976a..712d007f8 100644 --- a/codegen/schema/rbac.cue +++ b/codegen/schema/rbac.cue @@ -43,14 +43,19 @@ import ( _operation: strings.Replace(strings.Replace(handle, "-", " ", -1), "_", " ", -1) _opSplit: strings.Split(_operation, ".") + _opFlip: [_opSplit[len(_opSplit)-1]] + _opSplit[0:len(_opSplit)-1] _opFinal: strings.Replace(strings.ToTitle(strings.Join(_opFlip, " ")), " ", "", -1) if _resourceExpIdent == _|_ { - checkFuncName: #expIdent | *("Can" + _opFinal) + checkFuncName: #expIdent | *"Can\(_opFinal)" } - if _resourceExpIdent != _|_ { - checkFuncName: #expIdent | *("Can" + _opFinal + _resourceExpIdent) + if len(_opSplit) > 1 && _resourceExpIdent != _|_ { + checkFuncName: #expIdent | *"Can\(_opFinal)On\(_resourceExpIdent)" + } + + if len(_opSplit) <= 1 && _resourceExpIdent != _|_ { + checkFuncName: #expIdent | *"Can\(_opFinal)\(_resourceExpIdent)" } } diff --git a/compose/module-field.cue b/compose/module-field.cue index 62e6e6f99..46ef3b388 100644 --- a/compose/module-field.cue +++ b/compose/module-field.cue @@ -12,8 +12,8 @@ moduleField: schema.#resource & { rbac: { operations: { - "recod.value.read": description: "Read field value on records" - "recod.value.update": description: "Update field value on records" + "record.value.read": description: "Read field value on records" + "record.value.update": description: "Update field value on records" } } diff --git a/compose/rest/module.go b/compose/rest/module.go index d1d84d292..0499762c1 100644 --- a/compose/rest/module.go +++ b/compose/rest/module.go @@ -51,8 +51,8 @@ type ( CanCreateRecordOnModule(context.Context, *types.Module) bool CanReadRecord(context.Context, *types.Record) bool - CanReadRecordValue(context.Context, *types.ModuleField) bool - CanUpdateRecordValue(context.Context, *types.ModuleField) bool + CanReadRecordValueOnModuleField(context.Context, *types.ModuleField) bool + CanUpdateRecordValueOnModuleField(context.Context, *types.ModuleField) bool } ) @@ -196,8 +196,8 @@ func (ctrl Module) makeFieldsPayload(ctx context.Context, m *types.Module) (out out[i] = &moduleFieldPayload{ ModuleField: f, - CanReadRecordValue: ctrl.ac.CanReadRecordValue(ctx, f), - CanUpdateRecordValue: ctrl.ac.CanUpdateRecordValue(ctx, f), + CanReadRecordValue: ctrl.ac.CanReadRecordValueOnModuleField(ctx, f), + CanUpdateRecordValue: ctrl.ac.CanUpdateRecordValueOnModuleField(ctx, f), } } diff --git a/compose/service/access_control.gen.go b/compose/service/access_control.gen.go index ae387c225..523a52e29 100644 --- a/compose/service/access_control.gen.go +++ b/compose/service/access_control.gen.go @@ -97,61 +97,61 @@ func (svc accessControl) List() (out []map[string]string) { { "type": types.ModuleFieldResourceType, "any": types.ModuleFieldRbacResource(0, 0, 0), - "op": "recod.value.read", + "op": "record.value.read", }, { "type": types.ModuleFieldResourceType, "any": types.ModuleFieldRbacResource(0, 0, 0), - "op": "recod.value.update", + "op": "record.value.update", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "read", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "update", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "delete", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "manage", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "module.create", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "modules.search", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "chart.create", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "charts.search", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "page.create", }, { "type": types.NamespaceResourceType, - "any": types.NamespaceRbacResource(), + "any": types.NamespaceRbacResource(0), "op": "pages.search", }, { @@ -329,32 +329,32 @@ func (svc accessControl) CanDeleteModule(ctx context.Context, r *types.Module) b return svc.can(ctx, "delete", r) } -// CanCreateRecordModule checks if current user can create record +// CanCreateRecordOnModule checks if current user can create record // // This function is auto-generated -func (svc accessControl) CanCreateRecordModule(ctx context.Context, r *types.Module) bool { +func (svc accessControl) CanCreateRecordOnModule(ctx context.Context, r *types.Module) bool { return svc.can(ctx, "record.create", r) } -// CanSearchRecordsModule checks if current user can list, search or filter records +// CanSearchRecordsOnModule checks if current user can list, search or filter records // // This function is auto-generated -func (svc accessControl) CanSearchRecordsModule(ctx context.Context, r *types.Module) bool { +func (svc accessControl) CanSearchRecordsOnModule(ctx context.Context, r *types.Module) bool { return svc.can(ctx, "records.search", r) } -// CanReadRecodValueModuleField checks if current user can read field value on records +// CanReadRecordValueOnModuleField checks if current user can read field value on records // // This function is auto-generated -func (svc accessControl) CanReadRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { - return svc.can(ctx, "recod.value.read", r) +func (svc accessControl) CanReadRecordValueOnModuleField(ctx context.Context, r *types.ModuleField) bool { + return svc.can(ctx, "record.value.read", r) } -// CanUpdateRecodValueModuleField checks if current user can update field value on records +// CanUpdateRecordValueOnModuleField checks if current user can update field value on records // // This function is auto-generated -func (svc accessControl) CanUpdateRecodValueModuleField(ctx context.Context, r *types.ModuleField) bool { - return svc.can(ctx, "recod.value.update", r) +func (svc accessControl) CanUpdateRecordValueOnModuleField(ctx context.Context, r *types.ModuleField) bool { + return svc.can(ctx, "record.value.update", r) } // CanReadNamespace checks if current user can read @@ -385,45 +385,45 @@ func (svc accessControl) CanManageNamespace(ctx context.Context, r *types.Namesp return svc.can(ctx, "manage", r) } -// CanCreateModuleNamespace checks if current user can create module on namespace +// CanCreateModuleOnNamespace checks if current user can create module on namespace // // This function is auto-generated -func (svc accessControl) CanCreateModuleNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanCreateModuleOnNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "module.create", r) } -// CanSearchModulesNamespace checks if current user can list, search or filter module on namespace +// CanSearchModulesOnNamespace checks if current user can list, search or filter module on namespace // // This function is auto-generated -func (svc accessControl) CanSearchModulesNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanSearchModulesOnNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "modules.search", r) } -// CanCreateChartNamespace checks if current user can create chart on namespace +// CanCreateChartOnNamespace checks if current user can create chart on namespace // // This function is auto-generated -func (svc accessControl) CanCreateChartNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanCreateChartOnNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "chart.create", r) } -// CanSearchChartsNamespace checks if current user can list, search or filter chart on namespace +// CanSearchChartsOnNamespace checks if current user can list, search or filter chart on namespace // // This function is auto-generated -func (svc accessControl) CanSearchChartsNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanSearchChartsOnNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "charts.search", r) } -// CanCreatePageNamespace checks if current user can create page on namespace +// CanCreatePageOnNamespace checks if current user can create page on namespace // // This function is auto-generated -func (svc accessControl) CanCreatePageNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanCreatePageOnNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "page.create", r) } -// CanSearchPagesNamespace checks if current user can list, search or filter pages on namespace +// CanSearchPagesOnNamespace checks if current user can list, search or filter pages on namespace // // This function is auto-generated -func (svc accessControl) CanSearchPagesNamespace(ctx context.Context, r *types.Namespace) bool { +func (svc accessControl) CanSearchPagesOnNamespace(ctx context.Context, r *types.Namespace) bool { return svc.can(ctx, "pages.search", r) } @@ -562,8 +562,8 @@ func rbacResourceOperations(r string) map[string]bool { } case types.ModuleFieldResourceType: return map[string]bool{ - "recod.value.read": true, - "recod.value.update": true, + "record.value.read": true, + "record.value.update": true, } case types.NamespaceResourceType: return map[string]bool{ @@ -758,6 +758,29 @@ func rbacNamespaceResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.NamespaceResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for namespace resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } diff --git a/compose/service/record.go b/compose/service/record.go index 2d2c0de81..cec0c5055 100644 --- a/compose/service/record.go +++ b/compose/service/record.go @@ -62,8 +62,8 @@ type ( } recordValueAccessController interface { - CanReadRecordValue(context.Context, *types.ModuleField) bool - CanUpdateRecordValue(context.Context, *types.ModuleField) bool + CanReadRecordValueOnModuleField(context.Context, *types.ModuleField) bool + CanUpdateRecordValueOnModuleField(context.Context, *types.ModuleField) bool } recordAccessController interface { @@ -637,7 +637,7 @@ func RecordValueUpdateOpCheck(ctx context.Context, ac recordValueAccessControlle return nil } - if v.IsUpdated() && !ac.CanUpdateRecordValue(ctx, f) { + if v.IsUpdated() && !ac.CanUpdateRecordValueOnModuleField(ctx, f) { rve.Push(types.RecordValueError{Kind: "updateDenied", Meta: map[string]interface{}{"field": v.Name, "value": v.Value}}) } @@ -908,7 +908,7 @@ func (svc record) procUpdate(ctx context.Context, invokerID uint64, m *types.Mod upd = RecordUpdateOwner(invokerID, upd, old) upd.Values = old.Values.Merge(m.Fields, upd.Values, func(f *types.ModuleField) bool { - return svc.ac.CanUpdateRecordValue(ctx, m.Fields.FindByName(f.Name)) + return svc.ac.CanUpdateRecordValueOnModuleField(ctx, m.Fields.FindByName(f.Name)) }) if rve = RecordValueUpdateOpCheck(ctx, svc.ac, m, upd.Values); !rve.IsValid() { @@ -1095,7 +1095,7 @@ func (svc record) Organize(ctx context.Context, namespaceID, moduleID, recordID return fmt.Errorf("cannot reorder on multi-value field %q", posField) } - if !svc.ac.CanUpdateRecordValue(ctx, sf) { + if !svc.ac.CanUpdateRecordValueOnModuleField(ctx, sf) { return RecordErrNotAllowedToUpdate() } @@ -1119,7 +1119,7 @@ func (svc record) Organize(ctx context.Context, namespaceID, moduleID, recordID return fmt.Errorf("cannot update multi-value field %q", posField) } - if !svc.ac.CanUpdateRecordValue(ctx, vf) { + if !svc.ac.CanUpdateRecordValueOnModuleField(ctx, vf) { return RecordErrNotAllowedToUpdate() } @@ -1404,7 +1404,7 @@ func ComposeRecordFilterAC(ctx context.Context, ac recordValueAccessController, ) for _, f := range m.Fields { - readableFields[f.Name] = ac.CanReadRecordValue(ctx, f) + readableFields[f.Name] = ac.CanReadRecordValueOnModuleField(ctx, f) } for _, r := range rr { diff --git a/compose/types/locale.gen.go b/compose/types/locale.gen.go index d267f6df4..97fa977f0 100644 --- a/compose/types/locale.gen.go +++ b/compose/types/locale.gen.go @@ -55,7 +55,7 @@ var ( // // This function is auto-generated func (r Module) ResourceTranslation() string { - return ModuleResourceTranslation(r.ID) + return ModuleResourceTranslation(r.NamespaceID, r.ID) } // ModuleResourceTranslation returns string representation of Locale resource for Module @@ -63,9 +63,10 @@ func (r Module) ResourceTranslation() string { // Locale resource is in the compose:module/... format // // This function is auto-generated -func ModuleResourceTranslation(ID uint64) string { +func ModuleResourceTranslation(NamespaceID uint64, ID uint64) string { cpts := []interface{}{ ModuleResourceTranslationType, + strconv.FormatUint(NamespaceID, 10), strconv.FormatUint(ID, 10), } @@ -73,7 +74,7 @@ func ModuleResourceTranslation(ID uint64) string { } func ModuleResourceTranslationTpl() string { - return "%s/%s" + return "%s/%s/%s" } func (r *Module) DecodeTranslations(tt locale.ResourceTranslationIndex) { @@ -104,7 +105,7 @@ func (r *Module) EncodeTranslations() (out locale.ResourceTranslationSet) { // // This function is auto-generated func (r ModuleField) ResourceTranslation() string { - return ModuleFieldResourceTranslation(r.ID) + return ModuleFieldResourceTranslation(r.NamespaceID, r.ModuleID, r.ID) } // ModuleFieldResourceTranslation returns string representation of Locale resource for ModuleField @@ -112,9 +113,11 @@ func (r ModuleField) ResourceTranslation() string { // Locale resource is in the compose:module-field/... format // // This function is auto-generated -func ModuleFieldResourceTranslation(ID uint64) string { +func ModuleFieldResourceTranslation(NamespaceID uint64, ModuleID uint64, ID uint64) string { cpts := []interface{}{ ModuleFieldResourceTranslationType, + strconv.FormatUint(NamespaceID, 10), + strconv.FormatUint(ModuleID, 10), strconv.FormatUint(ID, 10), } @@ -122,7 +125,7 @@ func ModuleFieldResourceTranslation(ID uint64) string { } func ModuleFieldResourceTranslationTpl() string { - return "%s/%s" + return "%s/%s/%s/%s" } func (r *ModuleField) DecodeTranslations(tt locale.ResourceTranslationIndex) { @@ -251,7 +254,7 @@ func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) { // // This function is auto-generated func (r Page) ResourceTranslation() string { - return PageResourceTranslation(r.ID) + return PageResourceTranslation(r.NamespaceID, r.ID) } // PageResourceTranslation returns string representation of Locale resource for Page @@ -259,9 +262,10 @@ func (r Page) ResourceTranslation() string { // Locale resource is in the compose:page/... format // // This function is auto-generated -func PageResourceTranslation(ID uint64) string { +func PageResourceTranslation(NamespaceID uint64, ID uint64) string { cpts := []interface{}{ PageResourceTranslationType, + strconv.FormatUint(NamespaceID, 10), strconv.FormatUint(ID, 10), } @@ -269,7 +273,7 @@ func PageResourceTranslation(ID uint64) string { } func PageResourceTranslationTpl() string { - return "%s/%s" + return "%s/%s/%s" } func (r *Page) DecodeTranslations(tt locale.ResourceTranslationIndex) { diff --git a/compose/types/rbac.gen.go b/compose/types/rbac.gen.go index 98f15e2a4..4198023a2 100644 --- a/compose/types/rbac.gen.go +++ b/compose/types/rbac.gen.go @@ -153,21 +153,28 @@ func ModuleFieldRbacResourceTpl() string { // // This function is auto-generated func (r Namespace) RbacResource() string { - return NamespaceRbacResource() + return NamespaceRbacResource(r.ID) } // NamespaceRbacResource returns string representation of RBAC resource for Namespace // -// RBAC resource is in the corteza::compose:namespace/ format +// RBAC resource is in the corteza::compose:namespace/... format // // This function is auto-generated -func NamespaceRbacResource() string { - return NamespaceResourceType + "/" +func NamespaceRbacResource(id uint64) string { + cpts := []interface{}{NamespaceResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(NamespaceRbacResourceTpl(), cpts...) } func NamespaceRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Page by calling PageRbacResource fn diff --git a/pkg/envoy/resource/resource_translation_parse.gen.go b/pkg/envoy/resource/resource_translation_parse.gen.go index d912071c3..12bd1f611 100644 --- a/pkg/envoy/resource/resource_translation_parse.gen.go +++ b/pkg/envoy/resource/resource_translation_parse.gen.go @@ -9,7 +9,6 @@ package resource import ( "fmt" composeTypes "github.com/cortezaproject/corteza-server/compose/types" - systemTypes "github.com/cortezaproject/corteza-server/system/types" "strings" ) diff --git a/pkg/envoy/store/encoder.go b/pkg/envoy/store/encoder.go index 344c41249..299f31a88 100644 --- a/pkg/envoy/store/encoder.go +++ b/pkg/envoy/store/encoder.go @@ -49,8 +49,8 @@ type ( composeRecordAccessController } composeRecordValueAccessController interface { - CanReadRecordValue(context.Context, *types.ModuleField) bool - CanUpdateRecordValue(context.Context, *types.ModuleField) bool + CanReadRecordValueOnModuleField(context.Context, *types.ModuleField) bool + CanUpdateRecordValueOnModuleField(context.Context, *types.ModuleField) bool } composeRecordAccessController interface { diff --git a/system/service/access_control.gen.go b/system/service/access_control.gen.go index d5fc40d1e..6978d8e3f 100644 --- a/system/service/access_control.gen.go +++ b/system/service/access_control.gen.go @@ -56,182 +56,182 @@ func (svc accessControl) List() (out []map[string]string) { def := []map[string]string{ { "type": types.ApigwRouteResourceType, - "any": types.ApigwRouteRbacResource(), + "any": types.ApigwRouteRbacResource(0), "op": "read", }, { "type": types.ApigwRouteResourceType, - "any": types.ApigwRouteRbacResource(), + "any": types.ApigwRouteRbacResource(0), "op": "update", }, { "type": types.ApigwRouteResourceType, - "any": types.ApigwRouteRbacResource(), + "any": types.ApigwRouteRbacResource(0), "op": "delete", }, { "type": types.ApplicationResourceType, - "any": types.ApplicationRbacResource(), + "any": types.ApplicationRbacResource(0), "op": "read", }, { "type": types.ApplicationResourceType, - "any": types.ApplicationRbacResource(), + "any": types.ApplicationRbacResource(0), "op": "update", }, { "type": types.ApplicationResourceType, - "any": types.ApplicationRbacResource(), + "any": types.ApplicationRbacResource(0), "op": "delete", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(), + "any": types.AuthClientRbacResource(0), "op": "read", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(), + "any": types.AuthClientRbacResource(0), "op": "update", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(), + "any": types.AuthClientRbacResource(0), "op": "delete", }, { "type": types.AuthClientResourceType, - "any": types.AuthClientRbacResource(), + "any": types.AuthClientRbacResource(0), "op": "authorize", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(), + "any": types.QueueRbacResource(0), "op": "render", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(), + "any": types.QueueRbacResource(0), "op": "read", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(), + "any": types.QueueRbacResource(0), "op": "update", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(), + "any": types.QueueRbacResource(0), "op": "delete", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(), + "any": types.QueueRbacResource(0), "op": "queue.read", }, { "type": types.QueueResourceType, - "any": types.QueueRbacResource(), + "any": types.QueueRbacResource(0), "op": "queue.write", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(), + "any": types.ReportRbacResource(0), "op": "read", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(), + "any": types.ReportRbacResource(0), "op": "update", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(), + "any": types.ReportRbacResource(0), "op": "delete", }, { "type": types.ReportResourceType, - "any": types.ReportRbacResource(), + "any": types.ReportRbacResource(0), "op": "run", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(), + "any": types.RoleRbacResource(0), "op": "read", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(), + "any": types.RoleRbacResource(0), "op": "update", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(), + "any": types.RoleRbacResource(0), "op": "delete", }, { "type": types.RoleResourceType, - "any": types.RoleRbacResource(), + "any": types.RoleRbacResource(0), "op": "members.manage", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(), + "any": types.TemplateRbacResource(0), "op": "read", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(), + "any": types.TemplateRbacResource(0), "op": "update", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(), + "any": types.TemplateRbacResource(0), "op": "delete", }, { "type": types.TemplateResourceType, - "any": types.TemplateRbacResource(), + "any": types.TemplateRbacResource(0), "op": "render", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "read", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "update", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "delete", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "suspend", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "unsuspend", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "email.unmask", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "name.unmask", }, { "type": types.UserResourceType, - "any": types.UserRbacResource(), + "any": types.UserRbacResource(0), "op": "impersonate", }, { @@ -525,17 +525,17 @@ func (svc accessControl) CanDeleteQueue(ctx context.Context, r *types.Queue) boo return svc.can(ctx, "delete", r) } -// CanReadQueueQueue checks if current user can read from queue +// CanReadQueueOnQueue checks if current user can read from queue // // This function is auto-generated -func (svc accessControl) CanReadQueueQueue(ctx context.Context, r *types.Queue) bool { +func (svc accessControl) CanReadQueueOnQueue(ctx context.Context, r *types.Queue) bool { return svc.can(ctx, "queue.read", r) } -// CanWriteQueueQueue checks if current user can write to queue +// CanWriteQueueOnQueue checks if current user can write to queue // // This function is auto-generated -func (svc accessControl) CanWriteQueueQueue(ctx context.Context, r *types.Queue) bool { +func (svc accessControl) CanWriteQueueOnQueue(ctx context.Context, r *types.Queue) bool { return svc.can(ctx, "queue.write", r) } @@ -588,10 +588,10 @@ func (svc accessControl) CanDeleteRole(ctx context.Context, r *types.Role) bool return svc.can(ctx, "delete", r) } -// CanManageMembersRole checks if current user can manage members +// CanManageMembersOnRole checks if current user can manage members // // This function is auto-generated -func (svc accessControl) CanManageMembersRole(ctx context.Context, r *types.Role) bool { +func (svc accessControl) CanManageMembersOnRole(ctx context.Context, r *types.Role) bool { return svc.can(ctx, "members.manage", r) } @@ -658,17 +658,17 @@ func (svc accessControl) CanUnsuspendUser(ctx context.Context, r *types.User) bo return svc.can(ctx, "unsuspend", r) } -// CanUnmaskEmailUser checks if current user can unmask email +// CanUnmaskEmailOnUser checks if current user can unmask email // // This function is auto-generated -func (svc accessControl) CanUnmaskEmailUser(ctx context.Context, r *types.User) bool { +func (svc accessControl) CanUnmaskEmailOnUser(ctx context.Context, r *types.User) bool { return svc.can(ctx, "email.unmask", r) } -// CanUnmaskNameUser checks if current user can unmask name +// CanUnmaskNameOnUser checks if current user can unmask name // // This function is auto-generated -func (svc accessControl) CanUnmaskNameUser(ctx context.Context, r *types.User) bool { +func (svc accessControl) CanUnmaskNameOnUser(ctx context.Context, r *types.User) bool { return svc.can(ctx, "name.unmask", r) } @@ -1014,6 +1014,29 @@ func rbacApigwRouteResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.ApigwRouteResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for apigwRoute resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1035,6 +1058,29 @@ func rbacApplicationResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.ApplicationResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for application resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1056,6 +1102,29 @@ func rbacAuthClientResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.AuthClientResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for authClient resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1077,6 +1146,29 @@ func rbacQueueResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.QueueResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for queue resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1098,6 +1190,29 @@ func rbacReportResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.ReportResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for report resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1119,6 +1234,29 @@ func rbacRoleResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.RoleResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for role resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1140,6 +1278,29 @@ func rbacTemplateResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.TemplateResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for template resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } @@ -1161,6 +1322,29 @@ func rbacUserResourceValidator(r string, oo ...string) error { } } + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.UserResourceType):], sep), sep) + prc = []string{ + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for user resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } return nil } diff --git a/system/types/rbac.gen.go b/system/types/rbac.gen.go index ccb3413e5..e6a57df1e 100644 --- a/system/types/rbac.gen.go +++ b/system/types/rbac.gen.go @@ -41,21 +41,28 @@ const ( // // This function is auto-generated func (r ApigwRoute) RbacResource() string { - return ApigwRouteRbacResource() + return ApigwRouteRbacResource(r.ID) } // ApigwRouteRbacResource returns string representation of RBAC resource for ApigwRoute // -// RBAC resource is in the corteza::system:apigw-route/ format +// RBAC resource is in the corteza::system:apigw-route/... format // // This function is auto-generated -func ApigwRouteRbacResource() string { - return ApigwRouteResourceType + "/" +func ApigwRouteRbacResource(id uint64) string { + cpts := []interface{}{ApigwRouteResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(ApigwRouteRbacResourceTpl(), cpts...) } func ApigwRouteRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Application by calling ApplicationRbacResource fn @@ -64,21 +71,28 @@ func ApigwRouteRbacResourceTpl() string { // // This function is auto-generated func (r Application) RbacResource() string { - return ApplicationRbacResource() + return ApplicationRbacResource(r.ID) } // ApplicationRbacResource returns string representation of RBAC resource for Application // -// RBAC resource is in the corteza::system:application/ format +// RBAC resource is in the corteza::system:application/... format // // This function is auto-generated -func ApplicationRbacResource() string { - return ApplicationResourceType + "/" +func ApplicationRbacResource(id uint64) string { + cpts := []interface{}{ApplicationResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(ApplicationRbacResourceTpl(), cpts...) } func ApplicationRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for AuthClient by calling AuthClientRbacResource fn @@ -87,21 +101,28 @@ func ApplicationRbacResourceTpl() string { // // This function is auto-generated func (r AuthClient) RbacResource() string { - return AuthClientRbacResource() + return AuthClientRbacResource(r.ID) } // AuthClientRbacResource returns string representation of RBAC resource for AuthClient // -// RBAC resource is in the corteza::system:auth-client/ format +// RBAC resource is in the corteza::system:auth-client/... format // // This function is auto-generated -func AuthClientRbacResource() string { - return AuthClientResourceType + "/" +func AuthClientRbacResource(id uint64) string { + cpts := []interface{}{AuthClientResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(AuthClientRbacResourceTpl(), cpts...) } func AuthClientRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Queue by calling QueueRbacResource fn @@ -110,21 +131,28 @@ func AuthClientRbacResourceTpl() string { // // This function is auto-generated func (r Queue) RbacResource() string { - return QueueRbacResource() + return QueueRbacResource(r.ID) } // QueueRbacResource returns string representation of RBAC resource for Queue // -// RBAC resource is in the corteza::system:queue/ format +// RBAC resource is in the corteza::system:queue/... format // // This function is auto-generated -func QueueRbacResource() string { - return QueueResourceType + "/" +func QueueRbacResource(id uint64) string { + cpts := []interface{}{QueueResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(QueueRbacResourceTpl(), cpts...) } func QueueRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Report by calling ReportRbacResource fn @@ -133,21 +161,28 @@ func QueueRbacResourceTpl() string { // // This function is auto-generated func (r Report) RbacResource() string { - return ReportRbacResource() + return ReportRbacResource(r.ID) } // ReportRbacResource returns string representation of RBAC resource for Report // -// RBAC resource is in the corteza::system:report/ format +// RBAC resource is in the corteza::system:report/... format // // This function is auto-generated -func ReportRbacResource() string { - return ReportResourceType + "/" +func ReportRbacResource(id uint64) string { + cpts := []interface{}{ReportResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(ReportRbacResourceTpl(), cpts...) } func ReportRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Role by calling RoleRbacResource fn @@ -156,21 +191,28 @@ func ReportRbacResourceTpl() string { // // This function is auto-generated func (r Role) RbacResource() string { - return RoleRbacResource() + return RoleRbacResource(r.ID) } // RoleRbacResource returns string representation of RBAC resource for Role // -// RBAC resource is in the corteza::system:role/ format +// RBAC resource is in the corteza::system:role/... format // // This function is auto-generated -func RoleRbacResource() string { - return RoleResourceType + "/" +func RoleRbacResource(id uint64) string { + cpts := []interface{}{RoleResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(RoleRbacResourceTpl(), cpts...) } func RoleRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Template by calling TemplateRbacResource fn @@ -179,21 +221,28 @@ func RoleRbacResourceTpl() string { // // This function is auto-generated func (r Template) RbacResource() string { - return TemplateRbacResource() + return TemplateRbacResource(r.ID) } // TemplateRbacResource returns string representation of RBAC resource for Template // -// RBAC resource is in the corteza::system:template/ format +// RBAC resource is in the corteza::system:template/... format // // This function is auto-generated -func TemplateRbacResource() string { - return TemplateResourceType + "/" +func TemplateRbacResource(id uint64) string { + cpts := []interface{}{TemplateResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(TemplateRbacResourceTpl(), cpts...) } func TemplateRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for User by calling UserRbacResource fn @@ -202,21 +251,28 @@ func TemplateRbacResourceTpl() string { // // This function is auto-generated func (r User) RbacResource() string { - return UserRbacResource() + return UserRbacResource(r.ID) } // UserRbacResource returns string representation of RBAC resource for User // -// RBAC resource is in the corteza::system:user/ format +// RBAC resource is in the corteza::system:user/... format // // This function is auto-generated -func UserRbacResource() string { - return UserResourceType + "/" +func UserRbacResource(id uint64) string { + cpts := []interface{}{UserResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(UserRbacResourceTpl(), cpts...) } func UserRbacResourceTpl() string { - return "%s" + return "%s/%s" } // RbacResource returns string representation of RBAC resource for Component by calling ComponentRbacResource fn From 0ea543b2a2e2625c9efa7b8817ff10d196ccb4ba Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Wed, 26 Jan 2022 17:18:34 +0100 Subject: [PATCH 7/7] Migrate codegen for fed&automation, remove old def dir --- app/app.cue | 4 + automation/component.cue | 22 ++ automation/service/access_control.gen.go | 56 ++--- automation/types/rbac.gen.go | 11 +- automation/workflow.cue | 19 ++ def/README.md | 4 - def/automation.workflow.yaml | 23 -- def/automation.yaml | 21 -- def/federation.exposed-module.yaml | 9 - def/federation.node.yaml | 8 - def/federation.shared-module.yaml | 9 - def/federation.yaml | 19 -- federation/component.cue | 24 ++ federation/exposed-module.cue | 17 ++ federation/node.cue | 14 ++ federation/service/access_control.gen.go | 210 +++++++++--------- federation/shared-module.cue | 17 ++ federation/types/rbac.gen.go | 77 +++---- .../rbac_references_automation.gen.go | 4 - .../rbac_references_federation.gen.go | 56 +++++ pkg/envoy/resource/rbac_rules_parse.gen.go | 54 +++++ 21 files changed, 402 insertions(+), 276 deletions(-) create mode 100644 automation/component.cue create mode 100644 automation/workflow.cue delete mode 100644 def/README.md delete mode 100644 def/automation.workflow.yaml delete mode 100644 def/automation.yaml delete mode 100644 def/federation.exposed-module.yaml delete mode 100644 def/federation.node.yaml delete mode 100644 def/federation.shared-module.yaml delete mode 100644 def/federation.yaml create mode 100644 federation/component.cue create mode 100644 federation/exposed-module.cue create mode 100644 federation/node.cue create mode 100644 federation/shared-module.cue create mode 100644 pkg/envoy/resource/rbac_references_federation.gen.go diff --git a/app/app.cue b/app/app.cue index 37b1d1142..36765a6c9 100644 --- a/app/app.cue +++ b/app/app.cue @@ -4,6 +4,8 @@ import ( "github.com/cortezaproject/corteza-server/codegen/schema" "github.com/cortezaproject/corteza-server/system" "github.com/cortezaproject/corteza-server/compose" + "github.com/cortezaproject/corteza-server/automation" + "github.com/cortezaproject/corteza-server/federation" ) corteza: schema.#platform & { @@ -12,5 +14,7 @@ corteza: schema.#platform & { components: [ system.component, compose.component, + automation.component, + federation.component, ] } diff --git a/automation/component.cue b/automation/component.cue new file mode 100644 index 000000000..02f2eb66a --- /dev/null +++ b/automation/component.cue @@ -0,0 +1,22 @@ +package automation + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +component: schema.#component & { + handle: "automation" + + resources: { + "workflow": workflow + } + + rbac: operations: { + "grant": description: "Manage automation permissions" + "workflow.create": description: "Create workflows" + "triggers.search": description: "List, search or filter triggers" + "sessions.search": description: "List, search or filter sessions" + "workflows.search": description: "List, search or filter workflows" + "resource-translations.manage": description: "List, search, create, or update resource translations" + } +} diff --git a/automation/service/access_control.gen.go b/automation/service/access_control.gen.go index a44b281da..b7c8eb411 100644 --- a/automation/service/access_control.gen.go +++ b/automation/service/access_control.gen.go @@ -6,10 +6,6 @@ package service // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - automation.workflow.yaml -// - automation.yaml - import ( "context" "fmt" @@ -249,42 +245,48 @@ func (svc accessControl) CanManageSessionsOnWorkflow(ctx context.Context, r *typ // // This function is auto-generated func (svc accessControl) CanGrant(ctx context.Context) bool { - return svc.can(ctx, "grant", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "grant", r) } // CanCreateWorkflow checks if current user can create workflows // // This function is auto-generated func (svc accessControl) CanCreateWorkflow(ctx context.Context) bool { - return svc.can(ctx, "workflow.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "workflow.create", r) } // CanSearchTriggers checks if current user can list, search or filter triggers // // This function is auto-generated func (svc accessControl) CanSearchTriggers(ctx context.Context) bool { - return svc.can(ctx, "triggers.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "triggers.search", r) } // CanSearchSessions checks if current user can list, search or filter sessions // // This function is auto-generated func (svc accessControl) CanSearchSessions(ctx context.Context) bool { - return svc.can(ctx, "sessions.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "sessions.search", r) } // CanSearchWorkflows checks if current user can list, search or filter workflows // // This function is auto-generated func (svc accessControl) CanSearchWorkflows(ctx context.Context) bool { - return svc.can(ctx, "workflows.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "workflows.search", r) } // CanManageResourceTranslations checks if current user can list, search, create, or update resource translations // // This function is auto-generated func (svc accessControl) CanManageResourceTranslations(ctx context.Context) bool { - return svc.can(ctx, "resource-translations.manage", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "resource-translations.manage", r) } // rbacResourceValidator validates known component's resource by routing it to the appropriate validator @@ -330,24 +332,24 @@ func rbacResourceOperations(r string) map[string]bool { return nil } -// rbacWorkflowResourceValidator checks validity of rbac resource and operations +// rbacWorkflowResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacWorkflowResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for automation Workflow resource", o) - } - } - if !strings.HasPrefix(r, types.WorkflowResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for workflow resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.WorkflowResourceType):], sep), sep) @@ -363,7 +365,7 @@ func rbacWorkflowResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Workflow", i) + return fmt.Errorf("invalid path wildcard level (%d) for workflow resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -374,23 +376,23 @@ func rbacWorkflowResourceValidator(r string, oo ...string) error { return nil } -// rbacComponentResourceValidator checks validity of rbac resource and operations +// rbacComponentResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacComponentResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for automation resource", o) - } - } - if !strings.HasPrefix(r, types.ComponentResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for automation component resource", o) + } + } + return nil } diff --git a/automation/types/rbac.gen.go b/automation/types/rbac.gen.go index 67b46ebcd..9dabe3f2c 100644 --- a/automation/types/rbac.gen.go +++ b/automation/types/rbac.gen.go @@ -6,10 +6,6 @@ package types // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - automation.workflow.yaml -// - automation.yaml - import ( "fmt" "strconv" @@ -22,6 +18,11 @@ type ( Component struct{} ) +var ( + _ = fmt.Printf + _ = strconv.FormatUint +) + const ( WorkflowResourceType = "corteza::automation:workflow" ComponentResourceType = "corteza::automation" @@ -53,7 +54,6 @@ func WorkflowRbacResource(id uint64) string { } -// @todo template func WorkflowRbacResourceTpl() string { return "%s/%s" } @@ -77,7 +77,6 @@ func ComponentRbacResource() string { } -// @todo template func ComponentRbacResourceTpl() string { return "%s" } diff --git a/automation/workflow.cue b/automation/workflow.cue new file mode 100644 index 000000000..b8dfd40cc --- /dev/null +++ b/automation/workflow.cue @@ -0,0 +1,19 @@ +package automation + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +workflow: schema.#resource & { + rbac: { + operations: { + "read": description: "Read workflow" + "update": description: "Update workflow" + "delete": description: "Delete workflow" + "undelete": description: "Undelete workflow" + "execute": description: "Execute workflow" + "triggers.manage": description: "Manage workflow triggers" + "sessions.manage": description: "Manage workflow sessions" + } + } +} diff --git a/def/README.md b/def/README.md deleted file mode 100644 index 6733bffa2..000000000 --- a/def/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Obsolete and pending removal. - -All automation.* & federation.* yamls need to be converted -to cue and moved to automation/ diff --git a/def/automation.workflow.yaml b/def/automation.workflow.yaml deleted file mode 100644 index 83cb29328..000000000 --- a/def/automation.workflow.yaml +++ /dev/null @@ -1,23 +0,0 @@ -rbac: - operations: - read: - description: Read workflow - update: - description: Update workflow - delete: - description: Delete workflow - undelete: - description: Undelete workflow - execute: - description: Execute workflow - triggers.manage: - description: Manage workflow triggers - sessions.manage: - description: Manage workflow sessions - -# locale: -# resource: -# references: [ ID ] -# keys: -# - { path: name, field: "Meta.Name" } -# - { path: description, field: "Meta.Description" } diff --git a/def/automation.yaml b/def/automation.yaml deleted file mode 100644 index 94c8974b9..000000000 --- a/def/automation.yaml +++ /dev/null @@ -1,21 +0,0 @@ -rbac: - resource: { references: [] } - - operations: - grant: - description: Manage automation permissions - - workflow.create: - description: Create workflows - - triggers.search: - description: List, search or filter triggers - - sessions.search: - description: List, search or filter sessions - - workflows.search: - description: List, search or filter workflows - - resource-translations.manage: - description: List, search, create, or update resource translations diff --git a/def/federation.exposed-module.yaml b/def/federation.exposed-module.yaml deleted file mode 100644 index 7dff05e90..000000000 --- a/def/federation.exposed-module.yaml +++ /dev/null @@ -1,9 +0,0 @@ -rbac: - resource: - references: [ node, ID ] - - operations: - manage: - description: Manage shared module - -envoy: false diff --git a/def/federation.node.yaml b/def/federation.node.yaml deleted file mode 100644 index bf46d9edc..000000000 --- a/def/federation.node.yaml +++ /dev/null @@ -1,8 +0,0 @@ -rbac: - operations: - manage: - description: Manage federation node - module.create: - description: Create shared module - -envoy: false diff --git a/def/federation.shared-module.yaml b/def/federation.shared-module.yaml deleted file mode 100644 index 9ecb0ad40..000000000 --- a/def/federation.shared-module.yaml +++ /dev/null @@ -1,9 +0,0 @@ -rbac: - resource: - references: [ node, ID ] - - operations: - map: - description: Map shared module - -envoy: false diff --git a/def/federation.yaml b/def/federation.yaml deleted file mode 100644 index 5064f5a5b..000000000 --- a/def/federation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -rbac: - resource: { references: [] } - operations: - grant: - description: Manage federation permissions - pair: - description: Pair federation nodes - - settings.read: - description: Read settings - settings.manage: - description: Manage settings - - node.create: - description: Create new federation node - nodes.search: - description: List, search or filter federation nodes - -envoy: false diff --git a/federation/component.cue b/federation/component.cue new file mode 100644 index 000000000..c148788da --- /dev/null +++ b/federation/component.cue @@ -0,0 +1,24 @@ +package federation + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +component: schema.#component & { + handle: "federation" + + resources: { + "node": node + "exposed-module": exposedModule + "shared-module": sharedModule + } + + rbac: operations: { + "grant": description: "Manage federation permissions" + "pair": description: "Pair federation nodes" + "settings.read": description: "Read settings" + "settings.manage": description: "Manage settings" + "node.create": description: "Create new federation node" + "nodes.search": description: "List, search or filter federation nodes" + } +} diff --git a/federation/exposed-module.cue b/federation/exposed-module.cue new file mode 100644 index 000000000..07d0d3626 --- /dev/null +++ b/federation/exposed-module.cue @@ -0,0 +1,17 @@ +package federation + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +exposedModule: schema.#resource & { + parents: [ + {handle: "node"}, + ] + + rbac: { + operations: { + "manage": description: "Manage exposed module module" + } + } +} diff --git a/federation/node.cue b/federation/node.cue new file mode 100644 index 000000000..f82269b61 --- /dev/null +++ b/federation/node.cue @@ -0,0 +1,14 @@ +package federation + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +node: schema.#resource & { + rbac: { + operations: { + "manage": description: "Manage federation node" + "module.create": description: "Create shared module" + } + } +} diff --git a/federation/service/access_control.gen.go b/federation/service/access_control.gen.go index 761b11367..5b268c8a3 100644 --- a/federation/service/access_control.gen.go +++ b/federation/service/access_control.gen.go @@ -6,12 +6,6 @@ package service // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - federation.exposed-module.yaml -// - federation.node.yaml -// - federation.shared-module.yaml -// - federation.yaml - import ( "context" "fmt" @@ -60,11 +54,6 @@ func (svc accessControl) Effective(ctx context.Context, rr ...rbac.Resource) (ee func (svc accessControl) List() (out []map[string]string) { def := []map[string]string{ - { - "type": types.ExposedModuleResourceType, - "any": types.ExposedModuleRbacResource(0, 0), - "op": "manage", - }, { "type": types.NodeResourceType, "any": types.NodeRbacResource(0), @@ -75,6 +64,11 @@ func (svc accessControl) List() (out []map[string]string) { "any": types.NodeRbacResource(0), "op": "module.create", }, + { + "type": types.ExposedModuleResourceType, + "any": types.ExposedModuleRbacResource(0, 0), + "op": "manage", + }, { "type": types.SharedModuleResourceType, "any": types.SharedModuleRbacResource(0, 0), @@ -183,13 +177,6 @@ func (svc accessControl) CloneRulesByRoleID(ctx context.Context, fromRoleID uint return svc.rbac.CloneRulesByRoleID(ctx, fromRoleID, toRoleID...) } -// CanManageExposedModule checks if current user can manage shared module -// -// This function is auto-generated -func (svc accessControl) CanManageExposedModule(ctx context.Context, r *types.ExposedModule) bool { - return svc.can(ctx, "manage", r) -} - // CanManageNode checks if current user can manage federation node // // This function is auto-generated @@ -204,6 +191,13 @@ func (svc accessControl) CanCreateModuleOnNode(ctx context.Context, r *types.Nod return svc.can(ctx, "module.create", r) } +// CanManageExposedModule checks if current user can manage exposed module module +// +// This function is auto-generated +func (svc accessControl) CanManageExposedModule(ctx context.Context, r *types.ExposedModule) bool { + return svc.can(ctx, "manage", r) +} + // CanMapSharedModule checks if current user can map shared module // // This function is auto-generated @@ -215,42 +209,48 @@ func (svc accessControl) CanMapSharedModule(ctx context.Context, r *types.Shared // // This function is auto-generated func (svc accessControl) CanGrant(ctx context.Context) bool { - return svc.can(ctx, "grant", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "grant", r) } // CanPair checks if current user can pair federation nodes // // This function is auto-generated func (svc accessControl) CanPair(ctx context.Context) bool { - return svc.can(ctx, "pair", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "pair", r) } // CanReadSettings checks if current user can read settings // // This function is auto-generated func (svc accessControl) CanReadSettings(ctx context.Context) bool { - return svc.can(ctx, "settings.read", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "settings.read", r) } // CanManageSettings checks if current user can manage settings // // This function is auto-generated func (svc accessControl) CanManageSettings(ctx context.Context) bool { - return svc.can(ctx, "settings.manage", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "settings.manage", r) } // CanCreateNode checks if current user can create new federation node // // This function is auto-generated func (svc accessControl) CanCreateNode(ctx context.Context) bool { - return svc.can(ctx, "node.create", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "node.create", r) } // CanSearchNodes checks if current user can list, search or filter federation nodes // // This function is auto-generated func (svc accessControl) CanSearchNodes(ctx context.Context) bool { - return svc.can(ctx, "nodes.search", &types.Component{}) + r := &types.Component{} + return svc.can(ctx, "nodes.search", r) } // rbacResourceValidator validates known component's resource by routing it to the appropriate validator @@ -258,10 +258,10 @@ func (svc accessControl) CanSearchNodes(ctx context.Context) bool { // This function is auto-generated func rbacResourceValidator(r string, oo ...string) error { switch rbac.ResourceType(r) { - case types.ExposedModuleResourceType: - return rbacExposedModuleResourceValidator(r, oo...) case types.NodeResourceType: return rbacNodeResourceValidator(r, oo...) + case types.ExposedModuleResourceType: + return rbacExposedModuleResourceValidator(r, oo...) case types.SharedModuleResourceType: return rbacSharedModuleResourceValidator(r, oo...) case types.ComponentResourceType: @@ -276,15 +276,15 @@ func rbacResourceValidator(r string, oo ...string) error { // This function is auto-generated func rbacResourceOperations(r string) map[string]bool { switch rbac.ResourceType(r) { - case types.ExposedModuleResourceType: - return map[string]bool{ - "manage": true, - } case types.NodeResourceType: return map[string]bool{ "manage": true, "module.create": true, } + case types.ExposedModuleResourceType: + return map[string]bool{ + "manage": true, + } case types.SharedModuleResourceType: return map[string]bool{ "map": true, @@ -303,69 +303,24 @@ func rbacResourceOperations(r string) map[string]bool { return nil } -// rbacExposedModuleResourceValidator checks validity of rbac resource and operations -// -// Can be called without operations to check for validity of resource string only -// -// This function is auto-generated -func rbacExposedModuleResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for federation ExposedModule resource", o) - } - } - - if !strings.HasPrefix(r, types.ExposedModuleResourceType) { - // expecting resource to always include path - return fmt.Errorf("invalid resource type") - } - - const sep = "/" - var ( - pp = strings.Split(strings.Trim(r[len(types.ExposedModuleResourceType):], sep), sep) - prc = []string{ - "nodeID", - "ID", - } - ) - - if len(pp) != len(prc) { - return fmt.Errorf("invalid resource path structure") - } - - for i := 0; i < len(pp); i++ { - if pp[i] != "*" { - if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for ExposedModule", i) - } - - if _, err := cast.ToUint64E(pp[i]); err != nil { - return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) - } - } - } - return nil -} - -// rbacNodeResourceValidator checks validity of rbac resource and operations +// rbacNodeResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacNodeResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for federation Node resource", o) - } - } - if !strings.HasPrefix(r, types.NodeResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for node resource", o) + } + } + const sep = "/" var ( pp = strings.Split(strings.Trim(r[len(types.NodeResourceType):], sep), sep) @@ -381,7 +336,7 @@ func rbacNodeResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for Node", i) + return fmt.Errorf("invalid path wildcard level (%d) for node resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -392,29 +347,29 @@ func rbacNodeResourceValidator(r string, oo ...string) error { return nil } -// rbacSharedModuleResourceValidator checks validity of rbac resource and operations +// rbacExposedModuleResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated -func rbacSharedModuleResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for federation SharedModule resource", o) - } - } - - if !strings.HasPrefix(r, types.SharedModuleResourceType) { +func rbacExposedModuleResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.ExposedModuleResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for exposedModule resource", o) + } + } + const sep = "/" var ( - pp = strings.Split(strings.Trim(r[len(types.SharedModuleResourceType):], sep), sep) + pp = strings.Split(strings.Trim(r[len(types.ExposedModuleResourceType):], sep), sep) prc = []string{ - "nodeID", + "NodeID", "ID", } ) @@ -426,7 +381,7 @@ func rbacSharedModuleResourceValidator(r string, oo ...string) error { for i := 0; i < len(pp); i++ { if pp[i] != "*" { if i > 0 && pp[i-1] == "*" { - return fmt.Errorf("invalid resource path wildcard level (%d) for SharedModule", i) + return fmt.Errorf("invalid path wildcard level (%d) for exposedModule resource", i) } if _, err := cast.ToUint64E(pp[i]); err != nil { @@ -437,23 +392,68 @@ func rbacSharedModuleResourceValidator(r string, oo ...string) error { return nil } -// rbacComponentResourceValidator checks validity of rbac resource and operations +// rbacSharedModuleResourceValidator checks validity of RBAC resource and operations +// +// Can be called without operations to check for validity of resource string only +// +// This function is auto-generated +func rbacSharedModuleResourceValidator(r string, oo ...string) error { + if !strings.HasPrefix(r, types.SharedModuleResourceType) { + // expecting resource to always include path + return fmt.Errorf("invalid resource type") + } + + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for sharedModule resource", o) + } + } + + const sep = "/" + var ( + pp = strings.Split(strings.Trim(r[len(types.SharedModuleResourceType):], sep), sep) + prc = []string{ + "NodeID", + "ID", + } + ) + + if len(pp) != len(prc) { + return fmt.Errorf("invalid resource path structure") + } + + for i := 0; i < len(pp); i++ { + if pp[i] != "*" { + if i > 0 && pp[i-1] == "*" { + return fmt.Errorf("invalid path wildcard level (%d) for sharedModule resource", i) + } + + if _, err := cast.ToUint64E(pp[i]); err != nil { + return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i]) + } + } + } + return nil +} + +// rbacComponentResourceValidator checks validity of RBAC resource and operations // // Can be called without operations to check for validity of resource string only // // This function is auto-generated func rbacComponentResourceValidator(r string, oo ...string) error { - defOps := rbacResourceOperations(r) - for _, o := range oo { - if !defOps[o] { - return fmt.Errorf("invalid operation '%s' for federation resource", o) - } - } - if !strings.HasPrefix(r, types.ComponentResourceType) { // expecting resource to always include path return fmt.Errorf("invalid resource type") } + defOps := rbacResourceOperations(r) + for _, o := range oo { + if !defOps[o] { + return fmt.Errorf("invalid operation '%s' for federation component resource", o) + } + } + return nil } diff --git a/federation/shared-module.cue b/federation/shared-module.cue new file mode 100644 index 000000000..9509315b0 --- /dev/null +++ b/federation/shared-module.cue @@ -0,0 +1,17 @@ +package federation + +import ( + "github.com/cortezaproject/corteza-server/codegen/schema" +) + +sharedModule: schema.#resource & { + parents: [ + {handle: "node"}, + ] + + rbac: { + operations: { + "map": description: "Map shared module" + } + } +} diff --git a/federation/types/rbac.gen.go b/federation/types/rbac.gen.go index 7c59affe9..9e677c77c 100644 --- a/federation/types/rbac.gen.go +++ b/federation/types/rbac.gen.go @@ -6,12 +6,6 @@ package types // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - federation.exposed-module.yaml -// - federation.node.yaml -// - federation.shared-module.yaml -// - federation.yaml - import ( "fmt" "strconv" @@ -24,13 +18,48 @@ type ( Component struct{} ) +var ( + _ = fmt.Printf + _ = strconv.FormatUint +) + const ( - ExposedModuleResourceType = "corteza::federation:exposed-module" NodeResourceType = "corteza::federation:node" + ExposedModuleResourceType = "corteza::federation:exposed-module" SharedModuleResourceType = "corteza::federation:shared-module" ComponentResourceType = "corteza::federation" ) +// RbacResource returns string representation of RBAC resource for Node by calling NodeRbacResource fn +// +// RBAC resource is in the corteza::federation:node/... format +// +// This function is auto-generated +func (r Node) RbacResource() string { + return NodeRbacResource(r.ID) +} + +// NodeRbacResource returns string representation of RBAC resource for Node +// +// RBAC resource is in the corteza::federation:node/... format +// +// This function is auto-generated +func NodeRbacResource(id uint64) string { + cpts := []interface{}{NodeResourceType} + if id != 0 { + cpts = append(cpts, strconv.FormatUint(id, 10)) + } else { + cpts = append(cpts, "*") + } + + return fmt.Sprintf(NodeRbacResourceTpl(), cpts...) + +} + +func NodeRbacResourceTpl() string { + return "%s/%s" +} + // RbacResource returns string representation of RBAC resource for ExposedModule by calling ExposedModuleRbacResource fn // // RBAC resource is in the corteza::federation:exposed-module/... format @@ -63,42 +92,10 @@ func ExposedModuleRbacResource(nodeID uint64, id uint64) string { } -// @todo template func ExposedModuleRbacResourceTpl() string { return "%s/%s/%s" } -// RbacResource returns string representation of RBAC resource for Node by calling NodeRbacResource fn -// -// RBAC resource is in the corteza::federation:node/... format -// -// This function is auto-generated -func (r Node) RbacResource() string { - return NodeRbacResource(r.ID) -} - -// NodeRbacResource returns string representation of RBAC resource for Node -// -// RBAC resource is in the corteza::federation:node/... format -// -// This function is auto-generated -func NodeRbacResource(id uint64) string { - cpts := []interface{}{NodeResourceType} - if id != 0 { - cpts = append(cpts, strconv.FormatUint(id, 10)) - } else { - cpts = append(cpts, "*") - } - - return fmt.Sprintf(NodeRbacResourceTpl(), cpts...) - -} - -// @todo template -func NodeRbacResourceTpl() string { - return "%s/%s" -} - // RbacResource returns string representation of RBAC resource for SharedModule by calling SharedModuleRbacResource fn // // RBAC resource is in the corteza::federation:shared-module/... format @@ -131,7 +128,6 @@ func SharedModuleRbacResource(nodeID uint64, id uint64) string { } -// @todo template func SharedModuleRbacResourceTpl() string { return "%s/%s/%s" } @@ -155,7 +151,6 @@ func ComponentRbacResource() string { } -// @todo template func ComponentRbacResourceTpl() string { return "%s" } diff --git a/pkg/envoy/resource/rbac_references_automation.gen.go b/pkg/envoy/resource/rbac_references_automation.gen.go index f3abc0b12..231c3fbe2 100644 --- a/pkg/envoy/resource/rbac_references_automation.gen.go +++ b/pkg/envoy/resource/rbac_references_automation.gen.go @@ -6,10 +6,6 @@ package resource // the code is regenerated. // -// Definitions file that controls how this file is generated: -// - automation.workflow.yaml -// - automation.yaml - import ( "github.com/cortezaproject/corteza-server/automation/types" ) diff --git a/pkg/envoy/resource/rbac_references_federation.gen.go b/pkg/envoy/resource/rbac_references_federation.gen.go new file mode 100644 index 000000000..5900fd1c3 --- /dev/null +++ b/pkg/envoy/resource/rbac_references_federation.gen.go @@ -0,0 +1,56 @@ +package resource + +// This file is auto-generated. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +import ( + "github.com/cortezaproject/corteza-server/federation/types" +) + +// FederationNodeRbacReferences generates RBAC references +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func FederationNodeRbacReferences(node string) (res *Ref, pp []*Ref, err error) { + if node != "*" { + res = &Ref{ResourceType: types.NodeResourceType, Identifiers: MakeIdentifiers(node)} + } + + return +} + +// FederationExposedModuleRbacReferences generates RBAC references +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func FederationExposedModuleRbacReferences(nodeID string, exposedModule string) (res *Ref, pp []*Ref, err error) { + if nodeID != "*" { + pp = append(pp, &Ref{ResourceType: types.NodeResourceType, Identifiers: MakeIdentifiers(nodeID)}) + } + if exposedModule != "*" { + res = &Ref{ResourceType: types.ExposedModuleResourceType, Identifiers: MakeIdentifiers(exposedModule)} + } + + return +} + +// FederationSharedModuleRbacReferences generates RBAC references +// +// Resources with "envoy: false" are skipped +// +// This function is auto-generated +func FederationSharedModuleRbacReferences(nodeID string, sharedModule string) (res *Ref, pp []*Ref, err error) { + if nodeID != "*" { + pp = append(pp, &Ref{ResourceType: types.NodeResourceType, Identifiers: MakeIdentifiers(nodeID)}) + } + if sharedModule != "*" { + res = &Ref{ResourceType: types.SharedModuleResourceType, Identifiers: MakeIdentifiers(sharedModule)} + } + + return +} diff --git a/pkg/envoy/resource/rbac_rules_parse.gen.go b/pkg/envoy/resource/rbac_rules_parse.gen.go index f5c9d7a04..58d30aae9 100644 --- a/pkg/envoy/resource/rbac_rules_parse.gen.go +++ b/pkg/envoy/resource/rbac_rules_parse.gen.go @@ -8,7 +8,9 @@ package resource import ( "fmt" + automationTypes "github.com/cortezaproject/corteza-server/automation/types" composeTypes "github.com/cortezaproject/corteza-server/compose/types" + federationTypes "github.com/cortezaproject/corteza-server/federation/types" systemTypes "github.com/cortezaproject/corteza-server/system/types" "strings" ) @@ -178,6 +180,44 @@ func ParseRule(res string) (string, *Ref, []*Ref, error) { ) return resourceType, ref, pp, err + case automationTypes.WorkflowResourceType: + if len(path) != 1 { + return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) + } + ref, pp, err := AutomationWorkflowRbacReferences( + path[0], + ) + return resourceType, ref, pp, err + + case federationTypes.NodeResourceType: + if len(path) != 1 { + return "", nil, nil, fmt.Errorf("expecting 1 reference components in path, got %d", len(path)) + } + ref, pp, err := FederationNodeRbacReferences( + path[0], + ) + return resourceType, ref, pp, err + + case federationTypes.ExposedModuleResourceType: + if len(path) != 2 { + return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) + } + ref, pp, err := FederationExposedModuleRbacReferences( + path[0], + path[1], + ) + return resourceType, ref, pp, err + + case federationTypes.SharedModuleResourceType: + if len(path) != 2 { + return "", nil, nil, fmt.Errorf("expecting 2 reference components in path, got %d", len(path)) + } + ref, pp, err := FederationSharedModuleRbacReferences( + path[0], + path[1], + ) + return resourceType, ref, pp, err + case systemTypes.ComponentResourceType: if len(path) != 0 { return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) @@ -190,6 +230,20 @@ func ParseRule(res string) (string, *Ref, []*Ref, error) { return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) } + // Component resource, no path + return resourceType, nil, nil, nil + case automationTypes.ComponentResourceType: + if len(path) != 0 { + return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) + } + + // Component resource, no path + return resourceType, nil, nil, nil + case federationTypes.ComponentResourceType: + if len(path) != 0 { + return "", nil, nil, fmt.Errorf("expecting 0 reference components in path, got %d", len(path)) + } + // Component resource, no path return resourceType, nil, nil, nil }