mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 20:02:14 +08:00
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:
@ -36,6 +36,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||||
samanager "github.com/grafana/grafana/pkg/services/serviceaccounts/manager"
|
samanager "github.com/grafana/grafana/pkg/services/serviceaccounts/manager"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||||
"github.com/grafana/grafana/pkg/services/store/sanitizer"
|
"github.com/grafana/grafana/pkg/services/store/sanitizer"
|
||||||
@ -58,6 +59,7 @@ func ProvideBackgroundServiceRegistry(
|
|||||||
keyRetriever *dynamic.KeyRetriever, dynamicAngularDetectorsProvider *angulardetectorsprovider.Dynamic,
|
keyRetriever *dynamic.KeyRetriever, dynamicAngularDetectorsProvider *angulardetectorsprovider.Dynamic,
|
||||||
grafanaAPIServer grafanaapiserver.Service,
|
grafanaAPIServer grafanaapiserver.Service,
|
||||||
anon *anonimpl.AnonDeviceService,
|
anon *anonimpl.AnonDeviceService,
|
||||||
|
ssoSettings *ssosettingsimpl.SSOSettingsService,
|
||||||
// Need to make sure these are initialized, is there a better place to put them?
|
// Need to make sure these are initialized, is there a better place to put them?
|
||||||
_ dashboardsnapshots.Service, _ *alerting.AlertNotificationService,
|
_ dashboardsnapshots.Service, _ *alerting.AlertNotificationService,
|
||||||
_ serviceaccounts.Service, _ *guardian.Provider,
|
_ serviceaccounts.Service, _ *guardian.Provider,
|
||||||
@ -98,6 +100,7 @@ func ProvideBackgroundServiceRegistry(
|
|||||||
dynamicAngularDetectorsProvider,
|
dynamicAngularDetectorsProvider,
|
||||||
grafanaAPIServer,
|
grafanaAPIServer,
|
||||||
anon,
|
anon,
|
||||||
|
ssoSettings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
@ -250,6 +251,44 @@ func (s *SSOSettingsService) encryptSecrets(ctx context.Context, settings map[st
|
|||||||
return result, nil
|
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 {
|
func isSecret(fieldName string) bool {
|
||||||
secretFieldPatterns := []string{"secret"}
|
secretFieldPatterns := []string{"secret"}
|
||||||
|
|
||||||
|
@ -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 {
|
func setupTestEnv(t *testing.T) testEnv {
|
||||||
store := ssosettingstests.NewFakeStore()
|
store := ssosettingstests.NewFakeStore()
|
||||||
fallbackStrategy := ssosettingstests.NewFakeFallbackStrategy()
|
fallbackStrategy := ssosettingstests.NewFakeFallbackStrategy()
|
||||||
|
Reference in New Issue
Block a user