mirror of
https://github.com/grafana/grafana.git
synced 2025-09-22 05:49:51 +08:00
add context to api crud calls (#40047)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
This commit is contained in:
@ -11,10 +11,11 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetAPIKeys returns a list of API keys
|
||||||
func GetAPIKeys(c *models.ReqContext) response.Response {
|
func GetAPIKeys(c *models.ReqContext) response.Response {
|
||||||
query := models.GetApiKeysQuery{OrgId: c.OrgId, IncludeExpired: c.QueryBool("includeExpired")}
|
query := models.GetApiKeysQuery{OrgId: c.OrgId, IncludeExpired: c.QueryBool("includeExpired")}
|
||||||
|
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err := bus.DispatchCtx(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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,12 +37,13 @@ func GetAPIKeys(c *models.ReqContext) response.Response {
|
|||||||
return response.JSON(200, result)
|
return response.JSON(200, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteAPIKey deletes an API key
|
||||||
func DeleteAPIKey(c *models.ReqContext) response.Response {
|
func DeleteAPIKey(c *models.ReqContext) response.Response {
|
||||||
id := c.ParamsInt64(":id")
|
id := c.ParamsInt64(":id")
|
||||||
|
|
||||||
cmd := &models.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
|
cmd := &models.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
|
||||||
|
|
||||||
err := bus.Dispatch(cmd)
|
err := bus.DispatchCtx(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, models.ErrApiKeyNotFound) {
|
||||||
@ -55,6 +57,7 @@ func DeleteAPIKey(c *models.ReqContext) response.Response {
|
|||||||
return response.Success("API key deleted")
|
return response.Success("API key deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddAPIKey adds an API key
|
||||||
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyCommand) response.Response {
|
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyCommand) response.Response {
|
||||||
if !cmd.Role.IsValid() {
|
if !cmd.Role.IsValid() {
|
||||||
return response.Error(400, "Invalid role specified", nil)
|
return response.Error(400, "Invalid role specified", nil)
|
||||||
@ -77,7 +80,7 @@ func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyComman
|
|||||||
|
|
||||||
cmd.Key = newKeyInfo.HashedKey
|
cmd.Key = newKeyInfo.HashedKey
|
||||||
|
|
||||||
if err := bus.Dispatch(&cmd); err != nil {
|
if err := bus.DispatchCtx(c.Req.Context(), &cmd); err != nil {
|
||||||
if errors.Is(err, models.ErrInvalidApiKeyExpiration) {
|
if errors.Is(err, models.ErrInvalidApiKeyExpiration) {
|
||||||
return response.Error(400, err.Error(), nil)
|
return response.Error(400, err.Error(), nil)
|
||||||
}
|
}
|
||||||
|
@ -6,25 +6,36 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("sql", GetApiKeys)
|
bus.AddHandlerCtx("sql", GetAPIKeys)
|
||||||
bus.AddHandler("sql", GetApiKeyById)
|
bus.AddHandler("sql", GetApiKeyById)
|
||||||
bus.AddHandler("sql", GetApiKeyByName)
|
bus.AddHandler("sql", GetApiKeyByName)
|
||||||
bus.AddHandlerCtx("sql", DeleteApiKeyCtx)
|
bus.AddHandlerCtx("sql", DeleteApiKeyCtx)
|
||||||
bus.AddHandler("sql", AddApiKey)
|
bus.AddHandlerCtx("sql", AddAPIKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApiKeys(query *models.GetApiKeysQuery) error {
|
// GetAPIKeys queries the database based
|
||||||
sess := x.Limit(100, 0).Where("org_id=? and ( expires IS NULL or expires >= ?)",
|
// on input on GetApiKeysQuery
|
||||||
query.OrgId, timeNow().Unix()).Asc("name")
|
func GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error {
|
||||||
if query.IncludeExpired {
|
return withDbSession(ctx, x, func(dbSession *DBSession) error {
|
||||||
sess = x.Limit(100, 0).Where("org_id=?", query.OrgId).Asc("name")
|
var sess *xorm.Session
|
||||||
}
|
|
||||||
|
|
||||||
query.Result = make([]*models.ApiKey, 0)
|
if query.IncludeExpired {
|
||||||
return sess.Find(&query.Result)
|
sess = dbSession.Limit(100, 0).
|
||||||
|
Where("org_id=?", query.OrgId).
|
||||||
|
Asc("name")
|
||||||
|
} else {
|
||||||
|
sess = dbSession.Limit(100, 0).
|
||||||
|
Where("org_id=? and ( expires IS NULL or expires >= ?)", query.OrgId, timeNow().Unix()).
|
||||||
|
Asc("name")
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Result = make([]*models.ApiKey, 0)
|
||||||
|
return sess.Find(&query.Result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteApiKeyCtx(ctx context.Context, cmd *models.DeleteApiKeyCommand) error {
|
func DeleteApiKeyCtx(ctx context.Context, cmd *models.DeleteApiKeyCommand) error {
|
||||||
@ -48,8 +59,9 @@ func deleteAPIKey(sess *DBSession, id, orgID int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddApiKey(cmd *models.AddApiKeyCommand) error {
|
// AddAPIKey adds the API key to the database.
|
||||||
return inTransaction(func(sess *DBSession) error {
|
func AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error {
|
||||||
|
return inTransactionCtx(ctx, func(sess *DBSession) error {
|
||||||
key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name}
|
key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name}
|
||||||
exists, _ := sess.Get(&key)
|
exists, _ := sess.Get(&key)
|
||||||
if exists {
|
if exists {
|
||||||
|
@ -21,7 +21,7 @@ func TestApiKeyDataAccess(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: 1, Name: "hello", Key: "asd"}
|
cmd := models.AddApiKeyCommand{OrgId: 1, Name: "hello", Key: "asd"}
|
||||||
err := AddApiKey(&cmd)
|
err := 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) {
|
||||||
@ -35,7 +35,7 @@ func TestApiKeyDataAccess(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 := models.AddApiKeyCommand{OrgId: 1, Name: "non-expiring", Key: "asd1", SecondsToLive: 0}
|
||||||
err := AddApiKey(&cmd)
|
err := AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
query := models.GetApiKeyByNameQuery{KeyName: "non-expiring", OrgId: 1}
|
query := models.GetApiKeyByNameQuery{KeyName: "non-expiring", OrgId: 1}
|
||||||
@ -48,7 +48,7 @@ func TestApiKeyDataAccess(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 := models.AddApiKeyCommand{OrgId: 1, Name: "expiring-in-an-hour", Key: "asd2", SecondsToLive: 3600}
|
||||||
err := AddApiKey(&cmd)
|
err := AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
query := models.GetApiKeyByNameQuery{KeyName: "expiring-in-an-hour", OrgId: 1}
|
query := models.GetApiKeyByNameQuery{KeyName: "expiring-in-an-hour", OrgId: 1}
|
||||||
@ -57,7 +57,7 @@ func TestApiKeyDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
assert.True(t, *query.Result.Expires >= timeNow().Unix())
|
assert.True(t, *query.Result.Expires >= timeNow().Unix())
|
||||||
|
|
||||||
// timeNow() has been called twice since creation; once by AddApiKey and once by GetApiKeyByName
|
// timeNow() has been called twice since creation; once by AddAPIKey and once by GetApiKeyByName
|
||||||
// therefore two seconds should be subtracted by next value returned by timeNow()
|
// therefore two seconds should be subtracted by next value returned by timeNow()
|
||||||
// that equals the number by which timeSeed has been advanced
|
// that equals the number by which timeSeed has been advanced
|
||||||
then := timeNow().Add(-2 * time.Second)
|
then := timeNow().Add(-2 * time.Second)
|
||||||
@ -68,7 +68,7 @@ func TestApiKeyDataAccess(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 := models.AddApiKeyCommand{OrgId: 1, Name: "key-with-negative-lifespan", Key: "asd3", SecondsToLive: -3600}
|
||||||
err := AddApiKey(&cmd)
|
err := AddAPIKey(context.Background(), &cmd)
|
||||||
assert.EqualError(t, err, models.ErrInvalidApiKeyExpiration.Error())
|
assert.EqualError(t, err, models.ErrInvalidApiKeyExpiration.Error())
|
||||||
|
|
||||||
query := models.GetApiKeyByNameQuery{KeyName: "key-with-negative-lifespan", OrgId: 1}
|
query := models.GetApiKeyByNameQuery{KeyName: "key-with-negative-lifespan", OrgId: 1}
|
||||||
@ -79,24 +79,24 @@ func TestApiKeyDataAccess(t *testing.T) {
|
|||||||
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 := models.AddApiKeyCommand{OrgId: 1, Name: "key1", Key: "key1", SecondsToLive: 0}
|
||||||
err := AddApiKey(&cmd)
|
err := 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 = models.AddApiKeyCommand{OrgId: 1, Name: "key2", Key: "key2", SecondsToLive: 1}
|
||||||
err = AddApiKey(&cmd)
|
err = 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 = models.AddApiKeyCommand{OrgId: 1, Name: "key3", Key: "key3", SecondsToLive: 3600}
|
||||||
err = AddApiKey(&cmd)
|
err = AddAPIKey(context.Background(), &cmd)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
// advance mocked getTime by 1s
|
// advance mocked getTime by 1s
|
||||||
timeNow()
|
timeNow()
|
||||||
|
|
||||||
query := models.GetApiKeysQuery{OrgId: 1, IncludeExpired: false}
|
query := models.GetApiKeysQuery{OrgId: 1, IncludeExpired: false}
|
||||||
err = GetApiKeys(&query)
|
err = GetAPIKeys(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
for _, k := range query.Result {
|
for _, k := range query.Result {
|
||||||
@ -106,7 +106,7 @@ func TestApiKeyDataAccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
query = models.GetApiKeysQuery{OrgId: 1, IncludeExpired: true}
|
query = models.GetApiKeysQuery{OrgId: 1, IncludeExpired: true}
|
||||||
err = GetApiKeys(&query)
|
err = GetAPIKeys(context.Background(), &query)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
@ -137,12 +137,12 @@ func TestApiKeyErrors(t *testing.T) {
|
|||||||
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 := models.AddApiKeyCommand{OrgId: 0, Name: "duplicate", Key: "asd"}
|
||||||
err := AddApiKey(&cmd)
|
err := 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 := models.AddApiKeyCommand{OrgId: 0, Name: "duplicate", Key: "asd"}
|
||||||
err = AddApiKey(&cmd)
|
err = AddAPIKey(context.Background(), &cmd)
|
||||||
assert.EqualError(t, err, models.ErrDuplicateApiKey.Error())
|
assert.EqualError(t, err, models.ErrDuplicateApiKey.Error())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,7 @@ func TestTransaction(t *testing.T) {
|
|||||||
Convey("InTransaction", t, func() {
|
Convey("InTransaction", t, func() {
|
||||||
cmd := &models.AddApiKeyCommand{Key: "secret-key", Name: "key", OrgId: 1}
|
cmd := &models.AddApiKeyCommand{Key: "secret-key", Name: "key", OrgId: 1}
|
||||||
|
|
||||||
err := AddApiKey(cmd)
|
err := AddAPIKey(context.Background(), cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("can update key", func() {
|
Convey("can update key", func() {
|
||||||
|
Reference in New Issue
Block a user