2015-01-27 01:26:11 -06:00
package api
import (
2020-11-19 06:34:28 -06:00
"errors"
2021-11-29 03:18:01 -06:00
"net/http"
2019-10-23 03:40:12 -05:00
"time"
2015-02-26 10:23:28 -06:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
2015-02-05 03:37:13 -06:00
"github.com/grafana/grafana/pkg/bus"
2015-02-26 10:23:28 -06:00
"github.com/grafana/grafana/pkg/components/apikeygen"
2019-06-26 01:47:03 -05:00
"github.com/grafana/grafana/pkg/models"
2021-11-29 03:18:01 -06:00
"github.com/grafana/grafana/pkg/web"
2015-01-27 01:26:11 -06:00
)
2021-10-11 07:35:31 -05:00
// GetAPIKeys returns a list of API keys
2021-01-15 07:43:20 -06:00
func GetAPIKeys ( c * models . ReqContext ) response . Response {
2019-11-20 05:14:57 -06:00
query := models . GetApiKeysQuery { OrgId : c . OrgId , IncludeExpired : c . QueryBool ( "includeExpired" ) }
2015-01-27 01:26:11 -06:00
2021-12-28 10:36:22 -06:00
if err := bus . Dispatch ( c . Req . Context ( ) , & query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to list api keys" , err )
2015-01-27 01:26:11 -06:00
}
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 {
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
}
}
2015-05-18 14:23:40 -05:00
2021-01-15 07:43:20 -06:00
return response . JSON ( 200 , result )
2015-01-27 01:26:11 -06:00
}
2021-10-11 07:35:31 -05:00
// DeleteAPIKey deletes an API key
2021-01-15 07:43:20 -06:00
func DeleteAPIKey ( c * models . ReqContext ) response . Response {
2015-01-27 01:26:11 -06:00
id := c . ParamsInt64 ( ":id" )
2019-06-26 01:47:03 -05:00
cmd := & models . DeleteApiKeyCommand { Id : id , OrgId : c . OrgId }
2015-01-27 01:26:11 -06:00
2021-12-28 10:36:22 -06:00
err := bus . Dispatch ( c . Req . Context ( ) , cmd )
2015-01-27 01:26:11 -06:00
if err != nil {
2021-04-28 06:30:09 -05:00
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
}
2021-01-15 07:43:20 -06:00
return response . Success ( "API key deleted" )
2015-01-27 01:26:11 -06:00
}
2021-10-11 07:35:31 -05:00
// AddAPIKey adds an API key
2021-11-29 03:18:01 -06:00
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 ( ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , "Invalid role specified" , nil )
2015-01-27 01:26:11 -06:00
}
2019-06-26 01:47:03 -05:00
if hs . Cfg . ApiKeyMaxSecondsToLive != - 1 {
if cmd . SecondsToLive == 0 {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , "Number of seconds before expiration should be set" , nil )
2019-06-26 01:47:03 -05:00
}
if cmd . SecondsToLive > hs . Cfg . ApiKeyMaxSecondsToLive {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , "Number of seconds before expiration is greater than the global limit" , nil )
2019-06-26 01:47:03 -05:00
}
}
2015-02-23 13:07:49 -06:00
cmd . OrgId = c . OrgId
2021-10-20 07:36:11 -05:00
var err error
if hs . Cfg . FeatureToggles [ "service-accounts" ] {
2021-11-11 04:42:21 -06:00
//Every new API key must have an associated service account
2021-10-20 07:36:11 -05:00
if cmd . CreateNewServiceAccount {
2021-11-11 04:42:21 -06:00
//Create a new service account for the new API key
serviceAccount , err := hs . SQLStore . CloneUserToServiceAccount ( c . Req . Context ( ) , c . SignedInUser )
2021-10-20 07:36:11 -05:00
if err != nil {
2021-12-16 07:28:16 -06:00
hs . log . Warn ( "Unable to clone user to service account" , "err" , err )
2021-10-20 07:36:11 -05:00
return response . Error ( 500 , "Unable to clone user to service account" , err )
}
cmd . ServiceAccountId = serviceAccount . Id
2021-11-11 04:42:21 -06:00
} 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 }
2021-12-28 10:36:22 -06:00
err = bus . Dispatch ( c . Req . Context ( ) , & query )
2021-11-11 04:42:21 -06:00
if err != nil {
2021-12-16 07:28:16 -06:00
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 )
2021-11-11 04:42:21 -06:00
}
serviceAccountDetails := query . Result
if serviceAccountDetails . OrgId != c . OrgId || serviceAccountDetails . OrgId != cmd . OrgId {
2021-12-16 07:28:16 -06:00
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 )
2021-11-11 04:42:21 -06:00
return response . Error ( 403 , "Target service is not in the same organisation as requesting user or api key" , err )
}
2021-10-20 07:36:11 -05:00
}
2021-11-24 04:56:55 -06:00
} else {
if cmd . CreateNewServiceAccount {
return response . Error ( 400 , "Service accounts disabled. Retry create api request without service account flag." , err )
}
2021-10-20 07:36:11 -05:00
}
2015-02-26 10:23:28 -06:00
2019-10-23 03:40:12 -05:00
newKeyInfo , err := apikeygen . New ( cmd . OrgId , cmd . Name )
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Generating API key failed" , err )
2019-10-23 03:40:12 -05:00
}
2015-02-26 10:23:28 -06:00
cmd . Key = newKeyInfo . HashedKey
2015-01-27 01:26:11 -06:00
2021-12-28 10:36:22 -06:00
if err := bus . Dispatch ( c . Req . Context ( ) , & cmd ) ; err != nil {
2020-11-19 06:34:28 -06:00
if errors . Is ( err , models . ErrInvalidApiKeyExpiration ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , err . Error ( ) , nil )
2019-06-26 01:47:03 -05:00
}
2020-11-19 06:34:28 -06:00
if errors . Is ( err , models . ErrDuplicateApiKey ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 409 , err . Error ( ) , nil )
2019-07-11 03:20:34 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to add API Key" , err )
2015-01-27 01:26:11 -06:00
}
2015-02-26 10:23:28 -06:00
result := & dtos . NewApiKeyResult {
2020-09-07 10:06:11 -05:00
ID : cmd . Result . Id ,
2015-01-27 01:26:11 -06:00
Name : cmd . Result . Name ,
2020-09-07 10:06:11 -05:00
Key : newKeyInfo . ClientSecret ,
}
2015-01-27 01:26:11 -06:00
2021-01-15 07:43:20 -06:00
return response . JSON ( 200 , result )
2015-01-27 01:26:11 -06:00
}
2021-11-11 04:42:21 -06:00
// AddAPIKey adds an additional API key to a service account
2021-11-29 03:18:01 -06:00
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 )
}
2021-11-11 04:42:21 -06:00
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 )
}
2021-11-29 03:18:01 -06:00
return hs . AddAPIKey ( c )
2021-11-11 04:42:21 -06:00
}