diff --git a/pkg/api/apikey.go b/pkg/api/apikey.go index bffc312dc23..e51447f6783 100644 --- a/pkg/api/apikey.go +++ b/pkg/api/apikey.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/components/apikeygen" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/web" ) @@ -26,7 +27,7 @@ import ( // 404: notFoundError // 500: internalServerError func (hs *HTTPServer) GetAPIKeys(c *models.ReqContext) response.Response { - query := models.GetApiKeysQuery{OrgId: c.OrgId, User: c.SignedInUser, IncludeExpired: c.QueryBool("includeExpired")} + query := apikey.GetApiKeysQuery{OrgId: c.OrgId, User: c.SignedInUser, IncludeExpired: c.QueryBool("includeExpired")} if err := hs.apiKeyService.GetAPIKeys(c.Req.Context(), &query); err != nil { return response.Error(500, "Failed to list api keys", err) @@ -75,11 +76,11 @@ func (hs *HTTPServer) DeleteAPIKey(c *models.ReqContext) response.Response { return response.Error(http.StatusBadRequest, "id is invalid", err) } - cmd := &models.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId} + cmd := &apikey.DeleteCommand{Id: id, OrgId: c.OrgId} err = hs.apiKeyService.DeleteApiKey(c.Req.Context(), cmd) if err != nil { var status int - if errors.Is(err, models.ErrApiKeyNotFound) { + if errors.Is(err, apikey.ErrNotFound) { status = 404 } else { status = 500 @@ -104,7 +105,7 @@ func (hs *HTTPServer) DeleteAPIKey(c *models.ReqContext) response.Response { // 409: conflictError // 500: internalServerError func (hs *HTTPServer) AddAPIKey(c *models.ReqContext) response.Response { - cmd := models.AddApiKeyCommand{} + cmd := apikey.AddCommand{} if err := web.Bind(c.Req, &cmd); err != nil { return response.Error(http.StatusBadRequest, "bad request data", err) } @@ -133,10 +134,10 @@ func (hs *HTTPServer) AddAPIKey(c *models.ReqContext) response.Response { cmd.Key = newKeyInfo.HashedKey if err := hs.apiKeyService.AddAPIKey(c.Req.Context(), &cmd); err != nil { - if errors.Is(err, models.ErrInvalidApiKeyExpiration) { + if errors.Is(err, apikey.ErrInvalidExpiration) { return response.Error(400, err.Error(), nil) } - if errors.Is(err, models.ErrDuplicateApiKey) { + if errors.Is(err, apikey.ErrDuplicate) { return response.Error(409, err.Error(), nil) } return response.Error(500, "Failed to add API Key", err) @@ -164,7 +165,7 @@ type GetAPIkeysParams struct { type AddAPIkeyParams struct { // in:body // required:true - Body models.AddApiKeyCommand + Body apikey.AddCommand } // swagger:parameters deleteAPIkey diff --git a/pkg/middleware/middleware_basic_auth_test.go b/pkg/middleware/middleware_basic_auth_test.go index f3f52c8f63e..3a9d07c108e 100644 --- a/pkg/middleware/middleware_basic_auth_test.go +++ b/pkg/middleware/middleware_basic_auth_test.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/contexthandler" "github.com/grafana/grafana/pkg/services/login/logintest" "github.com/grafana/grafana/pkg/services/user" @@ -29,7 +30,7 @@ func TestMiddlewareBasicAuth(t *testing.T) { keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") require.NoError(t, err) - sc.apiKeyService.ExpectedAPIKey = &models.ApiKey{OrgId: orgID, Role: models.ROLE_EDITOR, Key: keyhash} + sc.apiKeyService.ExpectedAPIKey = &apikey.APIKey{OrgId: orgID, Role: models.ROLE_EDITOR, Key: keyhash} authHeader := util.GetBasicAuthHeader("api_key", "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9") sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec() diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index caffc796fe2..ea5f8d8d79a 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -20,6 +20,7 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey/apikeytest" "github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/contexthandler" @@ -151,7 +152,7 @@ func TestMiddlewareContext(t *testing.T) { keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") require.NoError(t, err) - sc.apiKeyService.ExpectedAPIKey = &models.ApiKey{OrgId: orgID, Role: models.ROLE_EDITOR, Key: keyhash} + sc.apiKeyService.ExpectedAPIKey = &apikey.APIKey{OrgId: orgID, Role: models.ROLE_EDITOR, Key: keyhash} sc.fakeReq("GET", "/").withValidApiKey().exec() @@ -164,7 +165,7 @@ func TestMiddlewareContext(t *testing.T) { middlewareScenario(t, "Valid API key, but does not match DB hash", func(t *testing.T, sc *scenarioContext) { const keyhash = "Something_not_matching" - sc.apiKeyService.ExpectedAPIKey = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash} + sc.apiKeyService.ExpectedAPIKey = &apikey.APIKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash} sc.fakeReq("GET", "/").withValidApiKey().exec() @@ -179,7 +180,7 @@ func TestMiddlewareContext(t *testing.T) { require.NoError(t, err) expires := sc.contextHandler.GetTime().Add(-1 * time.Second).Unix() - sc.apiKeyService.ExpectedAPIKey = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash, Expires: &expires} + sc.apiKeyService.ExpectedAPIKey = &apikey.APIKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash, Expires: &expires} sc.fakeReq("GET", "/").withValidApiKey().exec() diff --git a/pkg/models/apikey.go b/pkg/models/apikey.go deleted file mode 100644 index 462238a3d13..00000000000 --- a/pkg/models/apikey.go +++ /dev/null @@ -1,64 +0,0 @@ -package models - -import ( - "errors" - "time" -) - -var ( - ErrApiKeyNotFound = errors.New("API key not found") - ErrInvalidApiKey = errors.New("invalid API key") - ErrInvalidApiKeyExpiration = errors.New("negative value for SecondsToLive") - ErrDuplicateApiKey = errors.New("API key, organization ID and name must be unique") -) - -type ApiKey struct { - Id int64 - OrgId int64 - Name string - Key string - Role RoleType - Created time.Time - Updated time.Time - LastUsedAt *time.Time `xorm:"last_used_at"` - Expires *int64 - ServiceAccountId *int64 -} - -// --------------------- -// COMMANDS -// swagger:model -type AddApiKeyCommand struct { - Name string `json:"name" binding:"Required"` - Role RoleType `json:"role" binding:"Required"` - OrgId int64 `json:"-"` - Key string `json:"-"` - SecondsToLive int64 `json:"secondsToLive"` - Result *ApiKey `json:"-"` -} - -type DeleteApiKeyCommand struct { - Id int64 `json:"id"` - OrgId int64 `json:"-"` -} - -// ---------------------- -// QUERIES - -type GetApiKeysQuery struct { - OrgId int64 - IncludeExpired bool - User *SignedInUser - Result []*ApiKey -} - -type GetApiKeyByNameQuery struct { - KeyName string - OrgId int64 - Result *ApiKey -} - -type GetApiKeyByIdQuery struct { - ApiKeyId int64 - Result *ApiKey -} diff --git a/pkg/services/apikey/apikey.go b/pkg/services/apikey/apikey.go index ba6f58b50e0..7e2bab5aa19 100644 --- a/pkg/services/apikey/apikey.go +++ b/pkg/services/apikey/apikey.go @@ -2,17 +2,15 @@ package apikey import ( "context" - - "github.com/grafana/grafana/pkg/models" ) type Service interface { - GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error - GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey - DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCommand) error - AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error - GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error - GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error - GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) + GetAPIKeys(ctx context.Context, query *GetApiKeysQuery) error + GetAllAPIKeys(ctx context.Context, orgID int64) []*APIKey + DeleteApiKey(ctx context.Context, cmd *DeleteCommand) error + AddAPIKey(ctx context.Context, cmd *AddCommand) error + GetApiKeyById(ctx context.Context, query *GetByIDQuery) error + GetApiKeyByName(ctx context.Context, query *GetByNameQuery) error + GetAPIKeyByHash(ctx context.Context, hash string) (*APIKey, error) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error } diff --git a/pkg/services/apikey/apikeyimpl/apikey.go b/pkg/services/apikey/apikeyimpl/apikey.go index ec0bd935500..5a4a703c89f 100644 --- a/pkg/services/apikey/apikeyimpl/apikey.go +++ b/pkg/services/apikey/apikeyimpl/apikey.go @@ -3,7 +3,6 @@ package apikeyimpl import ( "context" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/sqlstore/db" "github.com/grafana/grafana/pkg/setting" @@ -17,25 +16,25 @@ func ProvideService(db db.DB, cfg *setting.Cfg) apikey.Service { return &Service{store: &sqlStore{db: db, cfg: cfg}} } -func (s *Service) GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error { +func (s *Service) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error { return s.store.GetAPIKeys(ctx, query) } -func (s *Service) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey { +func (s *Service) GetAllAPIKeys(ctx context.Context, orgID int64) []*apikey.APIKey { return s.store.GetAllAPIKeys(ctx, orgID) } -func (s *Service) GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error { +func (s *Service) GetApiKeyById(ctx context.Context, query *apikey.GetByIDQuery) error { return s.store.GetApiKeyById(ctx, query) } -func (s *Service) GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error { +func (s *Service) GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error { return s.store.GetApiKeyByName(ctx, query) } -func (s *Service) GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) { +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 *models.DeleteApiKeyCommand) error { +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 *models.AddApiKeyCommand) error { +func (s *Service) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) error { return s.store.AddAPIKey(ctx, cmd) } func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error { diff --git a/pkg/services/apikey/apikeyimpl/store.go b/pkg/services/apikey/apikeyimpl/store.go index 8b427771988..3f4dd84802d 100644 --- a/pkg/services/apikey/apikeyimpl/store.go +++ b/pkg/services/apikey/apikeyimpl/store.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore/db" "github.com/grafana/grafana/pkg/setting" @@ -14,13 +14,13 @@ import ( ) type store interface { - GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error - GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey - DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCommand) error - AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error - GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error - GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error - GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) + GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error + GetAllAPIKeys(ctx context.Context, orgID int64) []*apikey.APIKey + DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error + AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) error + GetApiKeyById(ctx context.Context, query *apikey.GetByIDQuery) error + GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error + GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error } @@ -32,7 +32,7 @@ type sqlStore struct { // timeNow makes it possible to test usage of time var timeNow = time.Now -func (ss *sqlStore) GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error { +func (ss *sqlStore) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error { return ss.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { var sess *xorm.Session @@ -56,13 +56,13 @@ func (ss *sqlStore) GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuer sess.And(filter.Where, filter.Args...) } - query.Result = make([]*models.ApiKey, 0) + query.Result = make([]*apikey.APIKey, 0) return sess.Find(&query.Result) }) } -func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey { - result := make([]*models.ApiKey, 0) +func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) []*apikey.APIKey { + result := make([]*apikey.APIKey, 0) err := ss.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { sess := dbSession.Where("service_account_id IS NULL").Asc("name") if orgID != -1 { @@ -77,7 +77,7 @@ func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.Ap return result } -func (ss *sqlStore) DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCommand) error { +func (ss *sqlStore) DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error { return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) 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) @@ -88,18 +88,18 @@ func (ss *sqlStore) DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCo if err != nil { return err } else if n == 0 { - return models.ErrApiKeyNotFound + return apikey.ErrNotFound } return nil }) } -func (ss *sqlStore) AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error { +func (ss *sqlStore) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) error { return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { - key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name} + key := apikey.APIKey{OrgId: cmd.OrgId, Name: cmd.Name} exists, _ := sess.Get(&key) if exists { - return models.ErrDuplicateApiKey + return apikey.ErrDuplicate } updated := timeNow() @@ -108,10 +108,10 @@ func (ss *sqlStore) AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) v := updated.Add(time.Second * time.Duration(cmd.SecondsToLive)).Unix() expires = &v } else if cmd.SecondsToLive < 0 { - return models.ErrInvalidApiKeyExpiration + return apikey.ErrInvalidExpiration } - t := models.ApiKey{ + t := apikey.APIKey{ OrgId: cmd.OrgId, Name: cmd.Name, Role: cmd.Role, @@ -130,56 +130,56 @@ func (ss *sqlStore) AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) }) } -func (ss *sqlStore) GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error { +func (ss *sqlStore) GetApiKeyById(ctx context.Context, query *apikey.GetByIDQuery) error { return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { - var apikey models.ApiKey - has, err := sess.ID(query.ApiKeyId).Get(&apikey) + var key apikey.APIKey + has, err := sess.ID(query.ApiKeyId).Get(&key) if err != nil { return err } else if !has { - return models.ErrInvalidApiKey + return apikey.ErrInvalid } - query.Result = &apikey + query.Result = &key return nil }) } -func (ss *sqlStore) GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error { +func (ss *sqlStore) GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error { return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { - var apikey models.ApiKey - has, err := sess.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&apikey) + var key apikey.APIKey + has, err := sess.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&key) if err != nil { return err } else if !has { - return models.ErrInvalidApiKey + return apikey.ErrInvalid } - query.Result = &apikey + query.Result = &key return nil }) } -func (ss *sqlStore) GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) { - var apikey models.ApiKey +func (ss *sqlStore) GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) { + var key apikey.APIKey err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { - has, err := sess.Table("api_key").Where(fmt.Sprintf("%s = ?", ss.db.GetDialect().Quote("key")), hash).Get(&apikey) + has, err := sess.Table("api_key").Where(fmt.Sprintf("%s = ?", ss.db.GetDialect().Quote("key")), hash).Get(&key) if err != nil { return err } else if !has { - return models.ErrInvalidApiKey + return apikey.ErrInvalid } return nil }) - return &apikey, err + return &key, err } func (ss *sqlStore) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error { now := timeNow() return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { - if _, err := sess.Table("api_key").ID(tokenID).Cols("last_used_at").Update(&models.ApiKey{LastUsedAt: &now}); err != nil { + if _, err := sess.Table("api_key").ID(tokenID).Cols("last_used_at").Update(&apikey.APIKey{LastUsedAt: &now}); err != nil { return err } diff --git a/pkg/services/apikey/apikeyimpl/store_test.go b/pkg/services/apikey/apikeyimpl/store_test.go index c861338b6cd..23cc044b60f 100644 --- a/pkg/services/apikey/apikeyimpl/store_test.go +++ b/pkg/services/apikey/apikeyimpl/store_test.go @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/sqlstore" ) @@ -40,12 +41,12 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { ss := &sqlStore{db: db, cfg: db.Cfg} t.Run("Given saved api key", func(t *testing.T) { - cmd := models.AddApiKeyCommand{OrgId: 1, Name: "hello", Key: "asd"} + cmd := apikey.AddCommand{OrgId: 1, Name: "hello", Key: "asd"} err := ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) t.Run("Should be able to get key by name", func(t *testing.T) { - query := models.GetApiKeyByNameQuery{KeyName: "hello", OrgId: 1} + query := apikey.GetByNameQuery{KeyName: "hello", OrgId: 1} err = ss.GetApiKeyByName(context.Background(), &query) assert.Nil(t, err) @@ -61,11 +62,11 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { }) t.Run("Add non expiring key", func(t *testing.T) { - cmd := models.AddApiKeyCommand{OrgId: 1, Name: "non-expiring", Key: "asd1", SecondsToLive: 0} + cmd := apikey.AddCommand{OrgId: 1, Name: "non-expiring", Key: "asd1", SecondsToLive: 0} err := ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) - query := models.GetApiKeyByNameQuery{KeyName: "non-expiring", OrgId: 1} + query := apikey.GetByNameQuery{KeyName: "non-expiring", OrgId: 1} err = ss.GetApiKeyByName(context.Background(), &query) assert.Nil(t, err) @@ -74,11 +75,11 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { t.Run("Add an expiring key", func(t *testing.T) { // expires in one hour - cmd := models.AddApiKeyCommand{OrgId: 1, Name: "expiring-in-an-hour", Key: "asd2", SecondsToLive: 3600} + cmd := apikey.AddCommand{OrgId: 1, Name: "expiring-in-an-hour", Key: "asd2", SecondsToLive: 3600} err := ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) - query := models.GetApiKeyByNameQuery{KeyName: "expiring-in-an-hour", OrgId: 1} + query := apikey.GetByNameQuery{KeyName: "expiring-in-an-hour", OrgId: 1} err = ss.GetApiKeyByName(context.Background(), &query) assert.Nil(t, err) @@ -94,7 +95,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { t.Run("Last Used At datetime update", func(t *testing.T) { // expires in one hour - cmd := models.AddApiKeyCommand{OrgId: 1, Name: "last-update-at", Key: "asd3", SecondsToLive: 3600} + cmd := apikey.AddCommand{OrgId: 1, Name: "last-update-at", Key: "asd3", SecondsToLive: 3600} err := ss.AddAPIKey(context.Background(), &cmd) require.NoError(t, err) @@ -103,7 +104,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { err = ss.UpdateAPIKeyLastUsedDate(context.Background(), cmd.Result.Id) require.NoError(t, err) - query := models.GetApiKeyByNameQuery{KeyName: "last-update-at", OrgId: 1} + query := apikey.GetByNameQuery{KeyName: "last-update-at", OrgId: 1} err = ss.GetApiKeyByName(context.Background(), &query) assert.Nil(t, err) @@ -112,28 +113,28 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { t.Run("Add a key with negative lifespan", func(t *testing.T) { // expires in one day - cmd := models.AddApiKeyCommand{OrgId: 1, Name: "key-with-negative-lifespan", Key: "asd3", SecondsToLive: -3600} + cmd := apikey.AddCommand{OrgId: 1, Name: "key-with-negative-lifespan", Key: "asd3", SecondsToLive: -3600} err := ss.AddAPIKey(context.Background(), &cmd) - assert.EqualError(t, err, models.ErrInvalidApiKeyExpiration.Error()) + assert.EqualError(t, err, apikey.ErrInvalidExpiration.Error()) - query := models.GetApiKeyByNameQuery{KeyName: "key-with-negative-lifespan", OrgId: 1} + query := apikey.GetByNameQuery{KeyName: "key-with-negative-lifespan", OrgId: 1} err = ss.GetApiKeyByName(context.Background(), &query) assert.EqualError(t, err, "invalid API key") }) t.Run("Add keys", func(t *testing.T) { // never expires - cmd := models.AddApiKeyCommand{OrgId: 1, Name: "key1", Key: "key1", SecondsToLive: 0} + cmd := apikey.AddCommand{OrgId: 1, Name: "key1", Key: "key1", SecondsToLive: 0} err := ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) // expires in 1s - cmd = models.AddApiKeyCommand{OrgId: 1, Name: "key2", Key: "key2", SecondsToLive: 1} + cmd = apikey.AddCommand{OrgId: 1, Name: "key2", Key: "key2", SecondsToLive: 1} err = ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) // expires in one hour - cmd = models.AddApiKeyCommand{OrgId: 1, Name: "key3", Key: "key3", SecondsToLive: 3600} + cmd = apikey.AddCommand{OrgId: 1, Name: "key3", Key: "key3", SecondsToLive: 3600} err = ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) @@ -146,7 +147,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { 1: {accesscontrol.ActionAPIKeyRead: []string{accesscontrol.ScopeAPIKeysAll}}, }, } - query := models.GetApiKeysQuery{OrgId: 1, IncludeExpired: false, User: testUser} + query := apikey.GetApiKeysQuery{OrgId: 1, IncludeExpired: false, User: testUser} err = ss.GetAPIKeys(context.Background(), &query) assert.Nil(t, err) @@ -156,7 +157,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) { } } - query = models.GetApiKeysQuery{OrgId: 1, IncludeExpired: true, User: testUser} + query = apikey.GetApiKeysQuery{OrgId: 1, IncludeExpired: true, User: testUser} err = ss.GetAPIKeys(context.Background(), &query) assert.Nil(t, err) @@ -183,22 +184,22 @@ func TestIntegrationApiKeyErrors(t *testing.T) { ss := &sqlStore{db: db, cfg: db.Cfg} t.Run("Delete non-existing key should return error", func(t *testing.T) { - cmd := models.DeleteApiKeyCommand{Id: 1} + cmd := apikey.DeleteCommand{Id: 1} err := ss.DeleteApiKey(context.Background(), &cmd) - assert.EqualError(t, err, models.ErrApiKeyNotFound.Error()) + 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 := models.AddApiKeyCommand{OrgId: 0, Name: "duplicate", Key: "asd"} + cmd := apikey.AddCommand{OrgId: 0, Name: "duplicate", Key: "asd"} err := ss.AddAPIKey(context.Background(), &cmd) assert.Nil(t, err) t.Run("Add API Key with existing Org ID and Name", func(t *testing.T) { - cmd := models.AddApiKeyCommand{OrgId: 0, Name: "duplicate", Key: "asd"} + cmd := apikey.AddCommand{OrgId: 0, Name: "duplicate", Key: "asd"} err = ss.AddAPIKey(context.Background(), &cmd) - assert.EqualError(t, err, models.ErrDuplicateApiKey.Error()) + assert.EqualError(t, err, apikey.ErrDuplicate.Error()) }) }) }) @@ -245,7 +246,7 @@ func TestIntegrationSQLStore_GetAPIKeys(t *testing.T) { store := &sqlStore{db: db, cfg: db.Cfg} seedApiKeys(t, store, 10) - query := &models.GetApiKeysQuery{OrgId: 1, User: tt.user} + query := &apikey.GetApiKeysQuery{OrgId: 1, User: tt.user} err := store.GetAPIKeys(context.Background(), query) require.NoError(t, err) assert.Len(t, query.Result, tt.expectedNumKeys) @@ -257,7 +258,7 @@ func seedApiKeys(t *testing.T, store store, num int) { t.Helper() for i := 0; i < num; i++ { - err := store.AddAPIKey(context.Background(), &models.AddApiKeyCommand{ + err := store.AddAPIKey(context.Background(), &apikey.AddCommand{ Name: fmt.Sprintf("key:%d", i), Key: fmt.Sprintf("key:%d", i), OrgId: 1, diff --git a/pkg/services/apikey/apikeytest/fake.go b/pkg/services/apikey/apikeytest/fake.go index 9b9c7cef88e..360c1decec6 100644 --- a/pkg/services/apikey/apikeytest/fake.go +++ b/pkg/services/apikey/apikeytest/fake.go @@ -3,37 +3,37 @@ package apikeytest import ( "context" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" ) type Service struct { ExpectedError error - ExpectedAPIKeys []*models.ApiKey - ExpectedAPIKey *models.ApiKey + ExpectedAPIKeys []*apikey.APIKey + ExpectedAPIKey *apikey.APIKey } -func (s *Service) GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error { +func (s *Service) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error { query.Result = s.ExpectedAPIKeys return s.ExpectedError } -func (s *Service) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey { +func (s *Service) GetAllAPIKeys(ctx context.Context, orgID int64) []*apikey.APIKey { return s.ExpectedAPIKeys } -func (s *Service) GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error { +func (s *Service) GetApiKeyById(ctx context.Context, query *apikey.GetByIDQuery) error { query.Result = s.ExpectedAPIKey return s.ExpectedError } -func (s *Service) GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error { +func (s *Service) GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error { query.Result = s.ExpectedAPIKey return s.ExpectedError } -func (s *Service) GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) { +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 *models.DeleteApiKeyCommand) error { +func (s *Service) DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error { return s.ExpectedError } -func (s *Service) AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error { +func (s *Service) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) error { cmd.Result = s.ExpectedAPIKey return s.ExpectedError } diff --git a/pkg/services/apikey/model.go b/pkg/services/apikey/model.go index 72c1a026489..4d5fcc30d73 100644 --- a/pkg/services/apikey/model.go +++ b/pkg/services/apikey/model.go @@ -1,3 +1,62 @@ package apikey -// TODO: define all apikey models here +import ( + "errors" + "time" + + "github.com/grafana/grafana/pkg/models" +) + +var ( + ErrNotFound = errors.New("API key not found") + ErrInvalid = errors.New("invalid API key") + ErrInvalidExpiration = errors.New("negative value for SecondsToLive") + ErrDuplicate = errors.New("API key, organization ID and name must be unique") +) + +type APIKey struct { + Id int64 + OrgId int64 + Name string + Key string + Role models.RoleType + Created time.Time + Updated time.Time + LastUsedAt *time.Time `xorm:"last_used_at"` + Expires *int64 + ServiceAccountId *int64 +} + +func (k APIKey) TableName() string { return "api_key" } + +// swagger:model +type AddCommand struct { + Name string `json:"name" binding:"Required"` + Role models.RoleType `json:"role" binding:"Required"` + OrgId int64 `json:"-"` + Key string `json:"-"` + SecondsToLive int64 `json:"secondsToLive"` + Result *APIKey `json:"-"` +} + +type DeleteCommand struct { + Id int64 `json:"id"` + OrgId int64 `json:"-"` +} + +type GetApiKeysQuery struct { + OrgId int64 + IncludeExpired bool + User *models.SignedInUser + Result []*APIKey +} +type GetByNameQuery struct { + KeyName string + OrgId int64 + Result *APIKey +} + +type GetByIDQuery struct { + ApiKeyId int64 + Result *APIKey +} diff --git a/pkg/services/contexthandler/contexthandler.go b/pkg/services/contexthandler/contexthandler.go index 2aaa4881fe6..0f3d96f5c20 100644 --- a/pkg/services/contexthandler/contexthandler.go +++ b/pkg/services/contexthandler/contexthandler.go @@ -189,7 +189,7 @@ func (h *ContextHandler) initContextWithAnonymousUser(reqContext *models.ReqCont return true } -func (h *ContextHandler) getPrefixedAPIKey(ctx context.Context, keyString string) (*models.ApiKey, error) { +func (h *ContextHandler) getPrefixedAPIKey(ctx context.Context, keyString string) (*apikey.APIKey, error) { // prefixed decode key decoded, err := apikeygenprefix.Decode(keyString) if err != nil { @@ -204,14 +204,14 @@ func (h *ContextHandler) getPrefixedAPIKey(ctx context.Context, keyString string return h.apiKeyService.GetAPIKeyByHash(ctx, hash) } -func (h *ContextHandler) getAPIKey(ctx context.Context, keyString string) (*models.ApiKey, error) { +func (h *ContextHandler) getAPIKey(ctx context.Context, keyString string) (*apikey.APIKey, error) { decoded, err := apikeygen.Decode(keyString) if err != nil { return nil, err } // fetch key - keyQuery := models.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId} + keyQuery := apikey.GetByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId} if err := h.apiKeyService.GetApiKeyByName(ctx, &keyQuery); err != nil { return nil, err } @@ -249,7 +249,7 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo defer span.End() var ( - apikey *models.ApiKey + apikey *apikey.APIKey errKey error ) if strings.HasPrefix(keyString, apikeygenprefix.GrafanaPrefix) { diff --git a/pkg/services/serviceaccounts/api/token.go b/pkg/services/serviceaccounts/api/token.go index 068e08d9820..12f61f24850 100644 --- a/pkg/services/serviceaccounts/api/token.go +++ b/pkg/services/serviceaccounts/api/token.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" apikeygenprefix "github.com/grafana/grafana/pkg/components/apikeygenprefixed" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts/database" "github.com/grafana/grafana/pkg/web" @@ -215,10 +216,10 @@ func (api *ServiceAccountsAPI) DeleteToken(c *models.ReqContext) response.Respon if err = api.store.DeleteServiceAccountToken(c.Req.Context(), c.OrgId, saID, tokenID); err != nil { status := http.StatusNotFound - if err != nil && !errors.Is(err, models.ErrApiKeyNotFound) { + if err != nil && !errors.Is(err, apikey.ErrNotFound) { status = http.StatusInternalServerError } else { - err = models.ErrApiKeyNotFound + err = apikey.ErrNotFound } return response.Error(status, failedToDeleteMsg, err) diff --git a/pkg/services/serviceaccounts/api/token_test.go b/pkg/services/serviceaccounts/api/token_test.go index 8973bcf85c5..6ba0af898f2 100644 --- a/pkg/services/serviceaccounts/api/token_test.go +++ b/pkg/services/serviceaccounts/api/token_test.go @@ -18,6 +18,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts/database" @@ -33,7 +34,7 @@ const ( serviceaccountIDTokensDetailPath = "/api/serviceaccounts/%v/tokens/%v" // #nosec G101 ) -func createTokenforSA(t *testing.T, store serviceaccounts.Store, keyName string, orgID int64, saID int64, secondsToLive int64) *models.ApiKey { +func createTokenforSA(t *testing.T, store serviceaccounts.Store, keyName string, orgID int64, saID int64, secondsToLive int64) *apikey.APIKey { key, err := apikeygen.New(orgID, keyName) require.NoError(t, err) @@ -42,7 +43,7 @@ func createTokenforSA(t *testing.T, store serviceaccounts.Store, keyName string, OrgId: orgID, Key: key.HashedKey, SecondsToLive: secondsToLive, - Result: &models.ApiKey{}, + Result: &apikey.APIKey{}, } err = store.AddServiceAccountToken(context.Background(), saID, &cmd) @@ -148,7 +149,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) { if actualCode == http.StatusOK { assert.Equal(t, tc.body["name"], actualBody["name"]) - query := models.GetApiKeyByNameQuery{KeyName: tc.body["name"].(string), OrgId: sa.OrgID} + query := apikey.GetByNameQuery{KeyName: tc.body["name"].(string), OrgId: sa.OrgID} err = apiKeyService.GetApiKeyByName(context.Background(), &query) require.NoError(t, err) @@ -245,7 +246,7 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) { _ = json.Unmarshal(actual.Body.Bytes(), &actualBody) require.Equal(t, tc.expectedCode, actualCode, endpoint, actualBody) - query := models.GetApiKeyByNameQuery{KeyName: tc.keyName, OrgId: sa.OrgID} + query := apikey.GetByNameQuery{KeyName: tc.keyName, OrgId: sa.OrgID} err := apiKeyService.GetApiKeyByName(context.Background(), &query) if actualCode == http.StatusOK { require.Error(t, err) @@ -258,10 +259,10 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) { type saStoreMockTokens struct { serviceaccounts.Store - saAPIKeys []*models.ApiKey + saAPIKeys []*apikey.APIKey } -func (s *saStoreMockTokens) ListTokens(ctx context.Context, orgID, saID int64) ([]*models.ApiKey, error) { +func (s *saStoreMockTokens) ListTokens(ctx context.Context, orgID, saID int64) ([]*apikey.APIKey, error) { return s.saAPIKeys, nil } @@ -272,7 +273,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) { type testCreateSAToken struct { desc string - tokens []*models.ApiKey + tokens []*apikey.APIKey expectedHasExpired bool expectedResponseBodyField string expectedCode int @@ -286,7 +287,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) { testCases := []testCreateSAToken{ { desc: "should be able to list serviceaccount with no expiration date", - tokens: []*models.ApiKey{{ + tokens: []*apikey.APIKey{{ Id: 1, OrgId: 1, ServiceAccountId: &saId, @@ -306,7 +307,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) { }, { desc: "should be able to list serviceaccount with secondsUntilExpiration", - tokens: []*models.ApiKey{{ + tokens: []*apikey.APIKey{{ Id: 1, OrgId: 1, ServiceAccountId: &saId, @@ -326,7 +327,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) { }, { desc: "should be able to list serviceaccount with expired token", - tokens: []*models.ApiKey{{ + tokens: []*apikey.APIKey{{ Id: 1, OrgId: 1, ServiceAccountId: &saId, diff --git a/pkg/services/serviceaccounts/database/database.go b/pkg/services/serviceaccounts/database/database.go index d1d98f8a9b2..8ecce955b88 100644 --- a/pkg/services/serviceaccounts/database/database.go +++ b/pkg/services/serviceaccounts/database/database.go @@ -438,7 +438,7 @@ func (s *ServiceAccountsStoreImpl) MigrateApiKey(ctx context.Context, orgId int6 return nil } -func (s *ServiceAccountsStoreImpl) CreateServiceAccountFromApikey(ctx context.Context, key *models.ApiKey) error { +func (s *ServiceAccountsStoreImpl) CreateServiceAccountFromApikey(ctx context.Context, key *apikey.APIKey) error { prefix := "sa-autogen" cmd := user.CreateUserCommand{ Login: fmt.Sprintf("%v-%v-%v", prefix, key.OrgId, key.Name), @@ -467,7 +467,7 @@ func (s *ServiceAccountsStoreImpl) CreateServiceAccountFromApikey(ctx context.Co // RevertApiKey converts service account token to old API key func (s *ServiceAccountsStoreImpl) RevertApiKey(ctx context.Context, saId int64, keyId int64) error { - query := models.GetApiKeyByIdQuery{ApiKeyId: keyId} + query := apikey.GetByIDQuery{ApiKeyId: keyId} if err := s.apiKeyService.GetApiKeyById(ctx, &query); err != nil { return err } diff --git a/pkg/services/serviceaccounts/database/stats_test.go b/pkg/services/serviceaccounts/database/stats_test.go index 467c5e534d2..f6f5de36fc3 100644 --- a/pkg/services/serviceaccounts/database/stats_test.go +++ b/pkg/services/serviceaccounts/database/stats_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/grafana/grafana/pkg/components/apikeygen" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/stretchr/testify/assert" @@ -27,7 +27,7 @@ func TestStore_UsageStats(t *testing.T) { OrgId: sa.OrgID, Key: key.HashedKey, SecondsToLive: 0, - Result: &models.ApiKey{}, + Result: &apikey.APIKey{}, } err = store.AddServiceAccountToken(context.Background(), sa.ID, &cmd) diff --git a/pkg/services/serviceaccounts/database/token_store.go b/pkg/services/serviceaccounts/database/token_store.go index 13bea20fcd0..15fc34fac5c 100644 --- a/pkg/services/serviceaccounts/database/token_store.go +++ b/pkg/services/serviceaccounts/database/token_store.go @@ -5,13 +5,14 @@ import ( "time" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/sqlstore" "xorm.io/xorm" ) -func (s *ServiceAccountsStoreImpl) ListTokens(ctx context.Context, orgId int64, serviceAccountId int64) ([]*models.ApiKey, error) { - result := make([]*models.ApiKey, 0) +func (s *ServiceAccountsStoreImpl) ListTokens(ctx context.Context, orgId int64, serviceAccountId int64) ([]*apikey.APIKey, error) { + result := make([]*apikey.APIKey, 0) err := s.sqlStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { var sess *xorm.Session @@ -32,7 +33,7 @@ func (s *ServiceAccountsStoreImpl) AddServiceAccountToken(ctx context.Context, s return err } - key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name} + key := apikey.APIKey{OrgId: cmd.OrgId, Name: cmd.Name} exists, _ := sess.Get(&key) if exists { return ErrDuplicateToken @@ -47,7 +48,7 @@ func (s *ServiceAccountsStoreImpl) AddServiceAccountToken(ctx context.Context, s return ErrInvalidTokenExpiration } - token := models.ApiKey{ + token := apikey.APIKey{ OrgId: cmd.OrgId, Name: cmd.Name, Role: models.ROLE_VIEWER, @@ -86,7 +87,7 @@ func (s *ServiceAccountsStoreImpl) DeleteServiceAccountToken(ctx context.Context // assignApiKeyToServiceAccount sets the API key service account ID func (s *ServiceAccountsStoreImpl) assignApiKeyToServiceAccount(sess *sqlstore.DBSession, apiKeyId int64, serviceAccountId int64) error { - key := models.ApiKey{Id: apiKeyId} + key := apikey.APIKey{Id: apiKeyId} exists, err := sess.Get(&key) if err != nil { s.log.Warn("API key not loaded", "err", err) @@ -94,7 +95,7 @@ func (s *ServiceAccountsStoreImpl) assignApiKeyToServiceAccount(sess *sqlstore.D } if !exists { s.log.Warn("API key not found", "err", err) - return models.ErrApiKeyNotFound + return apikey.ErrNotFound } key.ServiceAccountId = &serviceAccountId @@ -108,7 +109,7 @@ func (s *ServiceAccountsStoreImpl) assignApiKeyToServiceAccount(sess *sqlstore.D // detachApiKeyFromServiceAccount converts service account token to old API key func (s *ServiceAccountsStoreImpl) detachApiKeyFromServiceAccount(sess *sqlstore.DBSession, apiKeyId int64) error { - key := models.ApiKey{Id: apiKeyId} + key := apikey.APIKey{Id: apiKeyId} exists, err := sess.Get(&key) if err != nil { s.log.Warn("Cannot get API key", "err", err) @@ -116,7 +117,7 @@ func (s *ServiceAccountsStoreImpl) detachApiKeyFromServiceAccount(sess *sqlstore } if !exists { s.log.Warn("API key not found", "err", err) - return models.ErrApiKeyNotFound + return apikey.ErrNotFound } key.ServiceAccountId = nil diff --git a/pkg/services/serviceaccounts/database/token_store_test.go b/pkg/services/serviceaccounts/database/token_store_test.go index 0976b7b658d..47eb4ab1be8 100644 --- a/pkg/services/serviceaccounts/database/token_store_test.go +++ b/pkg/services/serviceaccounts/database/token_store_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/grafana/grafana/pkg/components/apikeygen" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/stretchr/testify/require" @@ -34,7 +34,7 @@ func TestStore_AddServiceAccountToken(t *testing.T) { OrgId: user.OrgID, Key: key.HashedKey, SecondsToLive: tc.secondsToLive, - Result: &models.ApiKey{}, + Result: &apikey.APIKey{}, } err = store.AddServiceAccountToken(context.Background(), user.ID, &cmd) @@ -84,7 +84,7 @@ func TestStore_AddServiceAccountToken_WrongServiceAccount(t *testing.T) { OrgId: sa.OrgID, Key: key.HashedKey, SecondsToLive: 0, - Result: &models.ApiKey{}, + Result: &apikey.APIKey{}, } err = store.AddServiceAccountToken(context.Background(), sa.ID+1, &cmd) @@ -105,7 +105,7 @@ func TestStore_DeleteServiceAccountToken(t *testing.T) { OrgId: sa.OrgID, Key: key.HashedKey, SecondsToLive: 0, - Result: &models.ApiKey{}, + Result: &apikey.APIKey{}, } err = store.AddServiceAccountToken(context.Background(), sa.ID, &cmd) diff --git a/pkg/services/serviceaccounts/models.go b/pkg/services/serviceaccounts/models.go index 3a93348e1b8..dad902ba164 100644 --- a/pkg/services/serviceaccounts/models.go +++ b/pkg/services/serviceaccounts/models.go @@ -5,6 +5,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/apikey" ) var ( @@ -68,7 +69,7 @@ type AddServiceAccountTokenCommand struct { OrgId int64 `json:"-"` Key string `json:"-"` SecondsToLive int64 `json:"secondsToLive"` - Result *models.ApiKey `json:"-"` + Result *apikey.APIKey `json:"-"` } // swagger: model diff --git a/pkg/services/serviceaccounts/serviceaccounts.go b/pkg/services/serviceaccounts/serviceaccounts.go index 2c0c85703a7..788f29db90c 100644 --- a/pkg/services/serviceaccounts/serviceaccounts.go +++ b/pkg/services/serviceaccounts/serviceaccounts.go @@ -4,6 +4,7 @@ import ( "context" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" ) // this should reflect the api @@ -27,7 +28,7 @@ type Store interface { MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) error MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error RevertApiKey(ctx context.Context, saId int64, keyId int64) error - ListTokens(ctx context.Context, orgID int64, serviceAccount int64) ([]*models.ApiKey, error) + ListTokens(ctx context.Context, orgID int64, serviceAccount int64) ([]*apikey.APIKey, error) DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *AddServiceAccountTokenCommand) error GetUsageMetrics(ctx context.Context) (map[string]interface{}, error) diff --git a/pkg/services/serviceaccounts/tests/common.go b/pkg/services/serviceaccounts/tests/common.go index 3a15cd1d4c3..a22b35d6960 100644 --- a/pkg/services/serviceaccounts/tests/common.go +++ b/pkg/services/serviceaccounts/tests/common.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -45,13 +46,13 @@ func SetupUserServiceAccount(t *testing.T, sqlStore *sqlstore.SQLStore, testUser return u1 } -func SetupApiKey(t *testing.T, sqlStore *sqlstore.SQLStore, testKey TestApiKey) *models.ApiKey { +func SetupApiKey(t *testing.T, sqlStore *sqlstore.SQLStore, testKey TestApiKey) *apikey.APIKey { role := models.ROLE_VIEWER if testKey.Role != "" { role = testKey.Role } - addKeyCmd := &models.AddApiKeyCommand{ + addKeyCmd := &apikey.AddCommand{ Name: testKey.Name, Role: role, OrgId: testKey.OrgId, @@ -71,7 +72,7 @@ func SetupApiKey(t *testing.T, sqlStore *sqlstore.SQLStore, testKey TestApiKey) err := sqlStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { // Force setting expires to time before now to make key expired var expires int64 = 1 - key := models.ApiKey{Expires: &expires} + key := apikey.APIKey{Expires: &expires} rowsAffected, err := sess.ID(addKeyCmd.Result.Id).Update(&key) require.Equal(t, int64(1), rowsAffected) return err @@ -182,7 +183,7 @@ func (s *ServiceAccountsStoreMock) RevertApiKey(ctx context.Context, saId int64, return nil } -func (s *ServiceAccountsStoreMock) ListTokens(ctx context.Context, orgID int64, serviceAccount int64) ([]*models.ApiKey, error) { +func (s *ServiceAccountsStoreMock) ListTokens(ctx context.Context, orgID int64, serviceAccount int64) ([]*apikey.APIKey, error) { s.Calls.ListTokens = append(s.Calls.ListTokens, []interface{}{ctx, orgID, serviceAccount}) return nil, nil } diff --git a/pkg/services/sqlstore/mockstore/mockstore.go b/pkg/services/sqlstore/mockstore/mockstore.go index 27817fc64b9..cc4a0a8b6d1 100644 --- a/pkg/services/sqlstore/mockstore/mockstore.go +++ b/pkg/services/sqlstore/mockstore/mockstore.go @@ -4,6 +4,7 @@ import ( "context" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore/migrator" @@ -39,7 +40,6 @@ type SQLStoreMock struct { ExpectedNotifierUsageStats []*models.NotifierUsageStats ExpectedPersistedDashboards models.HitList ExpectedSignedInUser *models.SignedInUser - ExpectedAPIKey *models.ApiKey ExpectedUserStars map[int64]bool ExpectedLoginAttempts int64 @@ -505,35 +505,6 @@ func (m *SQLStoreMock) GetOrCreateAlertNotificationState(ctx context.Context, cm return m.ExpectedError } -func (m *SQLStoreMock) GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error { - return m.ExpectedError -} - -func (m *SQLStoreMock) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey { - return nil -} - -func (m *SQLStoreMock) DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCommand) error { - return m.ExpectedError -} - -func (m *SQLStoreMock) AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error { - return m.ExpectedError -} - -func (m *SQLStoreMock) GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error { - return m.ExpectedError -} - -func (m *SQLStoreMock) GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error { - query.Result = m.ExpectedAPIKey - return m.ExpectedError -} - -func (m *SQLStoreMock) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error { - return nil -} - func (m *SQLStoreMock) UpdateTempUserStatus(ctx context.Context, cmd *models.UpdateTempUserStatusCommand) error { return m.ExpectedError } @@ -575,6 +546,6 @@ func (m *SQLStoreMock) IsAdminOfTeams(ctx context.Context, query *models.IsAdmin return m.ExpectedError } -func (m *SQLStoreMock) GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) { +func (m *SQLStoreMock) GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) { return nil, m.ExpectedError }