Auth: Remove api key endpoints (#106019)

* remove api key endpoints

* generate openapi specs

* remove methods from mock service

* remove ApiKeyDTO

* generate openapi specs

* remove apikey migration endpoints

* remove unused function
This commit is contained in:
Mihai Doarna
2025-06-04 17:03:06 +03:00
committed by GitHub
parent e78da0cc39
commit d57d184d20
25 changed files with 0 additions and 930 deletions
-4
View File
@@ -5,13 +5,9 @@ import (
)
type Service interface {
GetAPIKeys(ctx context.Context, query *GetApiKeysQuery) (res []*APIKey, err error)
GetAllAPIKeys(ctx context.Context, orgID int64) ([]*APIKey, error)
DeleteApiKey(ctx context.Context, cmd *DeleteCommand) error
AddAPIKey(ctx context.Context, cmd *AddCommand) (res *APIKey, err error)
GetApiKeyByName(ctx context.Context, query *GetByNameQuery) (res *APIKey, err error)
GetAPIKeyByHash(ctx context.Context, hash string) (*APIKey, error)
UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error
// IsDisabled returns true if the API key is not available for use.
IsDisabled(ctx context.Context, orgID int64) (bool, error)
}
-16
View File
@@ -37,9 +37,6 @@ func (s *Service) Usage(ctx context.Context, scopeParams *quota.ScopeParameters)
return s.store.Count(ctx, scopeParams)
}
func (s *Service) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) ([]*apikey.APIKey, error) {
return s.store.GetAPIKeys(ctx, query)
}
func (s *Service) GetAllAPIKeys(ctx context.Context, orgID int64) ([]*apikey.APIKey, error) {
return s.store.GetAllAPIKeys(ctx, orgID)
}
@@ -49,9 +46,6 @@ func (s *Service) GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQu
func (s *Service) GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) {
return s.store.GetAPIKeyByHash(ctx, hash)
}
func (s *Service) DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error {
return s.store.DeleteApiKey(ctx, cmd)
}
func (s *Service) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) (res *apikey.APIKey, err error) {
return s.store.AddAPIKey(ctx, cmd)
}
@@ -59,16 +53,6 @@ func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) e
return s.store.UpdateAPIKeyLastUsedDate(ctx, tokenID)
}
// IsDisabled returns true if the apikey service is disabled for the given org.
// This is the case if the org has no apikeys.
func (s *Service) IsDisabled(ctx context.Context, orgID int64) (bool, error) {
apikeys, err := s.store.CountAPIKeys(ctx, orgID)
if err != nil {
return false, err
}
return apikeys == 0, nil
}
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
-3
View File
@@ -8,10 +8,7 @@ import (
)
type store interface {
GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) (res []*apikey.APIKey, err error)
GetAllAPIKeys(ctx context.Context, orgID int64) ([]*apikey.APIKey, error)
CountAPIKeys(ctx context.Context, orgID int64) (int64, error)
DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error
AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) (res *apikey.APIKey, err error)
GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) (res *apikey.APIKey, err error)
GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error)
@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/tests/testsuite"
@@ -26,7 +25,6 @@ type getStore func(db.DB) store
type getApiKeysTestCase struct {
desc string
user identity.Requester
expectedNumKeys int
expectedAllNumKeys int
}
@@ -86,12 +84,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
assert.Nil(t, err)
assert.NotNil(t, key)
})
t.Run("Should be able to delete key by id", func(t *testing.T) {
key, err := ss.GetAPIKeyByHash(context.Background(), cmd.Key)
assert.NoError(t, err)
err = ss.DeleteApiKey(context.Background(), &apikey.DeleteCommand{ID: key.ID, OrgID: key.OrgID})
assert.NoError(t, err)
})
})
t.Run("Add non expiring key", func(t *testing.T) {
@@ -171,34 +163,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
// advance mocked getTime by 1s
timeNow()
testUser := &user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {accesscontrol.ActionAPIKeyRead: []string{accesscontrol.ScopeAPIKeysAll}},
},
}
query := apikey.GetApiKeysQuery{OrgID: 1, IncludeExpired: false, User: testUser}
keys, err := ss.GetAPIKeys(context.Background(), &query)
assert.Nil(t, err)
for _, k := range keys {
if k.Name == "key2" {
t.Fatalf("key2 should not be there")
}
}
query = apikey.GetApiKeysQuery{OrgID: 1, IncludeExpired: true, User: testUser}
keys, err = ss.GetAPIKeys(context.Background(), &query)
assert.Nil(t, err)
found := false
for _, k := range keys {
if k.Name == "key2" {
found = true
}
}
assert.True(t, found)
})
})
@@ -206,13 +170,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
db := db.InitTestDB(t)
ss := fn(db)
t.Run("Delete non-existing key should return error", func(t *testing.T) {
cmd := apikey.DeleteCommand{ID: 1}
err := ss.DeleteApiKey(context.Background(), &cmd)
assert.EqualError(t, err, apikey.ErrNotFound.Error())
})
t.Run("Testing API Duplicate Key Errors", func(t *testing.T) {
t.Run("Given saved api key", func(t *testing.T) {
cmd := apikey.AddCommand{OrgID: 0, Name: "duplicate", Key: "asd"}
@@ -235,7 +192,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
user: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"apikeys:read": {"apikeys:*"}},
}},
expectedNumKeys: 10,
expectedAllNumKeys: 10,
},
{
@@ -243,7 +199,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
user: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"apikeys:read": {"apikeys:id:1", "apikeys:id:3"}},
}},
expectedNumKeys: 2,
expectedAllNumKeys: 10,
},
{
@@ -251,7 +206,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
user: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"apikeys:read": {}},
}},
expectedNumKeys: 0,
expectedAllNumKeys: 10,
},
}
@@ -262,11 +216,6 @@ func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
store := fn(db)
seedApiKeys(t, store, 10)
query := &apikey.GetApiKeysQuery{OrgID: 1, User: tt.user}
keys, err := store.GetAPIKeys(context.Background(), query)
require.NoError(t, err)
assert.Len(t, keys, tt.expectedNumKeys)
res, err := store.GetAllAPIKeys(context.Background(), 1)
require.NoError(t, err)
assert.Equal(t, tt.expectedAllNumKeys, len(res))
@@ -5,10 +5,7 @@ import (
"fmt"
"time"
"github.com/grafana/grafana/pkg/util/xorm"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
@@ -21,34 +18,6 @@ type sqlStore struct {
// timeNow makes it possible to test usage of time
var timeNow = time.Now
func (ss *sqlStore) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) (res []*apikey.APIKey, err error) {
err = ss.db.WithDbSession(ctx, func(dbSession *db.Session) error {
var sess *xorm.Session
if query.IncludeExpired {
sess = dbSession.Limit(100, 0).
Where("org_id=?", query.OrgID).
Asc("name")
} else {
sess = dbSession.Limit(100, 0).
Where("org_id=? and ( expires IS NULL or expires >= ?)", query.OrgID, timeNow().Unix()).
Asc("name")
}
sess = sess.Where("service_account_id IS NULL")
filter, err := accesscontrol.Filter(query.User, "id", "apikeys:id:", accesscontrol.ActionAPIKeyRead)
if err != nil {
return err
}
sess.And(filter.Where, filter.Args...)
res = make([]*apikey.APIKey, 0)
return sess.Find(&res)
})
return res, err
}
func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) ([]*apikey.APIKey, error) {
result := make([]*apikey.APIKey, 0)
err := ss.db.WithDbSession(ctx, func(dbSession *db.Session) error {
@@ -61,42 +30,6 @@ func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) ([]*apikey.A
return result, err
}
func (ss *sqlStore) CountAPIKeys(ctx context.Context, orgID int64) (int64, error) {
type result struct {
Count int64
}
r := result{}
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) AS count FROM api_key WHERE org_id = ? and service_account_id IS NULL"
if _, err := sess.SQL(rawSQL, orgID).Get(&r); err != nil {
return err
}
return nil
})
if err != nil {
return 0, err
}
return r.Count, err
}
func (ss *sqlStore) DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error {
return ss.db.WithDbSession(ctx, func(sess *db.Session) error {
rawSQL := "DELETE FROM api_key WHERE id=? and org_id=? and service_account_id IS NULL"
result, err := sess.Exec(rawSQL, cmd.ID, cmd.OrgID)
if err != nil {
return err
}
n, err := result.RowsAffected()
if err != nil {
return err
} else if n == 0 {
return apikey.ErrNotFound
}
return nil
})
}
func (ss *sqlStore) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) (res *apikey.APIKey, err error) {
err = ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
key := apikey.APIKey{OrgID: cmd.OrgID, Name: cmd.Name}
-10
View File
@@ -8,14 +8,10 @@ import (
type Service struct {
ExpectedError error
ExpectedBool bool
ExpectedAPIKeys []*apikey.APIKey
ExpectedAPIKey *apikey.APIKey
}
func (s *Service) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) ([]*apikey.APIKey, error) {
return s.ExpectedAPIKeys, s.ExpectedError
}
func (s *Service) GetAllAPIKeys(ctx context.Context, orgID int64) ([]*apikey.APIKey, error) {
return s.ExpectedAPIKeys, s.ExpectedError
}
@@ -25,15 +21,9 @@ func (s *Service) GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQu
func (s *Service) GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) {
return s.ExpectedAPIKey, s.ExpectedError
}
func (s *Service) DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error {
return s.ExpectedError
}
func (s *Service) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) (*apikey.APIKey, error) {
return s.ExpectedAPIKey, s.ExpectedError
}
func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error {
return s.ExpectedError
}
func (s *Service) IsDisabled(ctx context.Context, orgID int64) (bool, error) {
return s.ExpectedBool, s.ExpectedError
}
-11
View File
@@ -4,7 +4,6 @@ import (
"errors"
"time"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
)
@@ -42,16 +41,6 @@ type AddCommand struct {
ServiceAccountID *int64 `json:"-"`
}
type DeleteCommand struct {
ID int64 `json:"id"`
OrgID int64 `json:"-"`
}
type GetApiKeysQuery struct {
OrgID int64
IncludeExpired bool
User identity.Requester
}
type GetByNameQuery struct {
KeyName string
OrgID int64
-13
View File
@@ -154,19 +154,6 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
Url: s.cfg.AppSubURL + "/org/serviceaccounts",
})
}
disabled, err := s.apiKeyService.IsDisabled(ctx, c.GetOrgID())
if err != nil {
return nil, err
}
if hasAccess(ac.ApiKeyAccessEvaluator) && !disabled {
accessNodeLinks = append(accessNodeLinks, &navtree.NavLink{
Text: "API keys",
Id: "apikeys",
SubTitle: "Manage and create API keys that are used to interact with Grafana HTTP APIs",
Icon: "key-skeleton-alt",
Url: s.cfg.AppSubURL + "/org/apikeys",
})
}
if s.license.FeatureEnabled("groupsync") &&
s.features.IsEnabled(ctx, featuremgmt.FlagGroupAttributeSync) &&
-26
View File
@@ -65,8 +65,6 @@ func (api *ServiceAccountsAPI) RegisterAPIEndpoints() {
serviceAccountsRoute.Get("/:serviceAccountId/tokens", saUIDResolver, auth(accesscontrol.EvalPermission(serviceaccounts.ActionRead, serviceaccounts.ScopeID)), routing.Wrap(api.ListTokens))
serviceAccountsRoute.Post("/:serviceAccountId/tokens", saUIDResolver, auth(accesscontrol.EvalPermission(serviceaccounts.ActionWrite, serviceaccounts.ScopeID)), routing.Wrap(api.CreateToken))
serviceAccountsRoute.Delete("/:serviceAccountId/tokens/:tokenId", saUIDResolver, auth(accesscontrol.EvalPermission(serviceaccounts.ActionWrite, serviceaccounts.ScopeID)), routing.Wrap(api.DeleteToken))
serviceAccountsRoute.Post("/migrate", auth(accesscontrol.EvalPermission(serviceaccounts.ActionCreate)), routing.Wrap(api.MigrateApiKeysToServiceAccounts))
serviceAccountsRoute.Post("/migrate/:keyId", auth(accesscontrol.EvalPermission(serviceaccounts.ActionCreate)), routing.Wrap(api.ConvertToServiceAccount))
}, requestmeta.SetOwner(requestmeta.TeamAuth))
}
@@ -302,30 +300,6 @@ func (api *ServiceAccountsAPI) SearchOrgServiceAccountsWithPaging(c *contextmode
return response.JSON(http.StatusOK, serviceAccountSearch)
}
// POST /api/serviceaccounts/migrate
func (api *ServiceAccountsAPI) MigrateApiKeysToServiceAccounts(ctx *contextmodel.ReqContext) response.Response {
results, err := api.service.MigrateApiKeysToServiceAccounts(ctx.Req.Context(), ctx.GetOrgID())
if err != nil {
return response.JSON(http.StatusInternalServerError, results)
}
return response.JSON(http.StatusOK, results)
}
// POST /api/serviceaccounts/migrate/:keyId
func (api *ServiceAccountsAPI) ConvertToServiceAccount(ctx *contextmodel.ReqContext) response.Response {
keyId, err := strconv.ParseInt(web.Params(ctx.Req)[":keyId"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "Key ID is invalid", err)
}
if err := api.service.MigrateApiKey(ctx.Req.Context(), ctx.GetOrgID(), keyId); err != nil {
return response.Error(http.StatusInternalServerError, "Error converting API key", err)
}
return response.Success("Service accounts migrated")
}
func (api *ServiceAccountsAPI) getAccessControlMetadata(c *contextmodel.ReqContext, saIDs map[string]bool) map[string]accesscontrol.Metadata {
if !c.QueryBool("accesscontrol") {
return map[string]accesscontrol.Metadata{}
@@ -2,7 +2,6 @@ package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
@@ -239,66 +238,6 @@ func TestServiceAccountsAPI_UpdateServiceAccount(t *testing.T) {
}
}
func TestServiceAccountsAPI_MigrateApiKeysToServiceAccounts(t *testing.T) {
type TestCase struct {
desc string
orgId int64
basicRole org.RoleType
permissions []accesscontrol.Permission
expectedMigrationResult *serviceaccounts.MigrationResult
expectedCode int
}
tests := []TestCase{
{
desc: "should be able to migrate API keys to service accounts with correct permissions",
orgId: 1,
basicRole: org.RoleAdmin,
permissions: []accesscontrol.Permission{
{Action: serviceaccounts.ActionCreate, Scope: serviceaccounts.ScopeAll},
},
expectedMigrationResult: &serviceaccounts.MigrationResult{
Total: 5,
Migrated: 4,
Failed: 1,
FailedDetails: []string{"API key name: failedKey - Error: migration error"},
},
expectedCode: http.StatusOK,
},
{
desc: "should not be able to migrate API keys to service accounts with wrong permissions",
orgId: 2,
basicRole: org.RoleAdmin,
permissions: []accesscontrol.Permission{
{Action: serviceaccounts.ActionCreate, Scope: serviceaccounts.ScopeAll},
},
expectedCode: http.StatusForbidden,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
server := setupTests(t, func(a *ServiceAccountsAPI) {
a.service = &satests.FakeServiceAccountService{ExpectedMigrationResult: tt.expectedMigrationResult}
})
req := server.NewRequest(http.MethodPost, "/api/serviceaccounts/migrate", nil)
webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgRole: tt.basicRole, OrgID: tt.orgId, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByActionContext(context.Background(), tt.permissions)}})
res, err := server.SendJSON(req)
require.NoError(t, err)
assert.Equal(t, tt.expectedCode, res.StatusCode)
if tt.expectedCode == http.StatusOK {
var result serviceaccounts.MigrationResult
err := json.NewDecoder(res.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, tt.expectedMigrationResult, &result)
}
require.NoError(t, res.Body.Close())
})
}
}
func setupTests(t *testing.T, opts ...func(a *ServiceAccountsAPI)) *webtest.Server {
t.Helper()
cfg := setting.NewCfg()
@@ -480,26 +480,6 @@ func (s *ServiceAccountsStoreImpl) MigrateApiKeysToServiceAccounts(ctx context.C
return migrationResult, nil
}
func (s *ServiceAccountsStoreImpl) MigrateApiKey(ctx context.Context, orgId int64, keyId int64) error {
basicKeys, err := s.apiKeyService.GetAllAPIKeys(ctx, orgId)
if err != nil {
return err
}
if len(basicKeys) == 0 {
return fmt.Errorf("no API keys to convert found")
}
for _, key := range basicKeys {
if keyId == key.ID {
err := s.CreateServiceAccountFromApikey(ctx, key)
if err != nil {
s.log.Error("Converting to service account failed with error", "keyId", keyId, "error", err)
return err
}
}
}
return nil
}
func (s *ServiceAccountsStoreImpl) CreateServiceAccountFromApikey(ctx context.Context, key *apikey.APIKey) error {
prefix := "sa-autogen"
cmd := user.CreateUserCommand{
@@ -2,7 +2,6 @@ package database
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -320,129 +319,6 @@ func TestIntegrationStore_RetrieveServiceAccount(t *testing.T) {
}
}
func TestIntegrationStore_MigrateApiKeys(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode")
}
cases := []struct {
desc string
serviceAccounts []user.CreateUserCommand
key tests.TestApiKey
expectedLogin string
expectedErr error
}{
{
desc: "api key should be migrated to service account token",
serviceAccounts: []user.CreateUserCommand{},
key: tests.TestApiKey{Name: "test1", Role: org.RoleEditor, OrgId: 1},
expectedLogin: "sa-AuToGeN-1-test1", // Using mixed-case to test case-insensitive search.
expectedErr: nil,
},
{
desc: "api key should be migrated to service account token on second attempt",
serviceAccounts: []user.CreateUserCommand{
{Login: "sa-autogen-1-test2"},
},
key: tests.TestApiKey{Name: "test2", Role: org.RoleEditor, OrgId: 1},
expectedLogin: "sa-AuToGeN-1-test2-001", // Using mixed-case to test case-insensitive search.
expectedErr: nil,
},
{
desc: "api key should be migrated to service account token on last attempt (the 10th)",
serviceAccounts: []user.CreateUserCommand{
{Login: "sa-autogen-1-test3"},
{Login: "sa-autogen-1-test3-001"},
{Login: "sa-autogen-1-test3-002"},
{Login: "sa-autogen-1-test3-003"},
{Login: "sa-autogen-1-test3-004"},
{Login: "sa-autogen-1-test3-005"},
{Login: "sa-autogen-1-test3-006"},
{Login: "sa-autogen-1-test3-007"},
{Login: "sa-autogen-1-test3-008"},
{Login: "sa-autogen-1-test3-009"},
},
key: tests.TestApiKey{Name: "test3", Role: org.RoleEditor, OrgId: 1},
expectedLogin: "sa-AuToGeN-1-test3-010", // Using mixed-case to test case-insensitive search.
expectedErr: nil,
},
{
desc: "api key should not be migrated to service account token because all attempts failed",
serviceAccounts: []user.CreateUserCommand{
{Login: "sa-autogen-1-test4"},
{Login: "sa-autogen-1-test4-001"},
{Login: "sa-autogen-1-test4-002"},
{Login: "sa-autogen-1-test4-003"},
{Login: "sa-autogen-1-test4-004"},
{Login: "sa-autogen-1-test4-005"},
{Login: "sa-autogen-1-test4-006"},
{Login: "sa-autogen-1-test4-007"},
{Login: "sa-autogen-1-test4-008"},
{Login: "sa-autogen-1-test4-009"},
{Login: "sa-autogen-1-test4-010"},
},
key: tests.TestApiKey{Name: "test4", Role: org.RoleEditor, OrgId: 1},
expectedErr: serviceaccounts.ErrServiceAccountAlreadyExists,
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
db, store := setupTestDatabase(t)
store.cfg.AutoAssignOrg = true
store.cfg.AutoAssignOrgId = 1
store.cfg.AutoAssignOrgRole = "Viewer"
_, err := store.orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: "main"})
require.NoError(t, err)
key := tests.SetupApiKey(t, db, store.cfg, c.key)
for _, sa := range c.serviceAccounts {
sa.IsServiceAccount = true
sa.OrgID = key.OrgID
_, err := store.userService.CreateServiceAccount(context.Background(), &sa)
require.NoError(t, err)
}
err = store.MigrateApiKey(context.Background(), key.OrgID, key.ID)
if c.expectedErr != nil {
require.ErrorIs(t, err, c.expectedErr)
} else {
require.NoError(t, err)
q := serviceaccounts.SearchOrgServiceAccountsQuery{
OrgID: key.OrgID,
Query: c.expectedLogin,
Page: 1,
Limit: 50,
SignedInUser: &user.SignedInUser{
UserID: 1,
OrgID: 1,
Permissions: map[int64]map[string][]string{
key.OrgID: {
"serviceaccounts:read": {"serviceaccounts:id:*"},
},
},
},
}
serviceAccounts, err := store.SearchOrgServiceAccounts(context.Background(), &q)
require.NoError(t, err)
require.Equal(t, int64(1), serviceAccounts.TotalCount)
saMigrated := serviceAccounts.ServiceAccounts[0]
require.Equal(t, string(key.Role), saMigrated.Role)
require.Equal(t, strings.ToLower(c.expectedLogin), saMigrated.Login)
tokens, err := store.ListTokens(context.Background(), &serviceaccounts.GetSATokensQuery{
OrgID: &key.OrgID,
ServiceAccountID: &saMigrated.Id,
})
require.NoError(t, err)
require.Len(t, tokens, 1)
}
})
}
}
func TestIntegrationStore_MigrateAllApiKeys(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode")
@@ -301,15 +301,6 @@ func (sa *ServiceAccountsService) DeleteServiceAccountToken(ctx context.Context,
return sa.store.DeleteServiceAccountToken(ctx, orgID, serviceAccountID, tokenID)
}
func (sa *ServiceAccountsService) MigrateApiKey(ctx context.Context, orgID, keyID int64) error {
if err := validOrgID(orgID); err != nil {
return err
}
if err := validAPIKeyID(keyID); err != nil {
return err
}
return sa.store.MigrateApiKey(ctx, orgID, keyID)
}
func (sa *ServiceAccountsService) MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error) {
if err := validOrgID(orgID); err != nil {
return nil, err
@@ -389,9 +380,3 @@ func validServiceAccountTokenID(tokenID int64) error {
}
return nil
}
func validAPIKeyID(apiKeyID int64) error {
if apiKeyID == 0 {
return serviceaccounts.ErrServiceAccountInvalidAPIKeyID.Errorf("invalid API key ID 0 has been specified")
}
return nil
}
@@ -75,11 +75,6 @@ func (f *FakeServiceAccountStore) MigrateApiKeysToServiceAccounts(ctx context.Co
return f.expectedMigratedResults, f.ExpectedError
}
// MigrateApiKey is a fake migrating an api key to a service account.
func (f *FakeServiceAccountStore) MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error {
return f.ExpectedError
}
// RevertApiKey is a fake reverting an api key to a service account.
func (f *FakeServiceAccountStore) RevertApiKey(ctx context.Context, saId int64, keyId int64) error {
return f.ExpectedError
@@ -15,7 +15,6 @@ type store interface {
EnableServiceAccount(ctx context.Context, orgID, serviceAccountID int64, enable bool) error
GetUsageMetrics(ctx context.Context) (*serviceaccounts.Stats, error)
ListTokens(ctx context.Context, query *serviceaccounts.GetSATokensQuery) ([]apikey.APIKey, error)
MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error
MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error)
RetrieveServiceAccount(ctx context.Context, query *serviceaccounts.GetServiceAccountQuery) (*serviceaccounts.ServiceAccountProfileDTO, error)
RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error)
@@ -123,10 +123,6 @@ func (s *ServiceAccountsProxy) ListTokens(ctx context.Context, query *serviceacc
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)
}
@@ -41,8 +41,6 @@ type Service interface {
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)
}
@@ -52,10 +52,6 @@ func (f *FakeServiceAccountService) ListTokens(ctx context.Context, query *servi
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) {
return f.ExpectedMigrationResult, f.ExpectedErr
}
@@ -137,20 +137,6 @@ func (_m *MockServiceAccountService) ListTokens(ctx context.Context, query *serv
return r0, r1
}
// MigrateApiKey provides a mock function with given fields: ctx, orgID, keyId
func (_m *MockServiceAccountService) MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error {
ret := _m.Called(ctx, orgID, keyId)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok {
r0 = rf(ctx, orgID, keyId)
} else {
r0 = ret.Error(0)
}
return r0
}
// MigrateApiKeysToServiceAccounts provides a mock function with given fields: ctx, orgID
func (_m *MockServiceAccountService) MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) (*serviceaccounts.MigrationResult, error) {
ret := _m.Called(ctx, orgID)