mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 12:23:10 +08:00
Chore: Move api key models into apikey service package (#53241)
* Chore: move api key models into apikey service package * force table name for api key
This commit is contained in:
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/components/apikeygen"
|
"github.com/grafana/grafana/pkg/components/apikeygen"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ import (
|
|||||||
// 404: notFoundError
|
// 404: notFoundError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) GetAPIKeys(c *models.ReqContext) response.Response {
|
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 {
|
if err := hs.apiKeyService.GetAPIKeys(c.Req.Context(), &query); err != nil {
|
||||||
return response.Error(500, "Failed to list api keys", err)
|
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)
|
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)
|
err = hs.apiKeyService.DeleteApiKey(c.Req.Context(), cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var status int
|
var status int
|
||||||
if errors.Is(err, models.ErrApiKeyNotFound) {
|
if errors.Is(err, apikey.ErrNotFound) {
|
||||||
status = 404
|
status = 404
|
||||||
} else {
|
} else {
|
||||||
status = 500
|
status = 500
|
||||||
@ -104,7 +105,7 @@ func (hs *HTTPServer) DeleteAPIKey(c *models.ReqContext) response.Response {
|
|||||||
// 409: conflictError
|
// 409: conflictError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext) response.Response {
|
||||||
cmd := models.AddApiKeyCommand{}
|
cmd := apikey.AddCommand{}
|
||||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
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
|
cmd.Key = newKeyInfo.HashedKey
|
||||||
if err := hs.apiKeyService.AddAPIKey(c.Req.Context(), &cmd); err != nil {
|
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)
|
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(409, err.Error(), nil)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Failed to add API Key", err)
|
return response.Error(500, "Failed to add API Key", err)
|
||||||
@ -164,7 +165,7 @@ type GetAPIkeysParams struct {
|
|||||||
type AddAPIkeyParams struct {
|
type AddAPIkeyParams struct {
|
||||||
// in:body
|
// in:body
|
||||||
// required:true
|
// required:true
|
||||||
Body models.AddApiKeyCommand
|
Body apikey.AddCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:parameters deleteAPIkey
|
// swagger:parameters deleteAPIkey
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/login"
|
"github.com/grafana/grafana/pkg/login"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"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/contexthandler"
|
||||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@ -29,7 +30,7 @@ func TestMiddlewareBasicAuth(t *testing.T) {
|
|||||||
keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
|
keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
|
||||||
require.NoError(t, err)
|
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")
|
authHeader := util.GetBasicAuthHeader("api_key", "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9")
|
||||||
sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec()
|
sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec()
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/login"
|
"github.com/grafana/grafana/pkg/login"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"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/apikey/apikeytest"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||||
@ -151,7 +152,7 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
|
keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
|
||||||
require.NoError(t, err)
|
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()
|
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) {
|
middlewareScenario(t, "Valid API key, but does not match DB hash", func(t *testing.T, sc *scenarioContext) {
|
||||||
const keyhash = "Something_not_matching"
|
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()
|
sc.fakeReq("GET", "/").withValidApiKey().exec()
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expires := sc.contextHandler.GetTime().Add(-1 * time.Second).Unix()
|
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()
|
sc.fakeReq("GET", "/").withValidApiKey().exec()
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -2,17 +2,15 @@ package apikey
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error
|
GetAPIKeys(ctx context.Context, query *GetApiKeysQuery) error
|
||||||
GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey
|
GetAllAPIKeys(ctx context.Context, orgID int64) []*APIKey
|
||||||
DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCommand) error
|
DeleteApiKey(ctx context.Context, cmd *DeleteCommand) error
|
||||||
AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error
|
AddAPIKey(ctx context.Context, cmd *AddCommand) error
|
||||||
GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error
|
GetApiKeyById(ctx context.Context, query *GetByIDQuery) error
|
||||||
GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error
|
GetApiKeyByName(ctx context.Context, query *GetByNameQuery) error
|
||||||
GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error)
|
GetAPIKeyByHash(ctx context.Context, hash string) (*APIKey, error)
|
||||||
UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error
|
UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package apikeyimpl
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/services/apikey"
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"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}}
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
return s.store.AddAPIKey(ctx, cmd)
|
||||||
}
|
}
|
||||||
func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error {
|
func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error {
|
||||||
|
@ -5,8 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -14,13 +14,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type store interface {
|
type store interface {
|
||||||
GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error
|
GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error
|
||||||
GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey
|
GetAllAPIKeys(ctx context.Context, orgID int64) []*apikey.APIKey
|
||||||
DeleteApiKey(ctx context.Context, cmd *models.DeleteApiKeyCommand) error
|
DeleteApiKey(ctx context.Context, cmd *apikey.DeleteCommand) error
|
||||||
AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error
|
AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) error
|
||||||
GetApiKeyById(ctx context.Context, query *models.GetApiKeyByIdQuery) error
|
GetApiKeyById(ctx context.Context, query *apikey.GetByIDQuery) error
|
||||||
GetApiKeyByName(ctx context.Context, query *models.GetApiKeyByNameQuery) error
|
GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error
|
||||||
GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error)
|
GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error)
|
||||||
UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error
|
UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ type sqlStore struct {
|
|||||||
// timeNow makes it possible to test usage of time
|
// timeNow makes it possible to test usage of time
|
||||||
var timeNow = time.Now
|
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 {
|
return ss.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||||
var sess *xorm.Session
|
var sess *xorm.Session
|
||||||
|
|
||||||
@ -56,13 +56,13 @@ func (ss *sqlStore) GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuer
|
|||||||
sess.And(filter.Where, filter.Args...)
|
sess.And(filter.Where, filter.Args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Result = make([]*models.ApiKey, 0)
|
query.Result = make([]*apikey.APIKey, 0)
|
||||||
return sess.Find(&query.Result)
|
return sess.Find(&query.Result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.ApiKey {
|
func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) []*apikey.APIKey {
|
||||||
result := make([]*models.ApiKey, 0)
|
result := make([]*apikey.APIKey, 0)
|
||||||
err := ss.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
err := ss.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||||
sess := dbSession.Where("service_account_id IS NULL").Asc("name")
|
sess := dbSession.Where("service_account_id IS NULL").Asc("name")
|
||||||
if orgID != -1 {
|
if orgID != -1 {
|
||||||
@ -77,7 +77,7 @@ func (ss *sqlStore) GetAllAPIKeys(ctx context.Context, orgID int64) []*models.Ap
|
|||||||
return result
|
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 {
|
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"
|
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)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
return models.ErrApiKeyNotFound
|
return apikey.ErrNotFound
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
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)
|
exists, _ := sess.Get(&key)
|
||||||
if exists {
|
if exists {
|
||||||
return models.ErrDuplicateApiKey
|
return apikey.ErrDuplicate
|
||||||
}
|
}
|
||||||
|
|
||||||
updated := timeNow()
|
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()
|
v := updated.Add(time.Second * time.Duration(cmd.SecondsToLive)).Unix()
|
||||||
expires = &v
|
expires = &v
|
||||||
} else if cmd.SecondsToLive < 0 {
|
} else if cmd.SecondsToLive < 0 {
|
||||||
return models.ErrInvalidApiKeyExpiration
|
return apikey.ErrInvalidExpiration
|
||||||
}
|
}
|
||||||
|
|
||||||
t := models.ApiKey{
|
t := apikey.APIKey{
|
||||||
OrgId: cmd.OrgId,
|
OrgId: cmd.OrgId,
|
||||||
Name: cmd.Name,
|
Name: cmd.Name,
|
||||||
Role: cmd.Role,
|
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 {
|
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
var apikey models.ApiKey
|
var key apikey.APIKey
|
||||||
has, err := sess.ID(query.ApiKeyId).Get(&apikey)
|
has, err := sess.ID(query.ApiKeyId).Get(&key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrInvalidApiKey
|
return apikey.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Result = &apikey
|
query.Result = &key
|
||||||
return nil
|
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 {
|
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
var apikey models.ApiKey
|
var key apikey.APIKey
|
||||||
has, err := sess.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&apikey)
|
has, err := sess.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrInvalidApiKey
|
return apikey.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Result = &apikey
|
query.Result = &key
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *sqlStore) GetAPIKeyByHash(ctx context.Context, hash string) (*models.ApiKey, error) {
|
func (ss *sqlStore) GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) {
|
||||||
var apikey models.ApiKey
|
var key apikey.APIKey
|
||||||
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrInvalidApiKey
|
return apikey.ErrInvalid
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return &apikey, err
|
return &key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *sqlStore) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error {
|
func (ss *sqlStore) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error {
|
||||||
now := timeNow()
|
now := timeNow()
|
||||||
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,12 +41,12 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) {
|
|||||||
ss := &sqlStore{db: db, cfg: db.Cfg}
|
ss := &sqlStore{db: db, cfg: db.Cfg}
|
||||||
|
|
||||||
t.Run("Given saved api key", func(t *testing.T) {
|
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)
|
err := ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
t.Run("Should be able to get key by name", func(t *testing.T) {
|
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)
|
err = ss.GetApiKeyByName(context.Background(), &query)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@ -61,11 +62,11 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Add non expiring key", func(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)
|
err := ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
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)
|
err = ss.GetApiKeyByName(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@ -74,11 +75,11 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Add an expiring key", func(t *testing.T) {
|
t.Run("Add an expiring key", func(t *testing.T) {
|
||||||
// expires in one hour
|
// 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)
|
err := ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
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)
|
err = ss.GetApiKeyByName(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Last Used At datetime update", func(t *testing.T) {
|
t.Run("Last Used At datetime update", func(t *testing.T) {
|
||||||
// expires in one hour
|
// 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)
|
err := ss.AddAPIKey(context.Background(), &cmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) {
|
|||||||
err = ss.UpdateAPIKeyLastUsedDate(context.Background(), cmd.Result.Id)
|
err = ss.UpdateAPIKeyLastUsedDate(context.Background(), cmd.Result.Id)
|
||||||
require.NoError(t, err)
|
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)
|
err = ss.GetApiKeyByName(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
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) {
|
t.Run("Add a key with negative lifespan", func(t *testing.T) {
|
||||||
// expires in one day
|
// 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)
|
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)
|
err = ss.GetApiKeyByName(context.Background(), &query)
|
||||||
assert.EqualError(t, err, "invalid API key")
|
assert.EqualError(t, err, "invalid API key")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Add keys", func(t *testing.T) {
|
t.Run("Add keys", func(t *testing.T) {
|
||||||
// never expires
|
// 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)
|
err := ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
// expires in 1s
|
// 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)
|
err = ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
// expires in one hour
|
// 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)
|
err = ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ func TestIntegrationApiKeyDataAccess(t *testing.T) {
|
|||||||
1: {accesscontrol.ActionAPIKeyRead: []string{accesscontrol.ScopeAPIKeysAll}},
|
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)
|
err = ss.GetAPIKeys(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
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)
|
err = ss.GetAPIKeys(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@ -183,22 +184,22 @@ func TestIntegrationApiKeyErrors(t *testing.T) {
|
|||||||
ss := &sqlStore{db: db, cfg: db.Cfg}
|
ss := &sqlStore{db: db, cfg: db.Cfg}
|
||||||
|
|
||||||
t.Run("Delete non-existing key should return error", func(t *testing.T) {
|
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)
|
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("Testing API Duplicate Key Errors", func(t *testing.T) {
|
||||||
t.Run("Given saved api key", 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)
|
err := ss.AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
t.Run("Add API Key with existing Org ID and Name", func(t *testing.T) {
|
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)
|
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}
|
store := &sqlStore{db: db, cfg: db.Cfg}
|
||||||
seedApiKeys(t, store, 10)
|
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)
|
err := store.GetAPIKeys(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, query.Result, tt.expectedNumKeys)
|
assert.Len(t, query.Result, tt.expectedNumKeys)
|
||||||
@ -257,7 +258,7 @@ func seedApiKeys(t *testing.T, store store, num int) {
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
for i := 0; i < num; i++ {
|
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),
|
Name: fmt.Sprintf("key:%d", i),
|
||||||
Key: fmt.Sprintf("key:%d", i),
|
Key: fmt.Sprintf("key:%d", i),
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
|
@ -3,37 +3,37 @@ package apikeytest
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
ExpectedError error
|
ExpectedError error
|
||||||
ExpectedAPIKeys []*models.ApiKey
|
ExpectedAPIKeys []*apikey.APIKey
|
||||||
ExpectedAPIKey *models.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
|
query.Result = s.ExpectedAPIKeys
|
||||||
return s.ExpectedError
|
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
|
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
|
query.Result = s.ExpectedAPIKey
|
||||||
return s.ExpectedError
|
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
|
query.Result = s.ExpectedAPIKey
|
||||||
return s.ExpectedError
|
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
|
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
|
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
|
cmd.Result = s.ExpectedAPIKey
|
||||||
return s.ExpectedError
|
return s.ExpectedError
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,62 @@
|
|||||||
package apikey
|
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
|
||||||
|
}
|
||||||
|
@ -189,7 +189,7 @@ func (h *ContextHandler) initContextWithAnonymousUser(reqContext *models.ReqCont
|
|||||||
return true
|
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
|
// prefixed decode key
|
||||||
decoded, err := apikeygenprefix.Decode(keyString)
|
decoded, err := apikeygenprefix.Decode(keyString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -204,14 +204,14 @@ func (h *ContextHandler) getPrefixedAPIKey(ctx context.Context, keyString string
|
|||||||
return h.apiKeyService.GetAPIKeyByHash(ctx, hash)
|
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)
|
decoded, err := apikeygen.Decode(keyString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch key
|
// 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 {
|
if err := h.apiKeyService.GetApiKeyByName(ctx, &keyQuery); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
|
|||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
apikey *models.ApiKey
|
apikey *apikey.APIKey
|
||||||
errKey error
|
errKey error
|
||||||
)
|
)
|
||||||
if strings.HasPrefix(keyString, apikeygenprefix.GrafanaPrefix) {
|
if strings.HasPrefix(keyString, apikeygenprefix.GrafanaPrefix) {
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
apikeygenprefix "github.com/grafana/grafana/pkg/components/apikeygenprefixed"
|
apikeygenprefix "github.com/grafana/grafana/pkg/components/apikeygenprefixed"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/database"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/database"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"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 {
|
if err = api.store.DeleteServiceAccountToken(c.Req.Context(), c.OrgId, saID, tokenID); err != nil {
|
||||||
status := http.StatusNotFound
|
status := http.StatusNotFound
|
||||||
if err != nil && !errors.Is(err, models.ErrApiKeyNotFound) {
|
if err != nil && !errors.Is(err, apikey.ErrNotFound) {
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
} else {
|
} else {
|
||||||
err = models.ErrApiKeyNotFound
|
err = apikey.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Error(status, failedToDeleteMsg, err)
|
return response.Error(status, failedToDeleteMsg, err)
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
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/apikey/apikeyimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/database"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/database"
|
||||||
@ -33,7 +34,7 @@ const (
|
|||||||
serviceaccountIDTokensDetailPath = "/api/serviceaccounts/%v/tokens/%v" // #nosec G101
|
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)
|
key, err := apikeygen.New(orgID, keyName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ func createTokenforSA(t *testing.T, store serviceaccounts.Store, keyName string,
|
|||||||
OrgId: orgID,
|
OrgId: orgID,
|
||||||
Key: key.HashedKey,
|
Key: key.HashedKey,
|
||||||
SecondsToLive: secondsToLive,
|
SecondsToLive: secondsToLive,
|
||||||
Result: &models.ApiKey{},
|
Result: &apikey.APIKey{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.AddServiceAccountToken(context.Background(), saID, &cmd)
|
err = store.AddServiceAccountToken(context.Background(), saID, &cmd)
|
||||||
@ -148,7 +149,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) {
|
|||||||
if actualCode == http.StatusOK {
|
if actualCode == http.StatusOK {
|
||||||
assert.Equal(t, tc.body["name"], actualBody["name"])
|
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)
|
err = apiKeyService.GetApiKeyByName(context.Background(), &query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -245,7 +246,7 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) {
|
|||||||
_ = json.Unmarshal(actual.Body.Bytes(), &actualBody)
|
_ = json.Unmarshal(actual.Body.Bytes(), &actualBody)
|
||||||
require.Equal(t, tc.expectedCode, actualCode, endpoint, 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)
|
err := apiKeyService.GetApiKeyByName(context.Background(), &query)
|
||||||
if actualCode == http.StatusOK {
|
if actualCode == http.StatusOK {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@ -258,10 +259,10 @@ func TestServiceAccountsAPI_DeleteToken(t *testing.T) {
|
|||||||
|
|
||||||
type saStoreMockTokens struct {
|
type saStoreMockTokens struct {
|
||||||
serviceaccounts.Store
|
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
|
return s.saAPIKeys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +273,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) {
|
|||||||
|
|
||||||
type testCreateSAToken struct {
|
type testCreateSAToken struct {
|
||||||
desc string
|
desc string
|
||||||
tokens []*models.ApiKey
|
tokens []*apikey.APIKey
|
||||||
expectedHasExpired bool
|
expectedHasExpired bool
|
||||||
expectedResponseBodyField string
|
expectedResponseBodyField string
|
||||||
expectedCode int
|
expectedCode int
|
||||||
@ -286,7 +287,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) {
|
|||||||
testCases := []testCreateSAToken{
|
testCases := []testCreateSAToken{
|
||||||
{
|
{
|
||||||
desc: "should be able to list serviceaccount with no expiration date",
|
desc: "should be able to list serviceaccount with no expiration date",
|
||||||
tokens: []*models.ApiKey{{
|
tokens: []*apikey.APIKey{{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
ServiceAccountId: &saId,
|
ServiceAccountId: &saId,
|
||||||
@ -306,7 +307,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "should be able to list serviceaccount with secondsUntilExpiration",
|
desc: "should be able to list serviceaccount with secondsUntilExpiration",
|
||||||
tokens: []*models.ApiKey{{
|
tokens: []*apikey.APIKey{{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
ServiceAccountId: &saId,
|
ServiceAccountId: &saId,
|
||||||
@ -326,7 +327,7 @@ func TestServiceAccountsAPI_ListTokens(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "should be able to list serviceaccount with expired token",
|
desc: "should be able to list serviceaccount with expired token",
|
||||||
tokens: []*models.ApiKey{{
|
tokens: []*apikey.APIKey{{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
ServiceAccountId: &saId,
|
ServiceAccountId: &saId,
|
||||||
|
@ -438,7 +438,7 @@ func (s *ServiceAccountsStoreImpl) MigrateApiKey(ctx context.Context, orgId int6
|
|||||||
return nil
|
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"
|
prefix := "sa-autogen"
|
||||||
cmd := user.CreateUserCommand{
|
cmd := user.CreateUserCommand{
|
||||||
Login: fmt.Sprintf("%v-%v-%v", prefix, key.OrgId, key.Name),
|
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
|
// RevertApiKey converts service account token to old API key
|
||||||
func (s *ServiceAccountsStoreImpl) RevertApiKey(ctx context.Context, saId int64, keyId int64) error {
|
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 {
|
if err := s.apiKeyService.GetApiKeyById(ctx, &query); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/apikeygen"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -27,7 +27,7 @@ func TestStore_UsageStats(t *testing.T) {
|
|||||||
OrgId: sa.OrgID,
|
OrgId: sa.OrgID,
|
||||||
Key: key.HashedKey,
|
Key: key.HashedKey,
|
||||||
SecondsToLive: 0,
|
SecondsToLive: 0,
|
||||||
Result: &models.ApiKey{},
|
Result: &apikey.APIKey{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.AddServiceAccountToken(context.Background(), sa.ID, &cmd)
|
err = store.AddServiceAccountToken(context.Background(), sa.ID, &cmd)
|
||||||
|
@ -5,13 +5,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *ServiceAccountsStoreImpl) ListTokens(ctx context.Context, orgId int64, serviceAccountId int64) ([]*models.ApiKey, error) {
|
func (s *ServiceAccountsStoreImpl) ListTokens(ctx context.Context, orgId int64, serviceAccountId int64) ([]*apikey.APIKey, error) {
|
||||||
result := make([]*models.ApiKey, 0)
|
result := make([]*apikey.APIKey, 0)
|
||||||
err := s.sqlStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
err := s.sqlStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||||
var sess *xorm.Session
|
var sess *xorm.Session
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ func (s *ServiceAccountsStoreImpl) AddServiceAccountToken(ctx context.Context, s
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name}
|
key := apikey.APIKey{OrgId: cmd.OrgId, Name: cmd.Name}
|
||||||
exists, _ := sess.Get(&key)
|
exists, _ := sess.Get(&key)
|
||||||
if exists {
|
if exists {
|
||||||
return ErrDuplicateToken
|
return ErrDuplicateToken
|
||||||
@ -47,7 +48,7 @@ func (s *ServiceAccountsStoreImpl) AddServiceAccountToken(ctx context.Context, s
|
|||||||
return ErrInvalidTokenExpiration
|
return ErrInvalidTokenExpiration
|
||||||
}
|
}
|
||||||
|
|
||||||
token := models.ApiKey{
|
token := apikey.APIKey{
|
||||||
OrgId: cmd.OrgId,
|
OrgId: cmd.OrgId,
|
||||||
Name: cmd.Name,
|
Name: cmd.Name,
|
||||||
Role: models.ROLE_VIEWER,
|
Role: models.ROLE_VIEWER,
|
||||||
@ -86,7 +87,7 @@ func (s *ServiceAccountsStoreImpl) DeleteServiceAccountToken(ctx context.Context
|
|||||||
|
|
||||||
// assignApiKeyToServiceAccount sets the API key service account ID
|
// assignApiKeyToServiceAccount sets the API key service account ID
|
||||||
func (s *ServiceAccountsStoreImpl) assignApiKeyToServiceAccount(sess *sqlstore.DBSession, apiKeyId int64, serviceAccountId int64) error {
|
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)
|
exists, err := sess.Get(&key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn("API key not loaded", "err", err)
|
s.log.Warn("API key not loaded", "err", err)
|
||||||
@ -94,7 +95,7 @@ func (s *ServiceAccountsStoreImpl) assignApiKeyToServiceAccount(sess *sqlstore.D
|
|||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Warn("API key not found", "err", err)
|
s.log.Warn("API key not found", "err", err)
|
||||||
return models.ErrApiKeyNotFound
|
return apikey.ErrNotFound
|
||||||
}
|
}
|
||||||
key.ServiceAccountId = &serviceAccountId
|
key.ServiceAccountId = &serviceAccountId
|
||||||
|
|
||||||
@ -108,7 +109,7 @@ func (s *ServiceAccountsStoreImpl) assignApiKeyToServiceAccount(sess *sqlstore.D
|
|||||||
|
|
||||||
// detachApiKeyFromServiceAccount converts service account token to old API key
|
// detachApiKeyFromServiceAccount converts service account token to old API key
|
||||||
func (s *ServiceAccountsStoreImpl) detachApiKeyFromServiceAccount(sess *sqlstore.DBSession, apiKeyId int64) error {
|
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)
|
exists, err := sess.Get(&key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn("Cannot get API key", "err", err)
|
s.log.Warn("Cannot get API key", "err", err)
|
||||||
@ -116,7 +117,7 @@ func (s *ServiceAccountsStoreImpl) detachApiKeyFromServiceAccount(sess *sqlstore
|
|||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Warn("API key not found", "err", err)
|
s.log.Warn("API key not found", "err", err)
|
||||||
return models.ErrApiKeyNotFound
|
return apikey.ErrNotFound
|
||||||
}
|
}
|
||||||
key.ServiceAccountId = nil
|
key.ServiceAccountId = nil
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/apikeygen"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -34,7 +34,7 @@ func TestStore_AddServiceAccountToken(t *testing.T) {
|
|||||||
OrgId: user.OrgID,
|
OrgId: user.OrgID,
|
||||||
Key: key.HashedKey,
|
Key: key.HashedKey,
|
||||||
SecondsToLive: tc.secondsToLive,
|
SecondsToLive: tc.secondsToLive,
|
||||||
Result: &models.ApiKey{},
|
Result: &apikey.APIKey{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.AddServiceAccountToken(context.Background(), user.ID, &cmd)
|
err = store.AddServiceAccountToken(context.Background(), user.ID, &cmd)
|
||||||
@ -84,7 +84,7 @@ func TestStore_AddServiceAccountToken_WrongServiceAccount(t *testing.T) {
|
|||||||
OrgId: sa.OrgID,
|
OrgId: sa.OrgID,
|
||||||
Key: key.HashedKey,
|
Key: key.HashedKey,
|
||||||
SecondsToLive: 0,
|
SecondsToLive: 0,
|
||||||
Result: &models.ApiKey{},
|
Result: &apikey.APIKey{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.AddServiceAccountToken(context.Background(), sa.ID+1, &cmd)
|
err = store.AddServiceAccountToken(context.Background(), sa.ID+1, &cmd)
|
||||||
@ -105,7 +105,7 @@ func TestStore_DeleteServiceAccountToken(t *testing.T) {
|
|||||||
OrgId: sa.OrgID,
|
OrgId: sa.OrgID,
|
||||||
Key: key.HashedKey,
|
Key: key.HashedKey,
|
||||||
SecondsToLive: 0,
|
SecondsToLive: 0,
|
||||||
Result: &models.ApiKey{},
|
Result: &apikey.APIKey{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.AddServiceAccountToken(context.Background(), sa.ID, &cmd)
|
err = store.AddServiceAccountToken(context.Background(), sa.ID, &cmd)
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -68,7 +69,7 @@ type AddServiceAccountTokenCommand struct {
|
|||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
Key string `json:"-"`
|
Key string `json:"-"`
|
||||||
SecondsToLive int64 `json:"secondsToLive"`
|
SecondsToLive int64 `json:"secondsToLive"`
|
||||||
Result *models.ApiKey `json:"-"`
|
Result *apikey.APIKey `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger: model
|
// swagger: model
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// this should reflect the api
|
// this should reflect the api
|
||||||
@ -27,7 +28,7 @@ type Store interface {
|
|||||||
MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) error
|
MigrateApiKeysToServiceAccounts(ctx context.Context, orgID int64) error
|
||||||
MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error
|
MigrateApiKey(ctx context.Context, orgID int64, keyId int64) error
|
||||||
RevertApiKey(ctx context.Context, saId 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
|
DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error
|
||||||
AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *AddServiceAccountTokenCommand) error
|
AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *AddServiceAccountTokenCommand) error
|
||||||
GetUsageMetrics(ctx context.Context) (map[string]interface{}, error)
|
GetUsageMetrics(ctx context.Context) (map[string]interface{}, error)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
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/apikey/apikeyimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
@ -45,13 +46,13 @@ func SetupUserServiceAccount(t *testing.T, sqlStore *sqlstore.SQLStore, testUser
|
|||||||
return u1
|
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
|
role := models.ROLE_VIEWER
|
||||||
if testKey.Role != "" {
|
if testKey.Role != "" {
|
||||||
role = testKey.Role
|
role = testKey.Role
|
||||||
}
|
}
|
||||||
|
|
||||||
addKeyCmd := &models.AddApiKeyCommand{
|
addKeyCmd := &apikey.AddCommand{
|
||||||
Name: testKey.Name,
|
Name: testKey.Name,
|
||||||
Role: role,
|
Role: role,
|
||||||
OrgId: testKey.OrgId,
|
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 {
|
err := sqlStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
// Force setting expires to time before now to make key expired
|
// Force setting expires to time before now to make key expired
|
||||||
var expires int64 = 1
|
var expires int64 = 1
|
||||||
key := models.ApiKey{Expires: &expires}
|
key := apikey.APIKey{Expires: &expires}
|
||||||
rowsAffected, err := sess.ID(addKeyCmd.Result.Id).Update(&key)
|
rowsAffected, err := sess.ID(addKeyCmd.Result.Id).Update(&key)
|
||||||
require.Equal(t, int64(1), rowsAffected)
|
require.Equal(t, int64(1), rowsAffected)
|
||||||
return err
|
return err
|
||||||
@ -182,7 +183,7 @@ func (s *ServiceAccountsStoreMock) RevertApiKey(ctx context.Context, saId int64,
|
|||||||
return nil
|
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})
|
s.Calls.ListTokens = append(s.Calls.ListTokens, []interface{}{ctx, orgID, serviceAccount})
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"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/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
@ -39,7 +40,6 @@ type SQLStoreMock struct {
|
|||||||
ExpectedNotifierUsageStats []*models.NotifierUsageStats
|
ExpectedNotifierUsageStats []*models.NotifierUsageStats
|
||||||
ExpectedPersistedDashboards models.HitList
|
ExpectedPersistedDashboards models.HitList
|
||||||
ExpectedSignedInUser *models.SignedInUser
|
ExpectedSignedInUser *models.SignedInUser
|
||||||
ExpectedAPIKey *models.ApiKey
|
|
||||||
ExpectedUserStars map[int64]bool
|
ExpectedUserStars map[int64]bool
|
||||||
ExpectedLoginAttempts int64
|
ExpectedLoginAttempts int64
|
||||||
|
|
||||||
@ -505,35 +505,6 @@ func (m *SQLStoreMock) GetOrCreateAlertNotificationState(ctx context.Context, cm
|
|||||||
return m.ExpectedError
|
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 {
|
func (m *SQLStoreMock) UpdateTempUserStatus(ctx context.Context, cmd *models.UpdateTempUserStatusCommand) error {
|
||||||
return m.ExpectedError
|
return m.ExpectedError
|
||||||
}
|
}
|
||||||
@ -575,6 +546,6 @@ func (m *SQLStoreMock) IsAdminOfTeams(ctx context.Context, query *models.IsAdmin
|
|||||||
return m.ExpectedError
|
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
|
return nil, m.ExpectedError
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user