mirror of
https://github.com/grafana/grafana.git
synced 2024-11-27 11:20:27 -06:00
458371c8eb
* AccessControl: Extend scope parameters with extra params from context Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
195 lines
4.5 KiB
Go
195 lines
4.5 KiB
Go
package accesscontrol
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"strings"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
)
|
|
|
|
var logger = log.New("accesscontrol.evaluator")
|
|
|
|
type Evaluator interface {
|
|
// Evaluate permissions that are grouped by action
|
|
Evaluate(permissions map[string]map[string]struct{}) (bool, error)
|
|
// Inject params into the evaluator's templated scopes. e.g. "settings:" + eval.Parameters(":id") and returns a new Evaluator
|
|
Inject(params ScopeParams) (Evaluator, error)
|
|
// String returns a string representation of permission required by the evaluator
|
|
String() string
|
|
}
|
|
|
|
var _ Evaluator = new(permissionEvaluator)
|
|
|
|
// EvalPermission returns an evaluator that will require all scopes in combination with action to match
|
|
func EvalPermission(action string, scopes ...string) Evaluator {
|
|
return permissionEvaluator{Action: action, Scopes: scopes}
|
|
}
|
|
|
|
type permissionEvaluator struct {
|
|
Action string
|
|
Scopes []string
|
|
}
|
|
|
|
func (p permissionEvaluator) Evaluate(permissions map[string]map[string]struct{}) (bool, error) {
|
|
userScopes, ok := permissions[p.Action]
|
|
if !ok {
|
|
return false, nil
|
|
}
|
|
|
|
if len(p.Scopes) == 0 {
|
|
return true, nil
|
|
}
|
|
|
|
for _, target := range p.Scopes {
|
|
var err error
|
|
var matches bool
|
|
|
|
for scope := range userScopes {
|
|
matches, err = match(scope, target)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if matches {
|
|
break
|
|
}
|
|
}
|
|
if !matches {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func match(scope, target string) (bool, error) {
|
|
if scope == "" {
|
|
return false, nil
|
|
}
|
|
|
|
if !ValidateScope(scope) {
|
|
logger.Error(
|
|
"invalid scope",
|
|
"scope", scope,
|
|
"reason", "scopes should not contain meta-characters like * or ?, except in the last position",
|
|
)
|
|
return false, nil
|
|
}
|
|
|
|
prefix, last := scope[:len(scope)-1], scope[len(scope)-1]
|
|
//Prefix match
|
|
if last == '*' {
|
|
if strings.HasPrefix(target, prefix) {
|
|
logger.Debug("matched scope", "user scope", scope, "target scope", target)
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return scope == target, nil
|
|
}
|
|
|
|
func (p permissionEvaluator) Inject(params ScopeParams) (Evaluator, error) {
|
|
scopes := make([]string, 0, len(p.Scopes))
|
|
for _, scope := range p.Scopes {
|
|
tmpl, err := template.New("scope").Parse(scope)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var buf bytes.Buffer
|
|
if err = tmpl.Execute(&buf, params); err != nil {
|
|
return nil, err
|
|
}
|
|
scopes = append(scopes, buf.String())
|
|
}
|
|
return EvalPermission(p.Action, scopes...), nil
|
|
}
|
|
|
|
func (p permissionEvaluator) String() string {
|
|
return fmt.Sprintf("action:%s scopes:%s", p.Action, strings.Join(p.Scopes, ", "))
|
|
}
|
|
|
|
var _ Evaluator = new(allEvaluator)
|
|
|
|
// EvalAll returns evaluator that requires all passed evaluators to evaluate to true
|
|
func EvalAll(allOf ...Evaluator) Evaluator {
|
|
return allEvaluator{allOf: allOf}
|
|
}
|
|
|
|
type allEvaluator struct {
|
|
allOf []Evaluator
|
|
}
|
|
|
|
func (a allEvaluator) Evaluate(permissions map[string]map[string]struct{}) (bool, error) {
|
|
for _, e := range a.allOf {
|
|
if ok, err := e.Evaluate(permissions); !ok || err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (a allEvaluator) Inject(params ScopeParams) (Evaluator, error) {
|
|
var injected []Evaluator
|
|
for _, e := range a.allOf {
|
|
i, err := e.Inject(params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
injected = append(injected, i)
|
|
}
|
|
return EvalAll(injected...), nil
|
|
}
|
|
|
|
func (a allEvaluator) String() string {
|
|
permissions := make([]string, 0, len(a.allOf))
|
|
for _, e := range a.allOf {
|
|
permissions = append(permissions, e.String())
|
|
}
|
|
return fmt.Sprintf("all(%s)", strings.Join(permissions, " "))
|
|
}
|
|
|
|
var _ Evaluator = new(anyEvaluator)
|
|
|
|
// EvalAny returns evaluator that requires at least one of passed evaluators to evaluate to true
|
|
func EvalAny(anyOf ...Evaluator) Evaluator {
|
|
return anyEvaluator{anyOf: anyOf}
|
|
}
|
|
|
|
type anyEvaluator struct {
|
|
anyOf []Evaluator
|
|
}
|
|
|
|
func (a anyEvaluator) Evaluate(permissions map[string]map[string]struct{}) (bool, error) {
|
|
for _, e := range a.anyOf {
|
|
ok, err := e.Evaluate(permissions)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if ok {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (a anyEvaluator) Inject(params ScopeParams) (Evaluator, error) {
|
|
var injected []Evaluator
|
|
for _, e := range a.anyOf {
|
|
i, err := e.Inject(params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
injected = append(injected, i)
|
|
}
|
|
return EvalAny(injected...), nil
|
|
}
|
|
|
|
func (a anyEvaluator) String() string {
|
|
permissions := make([]string, 0, len(a.anyOf))
|
|
for _, e := range a.anyOf {
|
|
permissions = append(permissions, e.String())
|
|
}
|
|
return fmt.Sprintf("any(%s)", strings.Join(permissions, " "))
|
|
}
|