The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/accesscontrol/ossaccesscontrol/ossaccesscontrol.go

204 lines
6.4 KiB

package ossaccesscontrol
import (
"context"
"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/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/api"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
)
func ProvideService(
features featuremgmt.FeatureToggles, cfg *setting.Cfg,
store accesscontrol.PermissionsStore, routeRegister routing.RouteRegister,
) (*OSSAccessControlService, error) {
var errDeclareRoles error
s := ProvideOSSAccessControl(cfg, store)
if !s.IsDisabled() {
api := api.AccessControlAPI{
RouteRegister: routeRegister,
AccessControl: s,
}
api.RegisterAPIEndpoints()
errDeclareRoles = accesscontrol.DeclareFixedRoles(s)
}
return s, errDeclareRoles
}
func ProvideOSSAccessControl(cfg *setting.Cfg, store accesscontrol.PermissionsStore) *OSSAccessControlService {
s := &OSSAccessControlService{
cfg: cfg,
store: store,
log: log.New("accesscontrol"),
scopeResolvers: accesscontrol.NewScopeResolvers(),
roles: accesscontrol.BuildBasicRoleDefinitions(),
}
return s
}
// OSSAccessControlService is the service implementing role based access control.
type OSSAccessControlService struct {
log log.Logger
cfg *setting.Cfg
scopeResolvers accesscontrol.ScopeResolvers
store accesscontrol.PermissionsStore
registrations accesscontrol.RegistrationList
roles map[string]*accesscontrol.RoleDTO
}
func (ac *OSSAccessControlService) IsDisabled() bool {
if ac.cfg == nil {
return true
}
return !ac.cfg.RBACEnabled
}
func (ac *OSSAccessControlService) GetUsageStats(_ context.Context) map[string]interface{} {
return map[string]interface{}{
"stats.oss.accesscontrol.enabled.count": ac.getUsageMetrics(),
}
}
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 *user.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.scopeResolvers.GetScopeAttributeMutator(user.OrgID)
resolvedEvaluator, err := evaluator.MutateScopes(ctx, attributeMutator)
if err != nil {
return false, err
}
return resolvedEvaluator.Evaluate(user.Permissions[user.OrgID]), nil
}
// GetUserPermissions returns user permissions based on built-in roles
func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *user.SignedInUser, _ accesscontrol.Options) ([]accesscontrol.Permission, error) {
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
defer timer.ObserveDuration()
permissions := ac.getFixedPermissions(ctx, user)
dbPermissions, err := ac.store.GetUserPermissions(ctx, accesscontrol.GetUserPermissionsQuery{
OrgID: user.OrgID,
UserID: user.UserID,
Roles: accesscontrol.GetOrgRoles(ac.cfg, user),
Actions: append(TeamAdminActions, append(DashboardAdminActions, FolderAdminActions...)...),
})
if err != nil {
return nil, err
}
permissions = append(permissions, dbPermissions...)
keywordMutator := ac.scopeResolvers.GetScopeKeywordMutator(user)
for i := range permissions {
// if the permission has a keyword in its scope it will be resolved
permissions[i].Scope, err = keywordMutator(ctx, permissions[i].Scope)
if err != nil {
return nil, err
}
}
return permissions, nil
}
func (ac *OSSAccessControlService) getFixedPermissions(ctx context.Context, user *user.SignedInUser) []accesscontrol.Permission {
permissions := make([]accesscontrol.Permission, 0)
for _, builtin := range accesscontrol.GetOrgRoles(ac.cfg, user) {
if basicRole, ok := ac.roles[builtin]; ok {
permissions = append(permissions, basicRole.Permissions...)
}
}
return permissions
}
// RegisterFixedRoles registers all declared roles in RAM
func (ac *OSSAccessControlService) RegisterFixedRoles(ctx context.Context) 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) {
for br := range accesscontrol.BuiltInRolesWithParents(builtInRoles) {
if basicRole, ok := ac.roles[br]; ok {
basicRole.Permissions = append(basicRole.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
}
// RegisterScopeAttributeResolver allows the caller to register scope resolvers for a
// specific scope prefix (ex: datasources:name:)
func (ac *OSSAccessControlService) RegisterScopeAttributeResolver(scopePrefix string, resolver accesscontrol.ScopeAttributeResolver) {
ac.scopeResolvers.AddScopeAttributeResolver(scopePrefix, resolver)
}
func (ac *OSSAccessControlService) DeleteUserPermissions(ctx context.Context, userID int64) error {
return ac.store.DeleteUserPermissions(ctx, userID)
}