grafana/pkg/api/apikey.go

155 lines
5.0 KiB
Go
Raw Normal View History

2015-01-27 01:26:11 -06:00
package api
import (
"errors"
"net/http"
"time"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
2015-02-05 03:37:13 -06:00
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/web"
2015-01-27 01:26:11 -06:00
)
// GetAPIKeys returns a list of API keys
func GetAPIKeys(c *models.ReqContext) response.Response {
query := models.GetApiKeysQuery{OrgId: c.OrgId, IncludeExpired: c.QueryBool("includeExpired")}
2015-01-27 01:26:11 -06:00
if err := bus.Dispatch(c.Req.Context(), &query); err != nil {
return response.Error(500, "Failed to list api keys", err)
2015-01-27 01:26:11 -06:00
}
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
result := make([]*models.ApiKeyDTO, len(query.Result))
2015-01-27 01:26:11 -06:00
for i, t := range query.Result {
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
var expiration *time.Time = nil
if t.Expires != nil {
v := time.Unix(*t.Expires, 0)
expiration = &v
}
result[i] = &models.ApiKeyDTO{
Id: t.Id,
Name: t.Name,
Role: t.Role,
Expiration: expiration,
2015-01-27 01:26:11 -06:00
}
}
return response.JSON(200, result)
2015-01-27 01:26:11 -06:00
}
// DeleteAPIKey deletes an API key
func DeleteAPIKey(c *models.ReqContext) response.Response {
2015-01-27 01:26:11 -06:00
id := c.ParamsInt64(":id")
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
cmd := &models.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
2015-01-27 01:26:11 -06:00
err := bus.Dispatch(c.Req.Context(), cmd)
2015-01-27 01:26:11 -06:00
if err != nil {
var status int
if errors.Is(err, models.ErrApiKeyNotFound) {
status = 404
} else {
status = 500
}
return response.Error(status, "Failed to delete API key", err)
2015-01-27 01:26:11 -06:00
}
return response.Success("API key deleted")
2015-01-27 01:26:11 -06:00
}
// AddAPIKey adds an API key
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext) response.Response {
cmd := models.AddApiKeyCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
2015-01-27 01:26:11 -06:00
if !cmd.Role.IsValid() {
return response.Error(400, "Invalid role specified", nil)
2015-01-27 01:26:11 -06:00
}
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
if hs.Cfg.ApiKeyMaxSecondsToLive != -1 {
if cmd.SecondsToLive == 0 {
return response.Error(400, "Number of seconds before expiration should be set", nil)
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
}
if cmd.SecondsToLive > hs.Cfg.ApiKeyMaxSecondsToLive {
return response.Error(400, "Number of seconds before expiration is greater than the global limit", nil)
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
}
}
cmd.OrgId = c.OrgId
var err error
if hs.Cfg.FeatureToggles["service-accounts"] {
//Every new API key must have an associated service account
if cmd.CreateNewServiceAccount {
//Create a new service account for the new API key
serviceAccount, err := hs.SQLStore.CloneUserToServiceAccount(c.Req.Context(), c.SignedInUser)
if err != nil {
hs.log.Warn("Unable to clone user to service account", "err", err)
return response.Error(500, "Unable to clone user to service account", err)
}
cmd.ServiceAccountId = serviceAccount.Id
} else {
//Link the new API key to an existing service account
//Check if user and service account are in the same org
query := models.GetUserByIdQuery{Id: cmd.ServiceAccountId}
err = bus.Dispatch(c.Req.Context(), &query)
if err != nil {
hs.log.Warn("Unable to link new API key to existing service account", "err", err, "query", query)
return response.Error(500, "Unable to link new API key to existing service account", err)
}
serviceAccountDetails := query.Result
if serviceAccountDetails.OrgId != c.OrgId || serviceAccountDetails.OrgId != cmd.OrgId {
hs.log.Warn("Target service is not in the same organisation as requesting user or api key", "err", err, "reqOrg", cmd.OrgId, "serviceAccId", serviceAccountDetails.OrgId, "userOrgId", c.OrgId)
return response.Error(403, "Target service is not in the same organisation as requesting user or api key", err)
}
}
} else {
if cmd.CreateNewServiceAccount {
return response.Error(400, "Service accounts disabled. Retry create api request without service account flag.", err)
}
}
newKeyInfo, err := apikeygen.New(cmd.OrgId, cmd.Name)
if err != nil {
return response.Error(500, "Generating API key failed", err)
}
cmd.Key = newKeyInfo.HashedKey
2015-01-27 01:26:11 -06:00
if err := bus.Dispatch(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, models.ErrInvalidApiKeyExpiration) {
return response.Error(400, err.Error(), nil)
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
}
if errors.Is(err, models.ErrDuplicateApiKey) {
return response.Error(409, err.Error(), nil)
}
return response.Error(500, "Failed to add API Key", err)
2015-01-27 01:26:11 -06:00
}
result := &dtos.NewApiKeyResult{
ID: cmd.Result.Id,
2015-01-27 01:26:11 -06:00
Name: cmd.Result.Name,
Key: newKeyInfo.ClientSecret,
}
2015-01-27 01:26:11 -06:00
return response.JSON(200, result)
2015-01-27 01:26:11 -06:00
}
// AddAPIKey adds an additional API key to a service account
func (hs *HTTPServer) AdditionalAPIKey(c *models.ReqContext) response.Response {
cmd := models.AddApiKeyCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !hs.Cfg.FeatureToggles["service-accounts"] {
return response.Error(500, "Requires services-accounts feature", errors.New("feature missing"))
}
if cmd.CreateNewServiceAccount {
return response.Error(500, "Can't create service account while adding additional API key", nil)
}
return hs.AddAPIKey(c)
}