The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/serviceaccounts/proxy/service.go

191 lines
7.1 KiB

package proxy
import (
"context"
"strings"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/api"
"github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/manager"
"github.com/grafana/grafana/pkg/setting"
)
// ServiceAccountsProxy is a proxy for the serviceaccounts.Service interface
// that is used to add validations to service accounts and protects external
// service accounts from being modified by users.
type ServiceAccountsProxy struct {
log log.Logger
proxiedService serviceaccounts.Service
isProxyEnabled bool
}
func ProvideServiceAccountsProxy(
cfg *setting.Cfg,
ac accesscontrol.AccessControl,
accesscontrolService accesscontrol.Service,
features *featuremgmt.FeatureManager,
permissionService accesscontrol.ServiceAccountPermissionsService,
proxiedService *manager.ServiceAccountsService,
routeRegister routing.RouteRegister,
) (*ServiceAccountsProxy, error) {
s := &ServiceAccountsProxy{
log: log.New("serviceaccounts.proxy"),
proxiedService: proxiedService,
isProxyEnabled: features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts),
}
serviceaccountsAPI := api.NewServiceAccountsAPI(cfg, s, ac, accesscontrolService, routeRegister, permissionService, features)
serviceaccountsAPI.RegisterAPIEndpoints()
return s, nil
}
var _ serviceaccounts.Service = (*ServiceAccountsProxy)(nil)
func (s *ServiceAccountsProxy) AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error) {
if s.isProxyEnabled {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, cmd.OrgId, serviceAccountID)
if err != nil {
return nil, err
}
if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to create tokens for external service accounts", "serviceAccountID", serviceAccountID)
return nil, extsvcaccounts.ErrCannotCreateToken
}
}
return s.proxiedService.AddServiceAccountToken(ctx, serviceAccountID, cmd)
}
func (s *ServiceAccountsProxy) CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error) {
if s.isProxyEnabled {
if !isNameValid(saForm.Name) {
s.log.Error("Unable to create service account with a protected name", "name", saForm.Name)
return nil, extsvcaccounts.ErrInvalidName
}
}
return s.proxiedService.CreateServiceAccount(ctx, orgID, saForm)
}
func (s *ServiceAccountsProxy) DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error {
if s.isProxyEnabled {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
if err != nil {
return err
}
if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to delete external service accounts", "serviceAccountID", serviceAccountID)
return extsvcaccounts.ErrCannotBeDeleted
}
}
return s.proxiedService.DeleteServiceAccount(ctx, orgID, serviceAccountID)
}
func (s *ServiceAccountsProxy) DeleteServiceAccountToken(ctx context.Context, orgID int64, serviceAccountID int64, tokenID int64) error {
if s.isProxyEnabled {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
if err != nil {
return err
}
if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to delete tokens for external service accounts", "serviceAccountID", serviceAccountID)
return extsvcaccounts.ErrCannotDeleteToken
}
}
return s.proxiedService.DeleteServiceAccountToken(ctx, orgID, serviceAccountID, tokenID)
}
func (s *ServiceAccountsProxy) EnableServiceAccount(ctx context.Context, orgID int64, serviceAccountID int64, enable bool) error {
if s.isProxyEnabled {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
if err != nil {
return err
}
if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to enable/disable external service accounts", "serviceAccountID", serviceAccountID)
return extsvcaccounts.ErrCannotBeUpdated
}
}
return s.proxiedService.EnableServiceAccount(ctx, orgID, serviceAccountID, enable)
}
func (s *ServiceAccountsProxy) ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error) {
return s.proxiedService.ListTokens(ctx, query)
}
func (s *ServiceAccountsProxy) MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error {
return s.proxiedService.MigrateApiKey(ctx, orgID, keyId)
}
func (s *ServiceAccountsProxy) MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error) {
return s.proxiedService.MigrateApiKeysToServiceAccounts(ctx, orgID)
}
func (s *ServiceAccountsProxy) RetrieveServiceAccount(ctx context.Context, orgID, serviceAccountID int64) (*serviceaccounts.ServiceAccountProfileDTO, error) {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
if err != nil {
return nil, err
}
if s.isProxyEnabled {
sa.IsExternal = isExternalServiceAccount(sa.Login)
sa.RequiredBy = strings.ReplaceAll(sa.Name, serviceaccounts.ExtSvcPrefix, "")
}
return sa, nil
}
func (s *ServiceAccountsProxy) RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error) {
return s.proxiedService.RetrieveServiceAccountIdByName(ctx, orgID, name)
}
func (s *ServiceAccountsProxy) UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64, saForm *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) {
if s.isProxyEnabled {
if !isNameValid(*saForm.Name) {
s.log.Error("Invalid service account name", "name", *saForm.Name)
return nil, extsvcaccounts.ErrInvalidName
}
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
if err != nil {
return nil, err
}
if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to update external service accounts", "serviceAccountID", serviceAccountID)
return nil, extsvcaccounts.ErrCannotBeUpdated
}
}
return s.proxiedService.UpdateServiceAccount(ctx, orgID, serviceAccountID, saForm)
}
func (s *ServiceAccountsProxy) SearchOrgServiceAccounts(ctx context.Context, query *serviceaccounts.SearchOrgServiceAccountsQuery) (*serviceaccounts.SearchOrgServiceAccountsResult, error) {
sa, err := s.proxiedService.SearchOrgServiceAccounts(ctx, query)
if err != nil {
return nil, err
}
if s.isProxyEnabled {
for i := range sa.ServiceAccounts {
sa.ServiceAccounts[i].IsExternal = isExternalServiceAccount(sa.ServiceAccounts[i].Login)
}
}
return sa, nil
}
func isNameValid(name string) bool {
return !strings.HasPrefix(name, serviceaccounts.ExtSvcPrefix)
}
func isExternalServiceAccount(login string) bool {
return strings.HasPrefix(login, serviceaccounts.ServiceAccountPrefix+serviceaccounts.ExtSvcPrefix)
}