auth: implement feature flag for service account proxy (#77129)

* add FlagExternalServiceAccounts to proxy service

* add FlagExternalServiceAccounts value to tests

---------

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
linoman 2023-10-25 16:44:05 +02:00 committed by GitHub
parent 5f2fd8935d
commit dff7403b29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 45 deletions

View File

@ -2,11 +2,11 @@ package proxy
import ( import (
"context" "context"
"fmt"
"strings" "strings"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apikey" "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"
"github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/manager" "github.com/grafana/grafana/pkg/services/serviceaccounts/manager"
@ -19,14 +19,17 @@ import (
type ServiceAccountsProxy struct { type ServiceAccountsProxy struct {
log log.Logger log log.Logger
proxiedService serviceaccounts.Service proxiedService serviceaccounts.Service
isProxyEnabled bool
} }
func ProvideServiceAccountsProxy( func ProvideServiceAccountsProxy(
features *featuremgmt.FeatureManager,
proxiedService *manager.ServiceAccountsService, proxiedService *manager.ServiceAccountsService,
) (*ServiceAccountsProxy, error) { ) (*ServiceAccountsProxy, error) {
s := &ServiceAccountsProxy{ s := &ServiceAccountsProxy{
log: log.New("serviceaccounts.proxy"), log: log.New("serviceaccounts.proxy"),
proxiedService: proxiedService, proxiedService: proxiedService,
isProxyEnabled: features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabled(featuremgmt.FlagExternalServiceAuth),
} }
return s, nil return s, nil
} }
@ -34,55 +37,59 @@ func ProvideServiceAccountsProxy(
var _ serviceaccounts.Service = (*ServiceAccountsProxy)(nil) var _ serviceaccounts.Service = (*ServiceAccountsProxy)(nil)
func (s *ServiceAccountsProxy) AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error) { func (s *ServiceAccountsProxy) AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error) {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, cmd.OrgId, serviceAccountID) if s.isProxyEnabled {
if err != nil { sa, err := s.proxiedService.RetrieveServiceAccount(ctx, cmd.OrgId, serviceAccountID)
return nil, err if err != nil {
} return nil, err
}
if isExternalServiceAccount(sa.Login) { if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to create tokens for external service accounts", "serviceAccountID", serviceAccountID) s.log.Error("unable to create tokens for external service accounts", "serviceAccountID", serviceAccountID)
return nil, extsvcaccounts.ErrCannotCreateToken return nil, extsvcaccounts.ErrCannotCreateToken
}
} }
return s.proxiedService.AddServiceAccountToken(ctx, serviceAccountID, cmd) return s.proxiedService.AddServiceAccountToken(ctx, serviceAccountID, cmd)
} }
func (s *ServiceAccountsProxy) CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error) { func (s *ServiceAccountsProxy) CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error) {
if !isNameValid(saForm.Name) { if s.isProxyEnabled {
s.log.Error("Unable to create service account with a protected name", "name", saForm.Name) if !isNameValid(saForm.Name) {
return nil, extsvcaccounts.ErrInvalidName 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) return s.proxiedService.CreateServiceAccount(ctx, orgID, saForm)
} }
func (s *ServiceAccountsProxy) DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error { func (s *ServiceAccountsProxy) DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID) if s.isProxyEnabled {
if err != nil { sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
return err if err != nil {
} return err
}
if isExternalServiceAccount(sa.Login) { if isExternalServiceAccount(sa.Login) {
s.log.Error("unable to delete external service accounts", "serviceAccountID", serviceAccountID) s.log.Error("unable to delete external service accounts", "serviceAccountID", serviceAccountID)
return extsvcaccounts.ErrCannotBeDeleted return extsvcaccounts.ErrCannotBeDeleted
}
} }
return s.proxiedService.DeleteServiceAccount(ctx, orgID, serviceAccountID) return s.proxiedService.DeleteServiceAccount(ctx, orgID, serviceAccountID)
} }
func (s *ServiceAccountsProxy) DeleteServiceAccountToken(ctx context.Context, orgID int64, serviceAccountID int64, tokenID int64) error { func (s *ServiceAccountsProxy) DeleteServiceAccountToken(ctx context.Context, orgID int64, serviceAccountID int64, tokenID int64) error {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, 0, serviceAccountID) if s.isProxyEnabled {
if err != nil { sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
return err 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)
fmt.Println("sa.Login: ", sa.Login)
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, sa.OrgId, serviceAccountID, tokenID)
} }
func (s *ServiceAccountsProxy) ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error) { func (s *ServiceAccountsProxy) ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error) {
@ -103,7 +110,9 @@ func (s *ServiceAccountsProxy) RetrieveServiceAccount(ctx context.Context, orgID
return nil, err return nil, err
} }
sa.IsExternal = isExternalServiceAccount(sa.Login) if s.isProxyEnabled {
sa.IsExternal = isExternalServiceAccount(sa.Login)
}
return sa, nil return sa, nil
} }
@ -113,17 +122,19 @@ func (s *ServiceAccountsProxy) RetrieveServiceAccountIdByName(ctx context.Contex
} }
func (s *ServiceAccountsProxy) UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64, saForm *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) { func (s *ServiceAccountsProxy) UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64, saForm *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) {
if !isNameValid(*saForm.Name) { if s.isProxyEnabled {
s.log.Error("Invalid service account name", "name", *saForm.Name) if !isNameValid(*saForm.Name) {
return nil, extsvcaccounts.ErrInvalidName 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 { sa, err := s.proxiedService.RetrieveServiceAccount(ctx, orgID, serviceAccountID)
return nil, err if err != nil {
} return nil, err
if isExternalServiceAccount(sa.Login) { }
s.log.Error("unable to update external service accounts", "serviceAccountID", serviceAccountID) if isExternalServiceAccount(sa.Login) {
return nil, extsvcaccounts.ErrCannotBeUpdated s.log.Error("unable to update external service accounts", "serviceAccountID", serviceAccountID)
return nil, extsvcaccounts.ErrCannotBeUpdated
}
} }
return s.proxiedService.UpdateServiceAccount(ctx, orgID, serviceAccountID, saForm) return s.proxiedService.UpdateServiceAccount(ctx, orgID, serviceAccountID, saForm)
@ -135,8 +146,10 @@ func (s *ServiceAccountsProxy) SearchOrgServiceAccounts(ctx context.Context, que
return nil, err return nil, err
} }
for i := range sa.ServiceAccounts { if s.isProxyEnabled {
sa.ServiceAccounts[i].IsExternal = isExternalServiceAccount(sa.ServiceAccounts[i].Login) for i := range sa.ServiceAccounts {
sa.ServiceAccounts[i].IsExternal = isExternalServiceAccount(sa.ServiceAccounts[i].Login)
}
} }
return sa, nil return sa, nil
} }

View File

@ -23,6 +23,7 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
svc := ServiceAccountsProxy{ svc := ServiceAccountsProxy{
log.New("test"), log.New("test"),
serviceMock, serviceMock,
true,
} }
t.Run("should create service account", func(t *testing.T) { t.Run("should create service account", func(t *testing.T) {