3
0

Tweak logic & add base RBAC import/export test

This commit is contained in:
Tomaž Jerman
2023-02-27 12:05:43 +01:00
parent cf30aeb75d
commit c42cf298de
28 changed files with 1048 additions and 254 deletions
+2 -2
View File
@@ -83,9 +83,9 @@ func (e StoreEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt stri
case types.TriggerResourceType:
return e.encodeTriggers(ctx, p, s, nodes, tree)
default:
return e.encode(ctx, p, s, rt, nodes, tree)
}
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
+7
View File
@@ -1,11 +1,18 @@
package envoy
import (
"context"
"time"
"github.com/cortezaproject/corteza/server/automation/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/store"
)
func (e StoreEncoder) encode(ctx context.Context, p envoyx.EncodeParams, s store.Storer, rt string, nn envoyx.NodeSet, tree envoyx.Traverser) (err error) {
return
}
func (e StoreEncoder) setWorkflowDefaults(res *types.Workflow) (err error) {
if res.CreatedAt.IsZero() {
res.CreatedAt = time.Now()
+41 -43
View File
@@ -86,29 +86,6 @@ func (d *auxYamlDoc) UnmarshalYAML(n *yaml.Node) (err error) {
}
return err
// Access control nodes
case "allow":
aux, err = unmarshalAllowNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
return err
}
case "deny":
aux, err = unmarshalDenyNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
return err
}
// Resource translation nodes
case "locale", "translation", "translations", "i18n":
aux, err = unmarshalLocaleNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
return err
}
// Offload to custom handlers
default:
aux, err = d.unmarshalYAML(kv, v)
@@ -741,10 +718,28 @@ func unmarshalDenyNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
if y7s.IsMapping(n.Content[1]) {
return unmarshalNestedRBACNode(n, acc)
out, err = unmarshalNestedRBACNode(n, acc)
if err != nil {
return
}
} else {
out, err = unmarshalFlatRBACNode(n, acc)
if err != nil {
return
}
}
return unmarshalFlatRBACNode(n, acc)
for _, o := range out {
for _, r := range o.References {
if r.Scope.IsEmpty() {
continue
}
o.Scope = r.Scope
break
}
}
return
}
// unmarshalNestedRBACNode handles RBAC rules when they are nested inside a resource
@@ -765,25 +760,28 @@ func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err e
func unmarshalNestedRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
// Handles role
return out, y7s.EachMap(n, func(role, perm *yaml.Node) error {
// Handles operation
return y7s.EachMap(perm, func(res, op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
// Handles operations
return y7s.EachMap(perm, func(res, ops *yaml.Node) error {
// Handle operation (one RBAC rule per op)
return y7s.EachSeq(ops, func(op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
})
return nil
})
return nil
})
})
}
+8 -2
View File
@@ -73,8 +73,14 @@ func (e YamlEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt strin
return
}
default:
// When this encoder doesn't handle any node it shouldn't write anything;
// this just removes the need for an extra check at the end.
out, err = e.encode(ctx, out, p, rt, nodes, tt)
if err != nil {
return
}
}
// Don't output nil values since that will produce broken yaml docs
if out == nil {
return
}
+12
View File
@@ -0,0 +1,12 @@
package envoy
import (
"context"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"gopkg.in/yaml.v3"
)
func (e YamlEncoder) encode(ctx context.Context, base *yaml.Node, p envoyx.EncodeParams, rt string, nodes envoyx.NodeSet, tt envoyx.Traverser) (out *yaml.Node, err error) {
return
}
@@ -89,9 +89,9 @@ func (e StoreEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt stri
case types.{{.expIdent}}ResourceType:
return e.encode{{.expIdent}}s(ctx, p, s, nodes, tree)
{{ end -}}
default:
return e.encode(ctx, p, s, rt, nodes, tree)
}
return
}
{{- range .resources }}
@@ -36,6 +36,7 @@ func SplitResourceIdentifier(ref string) (out map[string]Ref) {
case "corteza::{{$rootCmp.ident}}:{{.ident}}":
scope := Scope{}
{{$res := .}}
{{range $i, $p := .parents}}
if gRef(pp, {{$i}}) == "" {
@@ -48,9 +49,17 @@ func SplitResourceIdentifier(ref string) (out map[string]Ref) {
{{ end }}
{{ if eq $p.handle $cmp.ident }}
out["{{$i}}"] = Ref{
{{ if and (eq $rootCmp.ident "compose") (eq $i 0) }}
aux := gRef(pp, {{ $i }})
if aux != "" {
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(aux)
}
{{ end }}
out["Path.{{$i}}"] = Ref{
ResourceType: "{{$cmp.fqrt}}",
Identifiers: MakeIdentifiers(gRef(pp, {{ $i }})),
Scope: scope,
}
{{break}}
{{ end }}
@@ -60,9 +69,14 @@ func SplitResourceIdentifier(ref string) (out map[string]Ref) {
if gRef(pp, {{len .parents}}) == "" {
return
}
out["{{len .parents}}"] = Ref{
{{if eq .ident "namespace"}}
scope.ResourceType = "{{.fqrt}}"
scope.Identifiers = MakeIdentifiers(gRef(pp, {{len .parents}}))
{{end}}
out["Path.{{len .parents}}"] = Ref{
ResourceType: "{{.fqrt}}",
Identifiers: MakeIdentifiers(gRef(pp, {{len .parents}})),
Scope: scope,
}
{{ end }}
@@ -91,6 +91,7 @@ func (d *auxYamlDoc) UnmarshalYAML(n *yaml.Node) (err error) {
return err
{{ end }}
{{ if eq .componentIdent "system" }}
// Access control nodes
case "allow":
aux, err = unmarshalAllowNode(v)
@@ -113,6 +114,7 @@ func (d *auxYamlDoc) UnmarshalYAML(n *yaml.Node) (err error) {
if err != nil {
return err
}
{{ end }}
// Offload to custom handlers
default:
@@ -600,10 +602,28 @@ func unmarshalDenyNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
if y7s.IsMapping(n.Content[1]) {
return unmarshalNestedRBACNode(n, acc)
out, err = unmarshalNestedRBACNode(n, acc)
if err != nil {
return
}
} else {
out, err = unmarshalFlatRBACNode(n, acc)
if err != nil {
return
}
}
return unmarshalFlatRBACNode(n, acc)
for _, o := range out {
for _, r := range o.References {
if r.Scope.IsEmpty() {
continue
}
o.Scope = r.Scope
break
}
}
return
}
// unmarshalNestedRBACNode handles RBAC rules when they are nested inside a resource
@@ -624,25 +644,28 @@ func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err e
func unmarshalNestedRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
// Handles role
return out, y7s.EachMap(n, func(role, perm *yaml.Node) error {
// Handles operation
return y7s.EachMap(perm, func(res, op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
// Handles operations
return y7s.EachMap(perm, func(res, ops *yaml.Node) error {
// Handle operation (one RBAC rule per op)
return y7s.EachSeq(ops, func(op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
})
return nil
})
return nil
})
})
}
@@ -67,8 +67,14 @@ func (e YamlEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt strin
}
{{ end -}}
default:
// When this encoder doesn't handle any node it shouldn't write anything;
// this just removes the need for an extra check at the end.
out, err = e.encode(ctx, out, p, rt, nodes, tt)
if err != nil {
return
}
}
// Don't output nil values since that will produce broken yaml docs
if out == nil {
return
}
+2 -2
View File
@@ -32,9 +32,9 @@ import (
[
{
template: "gocode/envoy/rbac_rules_parse.go.tpl"
output: "pkg/envoy/resource/rbac_rules_parse.gen.go"
output: "pkg/envoyx/rbac_rules_parse.gen.go"
payload: {
package: "resource"
package: "envoyx"
imports: [
for cmp in app.corteza.components {
"\(cmp.ident)Types \"github.com/cortezaproject/corteza/server/\(cmp.ident)/types\""
+2 -2
View File
@@ -98,9 +98,9 @@ func (e StoreEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt stri
case types.PageResourceType:
return e.encodePages(ctx, p, s, nodes, tree)
default:
return e.encode(ctx, p, s, rt, nodes, tree)
}
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
+4
View File
@@ -12,6 +12,10 @@ import (
"github.com/cortezaproject/corteza/server/store"
)
func (e StoreEncoder) encode(ctx context.Context, p envoyx.EncodeParams, s store.Storer, rt string, nn envoyx.NodeSet, tree envoyx.Traverser) (err error) {
return
}
func (e StoreEncoder) setChartDefaults(res *types.Chart) (err error) {
if res.CreatedAt.IsZero() {
res.CreatedAt = time.Now()
+41 -43
View File
@@ -127,29 +127,6 @@ func (d *auxYamlDoc) UnmarshalYAML(n *yaml.Node) (err error) {
}
return err
// Access control nodes
case "allow":
aux, err = unmarshalAllowNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
return err
}
case "deny":
aux, err = unmarshalDenyNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
return err
}
// Resource translation nodes
case "locale", "translation", "translations", "i18n":
aux, err = unmarshalLocaleNode(v)
d.nodes = append(d.nodes, aux...)
if err != nil {
return err
}
// Offload to custom handlers
default:
aux, err = d.unmarshalYAML(kv, v)
@@ -1803,10 +1780,28 @@ func unmarshalDenyNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
if y7s.IsMapping(n.Content[1]) {
return unmarshalNestedRBACNode(n, acc)
out, err = unmarshalNestedRBACNode(n, acc)
if err != nil {
return
}
} else {
out, err = unmarshalFlatRBACNode(n, acc)
if err != nil {
return
}
}
return unmarshalFlatRBACNode(n, acc)
for _, o := range out {
for _, r := range o.References {
if r.Scope.IsEmpty() {
continue
}
o.Scope = r.Scope
break
}
}
return
}
// unmarshalNestedRBACNode handles RBAC rules when they are nested inside a resource
@@ -1827,25 +1822,28 @@ func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err e
func unmarshalNestedRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
// Handles role
return out, y7s.EachMap(n, func(role, perm *yaml.Node) error {
// Handles operation
return y7s.EachMap(perm, func(res, op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
// Handles operations
return y7s.EachMap(perm, func(res, ops *yaml.Node) error {
// Handle operation (one RBAC rule per op)
return y7s.EachSeq(ops, func(op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
})
return nil
})
return nil
})
})
}
+8 -2
View File
@@ -103,8 +103,14 @@ func (e YamlEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt strin
}
default:
// When this encoder doesn't handle any node it shouldn't write anything;
// this just removes the need for an extra check at the end.
out, err = e.encode(ctx, out, p, rt, nodes, tt)
if err != nil {
return
}
}
// Don't output nil values since that will produce broken yaml docs
if out == nil {
return
}
+5
View File
@@ -7,8 +7,13 @@ import (
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/y7s"
"gopkg.in/yaml.v3"
)
func (e YamlEncoder) encode(ctx context.Context, base *yaml.Node, p envoyx.EncodeParams, rt string, nodes envoyx.NodeSet, tt envoyx.Traverser) (out *yaml.Node, err error) {
return
}
func (e YamlEncoder) encodeChartConfigC(ctx context.Context, p envoyx.EncodeParams, tt envoyx.Traverser, n *envoyx.Node, chart *types.Chart, cfg types.ChartConfig) (_ any, err error) {
reports, _ := y7s.MakeSeq()
+1 -2
View File
@@ -8,12 +8,11 @@ package envoyx
import (
"fmt"
"strings"
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"
)
// Parse generates resource setting logic for each resource
+2 -2
View File
@@ -69,7 +69,7 @@ func RBACRulesForNodes(rr rbac.RuleSet, nn ...*Node) (rules NodeSet, err error)
ref.Scope = n.Scope
// @todo make the thing not a pointer
rf[fmt.Sprintf("Resource.%d", i)] = *ref
rf[fmt.Sprintf("Path.%d", i)] = *ref
}
// Ref to the rule
@@ -93,7 +93,7 @@ func RBACRulesForNodes(rr rbac.RuleSet, nn ...*Node) (rules NodeSet, err error)
if dups[r.RoleID][r.Resource] == nil {
dups[r.RoleID][r.Resource] = make(map[string]bool)
}
dups[r.RoleID][r.Resource][r.Resource] = true
dups[r.RoleID][r.Resource][r.Operation] = true
}
}
+183 -103
View File
@@ -30,468 +30,548 @@ func SplitResourceIdentifier(ref string) (out map[string]Ref) {
switch rt {
case "corteza::system:apigwFilter":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:apigw-filter",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:apigwRoute":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:apigw-route",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:application":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:application",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:attachment":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:attachment",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:authClient":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:auth-client",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:authConfirmedClient":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:auth-confirmed-client",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:authOa2token":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:auth-oa2token",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:authSession":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:auth-session",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:credential":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:credential",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:dalConnection":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:dal-connection",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:dalSensitivityLevel":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:dal-sensitivity-level",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:dataPrivacyRequest":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:data-privacy-request",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:dataPrivacyRequestComment":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:data-privacy-request-comment",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:queue":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:queue",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:queueMessage":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:queue-message",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:reminder":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:reminder",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:report":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:report",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:resourceTranslation":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:resource-translation",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:role":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:role",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:roleMember":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:role-member",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:settingValue":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:settings",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:template":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:template",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::system:user":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::system:user",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::compose:attachment":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::compose:attachment",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::compose:chart":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
aux := gRef(pp, 0)
if aux != "" {
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(aux)
}
out["Path.0"] = Ref{
ResourceType: "corteza::compose:namespace",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
out["Path.1"] = Ref{
ResourceType: "corteza::compose:chart",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
Scope: scope,
}
case "corteza::compose:module":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
aux := gRef(pp, 0)
if aux != "" {
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(aux)
}
out["Path.0"] = Ref{
ResourceType: "corteza::compose:namespace",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
out["Path.1"] = Ref{
ResourceType: "corteza::compose:module",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
Scope: scope,
}
case "corteza::compose:moduleField":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
aux := gRef(pp, 0)
if aux != "" {
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(aux)
}
out["Path.0"] = Ref{
ResourceType: "corteza::compose:namespace",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
out["Path.1"] = Ref{
ResourceType: "corteza::compose:module",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
Scope: scope,
}
if gRef(pp, 2) == "" {
return
}
out["2"] = Ref{
out["Path.2"] = Ref{
ResourceType: "corteza::compose:module-field",
Identifiers: MakeIdentifiers(gRef(pp, 2)),
Scope: scope,
}
case "corteza::compose:namespace":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(gRef(pp, 0))
out["Path.0"] = Ref{
ResourceType: "corteza::compose:namespace",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::compose:page":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
aux := gRef(pp, 0)
if aux != "" {
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(aux)
}
out["Path.0"] = Ref{
ResourceType: "corteza::compose:namespace",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
out["Path.1"] = Ref{
ResourceType: "corteza::compose:page",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
Scope: scope,
}
case "corteza::compose:record":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
aux := gRef(pp, 0)
if aux != "" {
scope.ResourceType = "corteza::compose:namespace"
scope.Identifiers = MakeIdentifiers(aux)
}
out["Path.0"] = Ref{
ResourceType: "corteza::compose:namespace",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
out["Path.1"] = Ref{
ResourceType: "corteza::compose:module",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
Scope: scope,
}
if gRef(pp, 2) == "" {
return
}
out["2"] = Ref{
out["Path.2"] = Ref{
ResourceType: "corteza::compose:record",
Identifiers: MakeIdentifiers(gRef(pp, 2)),
Scope: scope,
}
case "corteza::compose:recordRevision":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::compose:record-revision",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::automation:session":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::automation:session",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::automation:trigger":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::automation:trigger",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
Scope: scope,
}
case "corteza::automation:workflow":
scope := Scope{}
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
out["Path.0"] = Ref{
ResourceType: "corteza::automation:workflow",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
}
case "corteza::federation:exposedModule":
if gRef(pp, 0) == "" {
return
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
ResourceType: "corteza::federation:exposed-module",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
}
case "corteza::federation:moduleMapping":
if gRef(pp, 0) == "" {
return
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
ResourceType: "corteza::federation:module-mapping",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
}
case "corteza::federation:node":
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
ResourceType: "corteza::federation:node",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
}
case "corteza::federation:nodeSync":
if gRef(pp, 0) == "" {
return
}
out["0"] = Ref{
ResourceType: "corteza::federation:node-sync",
Identifiers: MakeIdentifiers(gRef(pp, 0)),
}
case "corteza::federation:sharedModule":
if gRef(pp, 0) == "" {
return
}
if gRef(pp, 1) == "" {
return
}
out["1"] = Ref{
ResourceType: "corteza::federation:shared-module",
Identifiers: MakeIdentifiers(gRef(pp, 1)),
Scope: scope,
}
}
+13 -1
View File
@@ -64,6 +64,18 @@ func (set RuleSet) FilterAccess(a Access) (out RuleSet) {
return out
}
func (set RuleSet) FilterOperation(op string) (out RuleSet) {
out = make(RuleSet, 0, len(set))
for _, s := range set {
if s.Operation == op {
out = append(out, s)
}
}
return out
}
// FilterResource returns rules that match given list of resources
// Wildcards are not used!
//
@@ -117,7 +129,7 @@ func (u *Rule) setValue(name string, pos uint, v any) (err error) {
pp := strings.Split(name, ".")
switch pp[0] {
case "resource", "Resource":
case "resource", "Resource", "Path", "path":
ix, err := strconv.ParseUint(pp[1], 10, 64)
if err != nil {
return err
+2 -2
View File
@@ -131,9 +131,9 @@ func (e StoreEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt stri
case types.DalSensitivityLevelResourceType:
return e.encodeDalSensitivityLevels(ctx, p, s, nodes, tree)
default:
return e.encode(ctx, p, s, rt, nodes, tree)
}
return
}
// // // // // // // // // // // // // // // // // // // // // // // // //
+15
View File
@@ -5,11 +5,26 @@ import (
"time"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/store"
"github.com/cortezaproject/corteza/server/system/types"
)
func (e StoreEncoder) prepare(ctx context.Context, p envoyx.EncodeParams, s store.Storer, rt string, nn envoyx.NodeSet) (err error) {
switch rt {
case rbac.RuleResourceType:
return e.prepareRbacRule(ctx, p, s, nn)
}
return
}
func (e StoreEncoder) encode(ctx context.Context, p envoyx.EncodeParams, s store.Storer, rt string, nn envoyx.NodeSet, tree envoyx.Traverser) (err error) {
switch rt {
case rbac.RuleResourceType:
return e.encodeRbacRules(ctx, p, s, nn, tree)
}
return
}
+80
View File
@@ -0,0 +1,80 @@
package envoy
import (
"context"
"fmt"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/store"
)
func (e StoreEncoder) prepareRbacRule(ctx context.Context, p envoyx.EncodeParams, s store.Storer, nn envoyx.NodeSet) (err error) {
// @todo existing RBAC rules?
for _, n := range nn {
if n.Resource == nil {
panic("unexpected state: cannot call prepareRbacRule with nodes without a defined Resource")
}
res, ok := n.Resource.(*rbac.Rule)
if !ok {
panic("unexpected resource type: node expecting type of RbacRule")
}
// Run expressions on the nodes
err = e.runEvals(ctx, false, n)
if err != nil {
return
}
// @todo merge conflicts if we do existing assertion
n.Resource = res
}
return
}
// encodeRbacRules encodes a set of resource into the database
func (e StoreEncoder) encodeRbacRules(ctx context.Context, p envoyx.EncodeParams, s store.Storer, nn envoyx.NodeSet, tree envoyx.Traverser) (err error) {
for _, n := range nn {
err = e.encodeRbacRule(ctx, p, s, n, tree)
if err != nil {
return
}
}
return
}
// encodeRbacRule encodes the resource into the database
func (e StoreEncoder) encodeRbacRule(ctx context.Context, p envoyx.EncodeParams, s store.Storer, n *envoyx.Node, tree envoyx.Traverser) (err error) {
// Grab dependency references
var auxID uint64
for fieldLabel, ref := range n.References {
rn := tree.ParentForRef(n, ref)
if rn == nil {
err = fmt.Errorf("missing node for ref %v", ref)
return
}
auxID = rn.Resource.GetID()
if auxID == 0 {
err = fmt.Errorf("related resource doesn't provide an ID")
return
}
err = n.Resource.SetValue(fieldLabel, 0, auxID)
if err != nil {
return
}
}
// Flush to the DB
err = store.UpsertRbacRule(ctx, s, n.Resource.(*rbac.Rule))
if err != nil {
return
}
return
}
+41 -20
View File
@@ -3207,10 +3207,28 @@ func unmarshalDenyNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
if y7s.IsMapping(n.Content[1]) {
return unmarshalNestedRBACNode(n, acc)
out, err = unmarshalNestedRBACNode(n, acc)
if err != nil {
return
}
} else {
out, err = unmarshalFlatRBACNode(n, acc)
if err != nil {
return
}
}
return unmarshalFlatRBACNode(n, acc)
for _, o := range out {
for _, r := range o.References {
if r.Scope.IsEmpty() {
continue
}
o.Scope = r.Scope
break
}
}
return
}
// unmarshalNestedRBACNode handles RBAC rules when they are nested inside a resource
@@ -3231,25 +3249,28 @@ func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err e
func unmarshalNestedRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
// Handles role
return out, y7s.EachMap(n, func(role, perm *yaml.Node) error {
// Handles operation
return y7s.EachMap(perm, func(res, op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
// Handles operations
return y7s.EachMap(perm, func(res, ops *yaml.Node) error {
// Handle operation (one RBAC rule per op)
return y7s.EachSeq(ops, func(op *yaml.Node) error {
out = append(out, &envoyx.Node{
Resource: &rbac.Rule{
Resource: res.Value,
Operation: op.Value,
Access: acc,
},
ResourceType: rbac.RuleResourceType,
References: envoyx.MergeRefs(
map[string]envoyx.Ref{"RoleID": {
// Providing resource type as plain text to reduce cross component references
ResourceType: "corteza::system:role",
Identifiers: envoyx.MakeIdentifiers(role.Value),
}},
envoyx.SplitResourceIdentifier(res.Value),
),
})
return nil
})
return nil
})
})
}
+8 -2
View File
@@ -157,8 +157,14 @@ func (e YamlEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt strin
return
}
default:
// When this encoder doesn't handle any node it shouldn't write anything;
// this just removes the need for an extra check at the end.
out, err = e.encode(ctx, out, p, rt, nodes, tt)
if err != nil {
return
}
}
// Don't output nil values since that will produce broken yaml docs
if out == nil {
return
}
+195
View File
@@ -3,13 +3,208 @@ package envoy
import (
"context"
"fmt"
"strings"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/pkg/y7s"
"github.com/cortezaproject/corteza/server/system/types"
"gopkg.in/yaml.v3"
)
type (
// Wrapper to avoid constant type casting for role nodes
rbacRuleWrap struct {
node *envoyx.Node
rule *rbac.Rule
}
// Wrapper to keep role and rules together
rbacRuleRoleWrap struct {
rules []rbacRuleWrap
roleNode *envoyx.Node
role *types.Role
}
)
func (e YamlEncoder) encode(ctx context.Context, base *yaml.Node, p envoyx.EncodeParams, rt string, nodes envoyx.NodeSet, tt envoyx.Traverser) (out *yaml.Node, err error) {
switch rt {
case rbac.RuleResourceType:
return e.encodeRbacRules(ctx, base, p, nodes, tt)
}
return
}
func (e YamlEncoder) encodeRbacRules(ctx context.Context, base *yaml.Node, p envoyx.EncodeParams, nodes envoyx.NodeSet, tt envoyx.Traverser) (out *yaml.Node, err error) {
err = e.resolveRulePathDeps(ctx, tt, nodes)
if err != nil {
return
}
// Batch by access (allow, deny)
allows := make([]rbacRuleWrap, 0, len(nodes))
denies := make([]rbacRuleWrap, 0, len(nodes))
for _, n := range nodes {
r := n.Resource.(*rbac.Rule)
if r.Access == rbac.Allow {
allows = append(allows, rbacRuleWrap{
node: n,
rule: r,
})
} else {
denies = append(denies, rbacRuleWrap{
node: n,
rule: r,
})
}
}
// Encode allows
var aux *yaml.Node
aux, err = e.encodeRbacRulesByRole(p, allows, tt)
if err != nil {
return
}
out = base
out, err = y7s.AddMap(out, "allow", aux)
if err != nil {
return
}
// Encode denies
aux, err = e.encodeRbacRulesByRole(p, denies, tt)
if err != nil {
return
}
out, err = y7s.AddMap(out, "deny", aux)
if err != nil {
return
}
return
}
func (e YamlEncoder) encodeRbacRulesByRole(p envoyx.EncodeParams, rules []rbacRuleWrap, tt envoyx.Traverser) (out *yaml.Node, err error) {
// Batch by role; make sure to keep the multiple identifier thing in mind
byRole := make(map[string]rbacRuleRoleWrap)
for _, r := range rules {
role := r.node.References["RoleID"]
aux := rbacRuleRoleWrap{}
ok := false
// Check if an identifier already exists
for _, ident := range role.Identifiers.Slice {
aux, ok = byRole[ident]
if ok {
break
}
}
// If not, create a new entry and resolve the role
if !ok {
aux.roleNode = tt.ParentForRef(r.node, role)
if aux.roleNode != nil {
aux.role = aux.roleNode.Resource.(*types.Role)
}
}
// Add the rule to the batch and update potentially missing identifiers
aux.rules = append(aux.rules, r)
for _, ident := range role.Identifiers.Slice {
byRole[ident] = aux
}
}
// Encode
var aux *yaml.Node
for _, r := range byRole {
aux, err = e.encodeRbacRulesByResource(p, r.rules)
if err != nil {
return
}
out, err = y7s.AddMap(out,
r.roleNode.Identifiers.FriendlyIdentifier(), aux,
)
if err != nil {
return
}
}
return
}
func (e YamlEncoder) encodeRbacRulesByResource(p envoyx.EncodeParams, rules []rbacRuleWrap) (out *yaml.Node, err error) {
byResource := make(map[string][]rbacRuleWrap)
// Batch
for _, r := range rules {
byResource[r.rule.Resource] = append(byResource[r.rule.Resource], r)
}
// Encode
var aux *yaml.Node
for resource, rules := range byResource {
aux, err = e.encodeRbacRuleOperations(p, rules)
if err != nil {
return
}
out, err = y7s.AddMap(out, resource, aux)
if err != nil {
return
}
}
return
}
func (e YamlEncoder) encodeRbacRuleOperations(p envoyx.EncodeParams, rules []rbacRuleWrap) (out *yaml.Node, err error) {
ops, _ := y7s.MakeSeq()
for _, r := range rules {
ops, err = y7s.AddSeq(ops, r.rule.Operation)
if err != nil {
return
}
}
return ops, nil
}
func (e YamlEncoder) resolveRulePathDeps(ctx context.Context, tt envoyx.Traverser, nodes envoyx.NodeSet) (err error) {
for _, n := range nodes {
var auxIdent string
for fieldLabel, ref := range n.References {
// Only resolve path refs; others are done later on
if !strings.Contains(fieldLabel, "Path.") {
continue
}
rn := tt.ParentForRef(n, ref)
if rn == nil {
err = fmt.Errorf("missing node for ref %v", ref)
return
}
auxIdent = rn.Identifiers.FriendlyIdentifier()
if auxIdent == "" || auxIdent == "0" {
err = fmt.Errorf("related resource doesn't provide an ID")
return
}
err = n.Resource.SetValue(fieldLabel, 0, auxIdent)
if err != nil {
return
}
}
}
return
}
func (e YamlEncoder) encodeAuthClientSecurityC(ctx context.Context, p envoyx.EncodeParams, tt envoyx.Traverser, n *envoyx.Node, ac *types.AuthClient, sec *types.AuthClientSecurity) (_ any, err error) {
sqPermittedRoles, err := e.encodeRoleSlice(n, tt, "Security.PermittedRoles", sec.PermittedRoles)
if err != nil {
+1
View File
@@ -125,6 +125,7 @@ func cleanup(t *testing.T) {
store.TruncateUsers(ctx, defaultStore),
store.TruncateDalConnections(ctx, defaultStore),
store.TruncateDalSensitivityLevels(ctx, defaultStore),
store.TruncateRbacRules(ctx, defaultStore),
store.TruncateAutomationWorkflows(ctx, defaultStore),
store.TruncateAutomationTriggers(ctx, defaultStore),
+273
View File
@@ -0,0 +1,273 @@
package envoy
import (
"context"
"fmt"
"os"
"testing"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/store"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
)
func TestRbacImportExport(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
gg *envoyx.DepGraph
err error
)
cleanup(t)
// The test
//
// * imports some YAML files
// * checks the DB state
// * exports the DB into a YAML
// * clears the DB
// * imports the YAML
// * checks the DB state the same way as before
//
// The above outlined flow allows us to trivially check if the data is both
// imported and exported correctly.
//
// The initial step could also manually populate the DB but the YAML import
// is more convenient.
t.Run("initial import", func(t *testing.T) {
t.Run("parse configs", func(t *testing.T) {
nodes, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/rbac",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertRBACState(ctx, t, defaultStore, req)
})
// Prepare a temp file where we'll dump the YAML into
auxFile, err := os.CreateTemp(os.TempDir(), "*.yaml")
req.NoError(err)
spew.Dump(auxFile.Name())
// defer os.Remove(auxFile.Name())
defer auxFile.Close()
var rules envoyx.NodeSet
t.Run("export", func(t *testing.T) {
t.Run("export from DB", func(t *testing.T) {
nodes, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
},
Filter: map[string]envoyx.ResourceFilter{
types.ModuleResourceType: {},
types.NamespaceResourceType: {},
systemTypes.RoleResourceType: {},
},
})
req.NoError(err)
})
var rr rbac.RuleSet
t.Run("get all rules", func(t *testing.T) {
rr, _, err = store.SearchRbacRules(ctx, defaultStore, rbac.RuleFilter{})
req.NoError(err)
})
t.Run("connect rules to resources", func(t *testing.T) {
rules, err = envoyx.RBACRulesForNodes(rr, nodes...)
req.NoError(err)
nodes = append(nodes, rules...)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nodes...)
req.NoError(err)
})
t.Run("write file", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeIo,
Params: map[string]any{
"writer": auxFile,
},
}, gg)
req.NoError(err)
})
})
cleanup(t)
t.Run("second import", func(t *testing.T) {
t.Run("yaml parse", func(t *testing.T) {
nodes, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": fmt.Sprintf("file://%s", auxFile.Name()),
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nodes...)
req.NoError(err)
})
t.Run("run import", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertRBACState(ctx, t, defaultStore, req)
})
}
func assertRBACState(ctx context.Context, t *testing.T, s store.Storer, req *require.Assertions) {
t.Run("check state", func(t *testing.T) {
namespaces, _, err := store.SearchComposeNamespaces(ctx, defaultStore, types.NamespaceFilter{})
req.NoError(err)
ns1 := namespaces[0]
modules, _, err := store.SearchComposeModules(ctx, defaultStore, types.ModuleFilter{})
req.NoError(err)
mod1 := modules[0]
roles, _, err := store.SearchRoles(ctx, defaultStore, systemTypes.RoleFilter{})
req.NoError(err)
role1 := roles.FindByHandle("test_role_1")
role2 := roles.FindByHandle("test_role_2")
rr, _, err := store.SearchRbacRules(ctx, defaultStore, rbac.RuleFilter{})
req.NoError(err)
allows := rr.FilterAccess(rbac.Allow)
compareRules(req, *allows.FilterOperation("op1")[0], rbac.Rule{
RoleID: role1.ID,
Resource: "corteza::compose:namespace/*",
Operation: "op1",
Access: rbac.Allow,
})
compareRules(req, *allows.FilterOperation("op2")[0], rbac.Rule{
RoleID: role1.ID,
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
Operation: "op2",
Access: rbac.Allow,
})
compareRules(req, *allows.FilterOperation("op3")[0], rbac.Rule{
RoleID: role1.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/*", ns1.ID),
Operation: "op3",
Access: rbac.Allow,
})
compareRules(req, *allows.FilterOperation("op4")[0], rbac.Rule{
RoleID: role1.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/%d", ns1.ID, mod1.ID),
Operation: "op4",
Access: rbac.Allow,
})
denies := rr.FilterAccess(rbac.Deny)
compareRules(req, *denies.FilterOperation("op1")[0], rbac.Rule{
RoleID: role2.ID,
Resource: "corteza::compose:namespace/*",
Operation: "op1",
Access: rbac.Deny,
})
compareRules(req, *denies.FilterOperation("op2")[0], rbac.Rule{
RoleID: role2.ID,
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
Operation: "op2",
Access: rbac.Deny,
})
compareRules(req, *denies.FilterOperation("op3")[0], rbac.Rule{
RoleID: role2.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/*", ns1.ID),
Operation: "op3",
Access: rbac.Deny,
})
compareRules(req, *denies.FilterOperation("op4")[0], rbac.Rule{
RoleID: role2.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/%d", ns1.ID, mod1.ID),
Operation: "op4",
Access: rbac.Deny,
})
})
}
func compareRules(req *require.Assertions, a, b rbac.Rule) {
if a.RoleID != b.RoleID {
req.FailNow("RoleID missmatch")
}
if a.Resource != b.Resource {
req.FailNow("Resource missmatch")
}
if a.Operation != b.Operation {
req.FailNow("Operation missmatch")
}
if a.Access != b.Access {
req.FailNow("Access missmatch")
}
}
+33
View File
@@ -0,0 +1,33 @@
namespace:
test_ns_1:
name: Test Namespace 1
modules:
test_mod_1:
name: Test Module 1
roles:
test_role_1:
name: Test Role 1
test_role_2:
name: Test Role 2
allow:
test_role_1:
corteza::compose:namespace/*:
- op1
corteza::compose:namespace/test_ns_1:
- op2
corteza::compose:module/test_ns_1/*:
- op3
corteza::compose:module/test_ns_1/test_mod_1:
- op4
deny:
test_role_2:
corteza::compose:namespace/*:
- op1
corteza::compose:namespace/test_ns_1:
- op2
corteza::compose:module/test_ns_1/*:
- op3
corteza::compose:module/test_ns_1/test_mod_1:
- op4