mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ServiceAccounts: Add identifiable token prefix to service account tokens (#49011)
* Add prefixed API key gen. * Retrieve API Key by hash * Handle prefixed API keys for login * Add placeholder key generator * fix spelling * add get by hash sqlstore test * reformat query * quote usage of reserved keyword key * use constant * improve error handling and pre-select key type Co-authored-by: Victor Cinaglia <victor@grafana.com> * nits Co-authored-by: Victor Cinaglia <victor@grafana.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/apikeygen"
|
||||
apikeygenprefix "github.com/grafana/grafana/pkg/components/apikeygenprefixed"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/network"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
@@ -186,6 +187,45 @@ func (h *ContextHandler) initContextWithAnonymousUser(reqContext *models.ReqCont
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *ContextHandler) getPrefixedAPIKey(ctx context.Context, keyString string) (*models.ApiKey, error) {
|
||||
// prefixed decode key
|
||||
decoded, err := apikeygenprefix.Decode(keyString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash, err := decoded.Hash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.SQLStore.GetAPIKeyByHash(ctx, hash)
|
||||
}
|
||||
|
||||
func (h *ContextHandler) getAPIKey(ctx context.Context, keyString string) (*models.ApiKey, error) {
|
||||
decoded, err := apikeygen.Decode(keyString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fetch key
|
||||
keyQuery := models.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
|
||||
if err := h.SQLStore.GetApiKeyByName(ctx, &keyQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate api key
|
||||
isValid, err := apikeygen.IsValid(decoded, keyQuery.Result.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isValid {
|
||||
return nil, apikeygen.ErrInvalidApiKey
|
||||
}
|
||||
|
||||
return keyQuery.Result, nil
|
||||
}
|
||||
|
||||
func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bool {
|
||||
header := reqContext.Req.Header.Get("Authorization")
|
||||
parts := strings.SplitN(header, " ", 2)
|
||||
@@ -206,30 +246,22 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
|
||||
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAPIKey")
|
||||
defer span.End()
|
||||
|
||||
// base64 decode key
|
||||
decoded, err := apikeygen.Decode(keyString)
|
||||
if err != nil {
|
||||
reqContext.JsonApiErr(401, InvalidAPIKey, err)
|
||||
return true
|
||||
var (
|
||||
apikey *models.ApiKey
|
||||
errKey error
|
||||
)
|
||||
if strings.HasPrefix(keyString, apikeygenprefix.GrafanaPrefix) {
|
||||
apikey, errKey = h.getPrefixedAPIKey(reqContext.Req.Context(), keyString) // decode prefixed key
|
||||
} else {
|
||||
apikey, errKey = h.getAPIKey(reqContext.Req.Context(), keyString) // decode legacy api key
|
||||
}
|
||||
|
||||
// fetch key
|
||||
keyQuery := models.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
|
||||
if err := h.SQLStore.GetApiKeyByName(reqContext.Req.Context(), &keyQuery); err != nil {
|
||||
reqContext.JsonApiErr(401, InvalidAPIKey, err)
|
||||
return true
|
||||
}
|
||||
|
||||
apikey := keyQuery.Result
|
||||
|
||||
// validate api key
|
||||
isValid, err := apikeygen.IsValid(decoded, apikey.Key)
|
||||
if err != nil {
|
||||
reqContext.JsonApiErr(500, "Validating API key failed", err)
|
||||
return true
|
||||
}
|
||||
if !isValid {
|
||||
reqContext.JsonApiErr(401, InvalidAPIKey, err)
|
||||
if errKey != nil {
|
||||
status := http.StatusInternalServerError
|
||||
if errors.Is(errKey, apikeygen.ErrInvalidApiKey) {
|
||||
status = http.StatusUnauthorized
|
||||
}
|
||||
reqContext.JsonApiErr(status, InvalidAPIKey, errKey)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -239,7 +271,7 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
|
||||
getTime = time.Now
|
||||
}
|
||||
if apikey.Expires != nil && *apikey.Expires <= getTime().Unix() {
|
||||
reqContext.JsonApiErr(401, "Expired API key", err)
|
||||
reqContext.JsonApiErr(http.StatusUnauthorized, "Expired API key", nil)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user