auth: migrate api interface implementation (#77040)

* expand serviceaccount service interface

* implemet FakeServiceAccountService

* Replace SA service interface from api

* merge sa proxy tests with new fake service

* implement DeleteServiceAccountToken

* add test for DeleteServiceAccountToken
This commit is contained in:
linoman 2023-10-25 12:40:30 +02:00 committed by GitHub
parent c25ea17d10
commit 1bc81b7bd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 214 additions and 158 deletions

View File

@ -1,7 +1,6 @@
package api
import (
"context"
"net/http"
"strconv"
@ -11,7 +10,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware/requestmeta"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/org"
@ -23,7 +21,7 @@ import (
type ServiceAccountsAPI struct {
cfg *setting.Cfg
service service
service serviceaccounts.Service
accesscontrol accesscontrol.AccessControl
accesscontrolService accesscontrol.Service
RouterRegister routing.RouteRegister
@ -31,25 +29,9 @@ type ServiceAccountsAPI struct {
permissionService accesscontrol.ServiceAccountPermissionsService
}
// Service implements the API exposed methods for service accounts.
type service interface {
CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error)
RetrieveServiceAccount(ctx context.Context, orgID, serviceAccountID int64) (*serviceaccounts.ServiceAccountProfileDTO, error)
UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64,
saForm *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error)
SearchOrgServiceAccounts(ctx context.Context, query *serviceaccounts.SearchOrgServiceAccountsQuery) (*serviceaccounts.SearchOrgServiceAccountsResult, error)
ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error)
DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error
MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error)
MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error
// Service account tokens
AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error)
DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error
}
func NewServiceAccountsAPI(
cfg *setting.Cfg,
service service,
service serviceaccounts.Service,
accesscontrol accesscontrol.AccessControl,
accesscontrolService accesscontrol.Service,
routerRegister routing.RouteRegister,

View File

@ -1,7 +1,6 @@
package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
@ -16,9 +15,9 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
satests "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web/webtest"
@ -82,7 +81,7 @@ func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &fakeServiceAccountService{ExpectedServiceAccount: tt.expectedSA, ExpectedErr: tt.expectedErr}
a.service = &satests.FakeServiceAccountService{ExpectedServiceAccount: tt.expectedSA, ExpectedErr: tt.expectedErr}
})
req := server.NewRequest(http.MethodPost, "/api/serviceaccounts/", strings.NewReader(tt.body))
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
@ -162,7 +161,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &fakeServiceAccountService{ExpectedServiceAccountProfile: tt.expectedSA}
a.service = &satests.FakeServiceAccountService{ExpectedServiceAccountProfile: tt.expectedSA}
})
req := server.NewGetRequest(fmt.Sprintf("/api/serviceaccounts/%d", tt.id))
webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}})
@ -224,7 +223,7 @@ func TestServiceAccountsAPI_UpdateServiceAccount(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &fakeServiceAccountService{ExpectedServiceAccountProfile: tt.expectedSA}
a.service = &satests.FakeServiceAccountService{ExpectedServiceAccountProfile: tt.expectedSA}
})
req := server.NewRequest(http.MethodPatch, fmt.Sprintf("/api/serviceaccounts/%d", tt.id), strings.NewReader(tt.body))
@ -278,7 +277,7 @@ func TestServiceAccountsAPI_MigrateApiKeysToServiceAccounts(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &fakeServiceAccountService{ExpectedMigrationResult: tt.expectedMigrationResult}
a.service = &satests.FakeServiceAccountService{ExpectedMigrationResult: tt.expectedMigrationResult}
})
req := server.NewRequest(http.MethodPost, "/api/serviceaccounts/migrate", nil)
@ -303,7 +302,7 @@ func setupTests(t *testing.T, opts ...func(a *ServiceAccountsAPI)) *webtest.Serv
cfg := setting.NewCfg()
api := &ServiceAccountsAPI{
cfg: cfg,
service: &fakeServiceAccountService{},
service: &satests.FakeServiceAccountService{},
accesscontrolService: &actest.FakeService{},
accesscontrol: acimpl.ProvideAccessControl(cfg),
RouterRegister: routing.NewRouteRegister(),
@ -317,48 +316,3 @@ func setupTests(t *testing.T, opts ...func(a *ServiceAccountsAPI)) *webtest.Serv
api.RegisterAPIEndpoints()
return webtest.NewServer(t, api.RouterRegister)
}
var _ service = new(fakeServiceAccountService)
type fakeServiceAccountService struct {
service
ExpectedErr error
ExpectedAPIKey *apikey.APIKey
ExpectedServiceAccountTokens []apikey.APIKey
ExpectedServiceAccount *serviceaccounts.ServiceAccountDTO
ExpectedServiceAccountProfile *serviceaccounts.ServiceAccountProfileDTO
ExpectedMigrationResult *serviceaccounts.MigrationResult
}
func (f *fakeServiceAccountService) CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error) {
return f.ExpectedServiceAccount, f.ExpectedErr
}
func (f *fakeServiceAccountService) DeleteServiceAccount(ctx context.Context, orgID, id int64) error {
return f.ExpectedErr
}
func (f *fakeServiceAccountService) RetrieveServiceAccount(ctx context.Context, orgID, id int64) (*serviceaccounts.ServiceAccountProfileDTO, error) {
return f.ExpectedServiceAccountProfile, f.ExpectedErr
}
func (f *fakeServiceAccountService) ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error) {
return f.ExpectedServiceAccountTokens, f.ExpectedErr
}
func (f *fakeServiceAccountService) UpdateServiceAccount(ctx context.Context, orgID, id int64, cmd *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) {
return f.ExpectedServiceAccountProfile, f.ExpectedErr
}
func (f *fakeServiceAccountService) AddServiceAccountToken(ctx context.Context, id int64, cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error) {
return f.ExpectedAPIKey, f.ExpectedErr
}
func (f *fakeServiceAccountService) DeleteServiceAccountToken(ctx context.Context, orgID, id, tokenID int64) error {
return f.ExpectedErr
}
func (f *fakeServiceAccountService) MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error) {
fmt.Printf("fake migration result: %v", f.ExpectedMigrationResult)
return f.ExpectedMigrationResult, f.ExpectedErr
}

View File

@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
satests "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/web/webtest"
)
@ -43,7 +44,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &fakeServiceAccountService{}
a.service = &satests.FakeServiceAccountService{}
})
req := server.NewGetRequest(fmt.Sprintf("/api/serviceaccounts/%d/tokens", tt.id))
webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}})
@ -109,7 +110,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.cfg.ApiKeyMaxSecondsToLive = tt.tokenTTL
a.service = &fakeServiceAccountService{
a.service = &satests.FakeServiceAccountService{
ExpectedErr: tt.expectedErr,
ExpectedAPIKey: tt.expectedAPIKey,
}
@ -163,7 +164,7 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &fakeServiceAccountService{ExpectedErr: tt.expectedErr}
a.service = &satests.FakeServiceAccountService{ExpectedErr: tt.expectedErr}
})
req := server.NewRequest(http.MethodDelete, fmt.Sprintf("/api/serviceaccounts/%d/tokens/%d", tt.saID, tt.apikeyID), nil)

View File

@ -15,12 +15,13 @@ const (
)
var (
ErrCannotBeDeleted = errutil.BadRequest("extsvcaccounts.ErrCannotBeDeleted", errutil.WithPublicMessage("external service account cannot be deleted"))
ErrInvalidName = errutil.BadRequest("extsvcaccounts.ErrInvalidName", errutil.WithPublicMessage("only external service account names can be prefixed with 'extsvc-'"))
ErrCannotBeUpdated = errutil.BadRequest("extsvcaccounts.ErrCannotBeUpdated", errutil.WithPublicMessage("external service account cannot be updated"))
ErrCannotCreateToken = errutil.BadRequest("extsvcaccounts.ErrCannotCreateToken", errutil.WithPublicMessage("cannot add external service account token"))
ErrCannotBeDeleted = errutil.BadRequest("extsvcaccounts.ErrCannotBeDeleted", errutil.WithPublicMessage("external service account cannot be deleted"))
ErrCannotBeUpdated = errutil.BadRequest("extsvcaccounts.ErrCannotBeUpdated", errutil.WithPublicMessage("external service account cannot be updated"))
ErrCannotCreateToken = errutil.BadRequest("extsvcaccounts.ErrCannotCreateToken", errutil.WithPublicMessage("cannot add external service account token"))
ErrCannotDeleteToken = errutil.BadRequest("extsvcaccounts.ErrCannotDeleteToken", errutil.WithPublicMessage("cannot delete external service account token"))
ErrCannotListTokens = errutil.BadRequest("extsvcaccounts.ErrCannotListTokens", errutil.WithPublicMessage("cannot list external service account tokens"))
ErrCredentialsNotFound = errutil.NotFound("extsvcaccounts.credentials-not-found")
ErrInvalidName = errutil.BadRequest("extsvcaccounts.ErrInvalidName", errutil.WithPublicMessage("only external service account names can be prefixed with 'extsvc-'"))
)
// Credentials represents the credentials associated to an external service

View File

@ -2,6 +2,7 @@ package proxy
import (
"context"
"fmt"
"strings"
"github.com/grafana/grafana/pkg/infra/log"
@ -68,6 +69,34 @@ func (s *ServiceAccountsProxy) DeleteServiceAccount(ctx context.Context, orgID,
return s.proxiedService.DeleteServiceAccount(ctx, orgID, serviceAccountID)
}
func (s *ServiceAccountsProxy) DeleteServiceAccountToken(ctx context.Context, orgID int64, serviceAccountID int64, tokenID int64) error {
sa, err := s.proxiedService.RetrieveServiceAccount(ctx, 0, serviceAccountID)
if err != nil {
return err
}
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) {
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 {

View File

@ -8,56 +8,18 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
)
type FakeServiceAccountsService struct {
ExpectedServiceAccountProfileDTO *serviceaccounts.ServiceAccountProfileDTO
ExpectedSearchOrgServiceAccountsResult *serviceaccounts.SearchOrgServiceAccountsResult
}
var _ serviceaccounts.Service = (*FakeServiceAccountsService)(nil)
func newServiceAccountServiceFake() *FakeServiceAccountsService {
return &FakeServiceAccountsService{}
}
func (f *FakeServiceAccountsService) CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error) {
return nil, nil
}
func (f *FakeServiceAccountsService) DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error {
return nil
}
func (f *FakeServiceAccountsService) RetrieveServiceAccount(ctx context.Context, orgID, serviceAccountID int64) (*serviceaccounts.ServiceAccountProfileDTO, error) {
return f.ExpectedServiceAccountProfileDTO, nil
}
func (f *FakeServiceAccountsService) RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error) {
return 0, nil
}
func (f *FakeServiceAccountsService) UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64,
saForm *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) {
return nil, nil
}
func (f *FakeServiceAccountsService) AddServiceAccountToken(ctx context.Context, serviceAccountID int64,
cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error) {
return nil, nil
}
func (f *FakeServiceAccountsService) SearchOrgServiceAccounts(ctx context.Context, query *serviceaccounts.SearchOrgServiceAccountsQuery) (*serviceaccounts.SearchOrgServiceAccountsResult, error) {
return f.ExpectedSearchOrgServiceAccountsResult, nil
}
var _ serviceaccounts.Service = (*tests.FakeServiceAccountService)(nil)
func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
testOrgId := int64(1)
testServiceAccountId := int64(1)
serviceMock := newServiceAccountServiceFake()
testServiceAccountTokenId := int64(1)
serviceMock := &tests.FakeServiceAccountService{}
svc := ServiceAccountsProxy{
log.New("test"),
serviceMock,
@ -118,13 +80,44 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
serviceMock.ExpectedServiceAccountProfileDTO = tc.expectedServiceAccount
serviceMock.ExpectedServiceAccountProfile = tc.expectedServiceAccount
err := svc.DeleteServiceAccount(context.Background(), testOrgId, testServiceAccountId)
assert.Equal(t, err, tc.expectedError, tc.description)
})
}
})
t.Run("should delete service account", func(t *testing.T) {
testCases := []struct {
description string
expectedError error
expectedServiceAccount *serviceaccounts.ServiceAccountProfileDTO
}{
{
description: "should allow to delete a service account token",
expectedError: nil,
expectedServiceAccount: &serviceaccounts.ServiceAccountProfileDTO{
Login: "my-service-account",
},
},
{
description: "should not allow to delete a external service account token",
expectedError: extsvcaccounts.ErrCannotDeleteToken,
expectedServiceAccount: &serviceaccounts.ServiceAccountProfileDTO{
Login: "sa-extsvc-my-service-account",
},
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
serviceMock.ExpectedServiceAccountProfile = tc.expectedServiceAccount
err := svc.DeleteServiceAccountToken(context.Background(), testOrgId, testServiceAccountId, testServiceAccountTokenId)
assert.Equal(t, err, tc.expectedError, tc.description)
})
}
})
t.Run("should retrieve service account with IsExternal field", func(t *testing.T) {
testCases := []struct {
description string
@ -149,7 +142,7 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
serviceMock.ExpectedServiceAccountProfileDTO = tc.expectedServiceAccount
serviceMock.ExpectedServiceAccountProfile = tc.expectedServiceAccount
sa, err := svc.RetrieveServiceAccount(context.Background(), testOrgId, testServiceAccountId)
assert.NoError(t, err, tc.description)
assert.Equal(t, tc.expectedIsExternal, sa.IsExternal, tc.description)
@ -157,6 +150,23 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
}
})
t.Run("should mark external service accounts correctly", func(t *testing.T) {
serviceMock.ExpectedSearchOrgServiceAccountsResult = &serviceaccounts.SearchOrgServiceAccountsResult{
TotalCount: 2,
ServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
{Login: "test"},
{Login: serviceaccounts.ServiceAccountPrefix + serviceaccounts.ExtSvcPrefix + "test"},
},
Page: 1,
PerPage: 2,
}
res, err := svc.SearchOrgServiceAccounts(context.Background(), &serviceaccounts.SearchOrgServiceAccountsQuery{OrgID: 1})
require.Len(t, res.ServiceAccounts, 2)
require.NoError(t, err)
require.False(t, res.ServiceAccounts[0].IsExternal)
require.True(t, res.ServiceAccounts[1].IsExternal)
})
t.Run("should update service account", func(t *testing.T) {
nameWithoutProtectedPrefix := "my-updated-service-account"
nameWithProtectedPrefix := "extsvc-my-updated-service-account"
@ -211,7 +221,7 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
tc := tc
serviceMock.ExpectedServiceAccountProfileDTO = tc.expectedServiceAccount
serviceMock.ExpectedServiceAccountProfile = tc.expectedServiceAccount
_, err := svc.UpdateServiceAccount(context.Background(), testOrgId, testServiceAccountId, &tc.form)
assert.Equal(t, tc.expectedError, err, tc.description)
})
@ -250,7 +260,7 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
tc := tc
serviceMock.ExpectedServiceAccountProfileDTO = tc.expectedServiceAccount
serviceMock.ExpectedServiceAccountProfile = tc.expectedServiceAccount
_, err := svc.AddServiceAccountToken(context.Background(), testServiceAccountId, &tc.cmd)
assert.Equal(t, tc.expectedError, err, tc.description)
})
@ -264,28 +274,3 @@ func TestProvideServiceAccount_crudServiceAccount(t *testing.T) {
assert.True(t, isExternalServiceAccount("sa-extsvc-my-service-account"))
})
}
func TestProvideServiceAccount_SearchServiceAccount(t *testing.T) {
serviceMock := newServiceAccountServiceFake()
svc := ServiceAccountsProxy{
log.New("test"),
serviceMock,
}
t.Run("should mark external service accounts correctly", func(t *testing.T) {
serviceMock.ExpectedSearchOrgServiceAccountsResult = &serviceaccounts.SearchOrgServiceAccountsResult{
TotalCount: 2,
ServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
{Login: "test"},
{Login: serviceaccounts.ServiceAccountPrefix + serviceaccounts.ExtSvcPrefix + "test"},
},
Page: 1,
PerPage: 2,
}
res, err := svc.SearchOrgServiceAccounts(context.Background(), &serviceaccounts.SearchOrgServiceAccountsQuery{OrgID: 1})
require.Len(t, res.ServiceAccounts, 2)
require.NoError(t, err)
require.False(t, res.ServiceAccounts[0].IsExternal)
require.True(t, res.ServiceAccounts[1].IsExternal)
})
}

View File

@ -13,13 +13,23 @@ Service accounts are used to authenticate API requests. They are not users and
do not have a password.
*/
type Service interface {
AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *AddServiceAccountTokenCommand) (*apikey.APIKey, error)
CreateServiceAccount(ctx context.Context, orgID int64, saForm *CreateServiceAccountForm) (*ServiceAccountDTO, error)
DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error
RetrieveServiceAccount(ctx context.Context, orgID, serviceAccountID int64) (*ServiceAccountProfileDTO, error)
RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error)
SearchOrgServiceAccounts(ctx context.Context, query *SearchOrgServiceAccountsQuery) (*SearchOrgServiceAccountsResult, error)
UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64, saForm *UpdateServiceAccountForm) (*ServiceAccountProfileDTO, error)
UpdateServiceAccount(ctx context.Context, orgID, serviceAccountID int64,
saForm *UpdateServiceAccountForm) (*ServiceAccountProfileDTO, error)
// Tokens
AddServiceAccountToken(ctx context.Context, serviceAccountID int64,
cmd *AddServiceAccountTokenCommand) (*apikey.APIKey, error)
DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error
ListTokens(ctx context.Context, query *GetSATokensQuery) ([]apikey.APIKey, error)
// API specific functions
MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error
MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*MigrationResult, error)
}
//go:generate mockery --name ExtSvcAccountsService --structname MockExtSvcAccountsService --output tests --outpkg tests --filename extsvcaccmock.go

View File

@ -0,0 +1,69 @@
package tests
import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
)
type FakeServiceAccountService struct {
ExpectedAPIKey *apikey.APIKey
ExpectedErr error
ExpectedMigrationResult *serviceaccounts.MigrationResult
ExpectedSearchOrgServiceAccountsResult *serviceaccounts.SearchOrgServiceAccountsResult
ExpectedServiceAccount *serviceaccounts.ServiceAccountDTO
ExpectedServiceAccountID int64
ExpectedServiceAccountProfile *serviceaccounts.ServiceAccountProfileDTO
ExpectedServiceAccountTokens []apikey.APIKey
}
var _ serviceaccounts.Service = new(FakeServiceAccountService)
func (f *FakeServiceAccountService) AddServiceAccountToken(ctx context.Context, id int64, cmd *serviceaccounts.AddServiceAccountTokenCommand) (*apikey.APIKey, error) {
return f.ExpectedAPIKey, f.ExpectedErr
}
func (f *FakeServiceAccountService) CreateServiceAccount(ctx context.Context, orgID int64, saForm *serviceaccounts.CreateServiceAccountForm) (*serviceaccounts.ServiceAccountDTO, error) {
return f.ExpectedServiceAccount, f.ExpectedErr
}
func (f *FakeServiceAccountService) DeleteServiceAccount(ctx context.Context, orgID, id int64) error {
return f.ExpectedErr
}
func (f *FakeServiceAccountService) RetrieveServiceAccount(ctx context.Context, orgID, id int64) (*serviceaccounts.ServiceAccountProfileDTO, error) {
return f.ExpectedServiceAccountProfile, f.ExpectedErr
}
func (f *FakeServiceAccountService) RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error) {
return f.ExpectedServiceAccountID, f.ExpectedErr
}
func (f *FakeServiceAccountService) UpdateServiceAccount(ctx context.Context, orgID, id int64, cmd *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) {
return f.ExpectedServiceAccountProfile, f.ExpectedErr
}
func (f *FakeServiceAccountService) ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error) {
return f.ExpectedServiceAccountTokens, f.ExpectedErr
}
func (f *FakeServiceAccountService) MigrateApiKey(ctx context.Context, orgID, keyID int64) error {
return f.ExpectedErr
}
func (f *FakeServiceAccountService) MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error) {
fmt.Printf("fake migration result: %v", f.ExpectedMigrationResult)
return f.ExpectedMigrationResult, f.ExpectedErr
}
func (f *FakeServiceAccountService) SearchOrgServiceAccounts(ctx context.Context, query *serviceaccounts.SearchOrgServiceAccountsQuery) (*serviceaccounts.SearchOrgServiceAccountsResult, error) {
return f.ExpectedSearchOrgServiceAccountsResult, f.ExpectedErr
}
// Service account tokens
func (f *FakeServiceAccountService) DeleteServiceAccountToken(ctx context.Context, orgID, id, tokenID int64) error {
return f.ExpectedErr
}

View File

@ -33,6 +33,30 @@ func (s *MockServiceAccountService) DeleteServiceAccount(ctx context.Context, or
return mockedArgs.Error(0)
}
// DeleteServiceAccountToken implements serviceaccounts.Service
func (s *MockServiceAccountService) DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error {
mockedArgs := s.Called(ctx, orgID, serviceAccountID, tokenID)
return mockedArgs.Error(0)
}
// ListTokens implements serviceaccounts.Service
func (s *MockServiceAccountService) ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error) {
mockedArgs := s.Called(ctx, query)
return mockedArgs.Get(0).([]apikey.APIKey), mockedArgs.Error(1)
}
// MigrateApiKey implements serviceaccounts.Service
func (s *MockServiceAccountService) MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error {
mockedArgs := s.Called(ctx, orgID, keyId)
return mockedArgs.Error(0)
}
// MigrateApiKeysToServiceAccounts implements serviceaccounts.Service
func (s *MockServiceAccountService) MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error) {
mockedArgs := s.Called(ctx, orgID)
return mockedArgs.Get(0).(*serviceaccounts.MigrationResult), mockedArgs.Error(1)
}
// RetrieveServiceAccount implements serviceaccounts.Service
func (s *MockServiceAccountService) RetrieveServiceAccount(ctx context.Context, orgID int64, serviceAccountID int64) (*serviceaccounts.ServiceAccountProfileDTO, error) {
mockedArgs := s.Called(ctx, orgID, serviceAccountID)
@ -45,13 +69,14 @@ func (s *MockServiceAccountService) RetrieveServiceAccountIdByName(ctx context.C
return mockedArgs.Get(0).(int64), mockedArgs.Error(1)
}
// SearchOrgServiceAccounts implements serviceaccounts.Service
func (s *MockServiceAccountService) SearchOrgServiceAccounts(ctx context.Context, query *serviceaccounts.SearchOrgServiceAccountsQuery) (*serviceaccounts.SearchOrgServiceAccountsResult, error) {
mockedArgs := s.Called(ctx, query)
return mockedArgs.Get(0).(*serviceaccounts.SearchOrgServiceAccountsResult), mockedArgs.Error(1)
}
// UpdateServiceAccount implements serviceaccounts.Service
func (s *MockServiceAccountService) UpdateServiceAccount(ctx context.Context, orgID int64, serviceAccountID int64, saForm *serviceaccounts.UpdateServiceAccountForm) (*serviceaccounts.ServiceAccountProfileDTO, error) {
mockedArgs := s.Called(ctx, orgID, serviceAccountID)
return mockedArgs.Get(0).(*serviceaccounts.ServiceAccountProfileDTO), mockedArgs.Error(1)
}
func (s *MockServiceAccountService) SearchOrgServiceAccounts(ctx context.Context, query *serviceaccounts.SearchOrgServiceAccountsQuery) (*serviceaccounts.SearchOrgServiceAccountsResult, error) {
mockedArgs := s.Called(ctx, query)
return mockedArgs.Get(0).(*serviceaccounts.SearchOrgServiceAccountsResult), mockedArgs.Error(1)
}