Files
grafana/pkg/services/sqlstore/apikey.go
Jeremy Price 6dbb6408d4 Access Control: Add service accounts (#38994)
* Add extra fields to OSS types to support enterprise

* Create a service account at the same time as the API key

* Use service account credentials when accessing API with APIkey

* Add GetRole to service, merge RoleDTO and Role structs

This patch merges the identical OSS and Enterprise data structures, which improves the code for two reasons:

1.  Makes switching between OSS and Enterprise easier
2.  Reduces the chance of incompatibilities developing between the same functions in OSS and Enterprise

* If API key is not linked to a service account, continue login as usual

* Fallback to old auth if no service account linked to key

* Add CloneUserToServiceAccount

* Adding LinkAPIKeyToServiceAccount

* Handle api key link error

* Better error messages for OSS accesscontrol

* Set an invalid user id as default

* Re-arrange field names

* ServiceAccountId is integer

* Better error messages

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Eric Leijonmarck <eric.leijonmarck@gmail.com>
Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
2021-10-20 14:36:11 +02:00

126 lines
2.9 KiB
Go

package sqlstore
import (
"context"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"xorm.io/xorm"
)
func init() {
bus.AddHandlerCtx("sql", GetAPIKeys)
bus.AddHandler("sql", GetApiKeyById)
bus.AddHandler("sql", GetApiKeyByName)
bus.AddHandlerCtx("sql", DeleteApiKeyCtx)
bus.AddHandlerCtx("sql", AddAPIKey)
}
// GetAPIKeys queries the database based
// on input on GetApiKeysQuery
func GetAPIKeys(ctx context.Context, query *models.GetApiKeysQuery) error {
return withDbSession(ctx, x, func(dbSession *DBSession) error {
var sess *xorm.Session
if query.IncludeExpired {
sess = dbSession.Limit(100, 0).
Where("org_id=?", query.OrgId).
Asc("name")
} else {
sess = dbSession.Limit(100, 0).
Where("org_id=? and ( expires IS NULL or expires >= ?)", query.OrgId, timeNow().Unix()).
Asc("name")
}
query.Result = make([]*models.ApiKey, 0)
return sess.Find(&query.Result)
})
}
func DeleteApiKeyCtx(ctx context.Context, cmd *models.DeleteApiKeyCommand) error {
return withDbSession(ctx, x, func(sess *DBSession) error {
return deleteAPIKey(sess, cmd.Id, cmd.OrgId)
})
}
func deleteAPIKey(sess *DBSession, id, orgID int64) error {
rawSQL := "DELETE FROM api_key WHERE id=? and org_id=?"
result, err := sess.Exec(rawSQL, id, orgID)
if err != nil {
return err
}
n, err := result.RowsAffected()
if err != nil {
return err
} else if n == 0 {
return models.ErrApiKeyNotFound
}
return nil
}
// AddAPIKey adds the API key to the database.
func AddAPIKey(ctx context.Context, cmd *models.AddApiKeyCommand) error {
return inTransactionCtx(ctx, func(sess *DBSession) error {
key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name}
exists, _ := sess.Get(&key)
if exists {
return models.ErrDuplicateApiKey
}
updated := timeNow()
var expires *int64 = nil
if cmd.SecondsToLive > 0 {
v := updated.Add(time.Second * time.Duration(cmd.SecondsToLive)).Unix()
expires = &v
} else if cmd.SecondsToLive < 0 {
return models.ErrInvalidApiKeyExpiration
}
t := models.ApiKey{
OrgId: cmd.OrgId,
Name: cmd.Name,
Role: cmd.Role,
Key: cmd.Key,
Created: updated,
Updated: updated,
Expires: expires,
ServiceAccountId: cmd.ServiceAccountId,
}
if _, err := sess.Insert(&t); err != nil {
return err
}
cmd.Result = &t
return nil
})
}
func GetApiKeyById(query *models.GetApiKeyByIdQuery) error {
var apikey models.ApiKey
has, err := x.Id(query.ApiKeyId).Get(&apikey)
if err != nil {
return err
} else if !has {
return models.ErrInvalidApiKey
}
query.Result = &apikey
return nil
}
func GetApiKeyByName(query *models.GetApiKeyByNameQuery) error {
var apikey models.ApiKey
has, err := x.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&apikey)
if err != nil {
return err
} else if !has {
return models.ErrInvalidApiKey
}
query.Result = &apikey
return nil
}