2022-12-19 09:22:11 +01:00
package clients
import (
"context"
"errors"
"strings"
"time"
"github.com/grafana/grafana/pkg/components/apikeygen"
2023-03-30 17:04:10 +02:00
"github.com/grafana/grafana/pkg/components/satokengen"
2022-12-19 09:22:11 +01:00
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apikey"
2023-12-21 20:42:05 +01:00
authidentity "github.com/grafana/grafana/pkg/services/auth/identity"
2022-12-19 09:22:11 +01:00
"github.com/grafana/grafana/pkg/services/authn"
2023-07-12 12:31:36 +02:00
"github.com/grafana/grafana/pkg/services/login"
2022-12-19 09:22:11 +01:00
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
)
var (
2023-11-29 10:25:46 +01:00
errAPIKeyInvalid = errutil . Unauthorized ( "api-key.invalid" , errutil . WithPublicMessage ( "Invalid API key" ) )
errAPIKeyExpired = errutil . Unauthorized ( "api-key.expired" , errutil . WithPublicMessage ( "Expired API key" ) )
errAPIKeyRevoked = errutil . Unauthorized ( "api-key.revoked" , errutil . WithPublicMessage ( "Revoked API key" ) )
errAPIKeyOrgMismatch = errutil . Unauthorized ( "api-key.organization-mismatch" , errutil . WithPublicMessage ( "API key does not belong to the requested organization" ) )
2022-12-19 09:22:11 +01:00
)
2023-02-21 11:21:34 +01:00
var _ authn . HookClient = new ( APIKey )
2023-01-26 10:50:44 +01:00
var _ authn . ContextAwareClient = new ( APIKey )
2024-04-12 11:38:20 +02:00
var _ authn . IdentityResolverClient = new ( APIKey )
2022-12-19 09:22:11 +01:00
2024-03-11 15:56:53 +01:00
func ProvideAPIKey ( apiKeyService apikey . Service ) * APIKey {
2022-12-19 09:22:11 +01:00
return & APIKey {
log : log . New ( authn . ClientAPIKey ) ,
apiKeyService : apiKeyService ,
}
}
type APIKey struct {
log log . Logger
apiKeyService apikey . Service
}
2023-01-26 10:50:44 +01:00
func ( s * APIKey ) Name ( ) string {
return authn . ClientAPIKey
}
2022-12-19 09:22:11 +01:00
func ( s * APIKey ) Authenticate ( ctx context . Context , r * authn . Request ) ( * authn . Identity , error ) {
2024-04-12 11:38:20 +02:00
key , err := s . getAPIKey ( ctx , getTokenFromRequest ( r ) )
2022-12-19 09:22:11 +01:00
if err != nil {
if errors . Is ( err , apikeygen . ErrInvalidApiKey ) {
2023-01-13 10:28:50 +01:00
return nil , errAPIKeyInvalid . Errorf ( "API key is invalid" )
2022-12-19 09:22:11 +01:00
}
return nil , err
}
2024-04-12 11:38:20 +02:00
if r . OrgID == 0 {
r . OrgID = key . OrgID
2022-12-19 09:22:11 +01:00
}
2024-04-12 11:38:20 +02:00
if err := validateApiKey ( r . OrgID , key ) ; err != nil {
return nil , err
2023-11-29 10:25:46 +01:00
}
2022-12-19 09:22:11 +01:00
// if the api key don't belong to a service account construct the identity and return it
2024-04-12 11:38:20 +02:00
if key . ServiceAccountId == nil || * key . ServiceAccountId < 1 {
return newAPIKeyIdentity ( key ) , nil
2022-12-19 09:22:11 +01:00
}
2024-04-12 11:38:20 +02:00
return newServiceAccountIdentity ( key ) , nil
2022-12-19 09:22:11 +01:00
}
2024-04-15 02:54:50 -06:00
func ( s * APIKey ) IsEnabled ( ) bool {
return true
}
2022-12-19 09:22:11 +01:00
func ( s * APIKey ) getAPIKey ( ctx context . Context , token string ) ( * apikey . APIKey , error ) {
fn := s . getFromToken
2023-03-30 17:04:10 +02:00
if ! strings . HasPrefix ( token , satokengen . GrafanaPrefix ) {
2022-12-19 09:22:11 +01:00
fn = s . getFromTokenLegacy
}
apiKey , err := fn ( ctx , token )
if err != nil {
return nil , err
}
return apiKey , nil
}
func ( s * APIKey ) getFromToken ( ctx context . Context , token string ) ( * apikey . APIKey , error ) {
2023-03-30 17:04:10 +02:00
decoded , err := satokengen . Decode ( token )
2022-12-19 09:22:11 +01:00
if err != nil {
return nil , err
}
hash , err := decoded . Hash ( )
if err != nil {
return nil , err
}
return s . apiKeyService . GetAPIKeyByHash ( ctx , hash )
}
func ( s * APIKey ) getFromTokenLegacy ( ctx context . Context , token string ) ( * apikey . APIKey , error ) {
decoded , err := apikeygen . Decode ( token )
if err != nil {
return nil , err
}
// fetch key
2023-02-03 17:23:09 +01:00
keyQuery := apikey . GetByNameQuery { KeyName : decoded . Name , OrgID : decoded . OrgId }
2023-03-21 13:26:33 +01:00
key , err := s . apiKeyService . GetApiKeyByName ( ctx , & keyQuery )
if err != nil {
2022-12-19 09:22:11 +01:00
return nil , err
}
// validate api key
2023-03-21 13:26:33 +01:00
isValid , err := apikeygen . IsValid ( decoded , key . Key )
2022-12-19 09:22:11 +01:00
if err != nil {
return nil , err
}
if ! isValid {
return nil , apikeygen . ErrInvalidApiKey
}
2023-03-21 13:26:33 +01:00
return key , nil
2022-12-19 09:22:11 +01:00
}
func ( s * APIKey ) Test ( ctx context . Context , r * authn . Request ) bool {
return looksLikeApiKey ( getTokenFromRequest ( r ) )
}
2023-01-26 10:50:44 +01:00
func ( s * APIKey ) Priority ( ) uint {
return 30
}
2024-04-12 11:38:20 +02:00
func ( s * APIKey ) Namespace ( ) string {
return authn . NamespaceAPIKey
}
func ( s * APIKey ) ResolveIdentity ( ctx context . Context , orgID int64 , namespaceID authn . NamespaceID ) ( * authn . Identity , error ) {
if ! namespaceID . IsNamespace ( authn . NamespaceAPIKey ) {
return nil , authn . ErrInvalidNamepsaceID . Errorf ( "got unspected namespace: %s" , namespaceID . Namespace ( ) )
}
apiKeyID , err := namespaceID . ParseInt ( )
if err != nil {
return nil , err
}
key , err := s . apiKeyService . GetApiKeyById ( ctx , & apikey . GetByIDQuery {
ApiKeyID : apiKeyID ,
} )
if err != nil {
return nil , err
}
if err := validateApiKey ( orgID , key ) ; err != nil {
return nil , err
}
if key . ServiceAccountId != nil && * key . ServiceAccountId >= 1 {
return nil , authn . ErrInvalidNamepsaceID . Errorf ( "api key belongs to service account" )
}
return newAPIKeyIdentity ( key ) , nil
}
2023-02-21 11:21:34 +01:00
func ( s * APIKey ) Hook ( ctx context . Context , identity * authn . Identity , r * authn . Request ) error {
2023-12-08 16:53:11 +01:00
id , exists := s . getAPIKeyID ( ctx , identity , r )
if ! exists {
2023-02-21 11:21:34 +01:00
return nil
}
go func ( apikeyID int64 ) {
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-09-04 18:49:47 +02:00
s . log . Error ( "Panic during user last seen sync" , "err" , err )
2023-02-21 11:21:34 +01:00
}
} ( )
if err := s . apiKeyService . UpdateAPIKeyLastUsedDate ( context . Background ( ) , apikeyID ) ; err != nil {
2023-09-04 18:49:47 +02:00
s . log . Warn ( "Failed to update last use date for api key" , "id" , apikeyID )
2023-02-21 11:21:34 +01:00
}
} ( id )
return nil
}
2023-12-08 16:53:11 +01:00
func ( s * APIKey ) getAPIKeyID ( ctx context . Context , identity * authn . Identity , r * authn . Request ) ( apiKeyID int64 , exists bool ) {
2023-12-21 20:42:05 +01:00
namespace , identifier := identity . GetNamespacedID ( )
2023-12-08 16:53:11 +01:00
2023-12-21 20:42:05 +01:00
id , err := authidentity . IntIdentifier ( namespace , identifier )
if err != nil {
s . log . Warn ( "Failed to parse ID from identifier" , "err" , err )
return - 1 , false
}
2023-12-08 16:53:11 +01:00
if namespace == authn . NamespaceAPIKey {
return id , true
}
if namespace == authn . NamespaceServiceAccount {
// When the identity is service account, the ID in from the namespace is the service account ID.
// We need to fetch the API key in this scenario, as we could use it to uniquely identify a service account token.
apiKey , err := s . getAPIKey ( ctx , getTokenFromRequest ( r ) )
if err != nil {
s . log . Warn ( "Failed to fetch the API Key from request" )
return - 1 , false
}
return apiKey . ID , true
}
return - 1 , false
}
2022-12-19 09:22:11 +01:00
func looksLikeApiKey ( token string ) bool {
return token != ""
}
func getTokenFromRequest ( r * authn . Request ) string {
// api keys are only supported through http requests
if r . HTTPRequest == nil {
return ""
}
header := r . HTTPRequest . Header . Get ( "Authorization" )
if strings . HasPrefix ( header , bearerPrefix ) {
return strings . TrimPrefix ( header , bearerPrefix )
}
if strings . HasPrefix ( header , basicPrefix ) {
username , password , err := util . DecodeBasicAuthHeader ( header )
if err == nil && username == "api_key" {
return password
}
}
return ""
}
2024-04-12 11:38:20 +02:00
func validateApiKey ( orgID int64 , key * apikey . APIKey ) error {
if key . Expires != nil && * key . Expires <= time . Now ( ) . Unix ( ) {
return errAPIKeyExpired . Errorf ( "API key has expired" )
}
if key . IsRevoked != nil && * key . IsRevoked {
return errAPIKeyRevoked . Errorf ( "Api key is revoked" )
}
if orgID != key . OrgID {
return errAPIKeyOrgMismatch . Errorf ( "API does not belong in Organization" )
}
return nil
}
func newAPIKeyIdentity ( key * apikey . APIKey ) * authn . Identity {
return & authn . Identity {
ID : authn . NamespacedID ( authn . NamespaceAPIKey , key . ID ) ,
OrgID : key . OrgID ,
OrgRoles : map [ int64 ] org . RoleType { key . OrgID : key . Role } ,
ClientParams : authn . ClientParams { SyncPermissions : true } ,
AuthenticatedBy : login . APIKeyAuthModule ,
}
}
func newServiceAccountIdentity ( key * apikey . APIKey ) * authn . Identity {
return & authn . Identity {
ID : authn . NamespacedID ( authn . NamespaceServiceAccount , * key . ServiceAccountId ) ,
OrgID : key . OrgID ,
AuthenticatedBy : login . APIKeyAuthModule ,
ClientParams : authn . ClientParams { FetchSyncedUser : true , SyncPermissions : true } ,
}
}