3
0

Improve envoy RBAC rule management

This commit is contained in:
Tomaž Jerman
2021-09-18 18:23:39 +02:00
parent 104bfb23de
commit e46812b8d3
33 changed files with 970 additions and 519 deletions
+9
View File
@@ -2,6 +2,7 @@ package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza-server/system/types"
)
@@ -37,6 +38,14 @@ func NewAPIGateway(res *types.ApigwRoute) *APIGateway {
return r
}
func (r *APIGateway) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.ApigwRouteRbacResourceTpl(), types.ApigwRouteResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10)))
return
}
func (r *APIGateway) AddGatewayFilter(res *types.ApigwFilter) *APIGatewayFilter {
f := &APIGatewayFilter{
base: &base{},
+9
View File
@@ -2,6 +2,7 @@ package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza-server/system/types"
)
@@ -37,6 +38,14 @@ func (r *Application) SysID() uint64 {
return r.Res.ID
}
func (r *Application) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.ApplicationRbacResourceTpl(), types.ApplicationResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Name))
return
}
// FindApplication looks for the app in the resource set
func FindApplication(rr InterfaceSet, ii Identifiers) (ap *types.Application) {
var apRes *Application
@@ -73,6 +73,14 @@ func (r *AutomationWorkflow) AddAutomationTrigger(res *types.Trigger) *Automatio
return t
}
func (r *AutomationWorkflow) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.WorkflowRbacResourceTpl(), types.WorkflowResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *AutomationWorkflow) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
+6 -2
View File
@@ -43,8 +43,12 @@ func (r *ComposeChart) SysID() uint64 {
return r.Res.ID
}
func (r *ComposeChart) RBACPath() []*Ref {
return []*Ref{r.RefNs}
func (r *ComposeChart) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.ChartRbacResourceTpl(), types.ChartResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// func (r *ComposeChart) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
+29 -35
View File
@@ -80,9 +80,12 @@ func (r *ComposeModule) SysID() uint64 {
return r.Res.ID
}
// @todo name
func (r *ComposeModule) RBACPath() []*Ref {
return []*Ref{r.RefNs}
func (r *ComposeModule) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.ModuleRbacResourceTpl(), types.ModuleResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *ComposeModule) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
@@ -133,46 +136,33 @@ func (r *ComposeModule) AddField(f *ComposeModuleField) {
r.ResFields = append(r.ResFields, f)
}
// func FindComposeModuleField(mod *ComposeModule, ii Identifiers) (f *ComposeModuleField) {
// for _, f := range mod.ResFields {
// if f.Identifiers().HasAny(ii) {
// return f
// }
// }
// FindComposeModule looks for the module in the resource set
func FindComposeModuleField(rr InterfaceSet, mod, ii Identifiers) (field *types.ModuleField) {
var fieldRes *ComposeModuleField
// return nil
// }
// FindComposeModuleField looks for the module field in the given module
func FindComposeModuleField(mod *types.Module, ii Identifiers) (f *types.ModuleField) {
ids := make(map[uint64]bool)
handles := make(map[string]bool)
for i := range ii {
auxID, err := cast.ToUint64E(i)
if err == nil {
ids[auxID] = true
continue
rr.Walk(func(r Interface) error {
mr, ok := r.(*ComposeModule)
if !ok {
return nil
}
handles[i] = true
}
var ok bool
ff, _ := mod.Fields.Filter(func(mf *types.ModuleField) (bool, error) {
if _, ok = ids[mf.ID]; ok {
return true, nil
}
if _, ok = handles[mf.Name]; ok {
return true, nil
if !mr.Identifiers().HasAny(mod) {
return nil
}
return false, nil
for _, rf := range mr.ResFields {
if rf.Identifiers().HasAny(ii) {
fieldRes = rf
}
}
return nil
})
if len(ff) == 0 {
return nil
// Found it
if fieldRes != nil {
return fieldRes.Res
}
return ff[0]
return nil
}
func (r *ComposeModuleField) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
@@ -217,6 +207,10 @@ func NewComposeModuleField(res *types.ModuleField, nsRef, modRef string) *Compos
// Initial timestamps
r.SetTimestamps(MakeTimestampsCUDA(&res.CreatedAt, res.UpdatedAt, res.DeletedAt, nil))
res.ID = 0
res.NamespaceID = 0
res.ModuleID = 0
return r
}
+8
View File
@@ -27,6 +27,14 @@ func NewComposeNamespace(ns *types.Namespace) *ComposeNamespace {
return r
}
func (r *ComposeNamespace) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.NamespaceRbacResourceTpl(), types.NamespaceResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Slug))
return
}
func (r *ComposeNamespace) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
+6 -2
View File
@@ -150,8 +150,12 @@ func (r *ComposePage) SysID() uint64 {
return r.Res.ID
}
func (r *ComposePage) RBACPath() []*Ref {
return []*Ref{r.RefNs}
func (r *ComposePage) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = []*Ref{r.RefNs}
resource = fmt.Sprintf(types.PageRbacResourceTpl(), types.PageResourceType, r.RefNs.Identifiers.First(), firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *ComposePage) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
+8 -2
View File
@@ -3,6 +3,7 @@ package resource
import (
"fmt"
"github.com/cortezaproject/corteza-server/compose/types"
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
systemTypes "github.com/cortezaproject/corteza-server/system/types"
)
@@ -71,8 +72,13 @@ func (r *ComposeRecord) SetUserFlakes(uu UserstampIndex) {
r.AddRef(systemTypes.UserResourceType, "*")
}
func (r *ComposeRecord) RBACPath() []*Ref {
return []*Ref{r.RefNs, r.RefMod}
func (r *ComposeRecord) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = []*Ref{r.RefNs, r.RefMod}
// @todo specific records
resource = fmt.Sprintf(types.RecordRbacResourceTpl(), types.RecordResourceType, r.RefNs.Identifiers.First(), r.RefMod.Identifiers.First(), "*")
return
}
func FindComposeRecordResource(rr InterfaceSet, ii Identifiers) (rec *ComposeRecord) {
+36 -7
View File
@@ -13,21 +13,23 @@ type (
*base
Res *rbac.Rule
RefResource string
RefRes *Ref
RefRole *Ref
RefResource *Ref
RefPath []*Ref
}
)
func NewRbacRule(res *rbac.Rule, refRole string, resRef *Ref, refPath ...*Ref) *RbacRule {
func NewRbacRule(res *rbac.Rule, refRole string, refRes *Ref, refResource string, refPath ...*Ref) *RbacRule {
r := &RbacRule{base: &base{}}
r.SetResourceType(RbacResourceType)
r.Res = res
r.RefRole = r.AddRef(types.RoleResourceType, refRole)
if resRef != nil {
r.RefResource = r.AddRef(resRef.ResourceType, resRef.Identifiers.StringSlice()...)
r.RefResource = refResource
if refRes != nil {
r.RefRes = r.AddRef(refRes.ResourceType, refRes.Identifiers.StringSlice()...)
}
// any additional constraints
@@ -35,14 +37,41 @@ func NewRbacRule(res *rbac.Rule, refRole string, resRef *Ref, refPath ...*Ref) *
r.RefPath = append(r.RefPath, r.AddRef(rp.ResourceType, rp.Identifiers.StringSlice()...))
}
// ComposeRecords are internally grouped and identified with the module identifier.
if res.Resource == composeTypes.RecordResourceType && resRef != nil && len(refPath) == 2 {
r.AddRef(res.Resource, refPath[1].Identifiers.StringSlice()...)
// Handle cases for nested resources
if refRes != nil {
switch refRes.ResourceType {
case composeTypes.RecordResourceType:
r.handleComposeRecord(res, refPath)
case composeTypes.ModuleFieldResourceType:
r.handleComposeModuleField(res, refPath)
}
}
return r
}
func (r *RbacRule) handleComposeRecord(res *rbac.Rule, refPath []*Ref) {
// records are grouped under module
if len(refPath) < 2 {
return
}
r.AddRef(composeTypes.RecordResourceType, refPath[1].Identifiers.StringSlice()...)
}
func (r *RbacRule) handleComposeModuleField(res *rbac.Rule, refPath []*Ref) {
// module fields are grouped under module
if len(refPath) < 2 {
return
}
r.AddRef(composeTypes.ModuleFieldResourceTranslationType, refPath[1].Identifiers.StringSlice()...)
}
func RbacResourceErrNotFound(ii Identifiers) error {
return fmt.Errorf("rbac resource not found %v", ii.StringSlice())
}
+8
View File
@@ -54,6 +54,14 @@ func (r *Report) AddReportSource(res *types.ReportDataSource) *ReportSource {
return s
}
func (r *Report) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.ReportRbacResourceTpl(), types.ReportResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
func (r *Report) ResourceTranslationParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
@@ -12,7 +12,6 @@ type (
*base
Res types.ResourceTranslationSet
// RelResource string
RefResource string
RefRes *Ref
RefPath []*Ref
+9
View File
@@ -2,6 +2,7 @@ package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza-server/system/types"
)
@@ -31,6 +32,14 @@ func (r *Role) SysID() uint64 {
return r.Res.ID
}
func (r *Role) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.RoleRbacResourceTpl(), types.RoleResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// FindRole looks for the role in the resources
func FindRole(rr InterfaceSet, ii Identifiers) (rl *types.Role) {
var rlRes *Role
+9
View File
@@ -2,6 +2,7 @@ package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza-server/system/types"
)
@@ -31,6 +32,14 @@ func (r *Template) SysID() uint64 {
return r.Res.ID
}
func (r *Template) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.TemplateRbacResourceTpl(), types.TemplateResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle))
return
}
// FindTemplate looks for the template in the resources
func FindTemplate(rr InterfaceSet, ii Identifiers) (u *types.Template) {
var tRes *Template
+1 -1
View File
@@ -29,7 +29,7 @@ type (
RBACInterface interface {
Interface
RBACPath() []*Ref
RBACParts() (string, *Ref, []*Ref)
}
LocaleInterface interface {
+9
View File
@@ -2,6 +2,7 @@ package resource
import (
"fmt"
"strconv"
"github.com/cortezaproject/corteza-server/system/types"
)
@@ -31,6 +32,14 @@ func (r *User) SysID() uint64 {
return r.Res.ID
}
func (r *User) RBACParts() (resource string, ref *Ref, path []*Ref) {
ref = r.Ref()
path = nil
resource = fmt.Sprintf(types.UserRbacResourceTpl(), types.UserResourceType, firstOkString(strconv.FormatUint(r.Res.ID, 10), r.Res.Handle, r.Res.Username))
return
}
// FindUser looks for the user in the resources
func FindUser(rr InterfaceSet, ii Identifiers) (u *types.User) {
var uRes *User
+1 -1
View File
@@ -6,7 +6,7 @@ import (
func firstOkString(ss ...string) string {
for _, s := range ss {
if s != "" {
if s != "" && s != "0" {
return s
}
}
+2 -2
View File
@@ -167,7 +167,7 @@ func (n *composeModule) Encode(ctx context.Context, pl *payload) (err error) {
f.DeletedAt = nil
f.CreatedAt = *now()
if f.Kind == "Record" {
if f.Options != nil && f.Kind == "Record" {
refMod := f.Options.String("module")
if refMod == "" {
refMod = f.Options.String("moduleID")
@@ -186,7 +186,7 @@ func (n *composeModule) Encode(ctx context.Context, pl *payload) (err error) {
delete(f.Options, "module")
}
if f.Kind == "User" {
if f.Options != nil && f.Kind == "User" {
roles := resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["roles"])
if len(roles) == 0 {
roles = resource.ComposeModuleFieldExtractUserFieldRoles(f.Options["role"])
+6 -3
View File
@@ -12,13 +12,16 @@ type (
rbacRule struct {
cfg *EncoderConfig
res *resource.RbacRule
rule *rbac.Rule
refRes *resource.Ref
// point to the rbac rule
refRbacResource string
refRbacRes *resource.Ref
refPathRes []*resource.Ref
relRole *types.Role
refRole *resource.Ref
role *types.Role
}
)
+306 -249
View File
@@ -3,6 +3,7 @@ package store
import (
"context"
"fmt"
"strings"
automationTypes "github.com/cortezaproject/corteza-server/automation/types"
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
@@ -16,54 +17,62 @@ import (
var (
gRbacRules map[string]bool
rbacRuleIndex = func(r *rbac.Rule) string {
return fmt.Sprintf("%d_%s_%s", r.RoleID, r.Resource, r.Operation)
}
)
func newRbacRuleFromResource(res *resource.RbacRule, cfg *EncoderConfig) resourceState {
return &rbacRule{
cfg: mergeConfig(cfg, res.Config()),
res: res,
rule: res.Res,
refRbacResource: res.RefResource,
refRbacRes: res.RefRes,
refPathRes: res.RefPath,
refRole: res.RefRole,
}
}
func (n *rbacRule) Prepare(ctx context.Context, pl *payload) (err error) {
// Init global state
if gRbacRules == nil {
gRbacRules = make(map[string]bool)
rr, _, err := store.SearchRbacRules(ctx, pl.s, rbac.RuleFilter{})
if err != store.ErrNotFound && err != nil {
return err
}
for _, r := range rr {
gRbacRules[rbacRuleIndex(r)] = true
err = n.initGlobalIndex(ctx, pl.s)
if err != nil {
return
}
}
// Related role
n.relRole, err = findRole(ctx, pl.s, pl.state.ParentResources, n.res.RefRole.Identifiers)
n.role, err = findRole(ctx, pl.s, pl.state.ParentResources, n.refRole.Identifiers)
if err != nil {
return err
}
if n.relRole == nil {
return resource.RoleErrUnresolved(n.res.RefRole.Identifiers)
if n.role == nil {
return resource.RoleErrUnresolved(n.refRole.Identifiers)
}
refRes := n.refRbacRes
if refRes == nil {
return
}
// For now we will only allow resource specific RBAC rules if that resource is
// also present.
// Here we check if we can find it in case we're handling a resource specific rule.
refRes := n.res.RefResource
if refRes != nil && refRes.ResourceType == composeTypes.RecordResourceType {
for _, r := range pl.state.ParentResources {
if r.ResourceType() == composeTypes.RecordResourceType && r.Identifiers().HasAny(n.res.RefPath[1].Identifiers) {
return
}
found := false
switch refRes.ResourceType {
case composeTypes.RecordResourceType:
if found = n.handleComposeRecord(pl.state.ParentResources); !found {
return resource.RbacResourceErrNotFound(refRes.Identifiers)
}
} else if refRes != nil && len(refRes.Identifiers) > 0 {
case composeTypes.ModuleFieldResourceType:
if found = n.handleComposeModuleField(pl.state.ParentResources); !found {
return resource.RbacResourceErrNotFound(refRes.Identifiers)
}
default:
for _, r := range pl.state.ParentResources {
if refRes.ResourceType == r.ResourceType() && r.Identifiers().HasAny(refRes.Identifiers) {
return
@@ -77,236 +86,25 @@ func (n *rbacRule) Prepare(ctx context.Context, pl *payload) (err error) {
}
func (n *rbacRule) Encode(ctx context.Context, pl *payload) (err error) {
res := n.res.Res
// Related role
res.RoleID = n.relRole.ID
if res.RoleID == 0 {
rl := resource.FindRole(pl.state.ParentResources, n.res.RefRole.Identifiers)
res.RoleID = rl.ID
}
if res.RoleID == 0 {
return resource.RoleErrUnresolved(n.res.RefRole.Identifiers)
// Assure correct resource
n.rule.Resource, err = n.makeRBACResource(pl)
if err != nil {
return err
}
p0ID := uint64(0)
p1ID := uint64(0)
p2ID := uint64(0)
switch n.rule.Resource {
case composeTypes.ComponentResourceType:
res.Resource = composeTypes.ComponentRbacResource()
goto store
case systemTypes.ComponentResourceType:
res.Resource = systemTypes.ComponentRbacResource()
goto store
case automationTypes.ComponentResourceType:
res.Resource = automationTypes.ComponentRbacResource()
goto store
case federationTypes.ComponentResourceType:
res.Resource = federationTypes.ComponentRbacResource()
goto store
// Assure correct role
n.rule.RoleID = n.role.ID
if n.rule.RoleID == 0 {
rl := resource.FindRole(pl.state.ParentResources, n.refRole.Identifiers)
n.rule.RoleID = rl.ID
}
if n.rule.RoleID == 0 {
return resource.RoleErrUnresolved(n.refRole.Identifiers)
}
switch n.rule.Resource {
case automationTypes.WorkflowResourceType:
if n.res.RefResource != nil {
p1 := resource.FindAutomationWorkflow(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.AutomationWorkflowErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = automationTypes.WorkflowRbacResource(p1ID)
case composeTypes.NamespaceResourceType:
if n.res.RefResource != nil {
p1 := resource.FindComposeNamespace(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.ComposeNamespaceErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = composeTypes.NamespaceRbacResource(p1ID)
case composeTypes.ModuleResourceType:
if len(n.res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(n.res.RefPath[0].Identifiers)
}
p0ID = p0.ID
}
if n.res.RefResource != nil {
p1 := resource.FindComposeModule(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.ComposeModuleErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = composeTypes.ModuleRbacResource(p0ID, p1ID)
case composeTypes.ChartResourceType:
if len(n.res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(n.res.RefPath[0].Identifiers)
}
p0ID = p0.ID
}
if n.res.RefResource != nil {
p1 := resource.FindComposeChart(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.ComposeChartErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = composeTypes.ChartRbacResource(p0ID, p1ID)
case composeTypes.PageResourceType:
if len(n.res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(n.res.RefPath[0].Identifiers)
}
p0ID = p0.ID
}
if n.res.RefResource != nil {
p1 := resource.FindComposePage(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.ComposePageErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = composeTypes.PageRbacResource(p0ID, p1ID)
case composeTypes.RecordResourceType:
if len(n.res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(n.res.RefPath[0].Identifiers)
}
p0ID = p0.ID
}
if len(n.res.RefPath) > 1 {
p1 := resource.FindComposeModule(pl.state.ParentResources, n.res.RefPath[1].Identifiers)
if p1 == nil {
return resource.ComposeModuleErrUnresolved(n.res.RefPath[1].Identifiers)
}
p1ID = p1.ID
}
if n.res.RefResource != nil {
ref := n.res.RefPath[1]
p2 := resource.FindComposeRecordResource(pl.state.ParentResources, ref.Identifiers)
if p2 == nil {
return resource.ComposeRecordErrUnresolved(n.res.RefResource.Identifiers)
}
for i := range n.res.RefResource.Identifiers {
if p2ID = p2.IDMap[i]; p2ID > 0 {
break
}
}
if p2ID == 0 {
return resource.ComposeRecordErrUnresolved(n.res.RefResource.Identifiers)
}
}
res.Resource = composeTypes.RecordRbacResource(p0ID, p1ID, p2ID)
case composeTypes.ModuleFieldResourceType:
if len(n.res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(n.res.RefPath[0].Identifiers)
}
p0ID = p0.ID
}
if len(n.res.RefPath) > 1 {
p1 := resource.FindComposeModule(pl.state.ParentResources, n.res.RefPath[1].Identifiers)
if p1 == nil {
return resource.ComposeModuleErrUnresolved(n.res.RefPath[1].Identifiers)
}
p1ID = p1.ID
}
// @todo specific ModuleField RBAC
res.Resource = composeTypes.ModuleFieldRbacResource(p0ID, p1ID, p2ID)
case systemTypes.UserResourceType:
if n.res.RefResource != nil {
p1 := resource.FindUser(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.UserErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = systemTypes.UserRbacResource(p1ID)
case systemTypes.RoleResourceType:
if n.res.RefResource != nil {
p1 := resource.FindRole(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.RoleErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = systemTypes.RoleRbacResource(p1ID)
case systemTypes.ApplicationResourceType:
if n.res.RefResource != nil {
p1 := resource.FindApplication(pl.state.ParentResources, n.res.RefResource.Identifiers)
if p1 == nil {
return resource.ApplicationErrUnresolved(n.res.RefResource.Identifiers)
}
p1ID = p1.ID
}
res.Resource = systemTypes.ApplicationRbacResource(p1ID)
case systemTypes.ApigwRouteResourceType:
res.Resource = systemTypes.ApigwRouteRbacResource(p1ID)
case systemTypes.ApigwFilterResourceType:
res.Resource = systemTypes.ApigwFilterRbacResource(p1ID)
case systemTypes.AuthClientResourceType:
// @todo add support for importing rbac rules for specific client
res.Resource = systemTypes.AuthClientRbacResource(p1ID)
case systemTypes.TemplateResourceType:
// @todo add support for importing rbac rules for specific template
res.Resource = systemTypes.TemplateRbacResource(p1ID)
case systemTypes.ReportResourceType:
res.Resource = systemTypes.ReportRbacResource(p1ID)
case messagebus.QueueResourceType:
// @todo add support for importing rbac rules for specific queue
res.Resource = messagebus.QueueRbacResource(p1ID)
case federationTypes.NodeResourceType:
// @todo add support for importing rbac rules for specific queue
res.Resource = federationTypes.NodeRbacResource(p1ID)
case federationTypes.SharedModuleResourceType:
// @todo add support for importing rbac rules for specific queue
res.Resource = federationTypes.SharedModuleRbacResource(p0ID, p1ID)
case federationTypes.ExposedModuleResourceType:
// @todo add support for importing rbac rules for specific queue
res.Resource = federationTypes.ExposedModuleRbacResource(p0ID, p1ID)
default:
// @todo if we wish to support rbac for external stuff, this needs to pass through.
// this also requires some tweaks in the path ID thing.
return fmt.Errorf("unsupported resource type '%s' for RBAC store encode", n.rule.Resource)
}
store:
if _, exists := gRbacRules[rbacRuleIndex(res)]; !exists {
return store.CreateRbacRule(ctx, pl.s, res)
// Upsert
if _, exists := gRbacRules[n.rbacRuleIndex((n.rule))]; !exists {
return store.CreateRbacRule(ctx, pl.s, n.rule)
}
// On existing rbac rule, replace/merge right basically overwrites the existing rule;
@@ -314,8 +112,267 @@ store:
switch n.cfg.OnExisting {
case resource.Replace,
resource.MergeRight:
return store.UpdateRbacRule(ctx, pl.s, res)
return store.UpdateRbacRule(ctx, pl.s, n.rule)
}
return nil
}
func (n *rbacRule) rbacRuleIndex(r *rbac.Rule) string {
return fmt.Sprintf("%d_%s_%s", r.RoleID, r.Resource, r.Operation)
}
func (n *rbacRule) initGlobalIndex(ctx context.Context, s store.Storer) error {
gRbacRules = make(map[string]bool)
rr, _, err := store.SearchRbacRules(ctx, s, rbac.RuleFilter{})
if err != store.ErrNotFound && err != nil {
return err
}
for _, r := range rr {
gRbacRules[n.rbacRuleIndex(r)] = true
}
return nil
}
func (n *rbacRule) makeRBACResource(pl *payload) (string, error) {
p0ID := uint64(0)
p1ID := uint64(0)
p2ID := uint64(0)
rt := strings.Split(n.refRbacResource, "/")[0]
// Component level stuff
switch rt {
case composeTypes.ComponentResourceType:
return composeTypes.ComponentRbacResource(), nil
case systemTypes.ComponentResourceType:
return systemTypes.ComponentRbacResource(), nil
case automationTypes.ComponentResourceType:
return automationTypes.ComponentRbacResource(), nil
case federationTypes.ComponentResourceType:
return federationTypes.ComponentRbacResource(), nil
}
// Component resource stuff
switch rt {
case automationTypes.WorkflowResourceType:
if n.refRbacRes != nil {
p1 := resource.FindAutomationWorkflow(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.AutomationWorkflowErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return automationTypes.WorkflowRbacResource(p1ID), nil
case composeTypes.NamespaceResourceType:
if n.refRbacRes != nil {
p1 := resource.FindComposeNamespace(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return composeTypes.NamespaceRbacResource(p1ID), nil
case composeTypes.ModuleResourceType:
if len(n.refPathRes) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
}
p0ID = p0.ID
}
if n.refRbacRes != nil {
p1 := resource.FindComposeModule(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.ComposeModuleErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return composeTypes.ModuleRbacResource(p0ID, p1ID), nil
case composeTypes.ChartResourceType:
if len(n.refPathRes) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
}
p0ID = p0.ID
}
if n.refRbacRes != nil {
p1 := resource.FindComposeChart(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.ComposeChartErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return composeTypes.ChartRbacResource(p0ID, p1ID), nil
case composeTypes.PageResourceType:
if len(n.refPathRes) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
}
p0ID = p0.ID
}
if n.refRbacRes != nil {
p1 := resource.FindComposePage(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.ComposePageErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return composeTypes.PageRbacResource(p0ID, p1ID), nil
case composeTypes.RecordResourceType:
if len(n.refPathRes) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
}
p0ID = p0.ID
}
if len(n.refPathRes) > 1 {
p1 := resource.FindComposeModule(pl.state.ParentResources, n.refPathRes[1].Identifiers)
if p1 == nil {
return "", resource.ComposeModuleErrUnresolved(n.refPathRes[1].Identifiers)
}
p1ID = p1.ID
}
if n.refRbacRes != nil {
ref := n.refPathRes[1]
p2 := resource.FindComposeRecordResource(pl.state.ParentResources, ref.Identifiers)
if p2 == nil {
return "", resource.ComposeRecordErrUnresolved(n.refRbacRes.Identifiers)
}
for i := range n.refRbacRes.Identifiers {
if p2ID = p2.IDMap[i]; p2ID > 0 {
break
}
}
if p2ID == 0 {
return "", resource.ComposeRecordErrUnresolved(n.refRbacRes.Identifiers)
}
}
return composeTypes.RecordRbacResource(p0ID, p1ID, p2ID), nil
case composeTypes.ModuleFieldResourceType:
if len(n.refPathRes) > 0 {
p0 := resource.FindComposeNamespace(pl.state.ParentResources, n.refPathRes[0].Identifiers)
if p0 == nil {
return "", resource.ComposeNamespaceErrUnresolved(n.refPathRes[0].Identifiers)
}
p0ID = p0.ID
}
if len(n.refPathRes) > 1 {
p1 := resource.FindComposeModule(pl.state.ParentResources, n.refPathRes[1].Identifiers)
if p1 == nil {
return "", resource.ComposeModuleErrUnresolved(n.refPathRes[1].Identifiers)
}
p1ID = p1.ID
}
if n.refRbacRes != nil {
modRef := n.refPathRes[1]
p2 := resource.FindComposeModuleField(pl.state.ParentResources, modRef.Identifiers, n.refRbacRes.Identifiers)
if p2 == nil {
return "", resource.ComposeModuleFieldErrUnresolved(n.refRbacRes.Identifiers)
}
p2ID = p2.ID
}
return composeTypes.ModuleFieldRbacResource(p0ID, p1ID, p2ID), nil
case systemTypes.UserResourceType:
if n.refRbacRes != nil {
p1 := resource.FindUser(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.UserErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return systemTypes.UserRbacResource(p1ID), nil
case systemTypes.RoleResourceType:
if n.refRbacRes != nil {
p1 := resource.FindRole(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.RoleErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return systemTypes.RoleRbacResource(p1ID), nil
case systemTypes.ApplicationResourceType:
if n.refRbacRes != nil {
p1 := resource.FindApplication(pl.state.ParentResources, n.refRbacRes.Identifiers)
if p1 == nil {
return "", resource.ApplicationErrUnresolved(n.refRbacRes.Identifiers)
}
p1ID = p1.ID
}
return systemTypes.ApplicationRbacResource(p1ID), nil
case systemTypes.ApigwRouteResourceType:
return systemTypes.ApigwRouteRbacResource(p1ID), nil
case systemTypes.ApigwFilterResourceType:
return systemTypes.ApigwFilterRbacResource(p1ID), nil
case systemTypes.AuthClientResourceType:
// @todo add support for importing rbac rules for specific client
return systemTypes.AuthClientRbacResource(p1ID), nil
case systemTypes.TemplateResourceType:
// @todo add support for importing rbac rules for specific template
return systemTypes.TemplateRbacResource(p1ID), nil
case systemTypes.ReportResourceType:
return systemTypes.ReportRbacResource(p1ID), nil
case messagebus.QueueResourceType:
// @todo add support for importing rbac rules for specific queue
return messagebus.QueueRbacResource(p1ID), nil
case federationTypes.NodeResourceType:
// @todo add support for importing rbac rules for specific queue
return federationTypes.NodeRbacResource(p1ID), nil
case federationTypes.SharedModuleResourceType:
// @todo add support for importing rbac rules for specific queue
return federationTypes.SharedModuleRbacResource(p0ID, p1ID), nil
case federationTypes.ExposedModuleResourceType:
// @todo add support for importing rbac rules for specific queue
return federationTypes.ExposedModuleRbacResource(p0ID, p1ID), nil
}
// @todo if we wish to support rbac for external stuff, this needs to pass through.
// this also requires some tweaks in the path ID thing.
return "", fmt.Errorf("unsupported resource type '%s' for RBAC store encode", n.rule.Resource)
}
func (n *rbacRule) handleComposeRecord(pp []resource.Interface) bool {
for _, p := range pp {
if p.ResourceType() == composeTypes.RecordResourceType && p.Identifiers().HasAny(n.refPathRes[1].Identifiers) {
return true
}
}
return false
}
func (n *rbacRule) handleComposeModuleField(pp []resource.Interface) bool {
for _, p := range pp {
if p.ResourceType() == composeTypes.ModuleResourceType && p.Identifiers().HasAny(n.refPathRes[1].Identifiers) {
return true
}
}
return false
}
+14 -7
View File
@@ -7,21 +7,28 @@ import (
"github.com/cortezaproject/corteza-server/pkg/envoy"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/system/types"
)
func newRbacRule(rl *rbac.Rule) *rbacRule {
func newRbacRule(rl *rbac.Rule) (*rbacRule, error) {
res := rl.Resource
_, ref, pp, err := resource.ParseRule(res)
return &rbacRule{
rule: rl,
}
refRbacResource: res,
refRbacRes: ref,
refPathRes: pp,
refRole: resource.MakeRef(types.RoleResourceType, resource.MakeIdentifiers(strconv.FormatUint(rl.RoleID, 10))),
}, err
}
func (rl *rbacRule) MarshalEnvoy() ([]resource.Interface, error) {
refRole := strconv.FormatUint(rl.rule.RoleID, 10)
rl.rule.Resource = rbac.ResourceType(rl.rule.Resource)
return envoy.CollectNodes(
resource.NewRbacRule(rl.rule, refRole, rl.refRes, rl.refPathRes...),
resource.NewRbacRule(rl.rule, rl.refRole.Identifiers.First(), rl.refRbacRes, rl.refRbacResource, rl.refPathRes...),
)
}
@@ -201,7 +201,7 @@ func (n *resourceTranslation) makeResourceTranslation(pl *payload) (string, erro
p1ID = p1.ID
// field
f := resource.FindComposeModuleField(p1, n.refLocaleRes.Identifiers)
f := resource.FindComposeModuleField(pl.state.ParentResources, n.refPathRes[1].Identifiers, n.refLocaleRes.Identifiers)
if f == nil {
return "", resource.ComposeModuleFieldErrUnresolved(n.refLocaleRes.Identifiers)
}
+13 -10
View File
@@ -379,7 +379,7 @@ func (d *systemDecoder) decodeRbac(ctx context.Context, s store.Storer, ff []*rb
var nn rbac.RuleSet
var err error
c := func(r *resource.Ref, f *rbacFilter) (bool, error) {
c := func(r *resource.Ref, f *rbacFilter, path ...*resource.Ref) (bool, error) {
if r == nil {
return true, nil
}
@@ -390,6 +390,13 @@ func (d *systemDecoder) decodeRbac(ctx context.Context, s store.Storer, ff []*rb
return false, err
}
if r.ResourceType == composeTypes.ModuleFieldResourceType {
id, err = cast.ToUint64E(path[1].Identifiers.First())
if err != nil {
return false, err
}
}
return f.resourceID[id], nil
}
@@ -405,12 +412,8 @@ func (d *systemDecoder) decodeRbac(ctx context.Context, s store.Storer, ff []*rb
}
for _, n := range nn {
r := newRbacRule(n)
// parse the resource wo we can define relations/check if we can unmarshal it
rt, ref, pp, err := resource.ParseRule(n.Resource)
r.refRes = ref
r.refPathRes = pp
r, err := newRbacRule(n)
rt := strings.Split(r.refRbacResource, "/")[0]
if err != nil {
return &auxRsp{
err: err,
@@ -419,13 +422,13 @@ func (d *systemDecoder) decodeRbac(ctx context.Context, s store.Storer, ff []*rb
// somesort of a generic rule; no specifc resource
// @todo check for pp inclusion!!
if ref == nil && len(pp) == 0 {
if r.refRbacRes == nil && len(r.refPathRes) == 0 {
mm = append(mm, r)
} else {
// Check the resource ref and the path refs for validity.
// ComposeRecords are fetched in chunks so this check is not valid here.
if rt != composeTypes.RecordResourceType {
if ok, err := c(ref, f); err != nil {
if ok, err := c(r.refRbacRes, f, r.refPathRes...); err != nil {
return &auxRsp{
err: err,
}
@@ -434,7 +437,7 @@ func (d *systemDecoder) decodeRbac(ctx context.Context, s store.Storer, ff []*rb
}
}
for _, p := range pp {
for _, p := range r.refPathRes {
if ok, err := c(p, f); err != nil {
return &auxRsp{
err: err,
+3
View File
@@ -155,6 +155,9 @@ func (c *composeModule) MarshalYAML() (interface{}, error) {
func (c *composeModuleField) MarshalYAML() (interface{}, error) {
auxOpt := c.res.Options
if auxOpt == nil {
auxOpt = make(types.ModuleFieldOptions)
}
switch c.res.Kind {
case "Record":
ref := c.relMod.Handle
+30 -26
View File
@@ -1,6 +1,7 @@
package yaml
import (
"strconv"
"strings"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
@@ -12,18 +13,16 @@ type (
rbacRule struct {
res *rbac.Rule
// To help us construct the resource
resource string
// The resource in question
refRbacResource string
refRbacRes *resource.Ref
refResource string
refRes *resource.Ref
// The path items
refPathRes []*resource.Ref
// PathRes and PathResource slices hold parent resources we should nest the rule by
refPathRes []*resource.Ref
relPathResource []resource.Interface
refRole string
relRole *types.Role
// The role
refRole *resource.Ref
role *types.Role
}
rbacRuleSet []*rbacRule
)
@@ -32,15 +31,19 @@ func rbacRuleFromResource(r *resource.RbacRule, cfg *EncoderConfig) *rbacRule {
rr := r.Res.Resource
rr = strings.Trim(rr, ":")
if !strings.Contains(rr, ";") {
if !strings.Contains(rr, ":") {
rr = "corteza::" + rr
}
return &rbacRule{
res: r.Res,
refRes: r.RefResource,
refResource: rr,
refRole: r.RefRole.Identifiers.First(),
res: r.Res,
refRbacResource: rr,
refRbacRes: r.RefRes,
refPathRes: r.RefPath,
refRole: r.RefRole,
}
}
@@ -62,11 +65,13 @@ func (rr rbacRuleSet) groupByRole() []rbacRuleSet {
rolx := make(map[string]rbacRuleSet)
for _, r := range rr {
if _, has := rolx[r.refRole]; !has {
rolx[r.refRole] = make(rbacRuleSet, 0, 100)
roleKey := r.getRoleKey()
if _, has := rolx[roleKey]; !has {
rolx[roleKey] = make(rbacRuleSet, 0, 100)
}
rolx[r.refRole] = append(rolx[r.refRole], r)
rolx[roleKey] = append(rolx[roleKey], r)
}
rtr := make([]rbacRuleSet, 0, len(rolx))
@@ -97,13 +102,12 @@ func (rr rbacRuleSet) groupByResource() []rbacRuleSet {
return rtr
}
// Just a helper to make the above code shorter
func (r *rbacRule) bindRefs(res *resource.Ref, path []*resource.Ref, err error) error {
if err != nil {
return err
func (r *rbacRule) getRoleKey() string {
if r.role == nil {
return ""
}
r.refRes = res
r.refPathRes = path
return nil
if r.role.Handle != "" {
return r.role.Handle
}
return strconv.FormatUint(r.res.RoleID, 10)
}
+115 -74
View File
@@ -3,6 +3,7 @@ package yaml
import (
"context"
"fmt"
"strings"
automationTypes "github.com/cortezaproject/corteza-server/automation/types"
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
@@ -19,26 +20,32 @@ func (n *rbacRule) Prepare(ctx context.Context, state *envoy.ResourceState) (err
}
// Get the related role
n.relRole = resource.FindRole(state.ParentResources, rl.RefRole.Identifiers)
if n.relRole == nil {
n.role = resource.FindRole(state.ParentResources, rl.RefRole.Identifiers)
if n.role == nil {
return resource.RoleErrUnresolved(rl.RefRole.Identifiers)
}
if n.relRole == nil {
return resource.RoleErrUnresolved(rl.RefRole.Identifiers)
refRes := n.refRbacRes
if refRes == nil {
return
}
// For now we will only allow resource specific RBAC rules if that resource is
// also present.
// Here we check if we can find it in case we're handling a resource specific rule.
res := state.Res.(*resource.RbacRule)
refRes := res.RefResource
if refRes != nil && refRes.ResourceType == composeTypes.RecordResourceType {
for _, r := range state.ParentResources {
if r.ResourceType() == composeTypes.RecordResourceType && r.Identifiers().HasAny(res.RefPath[1].Identifiers) {
return
}
found := false
switch refRes.ResourceType {
case composeTypes.RecordResourceType:
if found = n.handleComposeRecord(state.ParentResources); !found {
return resource.RbacResourceErrNotFound(refRes.Identifiers)
}
} else if refRes != nil && len(refRes.Identifiers) > 0 {
case composeTypes.ModuleFieldResourceType:
if found = n.handleComposeModuleField(state.ParentResources); !found {
return resource.RbacResourceErrNotFound(refRes.Identifiers)
}
default:
for _, r := range state.ParentResources {
if refRes.ResourceType == r.ResourceType() && r.Identifiers().HasAny(refRes.Identifiers) {
return
@@ -58,186 +65,225 @@ func (r *rbacRule) Encode(ctx context.Context, doc *Document, state *envoy.Resou
// under the related namespace.
// For now all rules will be nested under a root node for simplicity sake.
refResource, err := r.makeRBACResource(state)
if err != nil {
return err
}
r.refRbacResource = refResource
r.res.Resource = refResource
doc.addRbacRule(r)
return nil
}
func (r *rbacRule) makeRBACResource(state *envoy.ResourceState) (string, error) {
res := state.Res.(*resource.RbacRule)
p0ID := "*"
p1ID := "*"
p2ID := "*"
switch r.res.Resource {
rt := strings.Split(r.refRbacResource, "/")[0]
switch rt {
case composeTypes.ComponentResourceType,
systemTypes.ComponentResourceType,
automationTypes.ComponentResourceType,
federationTypes.ComponentResourceType:
r.res.Resource += "/"
goto next
return rt, nil
}
// @todo the following stuff I'm not too sure about
switch r.res.Resource {
switch rt {
case composeTypes.NamespaceResourceType:
if res.RefResource != nil {
p1 := resource.FindComposeNamespace(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindComposeNamespace(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.ComposeNamespaceErrUnresolved(res.RefResource.Identifiers)
return "", resource.ComposeNamespaceErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Slug
}
r.res.Resource = fmt.Sprintf(composeTypes.NamespaceRbacResourceTpl(), composeTypes.NamespaceResourceType, p1ID)
return fmt.Sprintf(composeTypes.NamespaceRbacResourceTpl(), composeTypes.NamespaceResourceType, p1ID), nil
case composeTypes.ModuleResourceType:
if len(res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
}
p0ID = p0.Slug
}
if res.RefResource != nil {
p1 := resource.FindComposeModule(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindComposeModule(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.ComposeModuleErrUnresolved(res.RefResource.Identifiers)
return "", resource.ComposeModuleErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Handle
}
r.res.Resource = fmt.Sprintf(composeTypes.ModuleRbacResourceTpl(), composeTypes.ModuleResourceType, p0ID, p1ID)
return fmt.Sprintf(composeTypes.ModuleRbacResourceTpl(), composeTypes.ModuleResourceType, p0ID, p1ID), nil
case composeTypes.ChartResourceType:
if len(res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
}
p0ID = p0.Slug
}
if res.RefResource != nil {
p1 := resource.FindComposeChart(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindComposeChart(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.ComposeChartErrUnresolved(res.RefResource.Identifiers)
return "", resource.ComposeChartErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Handle
}
r.res.Resource = fmt.Sprintf(composeTypes.ChartRbacResourceTpl(), composeTypes.ChartResourceType, p0ID, p1ID)
return fmt.Sprintf(composeTypes.ChartRbacResourceTpl(), composeTypes.ChartResourceType, p0ID, p1ID), nil
case composeTypes.PageResourceType:
if len(res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
}
p0ID = p0.Slug
}
if res.RefResource != nil {
p1 := resource.FindComposePage(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindComposePage(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.ComposePageErrUnresolved(res.RefResource.Identifiers)
return "", resource.ComposePageErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Handle
}
r.res.Resource = fmt.Sprintf(composeTypes.PageRbacResourceTpl(), composeTypes.PageResourceType, p0ID, p1ID)
return fmt.Sprintf(composeTypes.PageRbacResourceTpl(), composeTypes.PageResourceType, p0ID, p1ID), nil
case composeTypes.RecordResourceType:
if len(res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
}
p0ID = p0.Slug
}
if len(res.RefPath) > 1 {
p1 := resource.FindComposeModule(state.ParentResources, res.RefPath[1].Identifiers)
if p1 == nil {
return resource.ComposeModuleErrUnresolved(res.RefPath[1].Identifiers)
return "", resource.ComposeModuleErrUnresolved(res.RefPath[1].Identifiers)
}
p1ID = p1.Handle
}
if res.RefResource != nil {
if res.RefRes != nil {
ref := res.RefPath[1]
p2 := resource.FindComposeRecordResource(state.ParentResources, ref.Identifiers)
if p2 == nil {
return resource.ComposeRecordErrUnresolved(res.RefResource.Identifiers)
return "", resource.ComposeRecordErrUnresolved(res.RefRes.Identifiers)
}
for i := range res.RefResource.Identifiers {
for i := range res.RefRes.Identifiers {
if _, ok := p2.IDMap[i]; ok {
p2ID = res.RefResource.Identifiers.First()
p2ID = res.RefRes.Identifiers.First()
break
}
}
if p2ID == "" {
return resource.ComposeRecordErrUnresolved(res.RefResource.Identifiers)
return "", resource.ComposeRecordErrUnresolved(res.RefRes.Identifiers)
}
}
r.res.Resource = fmt.Sprintf(composeTypes.RecordRbacResourceTpl(), composeTypes.RecordResourceType, p0ID, p1ID, p2ID)
return fmt.Sprintf(composeTypes.RecordRbacResourceTpl(), composeTypes.RecordResourceType, p0ID, p1ID, p2ID), nil
case composeTypes.ModuleFieldResourceType:
if len(res.RefPath) > 0 {
p0 := resource.FindComposeNamespace(state.ParentResources, res.RefPath[0].Identifiers)
if p0 == nil {
return resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
return "", resource.ComposeNamespaceErrUnresolved(res.RefPath[0].Identifiers)
}
p0ID = p0.Slug
}
if len(res.RefPath) > 1 {
p1 := resource.FindComposeModule(state.ParentResources, res.RefPath[1].Identifiers)
if p1 == nil {
return resource.ComposeModuleErrUnresolved(res.RefPath[1].Identifiers)
return "", resource.ComposeModuleErrUnresolved(res.RefPath[1].Identifiers)
}
p1ID = p1.Handle
}
if r.refRbacRes != nil {
modRef := r.refPathRes[1]
p2 := resource.FindComposeModuleField(state.ParentResources, modRef.Identifiers, r.refRbacRes.Identifiers)
if p2 == nil {
return "", resource.ComposeModuleFieldErrUnresolved(r.refRbacRes.Identifiers)
}
p2ID = p2.Name
}
// @todo specific module field RBAC
r.res.Resource = fmt.Sprintf(composeTypes.ModuleFieldRbacResourceTpl(), composeTypes.ModuleFieldResourceType, p0ID, p1ID, p2ID)
return fmt.Sprintf(composeTypes.ModuleFieldRbacResourceTpl(), composeTypes.ModuleFieldResourceType, p0ID, p1ID, p2ID), nil
case systemTypes.UserResourceType:
if res.RefResource != nil {
p1 := resource.FindUser(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindUser(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.UserErrUnresolved(res.RefResource.Identifiers)
return "", resource.UserErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Handle
}
r.res.Resource = fmt.Sprintf(systemTypes.UserRbacResourceTpl(), systemTypes.UserResourceType, p1ID)
return fmt.Sprintf(systemTypes.UserRbacResourceTpl(), systemTypes.UserResourceType, p1ID), nil
case systemTypes.RoleResourceType:
if res.RefResource != nil {
p1 := resource.FindRole(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindRole(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.RoleErrUnresolved(res.RefResource.Identifiers)
return "", resource.RoleErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Handle
}
r.res.Resource = fmt.Sprintf(systemTypes.RoleRbacResourceTpl(), systemTypes.RoleResourceType, p1ID)
return fmt.Sprintf(systemTypes.RoleRbacResourceTpl(), systemTypes.RoleResourceType, p1ID), nil
case systemTypes.ApplicationResourceType:
if res.RefResource != nil {
p1 := resource.FindApplication(state.ParentResources, res.RefResource.Identifiers)
if res.RefRes != nil {
p1 := resource.FindApplication(state.ParentResources, res.RefRes.Identifiers)
if p1 == nil {
return resource.ApplicationErrUnresolved(res.RefResource.Identifiers)
return "", resource.ApplicationErrUnresolved(res.RefRes.Identifiers)
}
p1ID = p1.Name
}
r.res.Resource = fmt.Sprintf(systemTypes.ApplicationRbacResourceTpl(), systemTypes.ApplicationResourceType, p1ID)
return fmt.Sprintf(systemTypes.ApplicationRbacResourceTpl(), systemTypes.ApplicationResourceType, p1ID), nil
// // @todo
// case systemTypes.ApigwRouteResourceType:
// case systemTypes.ApigwFilterResourceType:
default:
return fmt.Errorf("unsupported resource type '%s' for RBAC YAML encode", r.res.Resource)
// // @todo
// case systemTypes.ApigwRouteResourceType:
// case systemTypes.ApigwFilterResourceType:
}
next:
doc.addRbacRule(r)
return "", fmt.Errorf("unsupported resource type '%s' for RBAC YAML encode", r.res.Resource)
}
return nil
func (n *rbacRule) handleComposeRecord(pp []resource.Interface) bool {
for _, p := range pp {
if p.ResourceType() == composeTypes.RecordResourceType && p.Identifiers().HasAny(n.refPathRes[1].Identifiers) {
return true
}
}
return false
}
func (n *rbacRule) handleComposeModuleField(pp []resource.Interface) bool {
for _, p := range pp {
if p.ResourceType() == composeTypes.ModuleResourceType && p.Identifiers().HasAny(n.refPathRes[1].Identifiers) {
return true
}
}
return false
}
func (rr rbacRuleSet) MarshalYAML() (interface{}, error) {
@@ -272,13 +318,8 @@ func (rr rbacRuleSet) MarshalYAML() (interface{}, error) {
}
}
rr := roleRules[0].relRole
rk := rr.Handle
if rk == "" {
rk = rr.Name
}
roleNode, err = addMap(roleNode,
rk, resNode,
roleRules[0].getRoleKey(), resNode,
)
if err != nil {
return nil, err
+12 -20
View File
@@ -6,6 +6,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/pkg/y7s"
"github.com/cortezaproject/corteza-server/system/types"
"gopkg.in/yaml.v3"
)
@@ -46,7 +47,7 @@ func (rr rbacRuleSet) decodeRbac(a rbac.Access, rules *yaml.Node) (oo rbacRuleSe
Access: a,
Operation: op.Value,
},
refRole: roleRef,
refRole: resource.MakeRef(types.RoleResourceType, resource.MakeIdentifiers(roleRef)),
}
if res != "" {
@@ -75,21 +76,13 @@ func (rr rbacRuleSet) decodeRbac(a rbac.Access, rules *yaml.Node) (oo rbacRuleSe
}
func (rr rbacRuleSet) bindResource(resI resource.Interface) rbacRuleSet {
ref := &resource.Ref{
ResourceType: resI.ResourceType(),
Identifiers: resI.Identifiers(),
}
var path []*resource.Ref
if resRbac, ok := resI.(resource.RBACInterface); ok {
path = resRbac.RBACPath()
}
res, ref, pp := resI.(resource.RBACInterface).RBACParts()
rtr := make(rbacRuleSet, 0, len(rr))
for _, r := range rr {
_ = r.SetResource(ref.ResourceType)
r.refRes = ref
r.refPathRes = path
r.refRbacResource = res
r.refRbacRes = ref
r.refPathRes = pp
rtr = append(rtr, r)
}
@@ -100,20 +93,19 @@ func (rr rbacRuleSet) MarshalEnvoy() ([]resource.Interface, error) {
var nn = make([]resource.Interface, 0, len(rr))
for _, r := range rr {
nn = append(nn, resource.NewRbacRule(r.res, r.refRole, r.refRes, r.refPathRes...))
nn = append(nn, resource.NewRbacRule(r.res, r.refRole.Identifiers.First(), r.refRbacRes, r.refRbacResource, r.refPathRes...))
}
return nn, nil
}
func (r *rbacRule) SetResource(res string) error {
res, ref, pp, err := resource.ParseRule(res)
_, ref, pp, err := resource.ParseRule(res)
if err != nil {
return err
}
if res != "" {
r.res.Resource = res
}
return r.bindRefs(ref, pp, err)
r.refRbacResource = res
r.refRbacRes = ref
r.refPathRes = pp
return nil
}
@@ -155,7 +155,7 @@ func (r *resourceTranslation) makeResourceTranslationResource(state *envoy.Resou
p1ID = p1.Handle
// field
f := resource.FindComposeModuleField(p1, res.RefRes.Identifiers)
f := resource.FindComposeModuleField(state.ParentResources, res.RefPath[1].Identifiers, res.RefRes.Identifiers)
if f == nil {
return "", resource.ComposeModuleFieldErrUnresolved(res.RefRes.Identifiers)
}
@@ -86,7 +86,6 @@ func (rr resourceTranslationSet) bindResource(resI resource.Interface) resourceT
rtr := make(resourceTranslationSet, 0, len(rr))
for _, r := range rr {
r.refResourceTranslation = res
r.refLocaleRes = ref
r.refPathRes = pp
+82 -6
View File
@@ -1,12 +1,88 @@
# Setup so we don't need any boilerplate code
roles:
r1:
name: test role 1
r2:
name: test role 2
namespaces:
ns1:
name: test namespace 1
modules:
mod1:
name: test module 1
fields:
f1:
label: test module field 1
charts:
chr1:
name: test chart 1
# Access control
allow:
r1:
corteza::compose/:
- access
corteza::compose:
- allow;r1;compose;op1
corteza::compose:namespace/*:
- allow;r1;compose:namespace/*;op1
corteza::compose:namespace/ns1:
- allow;r1;compose:namespace/ns1;op1
corteza::compose:module/*/*:
- allow;r1;compose:module/*/*;op1
corteza::compose:module/ns1/*:
- allow;r1;compose:module/ns1/*;op1
corteza::compose:module/ns1/mod1:
- allow;r1;compose:module/ns1/mod1;op1
corteza::compose:module-field/*/*/*:
- allow;r1;compose:module-field/*/*/*;op1
corteza::compose:module-field/ns1/*/*:
- allow;r1;compose:module-field/ns1/*/*;op1
corteza::compose:module-field/ns1/mod1/*:
- allow;r1;compose:module-field/ns1/mod1/*;op1
corteza::compose:module-field/ns1/mod1/f1:
- allow;r1;compose:module-field/ns1/mod1/f1;op1
corteza::compose:chart/*/*:
- allow;r1;compose:chart/*/*;op1
corteza::compose:chart/ns1/*:
- allow;r1;compose:chart/ns1/*;op1
corteza::compose:chart/ns1/chr1:
- allow;r1;compose:chart/ns1/chr1;op1
deny:
r1:
corteza::compose:
- deny;r1;compose;op1
corteza::compose:namespace/*:
- access
# @todo see comment for rbac store marshal if I forget to mention this to you
corteza::system/:
- access
- deny;r1;compose:namespace/*;op1
corteza::compose:namespace/ns1:
- deny;r1;compose:namespace/ns1;op1
corteza::compose:module/*/*:
- deny;r1;compose:module/*/*;op1
corteza::compose:module/ns1/*:
- deny;r1;compose:module/ns1/*;op1
corteza::compose:module/ns1/mod1:
- deny;r1;compose:module/ns1/mod1;op1
corteza::compose:module-field/*/*/*:
- deny;r1;compose:module-field/*/*/*;op1
corteza::compose:module-field/ns1/*/*:
- deny;r1;compose:module-field/ns1/*/*;op1
corteza::compose:module-field/ns1/mod1/*:
- deny;r1;compose:module-field/ns1/mod1/*;op1
corteza::compose:module-field/ns1/mod1/f1:
- deny;r1;compose:module-field/ns1/mod1/f1;op1
corteza::compose:chart/*/*:
- deny;r1;compose:chart/*/*;op1
corteza::compose:chart/ns1/*:
- deny;r1;compose:chart/ns1/*;op1
corteza::compose:chart/ns1/chr1:
- deny;r1;compose:chart/ns1/chr1;op1
@@ -3,9 +3,6 @@ allow:
corteza::compose/:
- op1
corteza::system:apigw-route/*:
- op1
corteza::compose:namespace/*:
- op1
-64
View File
@@ -405,22 +405,6 @@ func TestYamlStore_base(t *testing.T) {
},
},
{
name: "rbac rules",
file: "rbac_rules",
pre: func() (err error) {
return collect(
storeRole(ctx, s, 100, "r1"),
)
},
check: func(req *require.Assertions) {
rr, _, err := store.SearchRbacRules(ctx, s, rbac.RuleFilter{})
req.NoError(err)
req.NotNil(rr)
req.Len(rr, 3)
},
},
{
name: "records; no namespace",
file: "records_no_ns",
@@ -552,54 +536,6 @@ func TestYamlStore_base(t *testing.T) {
},
},
{
name: "base access control",
file: "access_control_base",
check: func(req *require.Assertions) {
role, err := store.LookupRoleByHandle(ctx, s, "authenticated")
req.NoError(err)
req.NotNil(role)
rr, _, err := store.SearchRbacRules(ctx, s, rbac.RuleFilter{})
req.NoError(err)
req.Len(rr, 16)
// Check that the role is ok
for _, r := range rr {
req.Equal(role.ID, r.RoleID)
}
resources := []string{
"corteza::compose:namespace/11",
"corteza::compose:namespace/11",
"corteza::compose:module/11/12",
"corteza::compose:module/11/12",
"corteza::compose:page/11/13",
"corteza::compose:page/11/13",
"corteza::compose:chart/11/14",
"corteza::compose:chart/11/14",
"corteza::system:role/16",
"corteza::system:role/16",
"corteza::system:user/17",
"corteza::system:user/17",
"corteza::system:application/18",
"corteza::system:application/18",
"corteza::compose/",
"corteza::compose/",
}
for i, res := range resources {
req.Equal(res, rr[i].Resource)
if i%2 == 0 {
req.Equal("op1", rr[i].Operation)
req.Equal(rbac.Allow, rr[i].Access)
} else {
req.Equal("op2", rr[i].Operation)
req.Equal(rbac.Deny, rr[i].Access)
}
}
},
},
{
name: "settings",
file: "settings",
+1 -1
View File
@@ -73,7 +73,7 @@ func TestYamlStore_provision(t *testing.T) {
rrls, _, err = store.SearchRbacRules(ctx, s, rbac.RuleFilter{})
req.NoError(err)
req.NotNil(rrls)
req.Len(rrls, 15)
req.Len(rrls, 14)
})
t.Run("namespaces", func(t *testing.T) {
+228
View File
@@ -0,0 +1,228 @@
package envoy
import (
"context"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/envoy"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
"github.com/cortezaproject/corteza-server/pkg/envoy/yaml"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/store"
systemTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/stretchr/testify/require"
)
func TestYamlStore_rbac(t *testing.T) {
var (
ctx = context.Background()
namespace = "base"
s = initServices(ctx, t)
f = "rbac_rules.yaml"
req = require.New(t)
)
ctx = auth.SetIdentityToContext(ctx, auth.ServiceUser())
ni := uint64(10)
su.NextID = func() uint64 {
ni++
return ni
}
truncateStore(ctx, s, t)
// util to check rule's operation
checkPfx := func(req *require.Assertions, rr rbac.RuleSet, pfx string) {
for _, r := range rr {
req.Contains(r.Operation, pfx)
}
}
// util to check the current store state if it's as expected
checkStore := func() {
// Preload all the resource
ns, err := store.LookupComposeNamespaceBySlug(ctx, s, "ns1")
req.NoError(err)
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, ns.ID, "mod1")
req.NoError(err)
fld, err := store.LookupComposeModuleFieldByModuleIDName(ctx, s, mod.ID, "f1")
req.NoError(err)
chr, err := store.LookupComposeChartByNamespaceIDHandle(ctx, s, ns.ID, "chr1")
req.NoError(err)
// Preload RBAC rules
rr, _, err := store.SearchRbacRules(ctx, s, rbac.RuleFilter{})
req.NoError(err)
// Check
// - component
composeRR := rbacRuleFilterByResource(rr, types.ComponentRbacResource())
req.Len(composeRR, 2)
req.Len(composeRR.FilterAccess(rbac.Allow), 1)
req.Len(composeRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, composeRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, composeRR.FilterAccess(rbac.Deny), "deny")
// - namespace
nsRR := rbacRuleFilterByResource(rr, types.NamespaceRbacResource(0))
req.Len(nsRR, 2)
req.Len(nsRR.FilterAccess(rbac.Allow), 1)
req.Len(nsRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, nsRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, nsRR.FilterAccess(rbac.Deny), "deny")
nsRR = rbacRuleFilterByResource(rr, ns.RbacResource())
req.Len(nsRR, 2)
req.Len(nsRR.FilterAccess(rbac.Allow), 1)
req.Len(nsRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, nsRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, nsRR.FilterAccess(rbac.Deny), "deny")
// - module
modRR := rbacRuleFilterByResource(rr, types.ModuleRbacResource(0, 0))
req.Len(modRR, 2)
req.Len(modRR.FilterAccess(rbac.Allow), 1)
req.Len(modRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, modRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, modRR.FilterAccess(rbac.Deny), "deny")
modRR = rbacRuleFilterByResource(rr, types.ModuleRbacResource(ns.ID, 0))
req.Len(modRR, 2)
req.Len(modRR.FilterAccess(rbac.Allow), 1)
req.Len(modRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, modRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, modRR.FilterAccess(rbac.Deny), "deny")
modRR = rbacRuleFilterByResource(rr, types.ModuleRbacResource(ns.ID, mod.ID))
req.Len(modRR, 2)
req.Len(modRR.FilterAccess(rbac.Allow), 1)
req.Len(modRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, modRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, modRR.FilterAccess(rbac.Deny), "deny")
// - module field
mfRR := rbacRuleFilterByResource(rr, types.ModuleFieldRbacResource(0, 0, 0))
req.Len(mfRR, 2)
req.Len(mfRR.FilterAccess(rbac.Allow), 1)
req.Len(mfRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, mfRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, mfRR.FilterAccess(rbac.Deny), "deny")
mfRR = rbacRuleFilterByResource(rr, types.ModuleFieldRbacResource(ns.ID, 0, 0))
req.Len(mfRR, 2)
req.Len(mfRR.FilterAccess(rbac.Allow), 1)
req.Len(mfRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, mfRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, mfRR.FilterAccess(rbac.Deny), "deny")
mfRR = rbacRuleFilterByResource(rr, types.ModuleFieldRbacResource(ns.ID, mod.ID, 0))
req.Len(mfRR, 2)
req.Len(mfRR.FilterAccess(rbac.Allow), 1)
req.Len(mfRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, mfRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, mfRR.FilterAccess(rbac.Deny), "deny")
mfRR = rbacRuleFilterByResource(rr, types.ModuleFieldRbacResource(ns.ID, mod.ID, fld.ID))
req.Len(mfRR, 2)
req.Len(mfRR.FilterAccess(rbac.Allow), 1)
req.Len(mfRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, mfRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, mfRR.FilterAccess(rbac.Deny), "deny")
// - chart
chrRR := rbacRuleFilterByResource(rr, types.ChartRbacResource(0, 0))
req.Len(chrRR, 2)
req.Len(chrRR.FilterAccess(rbac.Allow), 1)
req.Len(chrRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, chrRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, chrRR.FilterAccess(rbac.Deny), "deny")
chrRR = rbacRuleFilterByResource(rr, types.ChartRbacResource(ns.ID, 0))
req.Len(chrRR, 2)
req.Len(chrRR.FilterAccess(rbac.Allow), 1)
req.Len(chrRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, chrRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, chrRR.FilterAccess(rbac.Deny), "deny")
chrRR = rbacRuleFilterByResource(rr, types.ChartRbacResource(ns.ID, chr.ID))
req.Len(chrRR, 2)
req.Len(chrRR.FilterAccess(rbac.Allow), 1)
req.Len(chrRR.FilterAccess(rbac.Deny), 1)
checkPfx(req, chrRR.FilterAccess(rbac.Allow), "allow")
checkPfx(req, chrRR.FilterAccess(rbac.Deny), "deny")
}
// YAML -> Store
nn, err := decodeYaml(ctx, namespace, f)
req.NoError(err)
err = encode(ctx, s, nn)
req.NoError(err)
checkStore()
// Store -> YAML -> Store
df := su.NewDecodeFilter().
ComposeNamespace(&types.NamespaceFilter{
Slug: "ns1",
}).
ComposeModule(&types.ModuleFilter{}).
ComposeChart(&types.ChartFilter{}).
Roles(&systemTypes.RoleFilter{Handle: "r1"}).
Rbac(&rbac.RuleFilter{})
// - decode store
sd := su.Decoder()
nn, err = sd.Decode(ctx, s, df)
req.NoError(err)
//
// - into yaml
ye := yaml.NewYamlEncoder(&yaml.EncoderConfig{})
bld := envoy.NewBuilder(ye)
g, err := bld.Build(ctx, nn...)
req.NoError(err)
err = envoy.Encode(ctx, g, ye)
req.NoError(err)
ss := ye.Stream()
//
// - cleanup the store
truncateStore(ctx, s, t)
//
// - back into store
se := su.NewStoreEncoder(s, &su.EncoderConfig{})
yd := yaml.Decoder()
nn = make([]resource.Interface, 0, len(nn))
for _, s := range ss {
mm, err := yd.Decode(ctx, s.Source, nil)
req.NoError(err)
nn = append(nn, mm...)
}
bld = envoy.NewBuilder(se)
g, err = bld.Build(ctx, nn...)
req.NoError(err)
err = envoy.Encode(ctx, g, se)
req.NoError(err)
//
// - check store state
checkStore()
truncateStore(ctx, s, t)
}
func rbacRuleFilterByResource(rr rbac.RuleSet, res string) rbac.RuleSet {
out := make(rbac.RuleSet, 0, len(rr)/2)
for _, r := range rr {
if r.Resource == res {
out = append(out, r)
}
}
return out
}