merge main
This commit is contained in:
commit
ebeb0c67be
98 files changed
+3159
-425
No files matched your search
@@ -63,7 +63,7 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
|
||||
s.metrics.tokenSigningDurationHistogram.Observe(time.Since(t).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
cacheKey := prefixCacheKey(id.GetCacheKey())
|
||||
cacheKey := getCacheKey(id)
|
||||
|
||||
type resultType struct {
|
||||
token string
|
||||
@@ -140,7 +140,7 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
|
||||
}
|
||||
|
||||
func (s *Service) RemoveIDToken(ctx context.Context, id identity.Requester) error {
|
||||
return s.cache.Delete(ctx, prefixCacheKey(id.GetCacheKey()))
|
||||
return s.cache.Delete(ctx, getCacheKey(id))
|
||||
}
|
||||
|
||||
func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error {
|
||||
@@ -181,8 +181,8 @@ func getAudience(orgID int64) jwt.Audience {
|
||||
return jwt.Audience{fmt.Sprintf("org:%d", orgID)}
|
||||
}
|
||||
|
||||
func prefixCacheKey(key string) string {
|
||||
return fmt.Sprintf("%s-%s", cachePrefix, key)
|
||||
func getCacheKey(ident identity.Requester) string {
|
||||
return cachePrefix + ident.GetCacheKey() + string(ident.GetOrgRole())
|
||||
}
|
||||
|
||||
func shouldLogErr(err error) bool {
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -101,4 +102,34 @@ func TestService_SignIdentity(t *testing.T) {
|
||||
assert.Equal(t, claims.TypeUser, gotClaims.Rest.Type)
|
||||
assert.Equal(t, "edpu3nnt61se8e", gotClaims.Rest.Identifier)
|
||||
})
|
||||
|
||||
t.Run("should sign new token if org role has changed", func(t *testing.T) {
|
||||
s := ProvideService(
|
||||
setting.NewCfg(), signer, remotecache.NewFakeCacheStorage(),
|
||||
&authntest.FakeService{}, nil,
|
||||
)
|
||||
|
||||
ident := &authn.Identity{
|
||||
ID: "1",
|
||||
Type: claims.TypeUser,
|
||||
AuthenticatedBy: login.AzureADAuthModule,
|
||||
Login: "U1",
|
||||
UID: "edpu3nnt61se8e",
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleAdmin},
|
||||
}
|
||||
|
||||
first, _, err := s.SignIdentity(context.Background(), ident)
|
||||
require.NoError(t, err)
|
||||
|
||||
second, _, err := s.SignIdentity(context.Background(), ident)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, first, second)
|
||||
|
||||
ident.OrgRoles[1] = org.RoleEditor
|
||||
third, _, err := s.SignIdentity(context.Background(), ident)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, first, third)
|
||||
})
|
||||
}
|
||||
@@ -86,29 +86,38 @@ func (s *UserSync) SyncUserHook(ctx context.Context, id *authn.Identity, _ *auth
|
||||
}
|
||||
|
||||
// Does user exist in the database?
|
||||
usr, userAuth, errUserInDB := s.getUser(ctx, id)
|
||||
if errUserInDB != nil && !errors.Is(errUserInDB, user.ErrUserNotFound) {
|
||||
s.log.FromContext(ctx).Error("Failed to fetch user", "error", errUserInDB, "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
usr, userAuth, err := s.getUser(ctx, id)
|
||||
if err != nil && !errors.Is(err, user.ErrUserNotFound) {
|
||||
s.log.FromContext(ctx).Error("Failed to fetch user", "error", err, "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
return errSyncUserInternal.Errorf("unable to retrieve user")
|
||||
}
|
||||
|
||||
if errors.Is(errUserInDB, user.ErrUserNotFound) {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
if !id.ClientParams.AllowSignUp {
|
||||
s.log.FromContext(ctx).Warn("Failed to create user, signup is not allowed for module", "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
return errUserSignupDisabled.Errorf("%w", errSignupNotAllowed)
|
||||
}
|
||||
|
||||
// create user
|
||||
var errCreate error
|
||||
usr, errCreate = s.createUser(ctx, id)
|
||||
if errCreate != nil {
|
||||
s.log.FromContext(ctx).Error("Failed to create user", "error", errCreate, "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
return errSyncUserInternal.Errorf("unable to create user: %w", errCreate)
|
||||
usr, err = s.createUser(ctx, id)
|
||||
|
||||
// There is a possibility for a race condition when creating a user. Most clients will probably not hit this
|
||||
// case but others will. The one we have seen this issue for is auth proxy. First time a new user loads grafana
|
||||
// several requests can get "user.ErrUserNotFound" at the same time but only one of the request will be allowed
|
||||
// to actually create the user, resulting in all other requests getting "user.ErrUserAlreadyExists". So we can
|
||||
// just try to fetch the user one more to make the other request work.
|
||||
if errors.Is(err, user.ErrUserAlreadyExists) {
|
||||
usr, _, err = s.getUser(ctx, id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.log.FromContext(ctx).Error("Failed to create user", "error", err, "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
return errSyncUserInternal.Errorf("unable to create user: %w", err)
|
||||
}
|
||||
} else {
|
||||
// update user
|
||||
if errUpdate := s.updateUserAttributes(ctx, usr, id, userAuth); errUpdate != nil {
|
||||
s.log.FromContext(ctx).Error("Failed to update user", "error", errUpdate, "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
if err := s.updateUserAttributes(ctx, usr, id, userAuth); err != nil {
|
||||
s.log.FromContext(ctx).Error("Failed to update user", "error", err, "auth_module", id.AuthenticatedBy, "auth_id", id.AuthID)
|
||||
return errSyncUserInternal.Errorf("unable to update user")
|
||||
}
|
||||
}
|
||||
@@ -311,6 +320,7 @@ func (s *UserSync) updateUserAttributes(ctx context.Context, usr *user.User, id
|
||||
func (s *UserSync) createUser(ctx context.Context, id *authn.Identity) (*user.User, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "user.sync.createUser")
|
||||
defer span.End()
|
||||
|
||||
// FIXME(jguer): this should be done in the user service
|
||||
// quota check: we can have quotas on both global and org level
|
||||
// therefore we need to query check quota for both user and org services
|
||||
@@ -330,19 +340,18 @@ func (s *UserSync) createUser(ctx context.Context, id *authn.Identity) (*user.Us
|
||||
isAdmin = *id.IsGrafanaAdmin
|
||||
}
|
||||
|
||||
usr, errCreateUser := s.userService.Create(ctx, &user.CreateUserCommand{
|
||||
usr, err := s.userService.Create(ctx, &user.CreateUserCommand{
|
||||
Login: id.Login,
|
||||
Email: id.Email,
|
||||
Name: id.Name,
|
||||
IsAdmin: isAdmin,
|
||||
SkipOrgSetup: len(id.OrgRoles) > 0,
|
||||
})
|
||||
if errCreateUser != nil {
|
||||
return nil, errCreateUser
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := s.upsertAuthConnection(ctx, usr.ID, id, true)
|
||||
if err != nil {
|
||||
if err := s.upsertAuthConnection(ctx, usr.ID, id, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
@@ -451,6 +452,35 @@ func TestUserSync_SyncUserHook(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserSync_SyncUserRetryFetch(t *testing.T) {
|
||||
userSrv := usertest.NewMockService(t)
|
||||
userSrv.On("GetByEmail", mock.Anything, mock.Anything).Return(nil, user.ErrUserNotFound).Once()
|
||||
userSrv.On("Create", mock.Anything, mock.Anything).Return(nil, user.ErrUserAlreadyExists).Once()
|
||||
userSrv.On("GetByEmail", mock.Anything, mock.Anything).Return(&user.User{ID: 1}, nil).Once()
|
||||
|
||||
s := ProvideUserSync(
|
||||
userSrv,
|
||||
authinfoimpl.ProvideOSSUserProtectionService(),
|
||||
&authinfotest.FakeService{},
|
||||
"atest.FakeQuotaService{},
|
||||
tracing.NewNoopTracerService(),
|
||||
featuremgmt.WithFeatures(),
|
||||
)
|
||||
|
||||
email := "test@test.com"
|
||||
|
||||
err := s.SyncUserHook(context.Background(), &authn.Identity{
|
||||
ClientParams: authn.ClientParams{
|
||||
SyncUser: true,
|
||||
AllowSignUp: true,
|
||||
LookUpParams: login.UserLookupParams{
|
||||
Email: &email,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestUserSync_FetchSyncedUserHook(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
|
||||
@@ -3,6 +3,8 @@ package authz
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/fullstorydev/grpchan"
|
||||
@@ -11,6 +13,7 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
authzlib "github.com/grafana/authlib/authz"
|
||||
@@ -22,6 +25,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
@@ -64,6 +69,10 @@ func ProvideAuthZClient(
|
||||
// Register the server
|
||||
server := rbac.NewService(
|
||||
sql,
|
||||
// When running in-proc we get a injection cycle between
|
||||
// authz client, resource client and apiserver so we need to use
|
||||
// package level function to get rest config
|
||||
store.NewAPIFolderStore(tracer, apiserver.GetRestConfig),
|
||||
legacy.NewLegacySQLStores(sql),
|
||||
store.NewUnionPermissionStore(
|
||||
store.NewStaticPermissionStore(acService),
|
||||
@@ -201,3 +210,67 @@ func newCloudLegacyClient(authCfg *Cfg, tracer tracing.Tracer) (authlib.AccessCl
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func RegisterRBACAuthZService(
|
||||
handler grpcserver.Provider,
|
||||
db legacysql.LegacyDatabaseProvider,
|
||||
tracer tracing.Tracer,
|
||||
reg prometheus.Registerer,
|
||||
cache cache.Cache,
|
||||
exchangeClient authnlib.TokenExchanger,
|
||||
folderAPIURL string,
|
||||
) {
|
||||
var folderStore store.FolderStore
|
||||
// FIXME: for now we default to using database read proxy for folders if the api url is not configured.
|
||||
// we should remove this and the sql implementation once we have verified that is works correctly
|
||||
if folderAPIURL == "" {
|
||||
folderStore = store.NewSQLFolderStore(db, tracer)
|
||||
} else {
|
||||
folderStore = store.NewAPIFolderStore(tracer, func(ctx context.Context) *rest.Config {
|
||||
return &rest.Config{
|
||||
Host: folderAPIURL,
|
||||
WrapTransport: func(rt http.RoundTripper) http.RoundTripper {
|
||||
return &tokenExhangeRoundTripper{te: exchangeClient, rt: rt}
|
||||
},
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
server := rbac.NewService(
|
||||
db,
|
||||
folderStore,
|
||||
legacy.NewLegacySQLStores(db),
|
||||
store.NewSQLPermissionStore(db, tracer),
|
||||
log.New("authz-grpc-server"),
|
||||
tracer,
|
||||
reg,
|
||||
cache,
|
||||
)
|
||||
|
||||
srv := handler.GetServer()
|
||||
authzv1.RegisterAuthzServiceServer(srv, server)
|
||||
authzextv1.RegisterAuthzExtentionServiceServer(srv, server)
|
||||
}
|
||||
|
||||
var _ http.RoundTripper = tokenExhangeRoundTripper{}
|
||||
|
||||
type tokenExhangeRoundTripper struct {
|
||||
te authnlib.TokenExchanger
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
func (t tokenExhangeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
res, err := t.te.Exchange(r.Context(), authnlib.TokenExchangeRequest{
|
||||
Namespace: "*",
|
||||
Audiences: []string{"folder.grafana.app"},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create access token: %w", err)
|
||||
}
|
||||
|
||||
r.Header.Set("X-Access-Token", "Bearer "+res.Token)
|
||||
return t.rt.RoundTrip(r)
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
"github.com/grafana/authlib/cache"
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@@ -41,6 +41,7 @@ type Service struct {
|
||||
authzextv1.UnimplementedAuthzExtentionServiceServer
|
||||
|
||||
store store.Store
|
||||
folderStore store.FolderStore
|
||||
permissionStore store.PermissionStore
|
||||
identityStore legacy.LegacyIdentityStore
|
||||
|
||||
@@ -63,6 +64,7 @@ type Service struct {
|
||||
|
||||
func NewService(
|
||||
sql legacysql.LegacyDatabaseProvider,
|
||||
folderStore store.FolderStore,
|
||||
identityStore legacy.LegacyIdentityStore,
|
||||
permissionStore store.PermissionStore,
|
||||
logger log.Logger,
|
||||
@@ -72,6 +74,7 @@ func NewService(
|
||||
) *Service {
|
||||
return &Service{
|
||||
store: store.NewStore(sql, tracer),
|
||||
folderStore: folderStore,
|
||||
permissionStore: permissionStore,
|
||||
identityStore: identityStore,
|
||||
logger: logger,
|
||||
@@ -209,40 +212,42 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ
|
||||
return listReq, nil
|
||||
}
|
||||
|
||||
func validateNamespace(ctx context.Context, nameSpace string) (claims.NamespaceInfo, error) {
|
||||
func validateNamespace(ctx context.Context, nameSpace string) (types.NamespaceInfo, error) {
|
||||
if nameSpace == "" {
|
||||
return claims.NamespaceInfo{}, status.Error(codes.InvalidArgument, "namespace is required")
|
||||
return types.NamespaceInfo{}, status.Error(codes.InvalidArgument, "namespace is required")
|
||||
}
|
||||
authInfo, has := claims.AuthInfoFrom(ctx)
|
||||
authInfo, has := types.AuthInfoFrom(ctx)
|
||||
if !has {
|
||||
return claims.NamespaceInfo{}, status.Error(codes.Internal, "could not get auth info from context")
|
||||
return types.NamespaceInfo{}, status.Error(codes.Internal, "could not get auth info from context")
|
||||
}
|
||||
if !claims.NamespaceMatches(authInfo.GetNamespace(), nameSpace) {
|
||||
return claims.NamespaceInfo{}, status.Error(codes.PermissionDenied, "namespace does not match")
|
||||
if !types.NamespaceMatches(authInfo.GetNamespace(), nameSpace) {
|
||||
return types.NamespaceInfo{}, status.Error(codes.PermissionDenied, "namespace does not match")
|
||||
}
|
||||
|
||||
ns, err := claims.ParseNamespace(nameSpace)
|
||||
ns, err := types.ParseNamespace(nameSpace)
|
||||
if err != nil {
|
||||
return claims.NamespaceInfo{}, err
|
||||
return types.NamespaceInfo{}, err
|
||||
}
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func (s *Service) validateSubject(ctx context.Context, subject string) (string, claims.IdentityType, error) {
|
||||
func (s *Service) validateSubject(ctx context.Context, subject string) (string, types.IdentityType, error) {
|
||||
if subject == "" {
|
||||
return "", "", status.Error(codes.InvalidArgument, "subject is required")
|
||||
}
|
||||
|
||||
ctxLogger := s.logger.FromContext(ctx)
|
||||
identityType, userUID, err := claims.ParseTypeID(subject)
|
||||
identityType, userUID, err := types.ParseTypeID(subject)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Permission check currently only checks user, anonymous user, service account and renderer permissions
|
||||
if !(identityType == claims.TypeUser || identityType == claims.TypeServiceAccount || identityType == claims.TypeAnonymous || identityType == claims.TypeRenderService) {
|
||||
if !types.IsIdentityType(identityType, types.TypeUser, types.TypeServiceAccount, types.TypeAnonymous, types.TypeRenderService) {
|
||||
ctxLogger.Error("unsupported identity type", "type", identityType)
|
||||
return "", "", status.Error(codes.PermissionDenied, "unsupported identity type")
|
||||
}
|
||||
|
||||
return userUID, identityType, nil
|
||||
}
|
||||
|
||||
@@ -264,30 +269,29 @@ func (s *Service) validateAction(ctx context.Context, group, resource, verb stri
|
||||
return action, nil
|
||||
}
|
||||
|
||||
func (s *Service) getIdentityPermissions(ctx context.Context, ns claims.NamespaceInfo, idType claims.IdentityType, userID, action string) (map[string]bool, error) {
|
||||
func (s *Service) getIdentityPermissions(ctx context.Context, ns types.NamespaceInfo, idType types.IdentityType, userID, action string) (map[string]bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getIdentityPermissions")
|
||||
defer span.End()
|
||||
|
||||
// When checking folder creation permissions, also check edit and admin action sets for folder, as the scoped folder create actions aren't stored in the DB separately
|
||||
var actionSets []string
|
||||
if action == "folders:create" {
|
||||
actionSets = append(actionSets, "folders:edit")
|
||||
actionSets = append(actionSets, "folders:admin")
|
||||
actionSets = append(actionSets, "folders:edit", "folders:admin")
|
||||
}
|
||||
|
||||
switch idType {
|
||||
case claims.TypeAnonymous:
|
||||
case types.TypeAnonymous:
|
||||
return s.getAnonymousPermissions(ctx, ns, action, actionSets)
|
||||
case claims.TypeRenderService:
|
||||
case types.TypeRenderService:
|
||||
return s.getRendererPermissions(ctx, action)
|
||||
case claims.TypeUser, claims.TypeServiceAccount:
|
||||
case types.TypeUser, types.TypeServiceAccount:
|
||||
return s.getUserPermissions(ctx, ns, userID, action, actionSets)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported identity type: %s", idType)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInfo, userID, action string, actionSets []string) (map[string]bool, error) {
|
||||
func (s *Service) getUserPermissions(ctx context.Context, ns types.NamespaceInfo, userID, action string, actionSets []string) (map[string]bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getUserPermissions")
|
||||
defer span.End()
|
||||
|
||||
@@ -342,7 +346,7 @@ func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInf
|
||||
return res.(map[string]bool), nil
|
||||
}
|
||||
|
||||
func (s *Service) getAnonymousPermissions(ctx context.Context, ns claims.NamespaceInfo, action string, actionSets []string) (map[string]bool, error) {
|
||||
func (s *Service) getAnonymousPermissions(ctx context.Context, ns types.NamespaceInfo, action string, actionSets []string) (map[string]bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getAnonymousPermissions")
|
||||
defer span.End()
|
||||
|
||||
@@ -378,7 +382,7 @@ func (s *Service) getRendererPermissions(ctx context.Context, action string) (ma
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetUserIdentifiers(ctx context.Context, ns claims.NamespaceInfo, userUID string) (*store.UserIdentifiers, error) {
|
||||
func (s *Service) GetUserIdentifiers(ctx context.Context, ns types.NamespaceInfo, userUID string) (*store.UserIdentifiers, error) {
|
||||
uidCacheKey := userIdentifierCacheKey(ns.Value, userUID)
|
||||
if cached, ok := s.idCache.Get(ctx, uidCacheKey); ok {
|
||||
return &cached, nil
|
||||
@@ -397,7 +401,7 @@ func (s *Service) GetUserIdentifiers(ctx context.Context, ns claims.NamespaceInf
|
||||
userIDQuery = store.UserIdentifierQuery{UserUID: userUID}
|
||||
}
|
||||
userIdentifiers, err := s.store.GetUserIdentifiers(ctx, userIDQuery)
|
||||
if err != nil || userIdentifiers == nil {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get user internal id: %w", err)
|
||||
}
|
||||
|
||||
@@ -407,7 +411,7 @@ func (s *Service) GetUserIdentifiers(ctx context.Context, ns claims.NamespaceInf
|
||||
return userIdentifiers, nil
|
||||
}
|
||||
|
||||
func (s *Service) getUserTeams(ctx context.Context, ns claims.NamespaceInfo, userIdentifiers *store.UserIdentifiers) ([]int64, error) {
|
||||
func (s *Service) getUserTeams(ctx context.Context, ns types.NamespaceInfo, userIdentifiers *store.UserIdentifiers) ([]int64, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getUserTeams")
|
||||
defer span.End()
|
||||
|
||||
@@ -441,7 +445,7 @@ func (s *Service) getUserTeams(ctx context.Context, ns claims.NamespaceInfo, use
|
||||
return teamIDs, nil
|
||||
}
|
||||
|
||||
func (s *Service) getUserBasicRole(ctx context.Context, ns claims.NamespaceInfo, userIdentifiers *store.UserIdentifiers) (store.BasicRole, error) {
|
||||
func (s *Service) getUserBasicRole(ctx context.Context, ns types.NamespaceInfo, userIdentifiers *store.UserIdentifiers) (store.BasicRole, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getUserBasicRole")
|
||||
defer span.End()
|
||||
|
||||
@@ -535,7 +539,7 @@ func (s *Service) checkInheritedPermissions(ctx context.Context, scopeMap map[st
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *Service) buildFolderTree(ctx context.Context, ns claims.NamespaceInfo) (folderTree, error) {
|
||||
func (s *Service) buildFolderTree(ctx context.Context, ns types.NamespaceInfo) (folderTree, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.buildFolderTree")
|
||||
defer span.End()
|
||||
|
||||
@@ -545,7 +549,7 @@ func (s *Service) buildFolderTree(ctx context.Context, ns claims.NamespaceInfo)
|
||||
}
|
||||
|
||||
res, err, _ := s.sf.Do(ns.Value+"_buildFolderTree", func() (interface{}, error) {
|
||||
folders, err := s.store.GetFolders(ctx, ns)
|
||||
folders, err := s.folderStore.ListFolders(ctx, ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get folders: %w", err)
|
||||
}
|
||||
|
||||
@@ -620,6 +620,7 @@ func setupService() *Service {
|
||||
folderCache: newCacheWrap[folderTree](cache, logger, shortCacheTTL),
|
||||
store: fStore,
|
||||
permissionStore: fStore,
|
||||
folderStore: fStore,
|
||||
identityStore: &fakeIdentityStore{},
|
||||
sf: new(singleflight.Group),
|
||||
}
|
||||
@@ -663,7 +664,7 @@ func (f *fakeStore) GetUserPermissions(ctx context.Context, namespace claims.Nam
|
||||
return f.userPermissions, nil
|
||||
}
|
||||
|
||||
func (f *fakeStore) GetFolders(ctx context.Context, namespace claims.NamespaceInfo) ([]store.Folder, error) {
|
||||
func (f *fakeStore) ListFolders(ctx context.Context, namespace claims.NamespaceInfo) ([]store.Folder, error) {
|
||||
f.calls++
|
||||
if f.err {
|
||||
return nil, fmt.Errorf("store error")
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/pager"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
type FolderStore interface {
|
||||
ListFolders(ctx context.Context, ns types.NamespaceInfo) ([]Folder, error)
|
||||
}
|
||||
|
||||
type Folder struct {
|
||||
UID string
|
||||
ParentUID *string
|
||||
}
|
||||
|
||||
var _ FolderStore = (*SQLFolderStore)(nil)
|
||||
|
||||
func NewSQLFolderStore(sql legacysql.LegacyDatabaseProvider, tracer tracing.Tracer) *SQLFolderStore {
|
||||
return &SQLFolderStore{sql, tracer}
|
||||
}
|
||||
|
||||
type SQLFolderStore struct {
|
||||
sql legacysql.LegacyDatabaseProvider
|
||||
tracer tracing.Tracer
|
||||
}
|
||||
|
||||
var sqlFolders = mustTemplate("folder_query.sql")
|
||||
|
||||
type listFoldersQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
|
||||
Query *FolderQuery
|
||||
FolderTable string
|
||||
}
|
||||
|
||||
type FolderQuery struct {
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
func (r listFoldersQuery) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newListFolders(sql *legacysql.LegacyDatabaseHelper, query *FolderQuery) listFoldersQuery {
|
||||
return listFoldersQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
Query: query,
|
||||
FolderTable: sql.Table("folder"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLFolderStore) ListFolders(ctx context.Context, ns types.NamespaceInfo) ([]Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.database.ListFolders")
|
||||
defer span.End()
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := newListFolders(sql, &FolderQuery{OrgID: ns.OrgID})
|
||||
q, err := sqltemplate.Execute(sqlFolders, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, query.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var folders []Folder
|
||||
for rows.Next() {
|
||||
var folder Folder
|
||||
if err := rows.Scan(&folder.UID, &folder.ParentUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
folders = append(folders, folder)
|
||||
}
|
||||
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
var _ FolderStore = (*APIFolderStore)(nil)
|
||||
|
||||
func NewAPIFolderStore(tracer tracing.Tracer, configProvider func(ctx context.Context) *rest.Config) *APIFolderStore {
|
||||
return &APIFolderStore{tracer, configProvider}
|
||||
}
|
||||
|
||||
type APIFolderStore struct {
|
||||
tracer tracing.Tracer
|
||||
configProvider func(ctx context.Context) *rest.Config
|
||||
}
|
||||
|
||||
func (s *APIFolderStore) ListFolders(ctx context.Context, ns types.NamespaceInfo) ([]Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz.apistore.ListFolders")
|
||||
defer span.End()
|
||||
|
||||
client, err := s.client(ctx, ns.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create resource client: %w", err)
|
||||
}
|
||||
|
||||
p := pager.New(func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
|
||||
return client.List(ctx, opts)
|
||||
})
|
||||
|
||||
const defaultPageSize = 500
|
||||
folders := make([]Folder, 0, defaultPageSize)
|
||||
err = p.EachListItem(ctx, metav1.ListOptions{Limit: defaultPageSize}, func(obj runtime.Object) error {
|
||||
object, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
folder := Folder{UID: object.GetName()}
|
||||
parent := object.GetFolder()
|
||||
if parent != "" {
|
||||
folder.ParentUID = &parent
|
||||
}
|
||||
|
||||
folders = append(folders, folder)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching folders: %w", err)
|
||||
}
|
||||
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
func (s *APIFolderStore) client(ctx context.Context, namespace string) (dynamic.ResourceInterface, error) {
|
||||
client, err := dynamic.NewForConfig(s.configProvider(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.Resource(folderv0alpha1.FolderResourceInfo.GroupVersionResource()).Namespace(namespace), nil
|
||||
}
|
||||
@@ -19,21 +19,3 @@ type UserIdentifierQuery struct {
|
||||
UserID int64
|
||||
UserUID string
|
||||
}
|
||||
|
||||
type FolderQuery struct {
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
type DashboardQuery struct {
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
type Folder struct {
|
||||
UID string
|
||||
ParentUID *string
|
||||
}
|
||||
|
||||
type Dashboard struct {
|
||||
UID string
|
||||
ParentUID *string
|
||||
}
|
||||
@@ -16,7 +16,6 @@ var (
|
||||
|
||||
sqlQueryBasicRoles = mustTemplate("basic_role_query.sql")
|
||||
sqlUserIdentifiers = mustTemplate("user_identifier_query.sql")
|
||||
sqlFolders = mustTemplate("folder_query.sql")
|
||||
)
|
||||
|
||||
func mustTemplate(filename string) *template.Template {
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
type Store interface {
|
||||
GetUserIdentifiers(ctx context.Context, query UserIdentifierQuery) (*UserIdentifiers, error)
|
||||
GetBasicRoles(ctx context.Context, ns claims.NamespaceInfo, query BasicRoleQuery) (*BasicRole, error)
|
||||
GetFolders(ctx context.Context, ns claims.NamespaceInfo) ([]Folder, error)
|
||||
}
|
||||
|
||||
type StoreImpl struct {
|
||||
@@ -104,41 +103,3 @@ func (s *StoreImpl) GetBasicRoles(ctx context.Context, ns claims.NamespaceInfo,
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (s *StoreImpl) GetFolders(ctx context.Context, ns claims.NamespaceInfo) ([]Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "authz_direct_db.database.GetFolders")
|
||||
defer span.End()
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := FolderQuery{OrgID: ns.OrgID}
|
||||
req := newGetFolders(sql, &query)
|
||||
q, err := sqltemplate.Execute(sqlFolders, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var folders []Folder
|
||||
for rows.Next() {
|
||||
var folder Folder
|
||||
if err := rows.Scan(&folder.UID, &folder.ParentUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
folders = append(folders, folder)
|
||||
}
|
||||
|
||||
return folders, nil
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package authz
|
||||
|
||||
import (
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
cache "github.com/grafana/authlib/cache"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func RegisterRBACAuthZService(
|
||||
handler grpcserver.Provider,
|
||||
db legacysql.LegacyDatabaseProvider,
|
||||
tracer tracing.Tracer,
|
||||
reg prometheus.Registerer,
|
||||
cache cache.Cache) {
|
||||
server := rbac.NewService(
|
||||
db,
|
||||
legacy.NewLegacySQLStores(db),
|
||||
store.NewSQLPermissionStore(db, tracer),
|
||||
log.New("authz-grpc-server"),
|
||||
tracer,
|
||||
reg,
|
||||
cache,
|
||||
)
|
||||
|
||||
srv := handler.GetServer()
|
||||
authzv1.RegisterAuthzServiceServer(srv, server)
|
||||
authzextv1.RegisterAuthzExtentionServiceServer(srv, server)
|
||||
}
|
||||
@@ -105,6 +105,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -200,8 +201,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -248,8 +251,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -309,8 +314,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -423,8 +430,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -504,8 +513,10 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -615,8 +626,10 @@ func TestLoader_Load_CustomSource(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -696,8 +709,10 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -801,8 +816,10 @@ func TestLoader_Load_RBACReady(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -883,8 +900,10 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
|
||||
ExposedComponents: []string{},
|
||||
}},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -964,8 +983,10 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -1060,8 +1081,10 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
|
||||
{Name: "Nginx Datasource", Type: "datasource", Role: org.RoleViewer, Action: plugins.ActionAppAccess, Slug: "nginx-datasource"},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -1272,8 +1295,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -1314,8 +1339,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -1463,8 +1490,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
@@ -1512,8 +1541,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Extensions: plugins.Extensions{
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedLinks: []plugins.AddedLink{},
|
||||
AddedComponents: []plugins.AddedComponent{},
|
||||
AddedFunctions: []plugins.AddedFunction{},
|
||||
|
||||
ExposedComponents: []plugins.ExposedComponent{},
|
||||
ExtensionPoints: []plugins.ExtensionPoint{},
|
||||
},
|
||||
|
||||
@@ -17,8 +17,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/database"
|
||||
@@ -44,7 +42,6 @@ type ServiceAccountsService struct {
|
||||
secretScanService secretscan.Checker
|
||||
orgService org.Service
|
||||
serverLock *serverlock.ServerLockService
|
||||
idService auth.IDService
|
||||
|
||||
secretScanEnabled bool
|
||||
secretScanInterval time.Duration
|
||||
@@ -61,7 +58,6 @@ func ProvideServiceAccountsService(
|
||||
acService accesscontrol.Service,
|
||||
permissions accesscontrol.ServiceAccountPermissionsService,
|
||||
serverLockService *serverlock.ServerLockService,
|
||||
idService auth.IDService,
|
||||
) (*ServiceAccountsService, error) {
|
||||
serviceAccountsStore := database.ProvideServiceAccountsStore(
|
||||
cfg,
|
||||
@@ -81,7 +77,6 @@ func ProvideServiceAccountsService(
|
||||
backgroundLog: log.New("serviceaccounts.background"),
|
||||
orgService: orgService,
|
||||
serverLock: serverLockService,
|
||||
idService: idService,
|
||||
}
|
||||
|
||||
if err := RegisterRoles(acService); err != nil {
|
||||
@@ -271,10 +266,6 @@ func (sa *ServiceAccountsService) UpdateServiceAccount(ctx context.Context, orgI
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := sa.idService.RemoveIDToken(ctx, &authn.Identity{ID: strconv.FormatInt(serviceAccountID, 10), Type: claims.TypeServiceAccount, OrgID: orgID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sa.store.UpdateServiceAccount(ctx, orgID, serviceAccountID, saForm)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user