Auth: reload SSO settings for HA setups (#80231)

* reload SSO settings for HA setups

* remove check for grafana HA

* add unit tests

* fetch all sso settings with one sql query

* register background service
This commit is contained in:
Mihai Doarna 2024-01-10 16:01:37 +02:00 committed by GitHub
parent 02136e5a2f
commit 772e5993b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 0 deletions

View File

@ -36,6 +36,7 @@ import (
"github.com/grafana/grafana/pkg/services/serviceaccounts"
samanager "github.com/grafana/grafana/pkg/services/serviceaccounts/manager"
"github.com/grafana/grafana/pkg/services/ssosettings"
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
"github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/services/store/sanitizer"
@ -58,6 +59,7 @@ func ProvideBackgroundServiceRegistry(
keyRetriever *dynamic.KeyRetriever, dynamicAngularDetectorsProvider *angulardetectorsprovider.Dynamic,
grafanaAPIServer grafanaapiserver.Service,
anon *anonimpl.AnonDeviceService,
ssoSettings *ssosettingsimpl.SSOSettingsService,
// Need to make sure these are initialized, is there a better place to put them?
_ dashboardsnapshots.Service, _ *alerting.AlertNotificationService,
_ serviceaccounts.Service, _ *guardian.Provider,
@ -98,6 +100,7 @@ func ProvideBackgroundServiceRegistry(
dynamicAngularDetectorsProvider,
grafanaAPIServer,
anon,
ssoSettings,
)
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"strings"
"time"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/db"
@ -250,6 +251,44 @@ func (s *SSOSettingsService) encryptSecrets(ctx context.Context, settings map[st
return result, nil
}
func (s *SSOSettingsService) Run(ctx context.Context) error {
ticker := time.NewTicker(1 * time.Minute)
// start a background process for reloading the SSO settings for all providers at a fixed interval
// it is useful for high availability setups running multiple Grafana instances
for {
select {
case <-ticker.C:
s.doReload(ctx)
case <-ctx.Done():
return ctx.Err()
}
}
}
func (s *SSOSettingsService) doReload(ctx context.Context) {
s.log.Debug("reloading SSO Settings for all providers")
settingsList, err := s.List(ctx)
if err != nil {
s.log.Error("failed to fetch SSO Settings for all providers", "err", err)
return
}
for provider, connector := range s.reloadables {
settings := getSettingsByProvider(provider, settingsList)
if len(settings) > 0 {
err = connector.Reload(ctx, *settings[0])
if err != nil {
s.log.Error("failed to reload SSO Settings", "provider", provider, "err", err)
continue
}
}
}
}
func isSecret(fieldName string) bool {
secretFieldPatterns := []string{"secret"}

View File

@ -634,6 +634,59 @@ func TestSSOSettingsService_Delete(t *testing.T) {
})
}
func TestSSOSettingsService_DoReload(t *testing.T) {
t.Run("successfully reload settings", func(t *testing.T) {
env := setupTestEnv(t)
settingsList := []*models.SSOSettings{
{
Provider: "github",
Settings: map[string]any{
"enabled": true,
"client_id": "github_client_id",
},
},
{
Provider: "google",
Settings: map[string]any{
"enabled": true,
"client_id": "google_client_id",
},
},
{
Provider: "azuread",
Settings: map[string]any{
"enabled": true,
"client_id": "azuread_client_id",
},
},
}
env.store.ExpectedSSOSettings = settingsList
reloadable := ssosettingstests.NewMockReloadable(t)
for _, settings := range settingsList {
reloadable.On("Reload", mock.Anything, *settings).Return(nil).Once()
env.reloadables[settings.Provider] = reloadable
}
env.service.doReload(context.Background())
})
t.Run("failed fetching the SSO settings", func(t *testing.T) {
env := setupTestEnv(t)
provider := "github"
env.store.ExpectedError = errors.New("failed fetching the settings")
reloadable := ssosettingstests.NewMockReloadable(t)
env.reloadables[provider] = reloadable
env.service.doReload(context.Background())
})
}
func setupTestEnv(t *testing.T) testEnv {
store := ssosettingstests.NewFakeStore()
fallbackStrategy := ssosettingstests.NewFakeFallbackStrategy()