3
0

Define base Compose resources supported by envoy

* ComposeNamespace,
* ComposeModule (+ fields),
* ComposeRecord (+ values),
* RBAC permissions.
This commit is contained in:
Tomaž Jerman 2020-10-19 16:58:05 +02:00
parent e7d1dbb357
commit d076dbd70f
9 changed files with 485 additions and 1 deletions

View File

@ -4,8 +4,9 @@ import (
"database/sql/driver"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"strconv"
"github.com/pkg/errors"
)
type (
@ -91,6 +92,19 @@ func (opt ModuleFieldOptions) Strings(key string) []string {
return nil
}
// String returns option value for key as single string
//
// Invalid, non-existing are returned as empty string ("")
func (opt ModuleFieldOptions) String(key string) string {
if _, has := opt[key]; has {
if v, ok := opt[key].(string); ok {
return v
}
}
return ""
}
// IsUnique - should value in this field be unique across records?
func (opt ModuleFieldOptions) IsUnique() bool {
return opt.Bool(moduleFieldOptionIsUnique)

View File

@ -0,0 +1,86 @@
package types
import (
compTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/envoy/util"
"gopkg.in/yaml.v3"
)
type (
ComposeModuleField struct {
compTypes.ModuleField `yaml:",inline"`
}
ComposeModuleFieldSet []*ComposeModuleField
ComposeModule struct {
compTypes.Module `yaml:",inline"`
Fields ComposeModuleFieldSet `yaml:"fields"`
Rbac `yaml:",inline"`
}
ComposeModuleSet []*ComposeModule
)
func (mm *ComposeModuleSet) UnmarshalYAML(n *yaml.Node) error {
cms := ComposeModuleSet{}
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
handle := ""
if n != nil {
handle = n.Value
}
mod := &ComposeModule{}
err := m.Decode(mod)
if err != nil {
return err
}
if mod.Handle == "" {
mod.Handle = handle
}
if mod.Name == "" {
mod.Name = handle
}
cms = append(cms, mod)
return nil
})
if err != nil {
return err
}
*mm = cms
return nil
}
func (ff *ComposeModuleFieldSet) UnmarshalYAML(n *yaml.Node) error {
ffs := ComposeModuleFieldSet{}
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
name := ""
if n != nil {
name = n.Value
}
f := &ComposeModuleField{}
err := m.Decode(f)
if err != nil {
return err
}
if f.Name == "" {
f.Name = name
}
if f.Label == "" {
f.Label = name
}
ffs = append(ffs, f)
return nil
})
if err != nil {
return err
}
*ff = ffs
return nil
}

View File

@ -0,0 +1,110 @@
package types
import (
"strconv"
"github.com/cortezaproject/corteza-server/compose/types"
)
type (
// ComposeModuleNode represents a ComposeModule
ComposeModuleNode struct {
Mod *ComposeModule
// Related namespace
Ns *ComposeNamespace
}
)
func (n *ComposeModuleNode) Identifiers() NodeIdentifiers {
ii := make(NodeIdentifiers, 0)
if n.Mod.Handle != "" {
ii = ii.Add(n.Mod.Handle)
}
if n.Mod.Name != "" {
ii = ii.Add(n.Mod.Name)
}
if n.Mod.ID > 0 {
ii = ii.Add(strconv.FormatUint(n.Mod.ID, 10))
}
return ii
}
func (n *ComposeModuleNode) Matches(resource string, identifiers ...string) bool {
if resource != n.Resource() {
return false
}
return n.Identifiers().HasAny(identifiers...)
}
func (n *ComposeModuleNode) Resource() string {
return types.ModuleRBACResource.String()
}
func (n *ComposeModuleNode) Relations() NodeRelationships {
rel := make(NodeRelationships)
// Related namespace
nsr := types.NamespaceRBACResource.String()
if n.Ns.Slug != "" {
rel.Add(nsr, n.Ns.Slug)
}
if n.Ns.Name != "" {
rel.Add(nsr, n.Ns.Name)
}
if n.Ns.ID > 0 {
rel.Add(nsr, strconv.FormatUint(n.Ns.ID, 10))
}
// Related modules via Record module fields
mdr := types.ModuleRBACResource.String()
for _, f := range n.Mod.Fields {
// @todo should a missing module property raise an error?
if f.Kind == "Record" && f.Options.String("module") != "" {
rel.Add(mdr, f.Options.String("module"))
}
}
return rel
}
func (n *ComposeModuleNode) Update(mm ...Node) {
for _, m := range mm {
n.updateNamespace(m)
n.updateRecFields(m)
}
}
func (n *ComposeModuleNode) updateNamespace(m Node) {
if m.Resource() != types.NamespaceRBACResource.String() {
return
}
mn := m.(*ComposeNamespaceNode)
n.Ns = mn.Ns
}
func (n *ComposeModuleNode) updateRecFields(m Node) {
if m.Resource() != types.ModuleRBACResource.String() {
return
}
mn := m.(*ComposeModuleNode)
// Check what record module field we can link this to
for _, f := range n.Mod.Fields {
if f.Kind != "Record" {
continue
}
if mn.Identifiers().HasAny(f.Options.String("module")) {
f.Options["module"] = strconv.FormatUint(mn.Mod.ID, 10)
}
}
n.Ns = mn.Ns
}

View File

@ -0,0 +1,48 @@
package types
import (
compTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/envoy/util"
"gopkg.in/yaml.v3"
)
type (
ComposeNamespace struct {
compTypes.Namespace `yaml:",inline"`
Modules ComposeModuleSet
Rbac `yaml:",inline"`
}
ComposeNamespaceSet []*ComposeNamespace
)
func (ss *ComposeNamespaceSet) UnmarshalYAML(n *yaml.Node) error {
nss := ComposeNamespaceSet{}
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
slug := ""
if n != nil {
slug = n.Value
}
ns := &ComposeNamespace{}
err := m.Decode(ns)
if err != nil {
return err
}
if ns.Slug == "" {
ns.Slug = slug
}
if ns.Name == "" {
ns.Name = slug
}
nss = append(nss, ns)
return nil
})
if err != nil {
return err
}
*ss = nss
return nil
}

View File

@ -0,0 +1,45 @@
package types
import (
"strconv"
"github.com/cortezaproject/corteza-server/compose/types"
)
type (
ComposeNamespaceNode struct {
Ns *ComposeNamespace
}
)
func (n *ComposeNamespaceNode) Identifiers() NodeIdentifiers {
ii := make(NodeIdentifiers, 0)
if n.Ns.Slug != "" {
ii = ii.Add(n.Ns.Slug)
}
if n.Ns.Name != "" {
ii = ii.Add(n.Ns.Name)
}
if n.Ns.ID > 0 {
ii = ii.Add(strconv.FormatUint(n.Ns.ID, 10))
}
return ii
}
func (n *ComposeNamespaceNode) Matches(resource string, identifiers ...string) bool {
if resource != n.Resource() {
return false
}
return n.Identifiers().HasAny(identifiers...)
}
func (n *ComposeNamespaceNode) Resource() string {
return types.NamespaceRBACResource.String()
}
func (n *ComposeNamespaceNode) Relations() NodeRelationships {
return nil
}

View File

@ -0,0 +1,72 @@
package types
import (
"errors"
compTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/envoy/util"
"gopkg.in/yaml.v3"
)
type (
ComposeRecordValue struct {
compTypes.RecordValue `yaml:",inline"`
}
ComposeRecordValueSet []*ComposeRecordValue
ComposeRecord struct {
compTypes.Record `yaml:",inline"`
Values ComposeRecordValueSet `yaml:"values"`
}
ComposeRecordSet []*ComposeRecord
)
var (
ErrRecordDecoderMapNotSupported = errors.New("record decoder: maps not supported")
)
func (rr *ComposeRecordSet) UnmarshalYAML(n *yaml.Node) error {
crs := ComposeRecordSet{}
if n.Kind != yaml.SequenceNode {
return ErrRecordDecoderMapNotSupported
}
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
var r *ComposeRecord
err := m.Decode(&r)
if err != nil {
return err
}
crs = append(crs, r)
return nil
})
if err != nil {
return err
}
*rr = crs
return nil
}
func (vv *ComposeRecordValueSet) UnmarshalYAML(n *yaml.Node) error {
vvs := ComposeRecordValueSet{}
err := util.YamlIterator(n, func(n, m *yaml.Node) error {
v := &ComposeRecordValue{}
v.Name = n.Value
v.Value = m.Value
v.Updated = true
vvs = append(vvs, v)
return nil
})
if err != nil {
return err
}
*vv = vvs
return nil
}

View File

@ -0,0 +1,76 @@
package types
import (
"github.com/cortezaproject/corteza-server/compose/types"
)
type (
RecordIterator func(func(*ComposeRecord) error) error
ComposeRecordNode struct {
Walk RecordIterator
// Metafields for relationship management
Mod *ComposeModule
}
)
func (n *ComposeRecordNode) Identifiers() NodeIdentifiers {
return identifiersForModule(n.Mod)
}
func (n *ComposeRecordNode) Resource() string {
return "compose:record:"
}
func (n *ComposeRecordNode) Matches(resource string, identifiers ...string) bool {
if resource != n.Resource() {
return false
}
return n.Identifiers().HasAny(identifiers...)
}
func (n *ComposeRecordNode) Relations() NodeRelationships {
// This omits the namespace rel. as it's transitively implied via the modules
rel := make(NodeRelationships)
mdr := types.ModuleRBACResource.String()
rrr := "compose:record:"
if n.Mod == nil {
return rel
}
// Original module
mIdentifiers := identifiersForModule(n.Mod)
rel.Add(mdr, mIdentifiers...)
// Field relationships
for _, f := range n.Mod.Fields {
if f.Kind == "Record" {
modID := f.Options.String("module")
// For the module
rel.Add(mdr, modID)
// For the records.
// Since this record depends on another module, it also depends on those records.
rel.Add(rrr, modID)
}
}
return rel
}
func (n *ComposeRecordNode) Update(mm ...Node) {
for _, m := range mm {
switch m.Resource() {
case types.ModuleRBACResource.String():
mn, _ := m.(*ComposeModuleNode)
// Direct module dependency
if n.Matches(n.Resource(), identifiersForModule(mn.Mod)...) {
n.Mod = mn.Mod
}
}
}
}

9
pkg/envoy/types/rbac.go Normal file
View File

@ -0,0 +1,9 @@
package types
type (
RbacRules map[string][]string
Rbac struct {
Allow RbacRules
Deny RbacRules
}
)

24
pkg/envoy/types/util.go Normal file
View File

@ -0,0 +1,24 @@
package types
import "strconv"
// A little helper to extract identifiers for a given module
func identifiersForModule(m *ComposeModule) NodeIdentifiers {
ii := make(NodeIdentifiers, 0)
if m == nil {
return ii
}
if m.Handle != "" {
ii = ii.Add(m.Handle)
}
if m.Name != "" {
ii = ii.Add(m.Name)
}
if m.ID > 0 {
ii = ii.Add(strconv.FormatUint(m.ID, 10))
}
return ii
}