mirror of
https://github.com/grafana/grafana.git
synced 2026-08-18 17:15:08 -05:00
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>
This commit is contained in:
co-authored by
Hugo Häggmark
Eric Leijonmarck
Emil Tullstedt
Ieva
Gabriel MABILLE
parent
bd97c79454
commit
6dbb6408d4
@@ -14,6 +14,15 @@ type AccessControl interface {
|
||||
// GetUserPermissions returns user permissions.
|
||||
GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*Permission, error)
|
||||
|
||||
// GetUserRoles returns user roles.
|
||||
GetUserRoles(ctx context.Context, user *models.SignedInUser) ([]*RoleDTO, error)
|
||||
|
||||
// CloneUserToServiceAccount Creates a new service account and assigns it the same roles as the user has
|
||||
CloneUserToServiceAccount(ctx context.Context, user *models.SignedInUser) (*models.User, error)
|
||||
|
||||
// LinkAPIKeyToServiceAccount Connects an APIkey to a service account. Multiple API keys may be linked to one account.
|
||||
LinkAPIKeyToServiceAccount(ctx context.Context, ApiKey *models.ApiKey, serviceAccount *models.User) error
|
||||
|
||||
//IsDisabled returns if access control is enabled or not
|
||||
IsDisabled() bool
|
||||
|
||||
|
||||
@@ -14,17 +14,24 @@ type fullAccessControl interface {
|
||||
}
|
||||
|
||||
type Calls struct {
|
||||
Evaluate []interface{}
|
||||
GetUserPermissions []interface{}
|
||||
IsDisabled []interface{}
|
||||
DeclareFixedRoles []interface{}
|
||||
GetUserBuiltInRoles []interface{}
|
||||
RegisterFixedRoles []interface{}
|
||||
CloneUserToServiceAccount []interface{}
|
||||
Evaluate []interface{}
|
||||
GetUserPermissions []interface{}
|
||||
GetUserRoles []interface{}
|
||||
IsDisabled []interface{}
|
||||
DeclareFixedRoles []interface{}
|
||||
GetUserBuiltInRoles []interface{}
|
||||
RegisterFixedRoles []interface{}
|
||||
LinkAPIKeyToServiceAccount []interface{}
|
||||
}
|
||||
|
||||
type Mock struct {
|
||||
// Unless an override is provided, user will be returned by CloneUserToServiceAccount
|
||||
createduser *models.User
|
||||
// Unless an override is provided, permissions will be returned by GetUserPermissions
|
||||
permissions []*accesscontrol.Permission
|
||||
// Unless an override is provided, roles will be returned by GetUserRoles
|
||||
roles []*accesscontrol.RoleDTO
|
||||
// Unless an override is provided, disabled will be returned by IsDisabled
|
||||
disabled bool
|
||||
// Unless an override is provided, builtInRoles will be returned by GetUserBuiltInRoles
|
||||
@@ -34,12 +41,15 @@ type Mock struct {
|
||||
Calls Calls
|
||||
|
||||
// Override functions
|
||||
EvaluateFunc func(context.Context, *models.SignedInUser, accesscontrol.Evaluator) (bool, error)
|
||||
GetUserPermissionsFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.Permission, error)
|
||||
IsDisabledFunc func() bool
|
||||
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
|
||||
GetUserBuiltInRolesFunc func(user *models.SignedInUser) []string
|
||||
RegisterFixedRolesFunc func() error
|
||||
CloneUserToServiceAccountFunc func(context.Context, *models.SignedInUser) (*models.User, error)
|
||||
LinkAPIKeyToServiceAccountFunc func(context.Context, *models.ApiKey, *models.User) error
|
||||
EvaluateFunc func(context.Context, *models.SignedInUser, accesscontrol.Evaluator) (bool, error)
|
||||
GetUserPermissionsFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.Permission, error)
|
||||
GetUserRolesFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.RoleDTO, error)
|
||||
IsDisabledFunc func() bool
|
||||
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
|
||||
GetUserBuiltInRolesFunc func(user *models.SignedInUser) []string
|
||||
RegisterFixedRolesFunc func() error
|
||||
}
|
||||
|
||||
// Ensure the mock stays in line with the interface
|
||||
@@ -99,6 +109,36 @@ func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser
|
||||
return m.permissions, nil
|
||||
}
|
||||
|
||||
func (m *Mock) GetUserRoles(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.RoleDTO, error) {
|
||||
m.Calls.GetUserRoles = append(m.Calls.GetUserRoles, []interface{}{ctx, user})
|
||||
// Use override if provided
|
||||
if m.GetUserRolesFunc != nil {
|
||||
return m.GetUserRolesFunc(ctx, user)
|
||||
}
|
||||
// Otherwise return the Roles list
|
||||
return m.roles, nil
|
||||
}
|
||||
|
||||
func (m *Mock) CloneUserToServiceAccount(ctx context.Context, user *models.SignedInUser) (*models.User, error) {
|
||||
m.Calls.CloneUserToServiceAccount = append(m.Calls.CloneUserToServiceAccount, []interface{}{ctx, user})
|
||||
// Use override if provided
|
||||
if m.CloneUserToServiceAccountFunc != nil {
|
||||
return m.CloneUserToServiceAccountFunc(ctx, user)
|
||||
}
|
||||
// Otherwise return the user
|
||||
return m.createduser, nil
|
||||
}
|
||||
|
||||
func (m *Mock) LinkAPIKeyToServiceAccount(ctx context.Context, apikey *models.ApiKey, service_account *models.User) error {
|
||||
m.Calls.LinkAPIKeyToServiceAccount = append(m.Calls.LinkAPIKeyToServiceAccount, []interface{}{ctx, apikey, service_account})
|
||||
// Use override if provided
|
||||
if m.LinkAPIKeyToServiceAccountFunc != nil {
|
||||
return m.LinkAPIKeyToServiceAccountFunc(ctx, apikey, service_account)
|
||||
}
|
||||
// Otherwise return the default
|
||||
return nil
|
||||
}
|
||||
|
||||
// Middleware checks if service disabled or not to switch to fallback authorization.
|
||||
// This mock return m.disabled unless an override is provided.
|
||||
func (m *Mock) IsDisabled() bool {
|
||||
|
||||
@@ -2,6 +2,7 @@ package ossaccesscontrol
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
@@ -71,6 +72,21 @@ func (ac *OSSAccessControlService) Evaluate(ctx context.Context, user *models.Si
|
||||
return evaluator.Evaluate(accesscontrol.GroupScopesByAction(permissions))
|
||||
}
|
||||
|
||||
// GetUserRoles returns user permissions based on built-in roles
|
||||
func (ac *OSSAccessControlService) GetUserRoles(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.RoleDTO, error) {
|
||||
return nil, errors.New("unsupported function") //OSS users will continue to use builtin roles via GetUserPermissions
|
||||
}
|
||||
|
||||
// CloneUserToServiceAccount creates a service account with permissions based on a user
|
||||
func (ac *OSSAccessControlService) CloneUserToServiceAccount(ctx context.Context, user *models.SignedInUser) (*models.User, error) {
|
||||
return nil, errors.New("clone user not implemented yet in service accounts") //Please switch on Enterprise to test this
|
||||
}
|
||||
|
||||
// Link creates a service account with permissions based on a user
|
||||
func (ac *OSSAccessControlService) LinkAPIKeyToServiceAccount(context.Context, *models.ApiKey, *models.User) error {
|
||||
return errors.New("link SA not implemented yet in service accounts") //Please switch on Enterprise to test this
|
||||
}
|
||||
|
||||
// GetUserPermissions returns user permissions based on built-in roles
|
||||
func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
|
||||
|
||||
Reference in New Issue
Block a user