Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
3780 lines
94 KiB
Go
Generated
3780 lines
94 KiB
Go
Generated
package envoy
|
|
|
|
// This file is auto-generated.
|
|
//
|
|
// Changes to this file may cause incorrect behavior and will be lost if
|
|
// the code is regenerated.
|
|
//
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"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"
|
|
systemTypes "github.com/cortezaproject/corteza/server/system/types"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cast"
|
|
"golang.org/x/text/language"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type (
|
|
// YamlDecoder is responsible for decoding YAML documents into Corteza resources
|
|
// which are then managed by envoy and imported via an encoder.
|
|
YamlDecoder struct{}
|
|
|
|
// documentContext provides a bit of metadata to the decoder such as
|
|
// root-level reference definitions (such as the namespace)
|
|
documentContext struct {
|
|
// references holds any references defined on the root of the document
|
|
//
|
|
// Example of defining a namespace reference:
|
|
// namespace: test_import_namespace
|
|
// modules:
|
|
// - name: Test Import Module
|
|
// handle: test_import_module
|
|
references map[string]string
|
|
|
|
// parentIdent holds the identifier of the parent resource (if nested).
|
|
// The parent ident can be handy if the resource requires the info before the
|
|
// original handling is finished (such as record datasource nodes).
|
|
parentIdent envoyx.Identifiers
|
|
}
|
|
|
|
// auxYamlDoc is a helper struct that registers custom YAML decoders
|
|
// to assist the package
|
|
auxYamlDoc struct {
|
|
nodes envoyx.NodeSet
|
|
}
|
|
)
|
|
|
|
const (
|
|
paramsKeyStream = "stream"
|
|
)
|
|
|
|
// CanFile returns true if the provided file can be decoded with this decoder
|
|
func (d YamlDecoder) CanFile(f *os.File) (ok bool) {
|
|
// @todo improve this check; for now a simple extension check should do the trick
|
|
return d.CanExt(f.Name())
|
|
}
|
|
|
|
// CanExt returns true if the provided file extension can be decoded with this decoder
|
|
func (d YamlDecoder) CanExt(name string) (ok bool) {
|
|
var (
|
|
pt = strings.Split(name, ".")
|
|
ext = strings.TrimSpace(pt[len(pt)-1])
|
|
)
|
|
return ext == "yaml" || ext == "yml"
|
|
}
|
|
|
|
// Decode returns a set of envoy nodes based on the provided params
|
|
//
|
|
// YamlDecoder expects the DecodeParam of `stream` which conforms
|
|
// to the io.Reader interface.
|
|
func (d YamlDecoder) Decode(ctx context.Context, p envoyx.DecodeParams) (out envoyx.NodeSet, err error) {
|
|
r, err := d.getReader(ctx, p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Offload decoding to the aux document
|
|
doc := &auxYamlDoc{}
|
|
err = yaml.NewDecoder(r).Decode(doc)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "system yaml decoder: failed to decode document")
|
|
return
|
|
}
|
|
|
|
return doc.nodes, nil
|
|
}
|
|
|
|
func (d *auxYamlDoc) UnmarshalYAML(n *yaml.Node) (err error) {
|
|
// Get the document context from the root level
|
|
dctx, err := d.getDocumentContext(n)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var aux envoyx.NodeSet
|
|
return y7s.EachMap(n, func(k, v *yaml.Node) error {
|
|
kv := strings.ToLower(k.Value)
|
|
|
|
switch kv {
|
|
// Decode all resources under the system component
|
|
case "application", "applications", "apps":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalApplicationMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalApplicationSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: application")
|
|
}
|
|
return err
|
|
|
|
case "apigwroute", "endpoints":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalApigwRouteMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalApigwRouteSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: apigwRoute")
|
|
}
|
|
return err
|
|
|
|
case "apigwfilter":
|
|
// @note apigwFilter doesn't support mapped inputs. This can be
|
|
// changed in the .cue definition under the
|
|
// .envoy.yaml.supportMappedInput field.
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalApigwFilterSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: apigwFilter")
|
|
}
|
|
return err
|
|
|
|
case "authclient", "authclients":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalAuthClientMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalAuthClientSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: authClient")
|
|
}
|
|
return err
|
|
|
|
case "queue":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalQueueMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalQueueSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: queue")
|
|
}
|
|
return err
|
|
|
|
case "report", "reports":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalReportMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalReportSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: report")
|
|
}
|
|
return err
|
|
|
|
case "role", "roles":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalRoleMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalRoleSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: role")
|
|
}
|
|
return err
|
|
|
|
case "template", "templates":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalTemplateMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalTemplateSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: template")
|
|
}
|
|
return err
|
|
|
|
case "user", "users", "usr":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalUserMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalUserSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: user")
|
|
}
|
|
return err
|
|
|
|
case "dalconnection", "connection", "connections":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalDalConnectionMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalDalConnectionSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: dalConnection")
|
|
}
|
|
return err
|
|
|
|
case "dalsensitivitylevel", "sensitivity_level":
|
|
if y7s.IsMapping(v) {
|
|
aux, err = d.unmarshalDalSensitivityLevelMap(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
|
|
if y7s.IsSeq(v) {
|
|
aux, err = d.unmarshalDalSensitivityLevelSeq(dctx, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
}
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: dalSensitivityLevel")
|
|
}
|
|
return err
|
|
|
|
// Decode access control nodes
|
|
case "allow":
|
|
aux, err = unmarshalAllowNode(v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: RBAC allow")
|
|
}
|
|
|
|
case "deny":
|
|
aux, err = unmarshalDenyNode(v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: RBAC deny")
|
|
}
|
|
|
|
// Resource translation nodes
|
|
case "locale", "translation", "translations", "i18n":
|
|
aux, err = unmarshalLocaleNode(v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node: locale")
|
|
}
|
|
|
|
// Offload to custom handlers
|
|
default:
|
|
aux, err = d.unmarshalYAML(kv, v)
|
|
d.nodes = append(d.nodes, aux...)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "failed to unmarshal node")
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource application
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalApplicationSeq unmarshals Application when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalApplicationSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalApplicationNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalApplicationMap unmarshals Application when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalApplicationMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalApplicationNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalApplicationNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalApplicationNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.Application
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Name", &r.Name)
|
|
ii = ii.Add(r.Name)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "ownerid", "owner":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "ownerID", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["OwnerID"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under application such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["ApplicationID"]; !ok {
|
|
a.References["ApplicationID"] = envoyx.Ref{
|
|
ResourceType: types.ApplicationResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.ApplicationResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource apigwRoute
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalApigwRouteSeq unmarshals ApigwRoute when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalApigwRouteSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalApigwRouteNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalApigwRouteMap unmarshals ApigwRoute when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalApigwRouteMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalApigwRouteNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalFiltersExtendedSeq unmarshals Filters when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalExtendedFiltersSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalFiltersExtendedNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalApigwRouteNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalApigwRouteNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.ApigwRoute
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Endpoint", &r.Endpoint)
|
|
ii = ii.Add(r.Endpoint)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "group":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "group", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["Group"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:apigw-group",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under apigwRoute such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "filters":
|
|
if y7s.IsSeq(n) {
|
|
nestedNodes, err = d.unmarshalExtendedFiltersSeq(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["ApigwRouteID"]; !ok {
|
|
a.References["ApigwRouteID"] = envoyx.Ref{
|
|
ResourceType: types.ApigwRouteResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.ApigwRouteResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource apigwFilter
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalApigwFilterSeq unmarshals ApigwFilter when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalApigwFilterSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalApigwFilterNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalApigwFilterMap unmarshals ApigwFilter when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
// @note this resource does not support map encoding.
|
|
// Refer to the corresponding definition files to adjust if needed.
|
|
|
|
// unmarshalApigwFilterNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalApigwFilterNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.ApigwFilter
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "route":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "route", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["Route"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:apigw-route",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under apigwFilter such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["ApigwFilterID"]; !ok {
|
|
a.References["ApigwFilterID"] = envoyx.Ref{
|
|
ResourceType: types.ApigwFilterResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.ApigwFilterResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource authClient
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalAuthClientSeq unmarshals AuthClient when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalAuthClientSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalAuthClientNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalAuthClientMap unmarshals AuthClient when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalAuthClientMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalAuthClientNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalAuthClientNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalAuthClientNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.AuthClient
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "ownedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "ownedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["OwnedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "security":
|
|
|
|
// Handle custom node decoder
|
|
//
|
|
// The decoder may update the passed resource with arbitrary values
|
|
// as well as provide additional references and identifiers for the node.
|
|
var (
|
|
auxRefs map[string]envoyx.Ref
|
|
auxIdents envoyx.Identifiers
|
|
)
|
|
auxRefs, auxIdents, err = d.unmarshalAuthClientSecurityNode(r, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
refs = envoyx.MergeRefs(refs, auxRefs)
|
|
ii = ii.Merge(auxIdents)
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under authClient such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["AuthClientID"]; !ok {
|
|
a.References["AuthClientID"] = envoyx.Ref{
|
|
ResourceType: types.AuthClientResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.AuthClientResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource queue
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalQueueSeq unmarshals Queue when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalQueueSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalQueueNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalQueueMap unmarshals Queue when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalQueueMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalQueueNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalQueueNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalQueueNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.Queue
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Queue", &r.Queue)
|
|
ii = ii.Add(r.Queue)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "queue":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "queue", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under queue such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["QueueID"]; !ok {
|
|
a.References["QueueID"] = envoyx.Ref{
|
|
ResourceType: types.QueueResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.QueueResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource report
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalReportSeq unmarshals Report when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalReportSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalReportNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalReportMap unmarshals Report when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalReportMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalReportNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalReportNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalReportNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.Report
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "ownedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "ownedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["OwnedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under report such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["ReportID"]; !ok {
|
|
a.References["ReportID"] = envoyx.Ref{
|
|
ResourceType: types.ReportResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.ReportResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource role
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalRoleSeq unmarshals Role when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalRoleSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalRoleNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalRoleMap unmarshals Role when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalRoleMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalRoleNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalRoleNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalRoleNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.Role
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under role such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["RoleID"]; !ok {
|
|
a.References["RoleID"] = envoyx.Ref{
|
|
ResourceType: types.RoleResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.RoleResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource template
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalTemplateSeq unmarshals Template when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalTemplateSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalTemplateNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalTemplateMap unmarshals Template when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalTemplateMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalTemplateNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalTemplateNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalTemplateNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.Template
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "ownerid":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "ownerID", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["OwnerID"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under template such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["TemplateID"]; !ok {
|
|
a.References["TemplateID"] = envoyx.Ref{
|
|
ResourceType: types.TemplateResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.TemplateResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource user
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalUserSeq unmarshals User when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalUserSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalUserNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalUserMap unmarshals User when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalUserMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalUserNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalUserNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalUserNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.User
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "roles":
|
|
|
|
// Handle custom node decoder
|
|
//
|
|
// The decoder may update the passed resource with arbitrary values
|
|
// as well as provide additional references and identifiers for the node.
|
|
var (
|
|
auxRefs map[string]envoyx.Ref
|
|
auxIdents envoyx.Identifiers
|
|
)
|
|
auxRefs, auxIdents, err = d.unmarshalUserRolesNode(r, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
refs = envoyx.MergeRefs(refs, auxRefs)
|
|
ii = ii.Merge(auxIdents)
|
|
|
|
break
|
|
|
|
case "usergroupid", "usergroup", "user_group", "user_group_id", "group":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "userGroupID", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UserGroupID"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user-group",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under user such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["UserID"]; !ok {
|
|
a.References["UserID"] = envoyx.Ref{
|
|
ResourceType: types.UserResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.UserResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource dalConnection
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalDalConnectionSeq unmarshals DalConnection when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalDalConnectionSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalDalConnectionNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalDalConnectionMap unmarshals DalConnection when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalDalConnectionMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalDalConnectionNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalDalConnectionNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalDalConnectionNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.DalConnection
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
rbacNodes envoyx.NodeSet
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
// Handle RBAC rules
|
|
case "allow":
|
|
auxOut, err = unmarshalAllowNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
|
|
case "deny":
|
|
auxOut, err = unmarshalDenyNode(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rbacNodes = append(rbacNodes, auxOut...)
|
|
auxOut = nil
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under dalConnection such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["DalConnectionID"]; !ok {
|
|
a.References["DalConnectionID"] = envoyx.Ref{
|
|
ResourceType: types.DalConnectionResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.DalConnectionResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
// Update RBAC resource nodes with references regarding the resource
|
|
rres := r.RbacResource()
|
|
for _, rn := range rbacNodes {
|
|
aux := rn.Resource.(*rbac.Rule)
|
|
if aux.Resource == "" {
|
|
aux.Resource = rres
|
|
}
|
|
|
|
// Since the rule belongs to the resource, it will have the same
|
|
// subset of references as the parent resource.
|
|
rn.References = envoyx.MergeRefs(rn.References, a.References)
|
|
|
|
// The RBAC rule's most specific identifier is the resource itself.
|
|
// Using this we can hardcode it to point to the location after the parent resource.
|
|
//
|
|
// @todo consider using a more descriptive identifier for the position
|
|
// such as `index-%d`.
|
|
rn.References["0"] = envoyx.Ref{
|
|
ResourceType: a.ResourceType,
|
|
Identifiers: a.Identifiers,
|
|
Scope: scope,
|
|
}
|
|
|
|
for _, r := range rn.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
rn.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
out = append(out, rbacNodes...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Functions for resource dalSensitivityLevel
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
// unmarshalDalSensitivityLevelSeq unmarshals DalSensitivityLevel when provided as a sequence node
|
|
func (d *auxYamlDoc) unmarshalDalSensitivityLevelSeq(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachSeq(n, func(n *yaml.Node) error {
|
|
aux, err = d.unmarshalDalSensitivityLevelNode(dctx, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalDalSensitivityLevelMap unmarshals DalSensitivityLevel when provided as a mapping node
|
|
//
|
|
// When map encoded, the map key is used as a preset identifier.
|
|
// The identifier is passed to the node function as a meta node
|
|
func (d *auxYamlDoc) unmarshalDalSensitivityLevelMap(dctx documentContext, n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var aux envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
aux, err = d.unmarshalDalSensitivityLevelNode(dctx, n, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, aux...)
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// unmarshalDalSensitivityLevelNode is a cookie-cutter function to unmarshal
|
|
// the yaml node into the corresponding Corteza type & Node
|
|
func (d *auxYamlDoc) unmarshalDalSensitivityLevelNode(dctx documentContext, n *yaml.Node, meta ...*yaml.Node) (out envoyx.NodeSet, err error) {
|
|
var r *types.DalSensitivityLevel
|
|
|
|
// @todo we're omitting errors because there will be a bunch due to invalid
|
|
// resource field types. This might be a bit unstable as other errors may
|
|
// also get ignored.
|
|
//
|
|
// A potential fix would be to firstly unmarshal into an any, check errors
|
|
// and then unmarshal into the resource while omitting errors.
|
|
n.Decode(&r)
|
|
|
|
// Identifiers are determined manually when iterating the yaml node.
|
|
// This is to help assure there are no duplicates and everything
|
|
// was accounted for especially when working with aliases such as
|
|
// user_name instead of userName.
|
|
ii := envoyx.Identifiers{}
|
|
|
|
// When a resource supports mapped input, the key is passed as meta which
|
|
// needs to be registered as an identifier (since it is)
|
|
if len(meta) > 0 {
|
|
y7s.DecodeScalar(meta[0], "Handle", &r.Handle)
|
|
ii = ii.Add(r.Handle)
|
|
}
|
|
|
|
var (
|
|
refs = make(map[string]envoyx.Ref)
|
|
auxOut envoyx.NodeSet
|
|
nestedNodes envoyx.NodeSet
|
|
scope envoyx.Scope
|
|
envoyConfig envoyx.EnvoyConfig
|
|
)
|
|
_ = auxOut
|
|
_ = refs
|
|
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
var auxNodeValue any
|
|
_ = auxNodeValue
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
case "createdby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "createdBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["CreatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "deletedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "deletedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["DeletedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "handle":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "handle", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "id":
|
|
// Handle identifiers
|
|
err = y7s.DecodeScalar(n, "id", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ii = ii.Add(auxNodeValue)
|
|
|
|
break
|
|
|
|
case "updatedby":
|
|
// Handle references
|
|
err = y7s.DecodeScalar(n, "updatedBy", &auxNodeValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Omit if not defined
|
|
tmp := cast.ToString(auxNodeValue)
|
|
if tmp == "0" || tmp == "" {
|
|
break
|
|
}
|
|
refs["UpdatedBy"] = envoyx.Ref{
|
|
ResourceType: "corteza::system:user",
|
|
Identifiers: envoyx.MakeIdentifiers(auxNodeValue),
|
|
}
|
|
|
|
break
|
|
|
|
case "(envoy)":
|
|
envoyConfig = d.decodeEnvoyConfig(n)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Make parent identifiers available through the dctx
|
|
dctx.parentIdent = ii
|
|
|
|
// Apply the scope to all of the references of the same type
|
|
for k, ref := range refs {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
refs[k] = ref
|
|
}
|
|
|
|
// Handle any resources that could be inserted under dalSensitivityLevel such as a module inside a namespace
|
|
//
|
|
// This operation is done in the second pass of the document so we have
|
|
// the complete context of the current resource; such as the identifier,
|
|
// references, and scope.
|
|
var auxNestedNodes envoyx.NodeSet
|
|
err = y7s.EachMap(n, func(k, n *yaml.Node) error {
|
|
nestedNodes = nil
|
|
|
|
switch strings.ToLower(k.Value) {
|
|
|
|
}
|
|
|
|
// Iterate nested nodes and update their reference to the current resource
|
|
//
|
|
// Any reference to the parent resource from the child resource is overwritten
|
|
// to avoid potential user-error edge cases.
|
|
for _, a := range nestedNodes {
|
|
// @note all nested resources fall under the same component and the same scope.
|
|
// Simply assign the same scope to all -- if it shouldn't be scoped
|
|
// the parent won't have it (saving CPU ticks :)
|
|
a.Scope = scope
|
|
|
|
if a.References == nil {
|
|
a.References = make(map[string]envoyx.Ref)
|
|
}
|
|
|
|
if _, ok := a.References["DalSensitivityLevelID"]; !ok {
|
|
a.References["DalSensitivityLevelID"] = envoyx.Ref{
|
|
ResourceType: types.DalSensitivityLevelResourceType,
|
|
Identifiers: ii,
|
|
Scope: scope,
|
|
}
|
|
}
|
|
|
|
for f, ref := range a.References {
|
|
if !strings.HasPrefix(ref.ResourceType, "corteza::compose") {
|
|
continue
|
|
}
|
|
ref.Scope = scope
|
|
a.References[f] = ref
|
|
}
|
|
|
|
for f, ref := range refs {
|
|
// Only inherit root references
|
|
// @todo improve; this is a hack
|
|
if strings.Contains(f, ".") {
|
|
continue
|
|
}
|
|
|
|
// Only assume refs if they're not yet set
|
|
if _, ok := a.References[f]; ok {
|
|
continue
|
|
}
|
|
|
|
a.References[f] = ref
|
|
}
|
|
}
|
|
auxNestedNodes = append(auxNestedNodes, nestedNodes...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
out = append(out, auxNestedNodes...)
|
|
|
|
a := &envoyx.Node{
|
|
Resource: r,
|
|
|
|
ResourceType: types.DalSensitivityLevelResourceType,
|
|
Identifiers: ii,
|
|
References: refs,
|
|
|
|
Config: envoyConfig,
|
|
}
|
|
|
|
// Put it all together...
|
|
out = append(out, a)
|
|
out = append(out, auxOut...)
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// RBAC unmarshal logic
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
func unmarshalAllowNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
return unmarshalRBACNode(n, rbac.Allow)
|
|
}
|
|
|
|
func unmarshalDenyNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
return unmarshalRBACNode(n, rbac.Deny)
|
|
}
|
|
|
|
func unmarshalRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
|
|
if y7s.IsMapping(n.Content[1]) {
|
|
out, err = unmarshalFlatRBACNode(n, acc)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
out, err = unmarshalNestedRBACNode(n, acc)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
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
|
|
//
|
|
// The edge-case exists since the node doesn't explicitly specify the resource
|
|
// it belongs to.
|
|
//
|
|
// Example:
|
|
//
|
|
// modules:
|
|
//
|
|
// module1:
|
|
// name: "module 1"
|
|
// fields: ...
|
|
// allow:
|
|
// role1:
|
|
// - read
|
|
// - delete
|
|
func unmarshalNestedRBACNode(n *yaml.Node, acc rbac.Access) (out envoyx.NodeSet, err error) {
|
|
return out, y7s.EachMap(n, func(role, op *yaml.Node) error {
|
|
out = append(out, &envoyx.Node{
|
|
Resource: &rbac.Rule{
|
|
Operation: op.Value,
|
|
Access: acc,
|
|
},
|
|
ResourceType: rbac.RuleResourceType,
|
|
References: 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),
|
|
},
|
|
},
|
|
})
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// unmarshalFlatRBACNode handles RBAC rules when they are provided on the root level
|
|
//
|
|
// Example:
|
|
//
|
|
// allow:
|
|
//
|
|
// role1:
|
|
// corteza::system/:
|
|
// - users.search
|
|
// - users.create
|
|
func unmarshalFlatRBACNode(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 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
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// i18n unmarshal logic
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
func unmarshalLocaleNode(n *yaml.Node) (out envoyx.NodeSet, err error) {
|
|
err = y7s.EachMap(n, func(lang, loc *yaml.Node) error {
|
|
langTag := systemTypes.Lang{Tag: language.Make(lang.Value)}
|
|
|
|
return y7s.EachMap(loc, func(res, kv *yaml.Node) error {
|
|
return y7s.EachMap(kv, func(k, msg *yaml.Node) error {
|
|
out = append(out, &envoyx.Node{
|
|
Resource: &systemTypes.ResourceTranslation{
|
|
Resource: res.Value,
|
|
Lang: langTag,
|
|
K: k.Value,
|
|
Message: msg.Value,
|
|
},
|
|
// Providing resource type as plain text to reduce cross component references
|
|
ResourceType: "corteza::system:resource-translation",
|
|
References: envoyx.SplitResourceIdentifier(res.Value),
|
|
})
|
|
return nil
|
|
})
|
|
})
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, o := range out {
|
|
for _, r := range o.References {
|
|
if r.Scope.IsEmpty() {
|
|
continue
|
|
}
|
|
o.Scope = r.Scope
|
|
break
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Envoy config unmarshal logic
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
func (d *auxYamlDoc) decodeEnvoyConfig(n *yaml.Node) (out envoyx.EnvoyConfig) {
|
|
y7s.EachMap(n, func(k, v *yaml.Node) (err error) {
|
|
switch strings.ToLower(k.Value) {
|
|
case "skipif", "skip":
|
|
return y7s.DecodeScalar(v, "decode skip if", &out.SkipIf)
|
|
case "onexisting", "mergealg":
|
|
out.MergeAlg = envoyx.CastMergeAlg(v.Value)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
// Utilities
|
|
// // // // // // // // // // // // // // // // // // // // // // // // //
|
|
|
|
func (d YamlDecoder) getReader(ctx context.Context, p envoyx.DecodeParams) (r io.Reader, err error) {
|
|
aux, ok := p.Params[paramsKeyStream]
|
|
if ok {
|
|
r, ok = aux.(io.Reader)
|
|
if ok {
|
|
return
|
|
}
|
|
}
|
|
|
|
// @todo consider adding support for managing files from a location
|
|
err = errors.Errorf("YAML decoder expects a stream conforming to io.Reader interface")
|
|
return
|
|
}
|
|
|
|
func (d *auxYamlDoc) getDocumentContext(n *yaml.Node) (dctx documentContext, err error) {
|
|
dctx = documentContext{
|
|
references: make(map[string]string),
|
|
}
|
|
|
|
err = y7s.EachMap(n, func(k, v *yaml.Node) error {
|
|
// @todo expand when needed. The previous implementation only supported
|
|
// namespaces on the root of the document.
|
|
|
|
if y7s.IsKind(v, yaml.ScalarNode) {
|
|
dctx.references[k.Value] = v.Value
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|