Files
grafana/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol.go
Gabriel MABILLE f7305965a4 AccessControl: Remove package lists for roles and grants (#47141)
* AccessControl: Remove package variables for roles and grants

Co-authored-by: Jguer <joao.guerreiro@grafana.com>

* Check for inheritance during role registration

Co-authored-by: Jguer <joao.guerreiro@grafana.com>

* Moving back role definition to accessscontrol

* Make settings reader role public

Co-authored-by: Jguer <joao.guerreiro@grafana.com>

* Nits

Co-authored-by: Jguer <joao.guerreiro@grafana.com>

* Forgot to update this

* Account for declaration error

* Fixing pkg/api init ossac

* Account for error in tests

* Update test to verify inheritance

* Nits.

* Place br inheritance behind a feature toggle

* Parent -> Parents

* Nit.

Co-authored-by: Jguer <joao.guerreiro@grafana.com>
2022-04-06 09:31:14 +02:00

278 lines
8.8 KiB
Go

package ossaccesscontrol
import (
"context"
"errors"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/api"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/prometheus/client_golang/prometheus"
)
func ProvideService(features featuremgmt.FeatureToggles, usageStats usagestats.Service,
provider accesscontrol.PermissionsProvider, routeRegister routing.RouteRegister) (*OSSAccessControlService, error) {
var errDeclareRoles error
s := ProvideOSSAccessControl(features, provider)
s.registerUsageMetrics(usageStats)
if !s.IsDisabled() {
api := api.AccessControlAPI{
RouteRegister: routeRegister,
AccessControl: s,
}
api.RegisterAPIEndpoints()
errDeclareRoles = accesscontrol.DeclareFixedRoles(s)
}
return s, errDeclareRoles
}
func macroRoles() map[string]*accesscontrol.RoleDTO {
return map[string]*accesscontrol.RoleDTO{
string(models.ROLE_ADMIN): {
Name: "fixed:builtins:admin",
DisplayName: string(models.ROLE_ADMIN),
Description: "Admin role",
Group: "Basic",
Version: 1,
Permissions: []accesscontrol.Permission{},
},
string(models.ROLE_EDITOR): {
Name: "fixed:builtins:editor",
DisplayName: string(models.ROLE_EDITOR),
Description: "Editor role",
Group: "Basic",
Version: 1,
Permissions: []accesscontrol.Permission{},
},
string(models.ROLE_VIEWER): {
Name: "fixed:builtins:viewer",
DisplayName: string(models.ROLE_VIEWER),
Description: "Viewer role",
Group: "Basic",
Version: 1,
Permissions: []accesscontrol.Permission{},
},
accesscontrol.RoleGrafanaAdmin: {
Name: "fixed:builtins:grafana_admin",
DisplayName: accesscontrol.RoleGrafanaAdmin,
Description: "Grafana Admin role",
Group: "Basic",
Version: 1,
Permissions: []accesscontrol.Permission{},
},
}
}
// ProvideOSSAccessControl creates an oss implementation of access control without usage stats registration
func ProvideOSSAccessControl(features featuremgmt.FeatureToggles, provider accesscontrol.PermissionsProvider) *OSSAccessControlService {
s := &OSSAccessControlService{
features: features,
provider: provider,
log: log.New("accesscontrol"),
scopeResolver: accesscontrol.NewScopeResolver(),
roles: macroRoles(),
}
return s
}
// OSSAccessControlService is the service implementing role based access control.
type OSSAccessControlService struct {
log log.Logger
features featuremgmt.FeatureToggles
scopeResolver accesscontrol.ScopeResolver
provider accesscontrol.PermissionsProvider
registrations accesscontrol.RegistrationList
roles map[string]*accesscontrol.RoleDTO
}
func (ac *OSSAccessControlService) IsDisabled() bool {
if ac.features == nil {
return true
}
return !ac.features.IsEnabled(featuremgmt.FlagAccesscontrol)
}
func (ac *OSSAccessControlService) registerUsageMetrics(usageStats usagestats.Service) {
usageStats.RegisterMetricsFunc(func(context.Context) (map[string]interface{}, error) {
return map[string]interface{}{
"stats.oss.accesscontrol.enabled.count": ac.getUsageMetrics(),
}, nil
})
}
func (ac *OSSAccessControlService) getUsageMetrics() interface{} {
if ac.IsDisabled() {
return 0
}
return 1
}
// Evaluate evaluates access to the given resources
func (ac *OSSAccessControlService) Evaluate(ctx context.Context, user *models.SignedInUser, evaluator accesscontrol.Evaluator) (bool, error) {
timer := prometheus.NewTimer(metrics.MAccessEvaluationsSummary)
defer timer.ObserveDuration()
metrics.MAccessEvaluationCount.Inc()
if user.Permissions == nil {
user.Permissions = map[int64]map[string][]string{}
}
if _, ok := user.Permissions[user.OrgId]; !ok {
permissions, err := ac.GetUserPermissions(ctx, user, accesscontrol.Options{ReloadCache: true})
if err != nil {
return false, err
}
user.Permissions[user.OrgId] = accesscontrol.GroupScopesByAction(permissions)
}
attributeMutator := ac.scopeResolver.GetResolveAttributeScopeMutator(user.OrgId)
resolvedEvaluator, err := evaluator.MutateScopes(ctx, attributeMutator)
if err != nil {
return false, err
}
return resolvedEvaluator.Evaluate(user.Permissions[user.OrgId])
}
// 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
}
// GetUserPermissions returns user permissions based on built-in roles
func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
defer timer.ObserveDuration()
permissions := ac.getFixedPermissions(ctx, user)
dbPermissions, err := ac.provider.GetUserPermissions(ctx, accesscontrol.GetUserPermissionsQuery{
OrgID: user.OrgId,
UserID: user.UserId,
Roles: ac.GetUserBuiltInRoles(user),
Actions: append(TeamAdminActions, append(DashboardAdminActions, FolderAdminActions...)...),
})
if err != nil {
return nil, err
}
permissions = append(permissions, dbPermissions...)
resolved := make([]*accesscontrol.Permission, 0, len(permissions))
keywordMutator := ac.scopeResolver.GetResolveKeywordScopeMutator(user)
for _, p := range permissions {
// if the permission has a keyword in its scope it will be resolved
p.Scope, err = keywordMutator(ctx, p.Scope)
if err != nil {
return nil, err
}
resolved = append(resolved, p)
}
return resolved, nil
}
func (ac *OSSAccessControlService) getFixedPermissions(ctx context.Context, user *models.SignedInUser) []*accesscontrol.Permission {
permissions := make([]*accesscontrol.Permission, 0)
for _, builtin := range ac.GetUserBuiltInRoles(user) {
if macroRole, ok := ac.roles[builtin]; ok {
for i := range macroRole.Permissions {
permissions = append(permissions, &macroRole.Permissions[i])
}
}
}
return permissions
}
func (ac *OSSAccessControlService) GetUserBuiltInRoles(user *models.SignedInUser) []string {
builtInRoles := []string{string(user.OrgRole)}
// With built-in role simplifying, inheritance is performed upon role registration.
if !ac.features.IsEnabled(featuremgmt.FlagAccesscontrolBuiltins) {
for _, br := range user.OrgRole.Children() {
builtInRoles = append(builtInRoles, string(br))
}
}
if user.IsGrafanaAdmin {
builtInRoles = append(builtInRoles, accesscontrol.RoleGrafanaAdmin)
}
return builtInRoles
}
// RegisterFixedRoles registers all declared roles in RAM
func (ac *OSSAccessControlService) RegisterFixedRoles() error {
// If accesscontrol is disabled no need to register roles
if ac.IsDisabled() {
return nil
}
ac.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
ac.registerFixedRole(registration.Role, registration.Grants)
return true
})
return nil
}
// RegisterFixedRole saves a fixed role and assigns it to built-in roles
func (ac *OSSAccessControlService) registerFixedRole(role accesscontrol.RoleDTO, builtInRoles []string) {
// Inheritance
brs := map[string]struct{}{}
for _, builtInRole := range builtInRoles {
brs[builtInRole] = struct{}{}
if builtInRole != accesscontrol.RoleGrafanaAdmin {
for _, parent := range models.RoleType(builtInRole).Parents() {
brs[string(parent)] = struct{}{}
}
}
}
for br := range brs {
if macroRole, ok := ac.roles[br]; ok {
macroRole.Permissions = append(macroRole.Permissions, role.Permissions...)
} else {
ac.log.Error("Unknown builtin role", "builtInRole", br)
}
}
}
// DeclareFixedRoles allow the caller to declare, to the service, fixed roles and their assignments
// to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
func (ac *OSSAccessControlService) DeclareFixedRoles(registrations ...accesscontrol.RoleRegistration) error {
// If accesscontrol is disabled no need to register roles
if ac.IsDisabled() {
return nil
}
for _, r := range registrations {
err := accesscontrol.ValidateFixedRole(r.Role)
if err != nil {
return err
}
err = accesscontrol.ValidateBuiltInRoles(r.Grants)
if err != nil {
return err
}
ac.registrations.Append(r)
}
return nil
}
// RegisterAttributeScopeResolver allows the caller to register scope resolvers for a
// specific scope prefix (ex: datasources:name:)
func (ac *OSSAccessControlService) RegisterAttributeScopeResolver(scopePrefix string, resolver accesscontrol.AttributeScopeResolveFunc) {
ac.scopeResolver.AddAttributeResolver(scopePrefix, resolver)
}