| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | package registry | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 18:29:09 +08:00
										 |  |  | // FIXME (gamab): we can eventually remove this package
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/serverlock" | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/slugify" | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/extsvcauth" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							| 
									
										
										
										
											2023-10-24 17:01:04 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts" | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var _ extsvcauth.ExternalServiceRegistry = &Registry{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | var lockTimeConfig = serverlock.LockTimeConfig{ | 
					
						
							|  |  |  | 	MaxInterval: 2 * time.Minute, | 
					
						
							|  |  |  | 	MinWait:     1 * time.Second, | 
					
						
							|  |  |  | 	MaxWait:     5 * time.Second, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 00:44:14 +08:00
										 |  |  | type serverLocker interface { | 
					
						
							|  |  |  | 	LockExecuteAndReleaseWithRetries(context.Context, string, serverlock.LockTimeConfig, func(ctx context.Context), ...serverlock.RetryOpt) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | type Registry struct { | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 	enabled bool | 
					
						
							|  |  |  | 	logger  log.Logger | 
					
						
							|  |  |  | 	saReg   extsvcauth.ExternalServiceRegistry | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 18:29:09 +08:00
										 |  |  | 	// FIXME (gamab): we can remove this field and use the saReg.GetExternalServiceNames directly
 | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	extSvcProviders map[string]extsvcauth.AuthProvider | 
					
						
							|  |  |  | 	lock            sync.Mutex | 
					
						
							| 
									
										
										
										
											2023-11-25 00:44:14 +08:00
										 |  |  | 	serverLock      serverLocker | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | func ProvideExtSvcRegistry(cfg *setting.Cfg, saSvc *extsvcaccounts.ExtSvcAccountsService, serverLock *serverlock.ServerLockService, features featuremgmt.FeatureToggles) *Registry { | 
					
						
							| 
									
										
										
										
											2025-10-25 00:02:53 +08:00
										 |  |  | 	//nolint:staticcheck // not yet migrated to OpenFeature
 | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 	enabled := features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts) && cfg.ManagedServiceAccountsEnabled | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | 	return &Registry{ | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 		extSvcProviders: map[string]extsvcauth.AuthProvider{}, | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 		enabled:         enabled, | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 		lock:            sync.Mutex{}, | 
					
						
							|  |  |  | 		logger:          log.New("extsvcauth.registry"), | 
					
						
							| 
									
										
										
										
											2023-11-13 23:23:11 +08:00
										 |  |  | 		saReg:           saSvc, | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 		serverLock:      serverLock, | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | // CleanUpOrphanedExternalServices remove external services present in store that have not been registered on startup.
 | 
					
						
							|  |  |  | func (r *Registry) CleanUpOrphanedExternalServices(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2023-11-25 00:44:14 +08:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-11-25 00:44:14 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	if errLock != nil { | 
					
						
							|  |  |  | 		return errLock | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-25 00:44:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return errCleanUp | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | // HasExternalService returns whether an external service has been saved with that name.
 | 
					
						
							| 
									
										
										
										
											2023-11-13 23:23:11 +08:00
										 |  |  | func (r *Registry) HasExternalService(ctx context.Context, name string) (bool, error) { | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	_, ok := r.extSvcProviders[slugify.Slugify(name)] | 
					
						
							| 
									
										
										
										
											2023-11-13 23:23:11 +08:00
										 |  |  | 	return ok, nil | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | // 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | // 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: | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 		if !r.enabled { | 
					
						
							|  |  |  | 			r.logger.Warn("Skipping External Service authentication, flag or configuration option is disabled", | 
					
						
							|  |  |  | 				"service", name, | 
					
						
							|  |  |  | 				"flag", featuremgmt.FlagExternalServiceAccounts, | 
					
						
							|  |  |  | 				"option", "ManagedServiceAccountsEnabled", | 
					
						
							|  |  |  | 			) | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		r.logger.Debug("Routing External Service removal to the External Service Account service", "service", name) | 
					
						
							| 
									
										
										
										
											2023-11-13 23:23:11 +08:00
										 |  |  | 		return r.saReg.RemoveExternalService(ctx, name) | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 		return extsvcauth.ErrUnknownProvider.Errorf("unknown provider '%v'", provider) | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2024-03-21 23:41:10 +08:00
										 |  |  | 		errSave   error | 
					
						
							|  |  |  | 		extSvc    *extsvcauth.ExternalService | 
					
						
							|  |  |  | 		lockName  = "ext-svc-save-" + cmd.Name | 
					
						
							|  |  |  | 		ctxLogger = r.logger.FromContext(ctx) | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 	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: | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 			if !r.enabled { | 
					
						
							|  |  |  | 				ctxLogger.Warn("Skipping External Service authentication, flag or configuration option disabled", | 
					
						
							|  |  |  | 					"service", cmd.Name, | 
					
						
							|  |  |  | 					"flag", featuremgmt.FlagExternalServiceAccounts, | 
					
						
							|  |  |  | 					"option", "ManagedServiceAccountsEnabled", | 
					
						
							|  |  |  | 				) | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-03-21 23:41:10 +08:00
										 |  |  | 			ctxLogger.Debug("Routing the External Service registration to the External Service Account service", "service", cmd.Name) | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 			extSvc, errSave = r.saReg.SaveExternalService(ctx, cmd) | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			errSave = extsvcauth.ErrUnknownProvider.Errorf("unknown provider '%v'", cmd.AuthProvider) | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-22 17:15:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return extSvc, errSave | 
					
						
							| 
									
										
										
										
											2023-10-06 00:13:06 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // 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{} | 
					
						
							| 
									
										
										
										
											2024-09-27 15:11:59 +08:00
										 |  |  | 	if r.enabled { | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | 		names, err := r.saReg.GetExternalServiceNames(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for i := range names { | 
					
						
							|  |  |  | 			extsvcs[names[i]] = extsvcauth.ServiceAccounts | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-26 18:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-16 19:07:42 +08:00
										 |  |  | 	return extsvcs, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-05 05:26:55 +08:00
										 |  |  | // 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)
 | 
					
						
							|  |  |  | // }
 |