mirror of
https://github.com/grafana/grafana.git
synced 2024-11-23 01:16:31 -06:00
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:
parent
c25ea17d10
commit
1bc81b7bd1
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
|
69
pkg/services/serviceaccounts/tests/fakes.go
Normal file
69
pkg/services/serviceaccounts/tests/fakes.go
Normal 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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user