From ea0f1eac47fe8241756fa30062632ec763b14dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Sun, 1 Dec 2024 21:21:55 +0100 Subject: [PATCH] Refactoring tests --- server/compose/service/chart_test.go | 2 +- server/compose/service/module_test.go | 21 +- server/compose/service/page_test.go | 2 +- server/compose/service/record.go | 2 + server/compose/service/record_test.go | 101 ++++---- server/pkg/rbac/service.go | 26 +- server/pkg/rbac/stats.go | 20 +- server/system/service/user_test.go | 13 +- server/tests/automation/permissions_test.go | 18 +- server/tests/compose/permissions_test.go | 18 +- server/tests/rbac/main_test.go | 223 ++++++++++++++++++ server/tests/rbac/rbac_rules_test.go | 151 ++++++++++++ server/tests/system/permissions_test.go | 61 +++-- server/tests/workflows/0002_rbac_fn_test.go | 8 +- .../tests/workflows/exec_permissions_test.go | 3 - .../invoker_and_runner_in_scope_test.go | 3 - 16 files changed, 543 insertions(+), 129 deletions(-) create mode 100644 server/tests/rbac/main_test.go create mode 100644 server/tests/rbac/rbac_rules_test.go diff --git a/server/compose/service/chart_test.go b/server/compose/service/chart_test.go index 736dfc29c..ed90bf307 100644 --- a/server/compose/service/chart_test.go +++ b/server/compose/service/chart_test.go @@ -48,7 +48,7 @@ func TestCharts(t *testing.T) { req := require.New(t) svc := &chart{ store: s, - ac: &accessControl{rbac: &rbac.ServiceAllowAll{}}, + ac: &accessControl{rbac: rbac.NoopSvc(rbac.Allow)}, } res, err := svc.Create(ctx, &types.Chart{Name: "My first chart", NamespaceID: namespaceID}) req.NoError(unwrapChartInternal(err)) diff --git a/server/compose/service/module_test.go b/server/compose/service/module_test.go index 83abfc797..f0ed07b08 100644 --- a/server/compose/service/module_test.go +++ b/server/compose/service/module_test.go @@ -58,10 +58,6 @@ func makeTestModuleService(t *testing.T, mods ...any) *module { } } - if svc.ac == nil { - svc.ac = &accessControl{rbac: rbac.NewService(log, nil)} - } - if svc.store == nil { t.Log("using SQLite in-memory Store") svc.store, err = sqlite.ConnectInMemoryWithDebug(ctx) @@ -78,7 +74,18 @@ func makeTestModuleService(t *testing.T, mods ...any) *module { req.NoError(store.TruncateComposeModuleFields(ctx, svc.store)) req.NoError(store.TruncateRbacRules(ctx, svc.store)) req.NoError(store.TruncateLabels(ctx, svc.store)) + } + if svc.ac == nil { + rc, err := rbac.NewService(ctx, log, svc.store, rbac.Config{ + Synchronous: true, + DecayInterval: time.Hour * 2, + CleanupInterval: time.Hour * 2, + ReindexInterval: time.Hour * 2, + IndexFlushInterval: time.Hour * 2, + }) + require.NoError(t, err) + svc.ac = &accessControl{rbac: rc} } resourceMaker(ctx, t, svc.store, mods...) @@ -122,7 +129,7 @@ func TestModules(t *testing.T) { svc := makeTestModuleService(t, ns, - &rbac.ServiceAllowAll{}, + rbac.NoopSvc(rbac.Allow), ) res, err := svc.Create(ctx, &types.Module{Name: "My first module", NamespaceID: ns.ID}) @@ -167,7 +174,7 @@ func TestModule_LabelSearch(t *testing.T) { req = require.New(t) svc = makeTestModuleService(t, ns, - &rbac.ServiceAllowAll{}, + rbac.NoopSvc(rbac.Allow), ) ctx = context.Background() @@ -239,7 +246,7 @@ func TestModule_LabelCRUD(t *testing.T) { req = require.New(t) svc = makeTestModuleService(t, ns, - &rbac.ServiceAllowAll{}, + rbac.NoopSvc(rbac.Allow), ) findAndReturnLabel = func(id uint64) map[string]string { diff --git a/server/compose/service/page_test.go b/server/compose/service/page_test.go index 73ca659a8..b0485eaf7 100644 --- a/server/compose/service/page_test.go +++ b/server/compose/service/page_test.go @@ -50,7 +50,7 @@ func TestPageDeleting(t *testing.T) { svc = &page{ store: s, - ac: &accessControl{rbac: &rbac.ServiceAllowAll{}}, + ac: &accessControl{rbac: rbac.NoopSvc(rbac.Allow)}, eventbus: eventbus.New(), locale: ResourceTranslationsManager(locale.Static()), } diff --git a/server/compose/service/record.go b/server/compose/service/record.go index b588eb87e..1d4ab96f1 100644 --- a/server/compose/service/record.go +++ b/server/compose/service/record.go @@ -12,6 +12,7 @@ import ( "github.com/cortezaproject/corteza/server/pkg/envoyx" "github.com/cortezaproject/corteza/server/pkg/filter" + "github.com/cortezaproject/corteza/server/pkg/rbac" "github.com/cortezaproject/corteza/server/pkg/revisions" "github.com/spf13/cast" @@ -44,6 +45,7 @@ type ( actionlog actionlog.Recorder + rbacSvc *rbac.Service ac recordAccessController eventbus eventDispatcher diff --git a/server/compose/service/record_test.go b/server/compose/service/record_test.go index 3d36e8165..0dd046134 100644 --- a/server/compose/service/record_test.go +++ b/server/compose/service/record_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/cortezaproject/corteza/server/pkg/dal" "github.com/cortezaproject/corteza/server/pkg/eventbus" @@ -26,7 +27,6 @@ import ( ) func makeTestRecordService(t *testing.T, mods ...any) *record { - var ( err error req = require.New(t) @@ -68,10 +68,6 @@ func makeTestRecordService(t *testing.T, mods ...any) *record { } } - if svc.ac == nil { - svc.ac = &accessControl{rbac: rbac.NewService(log, nil)} - } - if svc.store == nil { svc.store, err = sqlite.ConnectInMemoryWithDebug(ctx) req.NoError(err) @@ -83,7 +79,19 @@ func makeTestRecordService(t *testing.T, mods ...any) *record { req.NoError(store.TruncateComposeModules(ctx, svc.store)) req.NoError(store.TruncateComposeModuleFields(ctx, svc.store)) req.NoError(store.TruncateRbacRules(ctx, svc.store)) + } + if svc.ac == nil { + rc, err := rbac.NewService(ctx, log, svc.store, rbac.Config{ + Synchronous: true, + DecayInterval: time.Hour * 2, + CleanupInterval: time.Hour * 2, + ReindexInterval: time.Hour * 2, + IndexFlushInterval: time.Hour * 2, + }) + require.NoError(t, err) + svc.rbacSvc = rc + svc.ac = &accessControl{rbac: rc} } resourceMaker(ctx, t, svc.store, mods...) @@ -264,14 +272,8 @@ func TestRecord_boolFieldPermissionIssueKBR(t *testing.T) { writerRole = &sysTypes.Role{Name: "writer", ID: nextID()} // - rbacService = rbac.NewService( - zap.NewNop(), - //logger.MakeDebugLogger(), - nil, - ) svc = makeTestRecordService(t, - rbacService, logger.MakeDebugLogger(), u, ns, @@ -294,13 +296,13 @@ func TestRecord_boolFieldPermissionIssueKBR(t *testing.T) { svc.validator = defaultValidator(svc) - rbacService.UpdateRoles( + svc.rbacSvc.UpdateRoles( rbac.CommonRole.Make(readerRole.ID, readerRole.Name), rbac.CommonRole.Make(writerRole.ID, writerRole.Name), rbac.AuthenticatedRole.Make(authRoleID, "authenticated"), ) - rbacService.Grant(ctx, + svc.rbacSvc.Grant(ctx, // base permissions rbac.AllowRule(authRoleID, mod.RbacResource(), "record.create"), rbac.AllowRule(authRoleID, types.RecordRbacResource(0, 0, 0), "read"), @@ -382,12 +384,6 @@ func TestRecord_defValueFieldPermissionIssue(t *testing.T) { req = require.New(t) ctx = context.Background() - rbacService = rbac.NewService( - //zap.NewNop(), - logger.MakeDebugLogger(), - nil, - ) - user = &sysTypes.User{ID: nextID()} modConf = types.ModuleConfig{DAL: types.ModuleConfigDAL{ConnectionID: 1}} @@ -398,7 +394,6 @@ func TestRecord_defValueFieldPermissionIssue(t *testing.T) { readableField = &types.ModuleField{ID: nextID(), ModuleID: mod.ID, NamespaceID: ns.ID, Name: "readable", Kind: "String", DefaultValue: types.RecordValueSet{{Value: "def-r"}}} svc = makeTestRecordService(t, - rbacService, user, ns, mod, @@ -440,12 +435,12 @@ func TestRecord_defValueFieldPermissionIssue(t *testing.T) { t.Log("setting up security") - rbacService.UpdateRoles( + svc.rbacSvc.UpdateRoles( rbac.CommonRole.Make(editorRole.ID, editorRole.Name), rbac.AuthenticatedRole.Make(authRoleID, "authenticated"), ) - rbacService.Grant(ctx, + svc.rbacSvc.Grant(ctx, // base permissions rbac.AllowRule(authRoleID, mod.RbacResource(), "record.create"), rbac.AllowRule(authRoleID, types.RecordRbacResource(0, 0, 0), "read"), @@ -526,11 +521,6 @@ func TestRecord_refAccessControl(t *testing.T) { req.NoError(store.TruncateRbacRules(ctx, s)) var ( - rbacService = rbac.NewService( - //zap.NewNop(), - logger.MakeDebugLogger(), - nil, - ) nextIDi uint64 = 1 nextID = func() uint64 { nextIDi++ @@ -558,7 +548,6 @@ func TestRecord_refAccessControl(t *testing.T) { testerRole = &sysTypes.Role{Name: "tester", ID: nextID()} svc = makeTestRecordService(t, - rbacService, user, ns, mod1, @@ -581,7 +570,7 @@ func TestRecord_refAccessControl(t *testing.T) { svc.validator = defaultValidator(svc) t.Log("inform rbac service about new roles") - rbacService.UpdateRoles( + svc.rbacSvc.UpdateRoles( rbac.CommonRole.Make(testerRole.ID, testerRole.Name), ) @@ -596,7 +585,7 @@ func TestRecord_refAccessControl(t *testing.T) { req.EqualError(err, "not allowed to create records") t.Logf("granting permissions to create records on this module") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, mod1.RbacResource(), "record.create"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, mod1.RbacResource(), "record.create"))) t.Log("retry creating record on 1st module; should fail because we do not have permissions to update field") _, _, err = svc.Create(ctx, mod1rec1) @@ -604,7 +593,7 @@ func TestRecord_refAccessControl(t *testing.T) { req.True(types.IsRecordValueErrorSet(err).HasKind("updateDenied")) t.Logf("granting permissions to update records values on module field") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, mod1strField.RbacResource(), "record.value.update"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, mod1strField.RbacResource(), "record.value.update"))) t.Log("retry creating record on 1st module; should succeed") mod1rec1, _, err = svc.Create(ctx, mod1rec1) @@ -624,17 +613,17 @@ func TestRecord_refAccessControl(t *testing.T) { req.EqualError(err, "not allowed to create records") t.Log("grant record.create on namespace level") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, types.ModuleRbacResource(ns.ID, 0), "record.create"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, types.ModuleRbacResource(ns.ID, 0), "record.create"))) t.Log("grant record.value.update on namespace level") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, types.ModuleFieldRbacResource(ns.ID, 0, 0), "record.value.update"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, types.ModuleFieldRbacResource(ns.ID, 0, 0), "record.value.update"))) t.Log("create record on 2nd module with ref to record on the 1st module; most fail, not allowed to read (referenced) mod1rec1") _, _, err = svc.Create(ctx, mod2rec1) req.EqualError(err, "invalid record value input") t.Log("grant read on record") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, mod1rec1.RbacResource(), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, mod1rec1.RbacResource(), "read"))) t.Log("create record on 2nd module with ref to record on the 1st module") mod2rec1, _, err = svc.Create(ctx, mod2rec1) @@ -646,7 +635,7 @@ func TestRecord_refAccessControl(t *testing.T) { req.EqualError(err, "not allowed to update this record") t.Log("grant update on namespace level") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, types.RecordRbacResource(ns.ID, 0, 0), "update"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, types.RecordRbacResource(ns.ID, 0, 0), "update"))) t.Log("update record on 2nd module with unchanged values") mod2rec1, _, err = svc.Update(ctx, mod2rec1) @@ -664,7 +653,7 @@ func TestRecord_refAccessControl(t *testing.T) { } { t.Log("revoke read on record") - req.NoError(rbacService.Grant(ctx, rbac.DenyRule(testerRole.ID, mod1rec1.RbacResource(), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.DenyRule(testerRole.ID, mod1rec1.RbacResource(), "read"))) t.Log("link 2nd record to 1st one again but w/o permissions; must work, value did not change") mod2rec1.Values = mod2rec1.Values.Set(&types.RecordValue{Name: "ref", Value: fmt.Sprintf("%d", mod1rec1.ID)}) @@ -679,12 +668,6 @@ func TestRecord_searchAccessControl(t *testing.T) { req = require.New(t) ctx = context.Background() - rbacService = rbac.NewService( - //zap.NewNop(), - logger.MakeDebugLogger(), - nil, - ) - nextIDi uint64 = 1 nextID = func() uint64 { nextIDi++ @@ -699,7 +682,6 @@ func TestRecord_searchAccessControl(t *testing.T) { strField = &types.ModuleField{ID: nextID(), NamespaceID: ns.ID, ModuleID: mod.ID, Name: "str", Kind: "String"} svc = makeTestRecordService(t, - rbacService, user, ns, mod, @@ -734,13 +716,13 @@ func TestRecord_searchAccessControl(t *testing.T) { } t.Log("inform rbac service about new roles") - rbacService.UpdateRoles( + svc.rbacSvc.UpdateRoles( rbac.CommonRole.Make(testerRole.ID, testerRole.Name), ) t.Log("log-in with test user ") ctx = auth.SetIdentityToContext(ctx, auth.Authenticated(user.ID, testerRole.ID)) - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, mod.RbacResource(), "records.search"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, mod.RbacResource(), "records.search"))) t.Log("search for the newly created records; should not find any (all denied)") f.IncTotal = true @@ -750,8 +732,8 @@ func TestRecord_searchAccessControl(t *testing.T) { req.Equal(uint(0), f.Total) t.Log("allow read access for two records") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, rr[3].RbacResource(), "read"))) - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(testerRole.ID, rr[6].RbacResource(), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, rr[3].RbacResource(), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(testerRole.ID, rr[6].RbacResource(), "read"))) t.Log("search for the newly created records; should find 2 we're allowed to read") f.IncTotal = true @@ -770,8 +752,6 @@ func TestRecord_contextualRolesAccessControl(t *testing.T) { //log = zap.NewNop() log = logger.MakeDebugLogger() - rbacService = rbac.NewService(log, nil) - nextIDi uint64 = 1 nextID = func() uint64 { nextIDi++ @@ -794,7 +774,6 @@ func TestRecord_contextualRolesAccessControl(t *testing.T) { boolField = &types.ModuleField{ID: nextID(), NamespaceID: ns.ID, ModuleID: mod.ID, Name: "yes", Kind: "String"} svc = makeTestRecordService(t, - rbacService, log, user, ns, @@ -878,14 +857,14 @@ func TestRecord_contextualRolesAccessControl(t *testing.T) { // read: x x x x x x x x x (all but one) t.Log("inform rbac service about new roles") - rbacService.UpdateRoles( + svc.rbacSvc.UpdateRoles( rbac.CommonRole.Make(baseRole.ID, baseRole.Name), rbac.MakeContextRole(ownerRole.ID, ownerRole.Name, roleCheckFnMaker("resource.ownedBy == userID"), types.RecordResourceType), rbac.MakeContextRole(truthyRole.ID, truthyRole.Name, roleCheckFnMaker(`has(resource.values, "yes") ? resource.values.yes : false`), types.RecordResourceType), rbac.MakeContextRole(tttRole.ID, tttRole.Name, roleCheckFnMaker(`has(resource.values, "num") ? resource.values.num == 333 : false`), types.RecordResourceType), ) - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(baseRole.ID, types.ModuleRbacResource(0, 0), "records.search"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(baseRole.ID, types.ModuleRbacResource(0, 0), "records.search"))) t.Log("log-in with test user") ctx = auth.SetIdentityToContext(ctx, auth.Authenticated(user.ID, baseRole.ID)) @@ -896,19 +875,19 @@ func TestRecord_contextualRolesAccessControl(t *testing.T) { req.Len(hits, 0) t.Log("expecting to find 5 records (owned by us)") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(ownerRole.ID, types.RecordRbacResource(0, 0, 0), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(ownerRole.ID, types.RecordRbacResource(0, 0, 0), "read"))) hits, _, err = svc.Find(ctx, f) req.NoError(err) req.Len(hits, 5) t.Log("expecting to find 2 records (owned by us and with true value for 'yes' field)") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(truthyRole.ID, types.RecordRbacResource(0, 0, 0), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(truthyRole.ID, types.RecordRbacResource(0, 0, 0), "read"))) hits, _, err = svc.Find(ctx, f) req.NoError(err) req.Len(hits, 8) t.Log("expecting to find 2 records (owned by us and with true value for 'yes' field + 333 for num)") - req.NoError(rbacService.Grant(ctx, rbac.AllowRule(tttRole.ID, types.RecordRbacResource(0, 0, 0), "read"))) + req.NoError(svc.rbacSvc.Grant(ctx, rbac.AllowRule(tttRole.ID, types.RecordRbacResource(0, 0, 0), "read"))) hits, _, err = svc.Find(ctx, f) req.NoError(err) req.Len(hits, 9) @@ -931,11 +910,13 @@ func TestSetRecordOwner(t *testing.T) { req.NoError(store.TruncateRbacRules(ctx, s)) var ( - rbacService = rbac.NewService( - zap.NewNop(), - //logger.MakeDebugLogger(), - nil, - ) + rbacService = rbac.NewServiceMust(ctx, zap.NewNop(), s, rbac.Config{ + Synchronous: true, + DecayInterval: time.Hour * 2, + CleanupInterval: time.Hour * 2, + ReindexInterval: time.Hour * 2, + IndexFlushInterval: time.Hour * 2, + }) ac = &accessControl{rbac: rbacService} invoker = &sysTypes.User{ID: 1001} diff --git a/server/pkg/rbac/service.go b/server/pkg/rbac/service.go index 95944a80f..5757a9ca7 100644 --- a/server/pkg/rbac/service.go +++ b/server/pkg/rbac/service.go @@ -19,7 +19,7 @@ type ( mux sync.RWMutex cfg Config logger *zap.Logger - StatLogger *statsLogger + StatLogger *StatsLogger noop bool noopAccess Access @@ -198,6 +198,15 @@ func NewService(ctx context.Context, l *zap.Logger, store rbacRulesStore, cc Con return } +func NewServiceMust(ctx context.Context, l *zap.Logger, store rbacRulesStore, cc Config) (svc *Service) { + svc, err := NewService(ctx, l, store, cc) + if err != nil { + panic(fmt.Sprintf("NewServiceMust failed with: %v", err)) + } + + return +} + func initUsageCounter(ctx context.Context, cc Config) (svc *usageCounter[string]) { svc = &usageCounter[string]{ incChan: make(chan string, 1024), @@ -215,8 +224,8 @@ func initUsageCounter(ctx context.Context, cc Config) (svc *usageCounter[string] return } -func initStatsLogger(ctx context.Context, l *zap.Logger) (svc *statsLogger) { - svc = &statsLogger{ +func initStatsLogger(ctx context.Context, l *zap.Logger) (svc *StatsLogger) { + svc = &StatsLogger{ log: l.Named("rbac stats logger"), cacheHitChan: make(chan statsWrap, 1024), cacheMissChan: make(chan statsWrap, 1024), @@ -227,7 +236,7 @@ func initStatsLogger(ctx context.Context, l *zap.Logger) (svc *statsLogger) { return } -func initSvc(ctx context.Context, l *zap.Logger, cc Config, sl *statsLogger, uc *usageCounter[string]) (svc *Service) { +func initSvc(ctx context.Context, l *zap.Logger, cc Config, sl *StatsLogger, uc *usageCounter[string]) (svc *Service) { svc = &Service{ logger: l, @@ -411,6 +420,7 @@ func (svc *Service) Stats() (out Stats, err error) { out.CacheHits, out.CacheMisses, + out.CacheUpdates, out.AvgTiming, out.MinTiming, out.MaxTiming, @@ -423,14 +433,6 @@ func (svc *Service) Stats() (out Stats, err error) { return } -// AddRole adds an additional role after the service was initialized -func (svc *Service) AddRole(r *Role) { - svc.mux.Lock() - defer svc.mux.Unlock() - - svc.roles = append(svc.roles, r) -} - func (svc *Service) UpdateRoles(rr ...*Role) { svc.mux.Lock() defer svc.mux.Unlock() diff --git a/server/pkg/rbac/stats.go b/server/pkg/rbac/stats.go index 6ee6cbc9f..73a489720 100644 --- a/server/pkg/rbac/stats.go +++ b/server/pkg/rbac/stats.go @@ -3,6 +3,7 @@ package rbac import ( "context" "fmt" + "sort" "sync" "time" @@ -11,7 +12,7 @@ import ( ) type ( - statsLogger struct { + StatsLogger struct { lock sync.RWMutex log *zap.Logger @@ -44,12 +45,13 @@ type ( ) // Stats returns the tracked stats -func (l *statsLogger) Stats() (cacheHit uint, cacheMiss uint, avgTiming, minTiming, maxTiming time.Duration, lastHits []string, lastMisses []string, lastTimings []time.Duration) { +func (l *StatsLogger) Stats() (cacheHit uint, cacheMiss uint, cacheUpdates uint, avgTiming, minTiming, maxTiming time.Duration, lastHits []string, lastMisses []string, lastTimings []time.Duration) { l.lock.RLock() defer l.lock.RUnlock() return l.cacheHits, l.cacheMisses, + l.cacheUpdates, l.avgTiming, l.minTiming, l.maxTiming, @@ -59,7 +61,7 @@ func (l *statsLogger) Stats() (cacheHit uint, cacheMiss uint, avgTiming, minTimi } // Timing logs the giving duration -func (l *statsLogger) Timing(timing time.Duration) { +func (l *StatsLogger) Timing(timing time.Duration) { l.lock.Lock() defer l.lock.Unlock() @@ -96,7 +98,7 @@ func (l *statsLogger) Timing(timing time.Duration) { } } -func (l *statsLogger) CacheHit(roles []uint64, resource string, op string) { +func (l *StatsLogger) CacheHit(roles []uint64, resource string, op string) { l.lock.Lock() defer l.lock.Unlock() @@ -109,7 +111,7 @@ func (l *statsLogger) CacheHit(roles []uint64, resource string, op string) { l.lastHits.Add(l.strfEntry(roles, resource, op)) } -func (l *statsLogger) CacheMiss(roles []uint64, resource string, op string) { +func (l *StatsLogger) CacheMiss(roles []uint64, resource string, op string) { l.lock.Lock() defer l.lock.Unlock() @@ -122,7 +124,7 @@ func (l *statsLogger) CacheMiss(roles []uint64, resource string, op string) { l.lastMisses.Add(l.strfEntry(roles, resource, op)) } -func (l *statsLogger) CacheUpdate(in *Rule) { +func (l *StatsLogger) CacheUpdate(in *Rule) { l.lock.Lock() defer l.lock.Unlock() @@ -134,11 +136,13 @@ func (l *statsLogger) CacheUpdate(in *Rule) { // // // // // // // // // // // // // // // // // // // // // // // // // // Utils -func (l *statsLogger) strfEntry(roles []uint64, resource string, op string) string { +func (l *StatsLogger) strfEntry(roles []uint64, resource string, op string) string { + sort.Slice(roles, func(i, j int) bool { return roles[i] < roles[j] }) + return fmt.Sprintf("%v %s %s", roles, op, resource) } -func (l *statsLogger) watch(ctx context.Context) { +func (l *StatsLogger) watch(ctx context.Context) { t := time.NewTicker(time.Minute * 5) go func() { diff --git a/server/system/service/user_test.go b/server/system/service/user_test.go index 05b906a29..01f682301 100644 --- a/server/system/service/user_test.go +++ b/server/system/service/user_test.go @@ -3,6 +3,7 @@ package service import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" @@ -38,8 +39,6 @@ func TestUser_ProtectedSearch(t *testing.T) { testUser = &types.User{ID: 42} - acRBAC = rbac.NewService(zap.NewNop(), nil) - s store.Storer ) @@ -49,6 +48,16 @@ func TestUser_ProtectedSearch(t *testing.T) { req.NoError(err) } + var ( + acRBAC = rbac.NewServiceMust(ctx, zap.NewNop(), s, rbac.Config{ + Synchronous: true, + DecayInterval: time.Hour * 2, + CleanupInterval: time.Hour * 2, + ReindexInterval: time.Hour * 2, + IndexFlushInterval: time.Hour * 2, + }) + ) + acRBAC.UpdateRoles(rbac.CommonRole.Make(testRoleID, "test-role")) req.NoError(acRBAC.Grant(ctx, rbac.AllowRule(testRoleID, types.ComponentRbacResource(), "users.search"), diff --git a/server/tests/automation/permissions_test.go b/server/tests/automation/permissions_test.go index f46e4dcf1..7e5213563 100644 --- a/server/tests/automation/permissions_test.go +++ b/server/tests/automation/permissions_test.go @@ -1,6 +1,7 @@ package automation import ( + "context" "fmt" "net/http" "testing" @@ -8,7 +9,7 @@ import ( "github.com/cortezaproject/corteza/server/automation/types" "github.com/cortezaproject/corteza/server/pkg/rbac" "github.com/cortezaproject/corteza/server/tests/helpers" - "github.com/steinfletcher/apitest-jsonpath" + jsonpath "github.com/steinfletcher/apitest-jsonpath" ) func TestPermissionsEffective(t *testing.T) { @@ -68,6 +69,7 @@ func TestPermissionsUpdate(t *testing.T) { } func TestPermissionsDelete(t *testing.T) { + ctx := context.Background() h := newHelper(t) p := rbac.Global() @@ -77,12 +79,12 @@ func TestPermissionsDelete(t *testing.T) { // New role. permDelRole := h.roleID + 1 - h.a.Len(rbac.Global().FindRulesByRoleID(permDelRole), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, permDelRole)), 0) // Setup a few fake rules for new role helpers.Grant(rbac.AllowRule(permDelRole, types.ComponentRbacResource(), "workflow.create")) - h.a.Len(p.FindRulesByRoleID(permDelRole), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, permDelRole)), 1) h.apiInit(). Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)). @@ -93,7 +95,7 @@ func TestPermissionsDelete(t *testing.T) { End() // Make sure all rules for this role are deleted - for _, r := range p.FindRulesByRoleID(permDelRole) { + for _, r := range mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, permDelRole)) { h.a.True(r.Access == rbac.Inherit) } } @@ -113,3 +115,11 @@ func TestPermissionsTrace(t *testing.T) { Assert(jsonpath.Present(`$.response`)). End() } + +func mustFindRulesByRoleID(rr rbac.RuleSet, err error) rbac.RuleSet { + if err != nil { + panic(err) + } + + return rr +} diff --git a/server/tests/compose/permissions_test.go b/server/tests/compose/permissions_test.go index 39fa2b62c..ef16aed81 100644 --- a/server/tests/compose/permissions_test.go +++ b/server/tests/compose/permissions_test.go @@ -1,6 +1,7 @@ package compose import ( + "context" "fmt" "net/http" "testing" @@ -8,7 +9,7 @@ import ( "github.com/cortezaproject/corteza/server/compose/types" "github.com/cortezaproject/corteza/server/pkg/rbac" "github.com/cortezaproject/corteza/server/tests/helpers" - "github.com/steinfletcher/apitest-jsonpath" + jsonpath "github.com/steinfletcher/apitest-jsonpath" ) func TestPermissionsEffective(t *testing.T) { @@ -74,6 +75,7 @@ func TestPermissionsUpdate(t *testing.T) { } func TestPermissionsDelete(t *testing.T) { + ctx := context.Background() h := newHelper(t) p := rbac.Global() @@ -83,12 +85,12 @@ func TestPermissionsDelete(t *testing.T) { // New role. permDelRole := h.roleID + 1 - h.a.Len(rbac.Global().FindRulesByRoleID(permDelRole), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, permDelRole)), 0) // Setup a few fake rules for new role helpers.Grant(rbac.AllowRule(permDelRole, types.ComponentRbacResource(), "namespace.create")) - h.a.Len(p.FindRulesByRoleID(permDelRole), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, permDelRole)), 1) h.apiInit(). Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)). @@ -99,7 +101,7 @@ func TestPermissionsDelete(t *testing.T) { End() // Make sure all rules for this role are deleted - for _, r := range p.FindRulesByRoleID(permDelRole) { + for _, r := range mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, permDelRole)) { h.a.True(r.Access == rbac.Inherit) } } @@ -119,3 +121,11 @@ func TestPermissionsTrace(t *testing.T) { Assert(jsonpath.Present(`$.response`)). End() } + +func mustFindRulesByRoleID(rr rbac.RuleSet, err error) rbac.RuleSet { + if err != nil { + panic(err) + } + + return rr +} diff --git a/server/tests/rbac/main_test.go b/server/tests/rbac/main_test.go new file mode 100644 index 000000000..94870ee07 --- /dev/null +++ b/server/tests/rbac/main_test.go @@ -0,0 +1,223 @@ +package rbac + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + automationEnvoy "github.com/cortezaproject/corteza/server/automation/envoy" + composeEnvoy "github.com/cortezaproject/corteza/server/compose/envoy" + systemEnvoy "github.com/cortezaproject/corteza/server/system/envoy" + "github.com/cortezaproject/corteza/server/system/types" + "github.com/stretchr/testify/require" + + "github.com/cortezaproject/corteza/server/pkg/cli" + "github.com/cortezaproject/corteza/server/pkg/envoyx" + "github.com/cortezaproject/corteza/server/pkg/id" + "github.com/cortezaproject/corteza/server/pkg/rbac" + "github.com/cortezaproject/corteza/server/store" + "github.com/cortezaproject/corteza/server/tests/helpers" + _ "github.com/joho/godotenv/autoload" + "go.uber.org/zap" +) + +type ( + sesWrap struct { + identity uint64 + roles []uint64 + context context.Context + } + + resWrap struct { + resource string + } + + testStorage struct { + upserts []*rbac.Rule + + returnRuleSearch []*rbac.Rule + } + + svcModFnc func(*rbac.Service) +) + +var ( + defaultEnvoy *envoyx.Service + defaultStore store.Storer +) + +func init() { + helpers.RecursiveDotEnvLoad() + id.Init(cli.Context()) +} + +func TestMain(m *testing.M) { + InitTestApp() + os.Exit(m.Run()) +} + +func InitTestApp() { + ctx := cli.Context() + + if defaultStore == nil { + initStore(ctx) + } + + if defaultEnvoy == nil { + initSvc(ctx) + } +} + +func initStore(ctx context.Context) { + var err error + // dsn := "postgres://corteza:corteza@127.0.0.1:3402/testing?sslmode=disable" + dsn := "sqlite3+debug://file::memory:?cache=shared&mode=memory" + defaultStore, err = store.Connect(ctx, zap.NewNop(), dsn, true) + if err != nil { + panic(err) + } + + err = store.Upgrade(ctx, zap.NewNop(), defaultStore) + if err != nil { + panic(err) + } +} + +func cleanup(b *testing.B) { + var ( + ctx = context.Background() + ) + + err := collect( + store.TruncateRbacRules(ctx, defaultStore), + store.TruncateRoles(ctx, defaultStore), + store.TruncateUsers(ctx, defaultStore), + ) + if err != nil { + b.Fatalf("failed to decode scenario data: %v", err) + } +} + +func collect(ee ...error) error { + for _, e := range ee { + if e != nil { + return e + } + } + return nil +} + +func initSvc(ctx context.Context) { + defaultEnvoy = envoyx.New() + defaultEnvoy.AddDecoder(envoyx.DecodeTypeURI, + composeEnvoy.YamlDecoder{}, + systemEnvoy.YamlDecoder{}, + automationEnvoy.YamlDecoder{}, + ) + + defaultEnvoy.AddEncoder(envoyx.EncodeTypeStore, + composeEnvoy.StoreEncoder{}, + systemEnvoy.StoreEncoder{}, + automationEnvoy.StoreEncoder{}, + ) +} + +func initState(t *testing.T, maxIndexSize int, things ...svcModFnc) (context.Context, *require.Assertions, *rbac.Service, *testStorage) { + var ( + ctx = context.Background() + req = require.New(t) + ) + + store := &testStorage{} + svc, err := rbac.NewService(ctx, zap.NewNop(), defaultStore, rbac.Config{ + Synchronous: true, + + MaxIndexSize: maxIndexSize, + PullInitialState: intst, + DecayFactor: 1, + DecayInterval: time.Hour * 4, + CleanupInterval: time.Hour * 4, + + RuleStorage: store, + RoleStorage: store, + }) + req.NoError(err) + + for _, f := range things { + f(svc) + } + + return ctx, req, svc, store +} + +func mustStats(req *require.Assertions, svc *rbac.Service) rbac.Stats { + stats, err := svc.Stats() + req.NoError(err) + return stats +} + +func must(req *require.Assertions, err error) { + req.NoError(err) +} + +func checkHitRatios(req *require.Assertions, stats rbac.Stats, hits, misses uint, lastHitsLastMisses ...[][]uint64) { + req.Equal(hits, stats.CacheHits) + req.Equal(misses, stats.CacheMisses) + + if len(lastHitsLastMisses) > 0 { + for i := 0; i < len(lastHitsLastMisses[0]); i++ { + req.Contains(stats.LastHits[i], fmt.Sprintf("%v", lastHitsLastMisses[0][i])) + } + } + + if len(lastHitsLastMisses) > 1 { + for i := 0; i < len(lastHitsLastMisses[1]); i++ { + req.Contains(stats.LastMisses[i], fmt.Sprintf("%v", lastHitsLastMisses[1][i])) + } + } +} + +// Utils + +func (ts *testStorage) SearchRbacRules(ctx context.Context, f rbac.RuleFilter) (rs rbac.RuleSet, rf rbac.RuleFilter, er error) { + return ts.returnRuleSearch, f, nil +} + +func (ts *testStorage) UpsertRbacRule(ctx context.Context, rr ...*rbac.Rule) (err error) { + ts.upserts = append(ts.upserts, rr...) + return +} + +func (testStorage) DeleteRbacRule(ctx context.Context, rr ...*rbac.Rule) (err error) { + return +} + +func (testStorage) TruncateRbacRules(ctx context.Context) (err error) { + return +} + +func (testStorage) SearchRoles(ctx context.Context, f types.RoleFilter) (rs types.RoleSet, rf types.RoleFilter, err error) { + return +} + +func (sw sesWrap) Identity() uint64 { + return sw.identity +} +func (sw sesWrap) Roles() []uint64 { + return sw.roles +} +func (sw sesWrap) Context() context.Context { + return sw.context +} + +func (rw resWrap) RbacResource() string { + return rw.resource +} + +func svcWithRoles(roles ...*rbac.Role) svcModFnc { + return func(s *rbac.Service) { + s.UpdateRoles(roles...) + } +} diff --git a/server/tests/rbac/rbac_rules_test.go b/server/tests/rbac/rbac_rules_test.go new file mode 100644 index 000000000..1bc14b6bf --- /dev/null +++ b/server/tests/rbac/rbac_rules_test.go @@ -0,0 +1,151 @@ +package rbac + +import ( + "testing" + + "github.com/cortezaproject/corteza/server/pkg/rbac" +) + +func TestGrant(t *testing.T) { + t.Run("completely empty index", func(t *testing.T) { + ctx, + req, + svc, + storage := initState(t, 0) + + svc.Grant(ctx, &rbac.Rule{ + RoleID: 1, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + }) + + // No cache update since resource not indexed + stats := mustStats(req, svc) + req.Len(storage.upserts, 1) + req.Equal(uint(0), stats.CacheUpdates) + }) + + t.Run("granting existing resource", func(t *testing.T) { + ctx, + req, + svc, + storage := initState(t, 0) + + must(req, svc.DebuggerSetIndex(1, "smt/1/1/1", &rbac.Rule{ + RoleID: 1, + Resource: "smt/1/1/1", + Operation: "write", + Access: rbac.Allow, + })) + + svc.Grant(ctx, &rbac.Rule{ + RoleID: 1, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + }) + + // Updated the index since resource indexed + stt := mustStats(req, svc) + req.Len(storage.upserts, 1) + req.Equal(uint(1), stt.CacheUpdates) + }) +} + +func TestCheck(t *testing.T) { + t.Run("completely empty index", func(t *testing.T) { + ctx, + req, + svc, + storage := initState( + t, + 0, + svcWithRoles(rbac.CommonRole.Make(1, "")), + ) + + storage.returnRuleSearch = []*rbac.Rule{{ + RoleID: 1, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + }} + + req.True(svc.Can(sesWrap{ + identity: 1, + roles: []uint64{1}, + context: ctx, + }, "read", resWrap{resource: "smt/1/1/1"})) + + checkHitRatios(req, mustStats(req, svc), 0, 1) + }) + + t.Run("half index, half unindex", func(t *testing.T) { + ctx, + req, + svc, + storage := initState( + t, + 0, + svcWithRoles(rbac.CommonRole.Make(1, ""), rbac.CommonRole.Make(2, "")), + ) + + storage.returnRuleSearch = []*rbac.Rule{{ + RoleID: 2, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + }} + + must(req, svc.DebuggerSetIndex(1, "smt/1/1/1", &rbac.Rule{ + RoleID: 1, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + })) + + req.True(svc.Can(sesWrap{ + identity: 1, + roles: []uint64{1, 2}, + context: ctx, + }, "read", resWrap{resource: "smt/1/1/1"})) + + checkHitRatios(req, mustStats(req, svc), 1, 1, [][]uint64{{1}}, [][]uint64{{2}}) + }) + + t.Run("all hits", func(t *testing.T) { + ctx, + req, + svc, + _ := initState( + t, + 0, + svcWithRoles( + rbac.CommonRole.Make(1, ""), + rbac.CommonRole.Make(2, ""), + ), + ) + + must(req, svc.DebuggerSetIndex(1, "smt/1/1/1", &rbac.Rule{ + RoleID: 1, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + })) + + must(req, svc.DebuggerAddIndex(2, "smt/1/1/1", &rbac.Rule{ + RoleID: 2, + Resource: "smt/1/1/1", + Operation: "read", + Access: rbac.Allow, + })) + + req.True(svc.Can(sesWrap{ + identity: 1, + roles: []uint64{1, 2}, + context: ctx, + }, "read", resWrap{resource: "smt/1/1/1"})) + + checkHitRatios(req, mustStats(req, svc), 1, 0, [][]uint64{{1, 2}}) + }) +} diff --git a/server/tests/system/permissions_test.go b/server/tests/system/permissions_test.go index f39d8be66..adb0f00cf 100644 --- a/server/tests/system/permissions_test.go +++ b/server/tests/system/permissions_test.go @@ -1,6 +1,7 @@ package system import ( + "context" "fmt" "net/http" "strconv" @@ -117,6 +118,8 @@ func TestPermissionsUpdate(t *testing.T) { } func TestPermissionsDelete(t *testing.T) { + ctx := context.Background() + h := newHelper(t) p := rbac.Global() @@ -126,12 +129,14 @@ func TestPermissionsDelete(t *testing.T) { // New role. permDelRole := h.roleID + 1 - h.a.Len(rbac.Global().FindRulesByRoleID(permDelRole), 0) + rr := mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, permDelRole)) + h.a.Len(rr, 0) // Setup a few fake rules for new role helpers.Grant(rbac.AllowRule(permDelRole, types.ComponentRbacResource(), "user.create")) - h.a.Len(p.FindRulesByRoleID(permDelRole), 1) + rr = mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, permDelRole)) + h.a.Len(rr, 1) h.apiInit(). Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)). @@ -142,7 +147,8 @@ func TestPermissionsDelete(t *testing.T) { End() // Make sure all rules for this role are deleted - for _, r := range p.FindRulesByRoleID(permDelRole) { + rr = mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, permDelRole)) + for _, r := range rr { h.a.True(r.Access == rbac.Inherit) } } @@ -166,6 +172,7 @@ func TestPermissionsTrace(t *testing.T) { func TestPermissionsCloneToSingleRole(t *testing.T) { h := newHelper(t) p := rbac.Global() + ctx := context.Background() // Make sure our user can grant helpers.AllowMe(h, types.ComponentRbacResource(), "grant") @@ -174,8 +181,8 @@ func TestPermissionsCloneToSingleRole(t *testing.T) { roleS := h.roleID + 1 roleT := h.roleID + 2 - h.a.Len(rbac.Global().FindRulesByRoleID(roleS), 0) - h.a.Len(rbac.Global().FindRulesByRoleID(roleT), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleS)), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleT)), 0) // Set up a few fake rules for new role helpers.Grant(rbac.AllowRule(roleS, types.ComponentRbacResource(), "user.create")) @@ -183,8 +190,8 @@ func TestPermissionsCloneToSingleRole(t *testing.T) { helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.update")) helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.delete")) - h.a.Len(p.FindRulesByRoleID(roleS), 1) - h.a.Len(p.FindRulesByRoleID(roleT), 2) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleS)), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleT)), 2) h.apiInit(). Post(fmt.Sprintf("/roles/%d/rules/clone", roleS)). @@ -196,12 +203,13 @@ func TestPermissionsCloneToSingleRole(t *testing.T) { End() // Make sure all rules for role S are intact - h.a.Len(p.FindRulesByRoleID(roleS), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleS)), 1) // Make sure all rules for role T are cloned from role S - h.a.Len(p.FindRulesByRoleID(roleT), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleT)), 1) } func TestPermissionsCloneToMultipleRole(t *testing.T) { + ctx := context.Background() h := newHelper(t) p := rbac.Global() @@ -213,9 +221,9 @@ func TestPermissionsCloneToMultipleRole(t *testing.T) { roleT := h.roleID + 2 roleY := h.roleID + 3 - h.a.Len(rbac.Global().FindRulesByRoleID(roleS), 0) - h.a.Len(rbac.Global().FindRulesByRoleID(roleT), 0) - h.a.Len(rbac.Global().FindRulesByRoleID(roleY), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleS)), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleT)), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleY)), 0) // Set up a few fake rules for new role helpers.Grant(rbac.AllowRule(roleS, types.ComponentRbacResource(), "user.create")) @@ -227,9 +235,9 @@ func TestPermissionsCloneToMultipleRole(t *testing.T) { helpers.Grant(rbac.AllowRule(roleY, types.ComponentRbacResource(), "user.update")) helpers.Grant(rbac.AllowRule(roleY, types.ComponentRbacResource(), "user.delete")) - h.a.Len(p.FindRulesByRoleID(roleS), 1) - h.a.Len(p.FindRulesByRoleID(roleT), 2) - h.a.Len(p.FindRulesByRoleID(roleY), 3) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleS)), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleT)), 2) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleY)), 3) h.apiInit(). Post(fmt.Sprintf("/roles/%d/rules/clone", roleS)). @@ -242,14 +250,15 @@ func TestPermissionsCloneToMultipleRole(t *testing.T) { End() // Make sure all rules for role S are intact - h.a.Len(p.FindRulesByRoleID(roleS), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleS)), 1) // Make sure all rules for role T are cloned from role S - h.a.Len(p.FindRulesByRoleID(roleT), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleT)), 1) // Make sure all rules for role Y are cloned from role S - h.a.Len(p.FindRulesByRoleID(roleY), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleY)), 1) } func TestPermissionsCloneNotAllowed(t *testing.T) { + ctx := context.Background() h := newHelper(t) p := rbac.Global() @@ -257,8 +266,8 @@ func TestPermissionsCloneNotAllowed(t *testing.T) { roleS := h.roleID + 1 roleT := h.roleID + 2 - h.a.Len(rbac.Global().FindRulesByRoleID(roleS), 0) - h.a.Len(rbac.Global().FindRulesByRoleID(roleT), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleS)), 0) + h.a.Len(mustFindRulesByRoleID(rbac.Global().FindRulesByRoleID(ctx, roleT)), 0) // Set up a few fake rules for new role helpers.Grant(rbac.AllowRule(roleS, types.ComponentRbacResource(), "user.create")) @@ -266,8 +275,8 @@ func TestPermissionsCloneNotAllowed(t *testing.T) { helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.update")) helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.delete")) - h.a.Len(p.FindRulesByRoleID(roleS), 1) - h.a.Len(p.FindRulesByRoleID(roleT), 2) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleS)), 1) + h.a.Len(mustFindRulesByRoleID(p.FindRulesByRoleID(ctx, roleT)), 2) h.apiInit(). Post(fmt.Sprintf("/roles/%d/rules/clone", roleS)). @@ -278,3 +287,11 @@ func TestPermissionsCloneNotAllowed(t *testing.T) { Assert(helpers.AssertError("role.errors.notAllowedToCloneRules")). End() } + +func mustFindRulesByRoleID(rr rbac.RuleSet, err error) rbac.RuleSet { + if err != nil { + panic(err) + } + + return rr +} diff --git a/server/tests/workflows/0002_rbac_fn_test.go b/server/tests/workflows/0002_rbac_fn_test.go index 44539c660..f7853e3d3 100644 --- a/server/tests/workflows/0002_rbac_fn_test.go +++ b/server/tests/workflows/0002_rbac_fn_test.go @@ -17,7 +17,9 @@ func Test0002_rbac_fn(t *testing.T) { loadScenario(ctx, t) - req.Len(rbac.Global().Rules(), 0) + rr, err := rbac.Global().Rules(ctx) + req.NoError(err) + req.Len(rr, 0) var ( aux = struct { @@ -30,5 +32,7 @@ func Test0002_rbac_fn(t *testing.T) { req.NoError(vars.Decode(&aux)) req.Equal("y", aux.CanCurrentRead) req.Equal("n", aux.CanOtherRead) - req.Len(rbac.Global().Rules(), 1) + rr, err = rbac.Global().Rules(ctx) + req.NoError(err) + req.Len(rr, 1) } diff --git a/server/tests/workflows/exec_permissions_test.go b/server/tests/workflows/exec_permissions_test.go index ba6bff02e..5d212e8d3 100644 --- a/server/tests/workflows/exec_permissions_test.go +++ b/server/tests/workflows/exec_permissions_test.go @@ -7,7 +7,6 @@ import ( "github.com/cortezaproject/corteza/server/automation/service" "github.com/cortezaproject/corteza/server/automation/types" "github.com/cortezaproject/corteza/server/pkg/auth" - "github.com/cortezaproject/corteza/server/pkg/rbac" "github.com/cortezaproject/corteza/server/tests/helpers" "github.com/stretchr/testify/require" ) @@ -46,8 +45,6 @@ func Test_exec_permissions(t *testing.T) { executors.ID, ) - rbac.Global().Reload(ctx) - t.Run("exec allowed", func(t *testing.T) { ctx = auth.SetIdentityToContext(ctx, execAllowed) _, _ = mustExecWorkflow(ctx, t, "wf", types.WorkflowExecParams{}) diff --git a/server/tests/workflows/invoker_and_runner_in_scope_test.go b/server/tests/workflows/invoker_and_runner_in_scope_test.go index 27569eeb9..1245d4b7d 100644 --- a/server/tests/workflows/invoker_and_runner_in_scope_test.go +++ b/server/tests/workflows/invoker_and_runner_in_scope_test.go @@ -6,7 +6,6 @@ import ( "github.com/cortezaproject/corteza/server/automation/types" "github.com/cortezaproject/corteza/server/pkg/auth" - "github.com/cortezaproject/corteza/server/pkg/rbac" sysTypes "github.com/cortezaproject/corteza/server/system/types" "github.com/cortezaproject/corteza/server/tests/helpers" "github.com/stretchr/testify/require" @@ -46,8 +45,6 @@ func Test_invoker_and_runner_in_scope(t *testing.T) { wfInvokers.ID, ) - rbac.Global().Reload(ctx) - t.Run("invoker set in scope", func(t *testing.T) { var ( req = require.New(t)