LBAC for datasources: Adds cleanup of lbac rules added for teams that have been deleted (#111405)
add cleanup of lbac rules for teams that have been deleted
This commit is contained in:
@@ -600,7 +600,6 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
deleteExpiredService := image.ProvideDeleteExpiredService(dBstore)
|
||||
tempuserService := tempuserimpl.ProvideService(sqlStore, cfg)
|
||||
cleanupServiceImpl := annotationsimpl.ProvideCleanupService(sqlStore, cfg)
|
||||
cleanUpService := cleanup.ProvideService(cfg, featureToggles, serverLockService, shortURLService, sqlStore, queryHistoryService, dashverService, serviceImpl, deleteExpiredService, tempuserService, tracingService, cleanupServiceImpl, dBstore, eventualRestConfigProvider, orgService)
|
||||
secretsKVStore, err := kvstore2.ProvideService(sqlStore, secretsService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -612,6 +611,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cleanUpService := cleanup.ProvideService(cfg, featureToggles, serverLockService, shortURLService, sqlStore, queryHistoryService, dashverService, serviceImpl, deleteExpiredService, tempuserService, tracingService, cleanupServiceImpl, dBstore, eventualRestConfigProvider, orgService, teamService, service15)
|
||||
correlationsService, err := correlations.ProvideService(sqlStore, routeRegisterImpl, service15, accessControl, inProcBus, quotaService, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1200,7 +1200,6 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
deleteExpiredService := image.ProvideDeleteExpiredService(dBstore)
|
||||
tempuserService := tempuserimpl.ProvideService(sqlStore, cfg)
|
||||
cleanupServiceImpl := annotationsimpl.ProvideCleanupService(sqlStore, cfg)
|
||||
cleanUpService := cleanup.ProvideService(cfg, featureToggles, serverLockService, shortURLService, sqlStore, queryHistoryService, dashverService, serviceImpl, deleteExpiredService, tempuserService, tracingService, cleanupServiceImpl, dBstore, eventualRestConfigProvider, orgService)
|
||||
secretsKVStore, err := kvstore2.ProvideService(sqlStore, secretsService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1212,6 +1211,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cleanUpService := cleanup.ProvideService(cfg, featureToggles, serverLockService, shortURLService, sqlStore, queryHistoryService, dashverService, serviceImpl, deleteExpiredService, tempuserService, tracingService, cleanupServiceImpl, dBstore, eventualRestConfigProvider, orgService, teamService, service15)
|
||||
correlationsService, err := correlations.ProvideService(sqlStore, routeRegisterImpl, service15, accessControl, inProcBus, quotaService, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -28,11 +28,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/queryhistory"
|
||||
"github.com/grafana/grafana/pkg/services/shorturls"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
@@ -58,12 +60,14 @@ type CleanUpService struct {
|
||||
alertRuleService AlertRuleService
|
||||
clientConfigProvider grafanaapiserver.RestConfigProvider
|
||||
orgService org.Service
|
||||
teamService team.Service
|
||||
dataSourceService datasources.DataSourceService
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, Features featuremgmt.FeatureToggles, serverLockService *serverlock.ServerLockService,
|
||||
shortURLService shorturls.Service, sqlstore db.DB, queryHistoryService queryhistory.Service,
|
||||
dashboardVersionService dashver.Service, dashSnapSvc dashboardsnapshots.Service, deleteExpiredImageService *image.DeleteExpiredService,
|
||||
tempUserService tempuser.Service, tracer tracing.Tracer, annotationCleaner annotations.Cleaner, service AlertRuleService, clientConfigProvider grafanaapiserver.RestConfigProvider, orgService org.Service) *CleanUpService {
|
||||
tempUserService tempuser.Service, tracer tracing.Tracer, annotationCleaner annotations.Cleaner, service AlertRuleService, clientConfigProvider grafanaapiserver.RestConfigProvider, orgService org.Service, teamService team.Service, dataSourceService datasources.DataSourceService) *CleanUpService {
|
||||
s := &CleanUpService{
|
||||
Cfg: cfg,
|
||||
Features: Features,
|
||||
@@ -81,6 +85,8 @@ func ProvideService(cfg *setting.Cfg, Features featuremgmt.FeatureToggles, serve
|
||||
alertRuleService: service,
|
||||
clientConfigProvider: clientConfigProvider,
|
||||
orgService: orgService,
|
||||
teamService: teamService,
|
||||
dataSourceService: dataSourceService,
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -125,6 +131,7 @@ func (srv *CleanUpService) clean(ctx context.Context) {
|
||||
{"expire old user invites", srv.expireOldUserInvites},
|
||||
{"delete stale query history", srv.deleteStaleQueryHistory},
|
||||
{"expire old email verifications", srv.expireOldVerifications},
|
||||
{"cleanup stale LBAC rules", srv.cleanupStaleLBACRules},
|
||||
}
|
||||
|
||||
if srv.Cfg.ShortLinkExpiration > 0 {
|
||||
@@ -418,3 +425,133 @@ func (srv *CleanUpService) cleanUpTrashAlertRules(ctx context.Context) {
|
||||
logger.Debug("Cleaned up deleted alert rules", "rows affected", affected)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupStaleLBACRules exists to clean up lbac rules that are stale from teams getting deleted as we do not have
|
||||
// cascading deletions on teams to delete existing lbac rules
|
||||
func (srv *CleanUpService) cleanupStaleLBACRules(ctx context.Context) {
|
||||
logger := srv.log.FromContext(ctx)
|
||||
|
||||
// Get all datasources
|
||||
allDataSources, err := srv.dataSourceService.GetAllDataSources(ctx, &datasources.GetAllDataSourcesQuery{})
|
||||
if err != nil {
|
||||
logger.Error("Failed to get datasources for LBAC cleanup", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
var totalCleaned int
|
||||
var totalDataSources int
|
||||
|
||||
for _, ds := range allDataSources {
|
||||
if ds.JsonData == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if datasource has team LBAC rules
|
||||
teamHTTPHeaders, err := datasources.GetTeamHTTPHeaders(ds.JsonData)
|
||||
if err != nil || teamHTTPHeaders == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
totalDataSources++
|
||||
|
||||
// Extract team UIDs and check if teams still exist
|
||||
cleanedRules, removedCount := srv.getLBACRulesForTeamsStillExisting(ctx, teamHTTPHeaders, ds.OrgID)
|
||||
|
||||
if removedCount > 0 {
|
||||
// Update the datasource with cleaned rules
|
||||
err := srv.updateDataSourceLBACRules(ctx, ds, cleanedRules)
|
||||
if err != nil {
|
||||
logger.Error("Failed to update datasource LBAC rules",
|
||||
"datasource", ds.UID, "error", err)
|
||||
} else {
|
||||
totalCleaned += removedCount
|
||||
logger.Debug("Cleaned stale LBAC rules",
|
||||
"datasource", ds.UID, "removed", removedCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if totalCleaned > 0 {
|
||||
logger.Info("Cleaned up stale team LBAC rules",
|
||||
"datasources_processed", totalDataSources,
|
||||
"total_rules_removed", totalCleaned)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *CleanUpService) getLBACRulesForTeamsStillExisting(ctx context.Context, teamHeaders *datasources.TeamHTTPHeaders, orgID int64) (*datasources.TeamHTTPHeaders, int) {
|
||||
logger := srv.log.FromContext(ctx)
|
||||
cleanedHeaders := &datasources.TeamHTTPHeaders{Headers: make(map[string][]datasources.TeamHTTPHeader)}
|
||||
removedCount := 0
|
||||
|
||||
for teamIdentifier, headers := range teamHeaders.Headers {
|
||||
// Determine if this is a UID or ID
|
||||
var teamUID string
|
||||
teamID, err := strconv.ParseInt(teamIdentifier, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
// It's a UID
|
||||
teamUID = teamIdentifier
|
||||
} else {
|
||||
// It's an ID, need to resolve to UID
|
||||
teamByID, err := srv.teamService.GetTeamByID(ctx, &team.GetTeamByIDQuery{
|
||||
OrgID: orgID,
|
||||
ID: teamID,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Debug("Team ID no longer exists, removing LBAC rules",
|
||||
"teamID", teamIdentifier, "orgID", orgID)
|
||||
removedCount++
|
||||
continue
|
||||
}
|
||||
teamUID = teamByID.UID
|
||||
}
|
||||
|
||||
// Check if team still exists by UID
|
||||
_, err = srv.teamService.GetTeamByID(ctx, &team.GetTeamByIDQuery{
|
||||
OrgID: orgID,
|
||||
UID: teamUID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Debug("Team UID no longer exists, removing LBAC rules",
|
||||
"teamUID", teamUID, "orgID", orgID)
|
||||
removedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// Team exists, keep the rules
|
||||
cleanedHeaders.Headers[teamIdentifier] = headers
|
||||
}
|
||||
|
||||
return cleanedHeaders, removedCount
|
||||
}
|
||||
|
||||
func (srv *CleanUpService) updateDataSourceLBACRules(ctx context.Context, ds *datasources.DataSource, cleanedHeaders *datasources.TeamHTTPHeaders) error {
|
||||
// Update JsonData with cleaned rules
|
||||
jsonData := ds.JsonData
|
||||
jsonData.Set("teamHttpHeaders", cleanedHeaders)
|
||||
|
||||
updateCmd := &datasources.UpdateDataSourceCommand{
|
||||
ID: ds.ID,
|
||||
OrgID: ds.OrgID,
|
||||
UID: ds.UID,
|
||||
Name: ds.Name,
|
||||
Type: ds.Type,
|
||||
Access: ds.Access,
|
||||
URL: ds.URL,
|
||||
User: ds.User,
|
||||
Database: ds.Database,
|
||||
BasicAuth: ds.BasicAuth,
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
WithCredentials: ds.WithCredentials,
|
||||
IsDefault: ds.IsDefault,
|
||||
JsonData: jsonData,
|
||||
AllowLBACRuleUpdates: true,
|
||||
Version: ds.Version,
|
||||
ReadOnly: ds.ReadOnly,
|
||||
APIVersion: ds.APIVersion,
|
||||
}
|
||||
|
||||
_, err := srv.dataSourceService.UpdateDataSource(ctx, updateCmd)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user