mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Team/User: UID migrations (#82298)
* Add user uid migration to run on every startup to protect against empty values in a upgrade downgrade scenario * Add team uid migration to run on every startup to protect against empty values in a upgrade downgrade scenario * Run team uid migration
This commit is contained in:
@@ -207,7 +207,8 @@ func setupDB(b testing.TB) benchScenario {
|
|||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
|
|
||||||
teamSvc := teamimpl.ProvideService(db, cfg)
|
teamSvc, err := teamimpl.ProvideService(db, cfg)
|
||||||
|
require.NoError(b, err)
|
||||||
orgService, err := orgimpl.ProvideService(db, cfg, quotaService)
|
orgService, err := orgimpl.ProvideService(db, cfg, quotaService)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
|||||||
@@ -613,9 +613,10 @@ func TestIntegrationMergeUser(t *testing.T) {
|
|||||||
t.Run("should be able to merge user", func(t *testing.T) {
|
t.Run("should be able to merge user", func(t *testing.T) {
|
||||||
// Restore after destructive operation
|
// Restore after destructive operation
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
teamSvc := teamimpl.ProvideService(sqlStore, setting.NewCfg())
|
teamSvc, err := teamimpl.ProvideService(sqlStore, setting.NewCfg())
|
||||||
|
require.NoError(t, err)
|
||||||
team1, err := teamSvc.CreateTeam("team1 name", "", 1)
|
team1, err := teamSvc.CreateTeam("team1 name", "", 1)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
usrSvc := setupTestUserService(t, sqlStore)
|
usrSvc := setupTestUserService(t, sqlStore)
|
||||||
const testOrgID int64 = 1
|
const testOrgID int64 = 1
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,8 @@ func setupTestEnv(t testing.TB) (*AccessControlStore, rs.Store, user.Service, te
|
|||||||
cfg.AutoAssignOrgId = 1
|
cfg.AutoAssignOrgId = 1
|
||||||
acstore := ProvideService(sql)
|
acstore := ProvideService(sql)
|
||||||
permissionStore := rs.NewStore(sql, featuremgmt.WithFeatures())
|
permissionStore := rs.NewStore(sql, featuremgmt.WithFeatures())
|
||||||
teamService := teamimpl.ProvideService(sql, cfg)
|
teamService, err := teamimpl.ProvideService(sql, cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
orgService, err := orgimpl.ProvideService(sql, cfg, quotatest.New(false, nil))
|
orgService, err := orgimpl.ProvideService(sql, cfg, quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -508,7 +508,8 @@ func checkSeededPermissions(t *testing.T, permissions []resourcePermissionDTO) {
|
|||||||
func seedPermissions(t *testing.T, resourceID string, sql *sqlstore.SQLStore, service *Service) {
|
func seedPermissions(t *testing.T, resourceID string, sql *sqlstore.SQLStore, service *Service) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
// seed team 1 with "Edit" permission on dashboard 1
|
// seed team 1 with "Edit" permission on dashboard 1
|
||||||
teamSvc := teamimpl.ProvideService(sql, sql.Cfg)
|
teamSvc, err := teamimpl.ProvideService(sql, sql.Cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
team, err := teamSvc.CreateTeam("test", "test@test.com", 1)
|
team, err := teamSvc.CreateTeam("test", "test@test.com", 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = service.SetTeamPermission(context.Background(), team.OrgID, team.ID, resourceID, "Edit")
|
_, err = service.SetTeamPermission(context.Background(), team.OrgID, team.ID, resourceID, "Edit")
|
||||||
|
|||||||
@@ -237,8 +237,9 @@ func setupTestEnvironment(t *testing.T, ops Options) (*Service, *sqlstore.SQLSto
|
|||||||
|
|
||||||
sql := db.InitTestDB(t)
|
sql := db.InitTestDB(t)
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
teamSvc := teamimpl.ProvideService(sql, cfg)
|
teamSvc, err := teamimpl.ProvideService(sql, cfg)
|
||||||
userSvc, err := userimpl.ProvideService(sql, nil, cfg, teamimpl.ProvideService(sql, cfg), nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
require.NoError(t, err)
|
||||||
|
userSvc, err := userimpl.ProvideService(sql, nil, cfg, teamSvc, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
license := licensingtest.NewFakeLicensing()
|
license := licensingtest.NewFakeLicensing()
|
||||||
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
|
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
|
||||||
|
|||||||
@@ -138,7 +138,8 @@ func GenerateDatasourcePermissions(b *testing.B, db *sqlstore.SQLStore, ac *stor
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateTeamsAndUsers(b *testing.B, db *sqlstore.SQLStore, users int) ([]int64, []int64) {
|
func generateTeamsAndUsers(b *testing.B, db *sqlstore.SQLStore, users int) ([]int64, []int64) {
|
||||||
teamSvc := teamimpl.ProvideService(db, db.Cfg)
|
teamSvc, err := teamimpl.ProvideService(db, db.Cfg)
|
||||||
|
require.NoError(b, err)
|
||||||
numberOfTeams := int(math.Ceil(float64(users) / UsersPerTeam))
|
numberOfTeams := int(math.Ceil(float64(users) / UsersPerTeam))
|
||||||
globalUserId := 0
|
globalUserId := 0
|
||||||
qs := quotatest.New(false, nil)
|
qs := quotatest.New(false, nil)
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ func NewTestMigrationStore(t testing.TB, sqlStore *sqlstore.SQLStore, cfg *setti
|
|||||||
|
|
||||||
license := licensingtest.NewFakeLicensing()
|
license := licensingtest.NewFakeLicensing()
|
||||||
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
|
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
|
||||||
teamSvc := teamimpl.ProvideService(sqlStore, cfg)
|
teamSvc, err := teamimpl.ProvideService(sqlStore, cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService)
|
orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
userSvc, err := userimpl.ProvideService(sqlStore, orgService, cfg, teamSvc, cache, quotaService, bundleregistry.ProvideService())
|
userSvc, err := userimpl.ProvideService(sqlStore, orgService, cfg, teamSvc, cache, quotaService, bundleregistry.ProvideService())
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@@ -600,3 +601,25 @@ func (ss *xormStore) getTeamMembers(ctx context.Context, query *team.GetTeamMemb
|
|||||||
func (ss *xormStore) RegisterDelete(query string) {
|
func (ss *xormStore) RegisterDelete(query string) {
|
||||||
ss.deletes = append(ss.deletes, query)
|
ss.deletes = append(ss.deletes, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is just to ensure that all teams have a valid uid.
|
||||||
|
// To protect against upgrade / downgrade we need to run this for a couple of releases.
|
||||||
|
// FIXME: Remove this migration and make uid field required https://github.com/grafana/identity-access-team/issues/552
|
||||||
|
func (ss *xormStore) uidMigration() error {
|
||||||
|
return ss.db.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||||
|
switch ss.db.GetDBType() {
|
||||||
|
case migrator.SQLite:
|
||||||
|
_, err := sess.Exec("UPDATE team SET uid=printf('t%09d',id) WHERE uid IS NULL;")
|
||||||
|
return err
|
||||||
|
case migrator.Postgres:
|
||||||
|
_, err := sess.Exec("UPDATE team SET uid='t' || lpad('' || id::text,9,'0') WHERE uid IS NULL;")
|
||||||
|
return err
|
||||||
|
case migrator.MySQL:
|
||||||
|
_, err := sess.Exec("UPDATE team SET uid=concat('t',lpad(id,9,'0')) WHERE uid IS NULL;")
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
// this branch should be unreachable
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Run("Testing Team commands and queries", func(t *testing.T) {
|
t.Run("Testing Team commands and queries", func(t *testing.T) {
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
teamSvc := ProvideService(sqlStore, sqlStore.Cfg)
|
teamSvc, err := ProvideService(sqlStore, sqlStore.Cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
testUser := &user.SignedInUser{
|
testUser := &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Permissions: map[int64]map[string][]string{
|
Permissions: map[int64]map[string][]string{
|
||||||
@@ -489,7 +490,8 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
store := db.InitTestDB(t, db.InitTestDBOpt{})
|
store := db.InitTestDB(t, db.InitTestDBOpt{})
|
||||||
teamSvc := ProvideService(store, store.Cfg)
|
teamSvc, err := ProvideService(store, store.Cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Seed 10 teams
|
// Seed 10 teams
|
||||||
for i := 1; i <= 10; i++ {
|
for i := 1; i <= 10; i++ {
|
||||||
@@ -525,7 +527,8 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
|
|||||||
|
|
||||||
// Seed 2 teams with 2 members
|
// Seed 2 teams with 2 members
|
||||||
setup := func(store *sqlstore.SQLStore) {
|
setup := func(store *sqlstore.SQLStore) {
|
||||||
teamSvc := ProvideService(store, store.Cfg)
|
teamSvc, err := ProvideService(store, store.Cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
team1, errCreateTeam := teamSvc.CreateTeam("group1 name", "test1@example.org", testOrgID)
|
team1, errCreateTeam := teamSvc.CreateTeam("group1 name", "test1@example.org", testOrgID)
|
||||||
require.NoError(t, errCreateTeam)
|
require.NoError(t, errCreateTeam)
|
||||||
team2, errCreateTeam := teamSvc.CreateTeam("group2 name", "test2@example.org", testOrgID)
|
team2, errCreateTeam := teamSvc.CreateTeam("group2 name", "test2@example.org", testOrgID)
|
||||||
@@ -559,7 +562,8 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
|
|||||||
|
|
||||||
store := db.InitTestDB(t, db.InitTestDBOpt{})
|
store := db.InitTestDB(t, db.InitTestDBOpt{})
|
||||||
setup(store)
|
setup(store)
|
||||||
teamSvc := ProvideService(store, store.Cfg)
|
teamSvc, err := ProvideService(store, store.Cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
type getTeamMembersTestCase struct {
|
type getTeamMembersTestCase struct {
|
||||||
desc string
|
desc string
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ type Service struct {
|
|||||||
store store
|
store store
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(db db.DB, cfg *setting.Cfg) team.Service {
|
func ProvideService(db db.DB, cfg *setting.Cfg) (team.Service, error) {
|
||||||
return &Service{store: &xormStore{db: db, cfg: cfg, deletes: []string{}}}
|
store := &xormStore{db: db, cfg: cfg, deletes: []string{}}
|
||||||
|
|
||||||
|
if err := store.uidMigration(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Service{store: &xormStore{db: db, cfg: cfg, deletes: []string{}}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CreateTeam(name, email string, orgID int64) (team.Team, error) {
|
func (s *Service) CreateTeam(name, email string, orgID int64) (team.Team, error) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/services/supportbundles"
|
"github.com/grafana/grafana/pkg/services/supportbundles"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@@ -60,6 +61,10 @@ func ProvideService(
|
|||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := s.uidMigration(db); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
bundleRegistry.RegisterSupportItemCollector(s.supportBundleCollector())
|
bundleRegistry.RegisterSupportItemCollector(s.supportBundleCollector())
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
@@ -485,3 +490,25 @@ func (s *Service) supportBundleCollector() supportbundles.Collector {
|
|||||||
Fn: collectorFn,
|
Fn: collectorFn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is just to ensure that all users have a valid uid.
|
||||||
|
// To protect against upgrade / downgrade we need to run this for a couple of releases.
|
||||||
|
// FIXME: Remove this migration and make uid field required https://github.com/grafana/identity-access-team/issues/552
|
||||||
|
func (s *Service) uidMigration(store db.DB) error {
|
||||||
|
return store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||||
|
switch store.GetDBType() {
|
||||||
|
case migrator.SQLite:
|
||||||
|
_, err := sess.Exec("UPDATE user SET uid=printf('u%09d',id) WHERE uid IS NULL;")
|
||||||
|
return err
|
||||||
|
case migrator.Postgres:
|
||||||
|
_, err := sess.Exec("UPDATE `user` SET uid='u' || lpad('' || id::text,9,'0') WHERE uid IS NULL;")
|
||||||
|
return err
|
||||||
|
case migrator.MySQL:
|
||||||
|
_, err := sess.Exec("UPDATE user SET uid=concat('u',lpad(id,9,'0')) WHERE uid IS NULL;")
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
// this branch should be unreachable
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -377,7 +377,9 @@ func (c K8sTestHelper) createTestUsers(orgName string) OrgUsers {
|
|||||||
store.Cfg.AutoAssignOrg = true
|
store.Cfg.AutoAssignOrg = true
|
||||||
store.Cfg.AutoAssignOrgId = int(orgId)
|
store.Cfg.AutoAssignOrgId = int(orgId)
|
||||||
|
|
||||||
teamSvc := teamimpl.ProvideService(store, store.Cfg)
|
teamSvc, err := teamimpl.ProvideService(store, store.Cfg)
|
||||||
|
require.NoError(c.t, err)
|
||||||
|
|
||||||
cache := localcache.ProvideService()
|
cache := localcache.ProvideService()
|
||||||
userSvc, err := userimpl.ProvideService(store,
|
userSvc, err := userimpl.ProvideService(store,
|
||||||
orgService, store.Cfg, teamSvc, cache, quotaService,
|
orgService, store.Cfg, teamSvc, cache, quotaService,
|
||||||
|
|||||||
Reference in New Issue
Block a user