Skip to content

Commit e7ca0fd

Browse files
authored
LBAC for datasources: Adds cleanup of lbac rules added for teams that have been deleted (grafana#111405)
add cleanup of lbac rules for teams that have been deleted
1 parent 8fe2925 commit e7ca0fd

File tree

2 files changed

+140
-3
lines changed

2 files changed

+140
-3
lines changed

pkg/server/wire_gen.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/services/cleanup/cleanup.go

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ import (
2828
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
2929
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
3030
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
31+
"github.com/grafana/grafana/pkg/services/datasources"
3132
"github.com/grafana/grafana/pkg/services/featuremgmt"
3233
"github.com/grafana/grafana/pkg/services/ngalert/image"
3334
"github.com/grafana/grafana/pkg/services/org"
3435
"github.com/grafana/grafana/pkg/services/queryhistory"
3536
"github.com/grafana/grafana/pkg/services/shorturls"
37+
"github.com/grafana/grafana/pkg/services/team"
3638
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
3739
"github.com/grafana/grafana/pkg/setting"
3840
)
@@ -58,12 +60,14 @@ type CleanUpService struct {
5860
alertRuleService AlertRuleService
5961
clientConfigProvider grafanaapiserver.RestConfigProvider
6062
orgService org.Service
63+
teamService team.Service
64+
dataSourceService datasources.DataSourceService
6165
}
6266

6367
func ProvideService(cfg *setting.Cfg, Features featuremgmt.FeatureToggles, serverLockService *serverlock.ServerLockService,
6468
shortURLService shorturls.Service, sqlstore db.DB, queryHistoryService queryhistory.Service,
6569
dashboardVersionService dashver.Service, dashSnapSvc dashboardsnapshots.Service, deleteExpiredImageService *image.DeleteExpiredService,
66-
tempUserService tempuser.Service, tracer tracing.Tracer, annotationCleaner annotations.Cleaner, service AlertRuleService, clientConfigProvider grafanaapiserver.RestConfigProvider, orgService org.Service) *CleanUpService {
70+
tempUserService tempuser.Service, tracer tracing.Tracer, annotationCleaner annotations.Cleaner, service AlertRuleService, clientConfigProvider grafanaapiserver.RestConfigProvider, orgService org.Service, teamService team.Service, dataSourceService datasources.DataSourceService) *CleanUpService {
6771
s := &CleanUpService{
6872
Cfg: cfg,
6973
Features: Features,
@@ -81,6 +85,8 @@ func ProvideService(cfg *setting.Cfg, Features featuremgmt.FeatureToggles, serve
8185
alertRuleService: service,
8286
clientConfigProvider: clientConfigProvider,
8387
orgService: orgService,
88+
teamService: teamService,
89+
dataSourceService: dataSourceService,
8490
}
8591
return s
8692
}
@@ -125,6 +131,7 @@ func (srv *CleanUpService) clean(ctx context.Context) {
125131
{"expire old user invites", srv.expireOldUserInvites},
126132
{"delete stale query history", srv.deleteStaleQueryHistory},
127133
{"expire old email verifications", srv.expireOldVerifications},
134+
{"cleanup stale LBAC rules", srv.cleanupStaleLBACRules},
128135
}
129136

130137
if srv.Cfg.ShortLinkExpiration > 0 {
@@ -418,3 +425,133 @@ func (srv *CleanUpService) cleanUpTrashAlertRules(ctx context.Context) {
418425
logger.Debug("Cleaned up deleted alert rules", "rows affected", affected)
419426
}
420427
}
428+
429+
// cleanupStaleLBACRules exists to clean up lbac rules that are stale from teams getting deleted as we do not have
430+
// cascading deletions on teams to delete existing lbac rules
431+
func (srv *CleanUpService) cleanupStaleLBACRules(ctx context.Context) {
432+
logger := srv.log.FromContext(ctx)
433+
434+
// Get all datasources
435+
allDataSources, err := srv.dataSourceService.GetAllDataSources(ctx, &datasources.GetAllDataSourcesQuery{})
436+
if err != nil {
437+
logger.Error("Failed to get datasources for LBAC cleanup", "error", err)
438+
return
439+
}
440+
441+
var totalCleaned int
442+
var totalDataSources int
443+
444+
for _, ds := range allDataSources {
445+
if ds.JsonData == nil {
446+
continue
447+
}
448+
449+
// Check if datasource has team LBAC rules
450+
teamHTTPHeaders, err := datasources.GetTeamHTTPHeaders(ds.JsonData)
451+
if err != nil || teamHTTPHeaders == nil {
452+
continue
453+
}
454+
455+
totalDataSources++
456+
457+
// Extract team UIDs and check if teams still exist
458+
cleanedRules, removedCount := srv.getLBACRulesForTeamsStillExisting(ctx, teamHTTPHeaders, ds.OrgID)
459+
460+
if removedCount > 0 {
461+
// Update the datasource with cleaned rules
462+
err := srv.updateDataSourceLBACRules(ctx, ds, cleanedRules)
463+
if err != nil {
464+
logger.Error("Failed to update datasource LBAC rules",
465+
"datasource", ds.UID, "error", err)
466+
} else {
467+
totalCleaned += removedCount
468+
logger.Debug("Cleaned stale LBAC rules",
469+
"datasource", ds.UID, "removed", removedCount)
470+
}
471+
}
472+
}
473+
474+
if totalCleaned > 0 {
475+
logger.Info("Cleaned up stale team LBAC rules",
476+
"datasources_processed", totalDataSources,
477+
"total_rules_removed", totalCleaned)
478+
}
479+
}
480+
481+
func (srv *CleanUpService) getLBACRulesForTeamsStillExisting(ctx context.Context, teamHeaders *datasources.TeamHTTPHeaders, orgID int64) (*datasources.TeamHTTPHeaders, int) {
482+
logger := srv.log.FromContext(ctx)
483+
cleanedHeaders := &datasources.TeamHTTPHeaders{Headers: make(map[string][]datasources.TeamHTTPHeader)}
484+
removedCount := 0
485+
486+
for teamIdentifier, headers := range teamHeaders.Headers {
487+
// Determine if this is a UID or ID
488+
var teamUID string
489+
teamID, err := strconv.ParseInt(teamIdentifier, 10, 64)
490+
491+
if err != nil {
492+
// It's a UID
493+
teamUID = teamIdentifier
494+
} else {
495+
// It's an ID, need to resolve to UID
496+
teamByID, err := srv.teamService.GetTeamByID(ctx, &team.GetTeamByIDQuery{
497+
OrgID: orgID,
498+
ID: teamID,
499+
})
500+
if err != nil {
501+
logger.Debug("Team ID no longer exists, removing LBAC rules",
502+
"teamID", teamIdentifier, "orgID", orgID)
503+
removedCount++
504+
continue
505+
}
506+
teamUID = teamByID.UID
507+
}
508+
509+
// Check if team still exists by UID
510+
_, err = srv.teamService.GetTeamByID(ctx, &team.GetTeamByIDQuery{
511+
OrgID: orgID,
512+
UID: teamUID,
513+
})
514+
515+
if err != nil {
516+
logger.Debug("Team UID no longer exists, removing LBAC rules",
517+
"teamUID", teamUID, "orgID", orgID)
518+
removedCount++
519+
continue
520+
}
521+
522+
// Team exists, keep the rules
523+
cleanedHeaders.Headers[teamIdentifier] = headers
524+
}
525+
526+
return cleanedHeaders, removedCount
527+
}
528+
529+
func (srv *CleanUpService) updateDataSourceLBACRules(ctx context.Context, ds *datasources.DataSource, cleanedHeaders *datasources.TeamHTTPHeaders) error {
530+
// Update JsonData with cleaned rules
531+
jsonData := ds.JsonData
532+
jsonData.Set("teamHttpHeaders", cleanedHeaders)
533+
534+
updateCmd := &datasources.UpdateDataSourceCommand{
535+
ID: ds.ID,
536+
OrgID: ds.OrgID,
537+
UID: ds.UID,
538+
Name: ds.Name,
539+
Type: ds.Type,
540+
Access: ds.Access,
541+
URL: ds.URL,
542+
User: ds.User,
543+
Database: ds.Database,
544+
BasicAuth: ds.BasicAuth,
545+
BasicAuthUser: ds.BasicAuthUser,
546+
WithCredentials: ds.WithCredentials,
547+
IsDefault: ds.IsDefault,
548+
JsonData: jsonData,
549+
AllowLBACRuleUpdates: true,
550+
Version: ds.Version,
551+
ReadOnly: ds.ReadOnly,
552+
APIVersion: ds.APIVersion,
553+
}
554+
555+
_, err := srv.dataSourceService.UpdateDataSource(ctx, updateCmd)
556+
return err
557+
}

0 commit comments

Comments
 (0)