API Keys: Removal & Redirect of Create endpoint (#92144)

* API keys: redirecting of create endpont

* update naming and using admonition

* fmt

* Apply suggestions from code review

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>

---------

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
Eric Leijonmarck 2024-08-28 12:20:36 +01:00 committed by GitHub
parent 909d0fac35
commit 6a19278f23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 52 additions and 116 deletions

View File

@ -29,6 +29,11 @@ If you use Grafana v9.1 or newer, use service accounts instead of API keys. For
## List API keys
{{% admonition type="warning" %}}
This endpoint is deprecated.
{{% /admonition %}}
`GET /api/auth/keys`
**Required permissions**
@ -75,6 +80,13 @@ Content-Type: application/json
## Create API Key
{{% admonition type="warning" %}}
This endpoint has been made obsolete in Grafana 11.3.0.
{{% /admonition %}}
Endpoint is obsolete and has been moved to [Grafana service account API]({{< relref "./serviceaccount/" >}}). For more information, refer to [Migrate to Grafana service account API]({{< relref "../../administration/api-keys/#migrate-api-keys-to-grafana-service-accounts-using-the-api" >}}).
`POST /api/auth/keys`
**Required permissions**
@ -114,14 +126,20 @@ Error statuses:
**Example Response**:
```http
HTTP/1.1 200
HTTP/1.1 301
Content-Type: application/json
{"name":"mykey","key":"eyJrIjoiWHZiSWd3NzdCYUZnNUtibE9obUpESmE3bzJYNDRIc0UiLCJuIjoibXlrZXkiLCJpZCI6MX1=","id":1}
""
```
## Delete API Key
{{% admonition type="warning" %}}
### DEPRECATED
{{% /admonition %}}
`DELETE /api/auth/keys/:id`
**Required permissions**

View File

@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/components/apikeygen"
"github.com/grafana/grafana/pkg/services/apikey"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/web"
@ -114,59 +113,15 @@ func (hs *HTTPServer) DeleteAPIKey(c *contextmodel.ReqContext) response.Response
// see: https://grafana.com/docs/grafana/next/administration/api-keys/#migrate-api-keys-to-grafana-service-accounts-using-the-api.
//
// Responses:
// 200: postAPIkeyResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 409: conflictError
// 500: internalServerError
// 301: statusMovedPermanently
func (hs *HTTPServer) AddAPIKey(c *contextmodel.ReqContext) response.Response {
cmd := apikey.AddCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !cmd.Role.IsValid() {
return response.Error(http.StatusBadRequest, "Invalid role specified", nil)
}
if !c.SignedInUser.GetOrgRole().Includes(cmd.Role) {
return response.Error(http.StatusForbidden, "Cannot assign a role higher than user's role", nil)
}
// Set the Location header to the new URL
hs.log.Warn("Obsolete and Permanently moved API endpoint called", "path", c.Req.URL.Path)
c.Context.Resp.Header().Set("Location", "/api/serviceaccounts/tokens")
if hs.Cfg.ApiKeyMaxSecondsToLive != -1 {
if cmd.SecondsToLive == 0 {
return response.Error(http.StatusBadRequest, "Number of seconds before expiration should be set", nil)
}
if cmd.SecondsToLive > hs.Cfg.ApiKeyMaxSecondsToLive {
return response.Error(http.StatusBadRequest, "Number of seconds before expiration is greater than the global limit", nil)
}
}
cmd.OrgID = c.SignedInUser.GetOrgID()
newKeyInfo, err := apikeygen.New(cmd.OrgID, cmd.Name)
if err != nil {
return response.Error(http.StatusInternalServerError, "Generating API key failed", err)
}
cmd.Key = newKeyInfo.HashedKey
key, err := hs.apiKeyService.AddAPIKey(c.Req.Context(), &cmd)
if err != nil {
if errors.Is(err, apikey.ErrInvalidExpiration) {
return response.Error(http.StatusBadRequest, err.Error(), nil)
}
if errors.Is(err, apikey.ErrDuplicate) {
return response.Error(http.StatusConflict, err.Error(), nil)
}
return response.Error(http.StatusInternalServerError, "Failed to add API Key", err)
}
result := &dtos.NewApiKeyResult{
ID: key.ID,
Name: key.Name,
Key: newKeyInfo.ClientSecret,
}
return response.JSON(http.StatusOK, result)
// Respond with a 301 Moved Permanently status code
// the Location header is enough for clients to know where to go next.
return response.JSON(http.StatusMovedPermanently, nil)
}
// swagger:parameters getAPIkeys
@ -178,13 +133,6 @@ type GetAPIkeysParams struct {
IncludeExpired bool `json:"includeExpired"`
}
// swagger:parameters addAPIkey
type AddAPIkeyParams struct {
// in:body
// required:true
Body apikey.AddCommand
}
// swagger:parameters deleteAPIkey
type DeleteAPIkeyParams struct {
// in:path

View File

@ -82,6 +82,11 @@ type UnauthorizedError GenericError
// swagger:response acceptedResponse
type AcceptedResponse GenericError
// StatusMovedPermanently
//
// swagger:response statusMovedPermanently
type StatusMovedPermanentlyRedirect GenericError
// documentation for PublicError defined in errutil.Error
// swagger:response publicErrorResponse

View File

@ -2225,34 +2225,9 @@
"summary": "Creates an API key.",
"operationId": "addAPIkey",
"deprecated": true,
"parameters": [
{
"name": "Body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/AddAPIKeyCommand"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/postAPIkeyResponse"
},
"400": {
"$ref": "#/responses/badRequestError"
},
"401": {
"$ref": "#/responses/unauthorisedError"
},
"403": {
"$ref": "#/responses/forbiddenError"
},
"409": {
"$ref": "#/responses/conflictError"
},
"500": {
"$ref": "#/responses/internalServerError"
"301": {
"$ref": "#/responses/statusMovedPermanently"
}
}
}
@ -24190,6 +24165,12 @@
"$ref": "#/definitions/SnapshotListResponseDTO"
}
},
"statusMovedPermanently": {
"description": "StatusMovedPermanently",
"schema": {
"$ref": "#/definitions/ErrorResponseBody"
}
},
"unauthorisedError": {
"description": "UnauthorizedError is returned when the request is not authenticated.",
"schema": {

View File

@ -1942,6 +1942,16 @@
},
"description": "(empty)"
},
"statusMovedPermanently": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponseBody"
}
}
},
"description": "StatusMovedPermanently"
},
"unauthorisedError": {
"content": {
"application/json": {
@ -15304,35 +15314,9 @@
"deprecated": true,
"description": "Will return details of the created API key.",
"operationId": "addAPIkey",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddAPIKeyCommand"
}
}
},
"required": true,
"x-originalParamName": "Body"
},
"responses": {
"200": {
"$ref": "#/components/responses/postAPIkeyResponse"
},
"400": {
"$ref": "#/components/responses/badRequestError"
},
"401": {
"$ref": "#/components/responses/unauthorisedError"
},
"403": {
"$ref": "#/components/responses/forbiddenError"
},
"409": {
"$ref": "#/components/responses/conflictError"
},
"500": {
"$ref": "#/components/responses/internalServerError"
"301": {
"$ref": "#/components/responses/statusMovedPermanently"
}
},
"summary": "Creates an API key.",