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/scope.go

200 lines
5.7 KiB

package accesscontrol
import (
"fmt"
"strconv"
"strings"
)
const (
maxPrefixParts = 2
)
// SplitScope returns kind, attribute and Identifier
func SplitScope(scope string) (string, string, string) {
if scope == "" {
return "", "", ""
}
fragments := strings.Split(scope, ":")
switch l := len(fragments); l {
case 1: // Splitting a wildcard scope "*" -> kind: "*"; attribute: "*"; identifier: "*"
return fragments[0], fragments[0], fragments[0]
case 2: // Splitting a wildcard scope with specified kind "dashboards:*" -> kind: "dashboards"; attribute: "*"; identifier: "*"
return fragments[0], fragments[1], fragments[1]
default: // Splitting a scope with all fields specified "dashboards:uid:my_dash" -> kind: "dashboards"; attribute: "uid"; identifier: "my_dash"
return fragments[0], fragments[1], strings.Join(fragments[2:], ":")
}
}
func ParseScopeID(scope string) (int64, error) {
id, err := strconv.ParseInt(ScopeSuffix(scope), 10, 64)
if err != nil {
return 0, ErrInvalidScope
}
return id, nil
}
func ParseScopeUID(scope string) (string, error) {
uid := ScopeSuffix(scope)
if len(uid) == 0 {
return "", ErrInvalidScope
}
return uid, nil
}
func ScopeSuffix(scope string) string {
return scope[len(ScopePrefix(scope)):]
}
func GetResourceScope(resource string, resourceID string) string {
return Scope(resource, "id", resourceID)
}
func GetResourceScopeUID(resource string, resourceID string) string {
return Scope(resource, "uid", resourceID)
}
func GetResourceScopeName(resource string, resourceID string) string {
return Scope(resource, "name", resourceID)
}
func GetResourceScopeType(resource string, typeName string) string {
return Scope(resource, "type", typeName)
}
func GetResourceAllScope(resource string) string {
return Scope(resource, "*")
}
func GetResourceAllIDScope(resource string) string {
return Scope(resource, "id", "*")
}
// Scope builds scope from parts
// e.g. Scope("users", "*") return "users:*"
func Scope(parts ...string) string {
b := strings.Builder{}
for i, c := range parts {
if i != 0 {
b.WriteRune(':')
}
b.WriteString(c)
}
return b.String()
}
// Parameter returns injectable scope part, based on URL parameters.
// e.g. Scope("users", Parameter(":id")) or "users:" + Parameter(":id")
func Parameter(key string) string {
return fmt.Sprintf(`{{ index .URLParams "%s" }}`, key)
}
// Field returns an injectable scope part for selected fields from the request's context available in accesscontrol.ScopeParams.
// e.g. Scope("orgs", Parameter("OrgID")) or "orgs:" + Parameter("OrgID")
func Field(key string) string {
return fmt.Sprintf(`{{ .%s }}`, key)
}
// ScopePrefix returns the prefix associated to a given scope
// we assume prefixes are all in the form <resource>:<attribute>:<value>
// ex: "datasources:name:test" returns "datasources:name:"
func ScopePrefix(scope string) string {
parts := strings.Split(scope, ":")
// We assume prefixes don't have more than maxPrefixParts parts
if len(parts) > maxPrefixParts {
parts = append(parts[:maxPrefixParts], "")
}
return strings.Join(parts, ":")
}
// ScopeProvider provides methods that construct scopes
type ScopeProvider interface {
GetResourceScope(resourceID string) string
GetResourceScopeUID(resourceID string) string
GetResourceScopeName(resourceID string) string
GetResourceScopeType(typeName string) string
GetResourceAllScope() string
GetResourceAllIDScope() string
}
type scopeProviderImpl struct {
root string
}
// NewScopeProvider creates a new ScopeProvider that is configured with specific root scope
func NewScopeProvider(root string) ScopeProvider {
return &scopeProviderImpl{
root: root,
}
}
// GetResourceScope returns scope that has the format "<rootScope>:id:<resourceID>"
func (s scopeProviderImpl) GetResourceScope(resourceID string) string {
return GetResourceScope(s.root, resourceID)
}
// GetResourceScopeUID returns scope that has the format "<rootScope>:uid:<resourceID>"
func (s scopeProviderImpl) GetResourceScopeUID(resourceID string) string {
return GetResourceScopeUID(s.root, resourceID)
}
// GetResourceScopeName returns scope that has the format "<rootScope>:name:<resourceID>"
func (s scopeProviderImpl) GetResourceScopeName(resourceID string) string {
return GetResourceScopeName(s.root, resourceID)
}
// GetResourceScopeType returns scope that has the format "<rootScope>:type:<typeName>"
func (s scopeProviderImpl) GetResourceScopeType(typeName string) string {
return GetResourceScopeType(s.root, typeName)
}
// GetResourceAllScope returns scope that has the format "<rootScope>:*"
func (s scopeProviderImpl) GetResourceAllScope() string {
return GetResourceAllScope(s.root)
}
// GetResourceAllIDScope returns scope that has the format "<rootScope>:id:*"
func (s scopeProviderImpl) GetResourceAllIDScope() string {
return GetResourceAllIDScope(s.root)
}
func WildcardsFromPrefix(prefix string) Wildcards {
return WildcardsFromPrefixes([]string{prefix})
}
// WildcardsFromPrefixes generates valid wildcards from prefixes
// datasource:uid: => "*", "datasource:*", "datasource:uid:*"
func WildcardsFromPrefixes(prefixes []string) Wildcards {
var b strings.Builder
wildcards := Wildcards{"*"}
for _, prefix := range prefixes {
parts := strings.Split(prefix, ":")
for _, p := range parts {
if p == "" {
continue
}
b.WriteString(p)
b.WriteRune(':')
wildcards = append(wildcards, b.String()+"*")
}
b.Reset()
}
return wildcards
}
type Wildcards []string
// Contains check if wildcards contains scope
func (wildcards Wildcards) Contains(scope string) bool {
for _, w := range wildcards {
if scope == w {
return true
}
}
return false
}
func isWildcard(scope string) bool {
return scope == "*" || strings.HasSuffix(scope, ":*")
}