Files
Gabriel MABILLE 7928245eb6 ManagedServiceAccounts: Add a config option to disable the feature on-prem (#93571)
* 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>
2024-09-27 09:11:59 +02:00

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)
// }