diff --git a/pkg/provision/migrations_202109_rbac.go b/pkg/provision/migrations_202109_rbac.go index f7640a1cd..11787c86f 100644 --- a/pkg/provision/migrations_202109_rbac.go +++ b/pkg/provision/migrations_202109_rbac.go @@ -7,12 +7,24 @@ import ( composeTypes "github.com/cortezaproject/corteza-server/compose/types" federationTypes "github.com/cortezaproject/corteza-server/federation/types" + "github.com/cortezaproject/corteza-server/pkg/filter" "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/cortezaproject/corteza-server/store" systemTypes "github.com/cortezaproject/corteza-server/system/types" "go.uber.org/zap" ) +type ( + resourceIndex struct { + fields map[uint64]*composeTypes.ModuleField + modules map[uint64]*composeTypes.Module + charts map[uint64]*composeTypes.Chart + pages map[uint64]*composeTypes.Page + exposedModules map[uint64]*federationTypes.ExposedModule + sharedModules map[uint64]*federationTypes.SharedModule + } +) + // MigrateOperations creates system roles func migratePre202109RbacRules(ctx context.Context, log *zap.Logger, s store.Storer) error { return store.Tx(ctx, s, func(ctx context.Context, s store.Storer) error { @@ -22,10 +34,16 @@ func migratePre202109RbacRules(ctx context.Context, log *zap.Logger, s store.Sto } log.Info("migrating RBAC rules to new format", zap.Int("rules", len(rr))) + + rx, err := preloadResourceIndex(ctx, s) + if err != nil { + return err + } + for _, r := range rr { var ( cr = *r - action = migratePre202109RbacRule(r) + action = migratePre202109RbacRule(r, rx) ) if action != 0 { @@ -50,7 +68,7 @@ func migratePre202109RbacRules(ctx context.Context, log *zap.Logger, s store.Sto // 0 - no action // -1 - remove // 1 - update -func migratePre202109RbacRule(r *rbac.Rule) (op int) { +func migratePre202109RbacRule(r *rbac.Rule, rx *resourceIndex) (op int) { const ( nsSep = "::" nsDef = "corteza" @@ -105,19 +123,69 @@ func migratePre202109RbacRule(r *rbac.Rule) (op int) { if len(parts) == 3 { var ID, _ = strconv.ParseUint(parts[2], 10, 64) + p1 := uint64(0) + p2 := uint64(0) + // exceptions with nested references switch rType { case composeTypes.ModuleFieldResourceType: - r.Resource = composeTypes.ModuleFieldRbacResource(0, 0, ID) + if ID > 0 { + if f, ok := rx.fields[ID]; ok { + p1 = f.NamespaceID + p2 = f.ModuleID + } + } + r.Resource = composeTypes.ModuleFieldRbacResource(p1, p2, ID) + case composeTypes.ModuleResourceType: - r.Resource = composeTypes.ModuleRbacResource(0, ID) + if ID > 0 { + if r, ok := rx.modules[ID]; ok { + p1 = r.NamespaceID + } + } + r.Resource = composeTypes.ModuleRbacResource(p1, ID) + + // ID belongs to module! case composeTypes.RecordResourceType: - // ID belongs to module! - r.Resource = composeTypes.RecordRbacResource(0, ID, 0) + if ID > 0 { + if r, ok := rx.modules[ID]; ok { + p1 = r.NamespaceID + } + } + r.Resource = composeTypes.RecordRbacResource(p1, ID, 0) + case composeTypes.ChartResourceType: - r.Resource = composeTypes.ChartRbacResource(0, ID) + if ID > 0 { + if r, ok := rx.charts[ID]; ok { + p1 = r.NamespaceID + } + } + r.Resource = composeTypes.ChartRbacResource(p1, ID) + case composeTypes.PageResourceType: - r.Resource = composeTypes.PageRbacResource(0, ID) + if ID > 0 { + if r, ok := rx.pages[ID]; ok { + p1 = r.NamespaceID + } + } + r.Resource = composeTypes.PageRbacResource(p1, ID) + + case federationTypes.ExposedModuleResourceType: + if ID > 0 { + if r, ok := rx.exposedModules[ID]; ok { + p1 = r.NodeID + } + } + r.Resource = federationTypes.ExposedModuleRbacResource(p1, ID) + + case federationTypes.SharedModuleResourceType: + if ID > 0 { + if r, ok := rx.sharedModules[ID]; ok { + p1 = r.NodeID + } + } + r.Resource = federationTypes.SharedModuleRbacResource(p1, ID) + default: r.Resource = rType + "/" + func() string { if ID == 0 { @@ -133,3 +201,68 @@ func migratePre202109RbacRule(r *rbac.Rule) (op int) { return } + +// helper to preloadresources that may be used when properly constructing rules +func preloadResourceIndex(ctx context.Context, s store.Storer) (*resourceIndex, error) { + rx := &resourceIndex{} + + rx.modules = make(map[uint64]*composeTypes.Module) + modules, _, err := store.SearchComposeModules(ctx, s, composeTypes.ModuleFilter{Paging: filter.Paging{Limit: 0}}) + modIDs := make([]uint64, 0, len(modules)) + if err != nil { + return nil, err + } + for _, r := range modules { + rx.modules[r.ID] = r + modIDs = append(modIDs, r.ID) + } + + if len(modIDs) > 0 { + rx.fields = make(map[uint64]*composeTypes.ModuleField) + fields, _, err := store.SearchComposeModuleFields(ctx, s, composeTypes.ModuleFieldFilter{ModuleID: modIDs}) + if err != nil { + return nil, err + } + for _, r := range fields { + rx.fields[r.ID] = r + } + } + + rx.charts = make(map[uint64]*composeTypes.Chart) + chart, _, err := store.SearchComposeCharts(ctx, s, composeTypes.ChartFilter{Paging: filter.Paging{Limit: 0}}) + if err != nil { + return nil, err + } + for _, r := range chart { + rx.charts[r.ID] = r + } + + rx.pages = make(map[uint64]*composeTypes.Page) + page, _, err := store.SearchComposePages(ctx, s, composeTypes.PageFilter{Paging: filter.Paging{Limit: 0}}) + if err != nil { + return nil, err + } + for _, r := range page { + rx.pages[r.ID] = r + } + + rx.exposedModules = make(map[uint64]*federationTypes.ExposedModule) + exposedModule, _, err := store.SearchFederationExposedModules(ctx, s, federationTypes.ExposedModuleFilter{Paging: filter.Paging{Limit: 0}}) + if err != nil { + return nil, err + } + for _, r := range exposedModule { + rx.exposedModules[r.ID] = r + } + + rx.sharedModules = make(map[uint64]*federationTypes.SharedModule) + sharedModule, _, err := store.SearchFederationSharedModules(ctx, s, federationTypes.SharedModuleFilter{Paging: filter.Paging{Limit: 0}}) + if err != nil { + return nil, err + } + for _, r := range sharedModule { + rx.sharedModules[r.ID] = r + } + + return rx, nil +} diff --git a/pkg/provision/migrations_202109_rbac_test.go b/pkg/provision/migrations_202109_rbac_test.go index a0a0601da..42788fe88 100644 --- a/pkg/provision/migrations_202109_rbac_test.go +++ b/pkg/provision/migrations_202109_rbac_test.go @@ -3,11 +3,22 @@ package provision import ( "testing" + composeTypes "github.com/cortezaproject/corteza-server/compose/types" + federationTypes "github.com/cortezaproject/corteza-server/federation/types" "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/stretchr/testify/require" ) func Test_migratePre202109RbacRule(t *testing.T) { + rx := &resourceIndex{ + fields: make(map[uint64]*composeTypes.ModuleField), + modules: make(map[uint64]*composeTypes.Module), + charts: make(map[uint64]*composeTypes.Chart), + pages: make(map[uint64]*composeTypes.Page), + exposedModules: make(map[uint64]*federationTypes.ExposedModule), + sharedModules: make(map[uint64]*federationTypes.SharedModule), + } + tcc := []struct { wantOp int rule *rbac.Rule @@ -23,7 +34,7 @@ func Test_migratePre202109RbacRule(t *testing.T) { } for _, tc := range tcc { t.Run(tc.rule.String(), func(t *testing.T) { - require.Equal(t, tc.wantOp, migratePre202109RbacRule(tc.rule)) + require.Equal(t, tc.wantOp, migratePre202109RbacRule(tc.rule, rx)) if tc.wantRule != nil { require.Equal(t, tc.wantRule.String(), tc.rule.String()) }