mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 02:02:33 +08:00

* ManagedServiceAccounts: Add a config option to disabled by default * Update log in pkg/services/extsvcauth/registry/service.go Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
191 lines
6.4 KiB
Go
191 lines
6.4 KiB
Go
package registry
|
|
|
|
// FIXME (gamab): we can eventually remove this package
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/infra/serverlock"
|
|
"github.com/grafana/grafana/pkg/infra/slugify"
|
|
"github.com/grafana/grafana/pkg/services/extsvcauth"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
var _ extsvcauth.ExternalServiceRegistry = &Registry{}
|
|
|
|
var lockTimeConfig = serverlock.LockTimeConfig{
|
|
MaxInterval: 2 * time.Minute,
|
|
MinWait: 1 * time.Second,
|
|
MaxWait: 5 * time.Second,
|
|
}
|
|
|
|
type serverLocker interface {
|
|
LockExecuteAndReleaseWithRetries(context.Context, string, serverlock.LockTimeConfig, func(ctx context.Context), ...serverlock.RetryOpt) error
|
|
}
|
|
|
|
type Registry struct {
|
|
enabled bool
|
|
logger log.Logger
|
|
saReg extsvcauth.ExternalServiceRegistry
|
|
|
|
// FIXME (gamab): we can remove this field and use the saReg.GetExternalServiceNames directly
|
|
extSvcProviders map[string]extsvcauth.AuthProvider
|
|
lock sync.Mutex
|
|
serverLock serverLocker
|
|
}
|
|
|
|
func ProvideExtSvcRegistry(cfg *setting.Cfg, saSvc *extsvcaccounts.ExtSvcAccountsService, serverLock *serverlock.ServerLockService, features featuremgmt.FeatureToggles) *Registry {
|
|
enabled := features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts) && cfg.ManagedServiceAccountsEnabled
|
|
return &Registry{
|
|
extSvcProviders: map[string]extsvcauth.AuthProvider{},
|
|
enabled: enabled,
|
|
lock: sync.Mutex{},
|
|
logger: log.New("extsvcauth.registry"),
|
|
saReg: saSvc,
|
|
serverLock: serverLock,
|
|
}
|
|
}
|
|
|
|
// CleanUpOrphanedExternalServices remove external services present in store that have not been registered on startup.
|
|
func (r *Registry) CleanUpOrphanedExternalServices(ctx context.Context) error {
|
|
var errCleanUp error
|
|
|
|
errLock := r.serverLock.LockExecuteAndReleaseWithRetries(ctx, "ext-svc-clean-up", lockTimeConfig, func(ctx context.Context) {
|
|
extsvcs, err := r.retrieveExtSvcProviders(ctx)
|
|
if err != nil {
|
|
r.logger.Error("Could not retrieve external services from store", "error", err.Error())
|
|
errCleanUp = err
|
|
return
|
|
}
|
|
for name, provider := range extsvcs {
|
|
// The service did not register this time. Removed.
|
|
if _, ok := r.extSvcProviders[slugify.Slugify(name)]; !ok {
|
|
r.logger.Info("Detected removed External Service", "service", name, "provider", provider)
|
|
switch provider {
|
|
case extsvcauth.ServiceAccounts:
|
|
if err := r.saReg.RemoveExternalService(ctx, name); err != nil {
|
|
errCleanUp = err
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
if errLock != nil {
|
|
return errLock
|
|
}
|
|
|
|
return errCleanUp
|
|
}
|
|
|
|
// HasExternalService returns whether an external service has been saved with that name.
|
|
func (r *Registry) HasExternalService(ctx context.Context, name string) (bool, error) {
|
|
_, ok := r.extSvcProviders[slugify.Slugify(name)]
|
|
return ok, nil
|
|
}
|
|
|
|
// GetExternalServiceNames returns the list of external services registered in store.
|
|
func (r *Registry) GetExternalServiceNames(ctx context.Context) ([]string, error) {
|
|
extSvcProviders, err := r.retrieveExtSvcProviders(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
names := []string{}
|
|
for s := range extSvcProviders {
|
|
names = append(names, s)
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
// RemoveExternalService removes an external service and its associated resources from the database (ex: service account, token).
|
|
func (r *Registry) RemoveExternalService(ctx context.Context, name string) error {
|
|
provider, ok := r.extSvcProviders[slugify.Slugify(name)]
|
|
if !ok {
|
|
r.logger.Debug("external service not found", "service", name)
|
|
return nil
|
|
}
|
|
|
|
switch provider {
|
|
case extsvcauth.ServiceAccounts:
|
|
if !r.enabled {
|
|
r.logger.Warn("Skipping External Service authentication, flag or configuration option is disabled",
|
|
"service", name,
|
|
"flag", featuremgmt.FlagExternalServiceAccounts,
|
|
"option", "ManagedServiceAccountsEnabled",
|
|
)
|
|
return nil
|
|
}
|
|
r.logger.Debug("Routing External Service removal to the External Service Account service", "service", name)
|
|
return r.saReg.RemoveExternalService(ctx, name)
|
|
default:
|
|
return extsvcauth.ErrUnknownProvider.Errorf("unknown provider '%v'", provider)
|
|
}
|
|
}
|
|
|
|
// SaveExternalService creates or updates an external service in the database. Based on the requested auth provider,
|
|
// it generates client_id, secrets and any additional provider specificities (ex: rsa keys). It also ensures that the
|
|
// associated service account has the correct permissions.
|
|
func (r *Registry) SaveExternalService(ctx context.Context, cmd *extsvcauth.ExternalServiceRegistration) (*extsvcauth.ExternalService, error) {
|
|
var (
|
|
errSave error
|
|
extSvc *extsvcauth.ExternalService
|
|
lockName = "ext-svc-save-" + cmd.Name
|
|
ctxLogger = r.logger.FromContext(ctx)
|
|
)
|
|
|
|
err := r.serverLock.LockExecuteAndReleaseWithRetries(ctx, lockName, lockTimeConfig, func(ctx context.Context) {
|
|
// Record provider in case of removal
|
|
r.lock.Lock()
|
|
r.extSvcProviders[slugify.Slugify(cmd.Name)] = cmd.AuthProvider
|
|
r.lock.Unlock()
|
|
|
|
switch cmd.AuthProvider {
|
|
case extsvcauth.ServiceAccounts:
|
|
if !r.enabled {
|
|
ctxLogger.Warn("Skipping External Service authentication, flag or configuration option disabled",
|
|
"service", cmd.Name,
|
|
"flag", featuremgmt.FlagExternalServiceAccounts,
|
|
"option", "ManagedServiceAccountsEnabled",
|
|
)
|
|
return
|
|
}
|
|
ctxLogger.Debug("Routing the External Service registration to the External Service Account service", "service", cmd.Name)
|
|
extSvc, errSave = r.saReg.SaveExternalService(ctx, cmd)
|
|
default:
|
|
errSave = extsvcauth.ErrUnknownProvider.Errorf("unknown provider '%v'", cmd.AuthProvider)
|
|
}
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return extSvc, errSave
|
|
}
|
|
|
|
// retrieveExtSvcProviders fetches external services from store and map their associated provider
|
|
func (r *Registry) retrieveExtSvcProviders(ctx context.Context) (map[string]extsvcauth.AuthProvider, error) {
|
|
extsvcs := map[string]extsvcauth.AuthProvider{}
|
|
if r.enabled {
|
|
names, err := r.saReg.GetExternalServiceNames(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range names {
|
|
extsvcs[names[i]] = extsvcauth.ServiceAccounts
|
|
}
|
|
}
|
|
|
|
return extsvcs, nil
|
|
}
|
|
|
|
// func (r *Registry) Run(ctx context.Context) error {
|
|
// // This is a one-time background job.
|
|
// // Cleans up external services that have not been registered this time.
|
|
// return r.CleanUpOrphanedExternalServices(ctx)
|
|
// }
|