2021-03-22 15:22:48 +03:00
|
|
|
package accesscontrol
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2022-03-16 10:43:44 +01:00
|
|
|
"fmt"
|
2021-08-23 14:03:20 +02:00
|
|
|
"strings"
|
2021-03-22 15:22:48 +03:00
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2022-04-28 13:06:49 +04:00
|
|
|
"github.com/grafana/grafana/pkg/registry"
|
2022-08-10 11:56:48 +02:00
|
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
|
|
|
"github.com/grafana/grafana/pkg/services/user"
|
2022-05-05 16:31:14 +01:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2021-03-22 15:22:48 +03:00
|
|
|
)
|
|
|
|
|
|
2022-02-11 17:40:43 +01:00
|
|
|
type Options struct {
|
|
|
|
|
ReloadCache bool
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-22 15:22:48 +03:00
|
|
|
type AccessControl interface {
|
2022-04-28 13:06:49 +04:00
|
|
|
registry.ProvidesUsageStats
|
|
|
|
|
|
2021-08-24 11:36:28 +02:00
|
|
|
// Evaluate evaluates access to the given resources.
|
2022-08-10 11:56:48 +02:00
|
|
|
Evaluate(ctx context.Context, user *user.SignedInUser, evaluator Evaluator) (bool, error)
|
2021-03-22 15:22:48 +03:00
|
|
|
|
2022-05-12 17:15:18 +02:00
|
|
|
// GetUserPermissions returns user permissions with only action and scope fields set.
|
2022-08-10 11:56:48 +02:00
|
|
|
GetUserPermissions(ctx context.Context, user *user.SignedInUser, options Options) ([]Permission, error)
|
2021-03-22 15:22:48 +03:00
|
|
|
|
2021-08-24 11:36:28 +02:00
|
|
|
//IsDisabled returns if access control is enabled or not
|
2021-04-06 16:49:09 +03:00
|
|
|
IsDisabled() bool
|
2021-08-04 14:44:37 +02:00
|
|
|
|
2022-01-18 17:34:35 +01:00
|
|
|
// DeclareFixedRoles allows the caller to declare, to the service, fixed roles and their
|
2021-08-04 14:44:37 +02:00
|
|
|
// assignments to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
|
|
|
|
|
DeclareFixedRoles(...RoleRegistration) error
|
2022-01-18 17:34:35 +01:00
|
|
|
|
2022-05-02 09:29:30 +02:00
|
|
|
// RegisterScopeAttributeResolver allows the caller to register a scope resolver for a
|
2022-01-18 17:34:35 +01:00
|
|
|
// specific scope prefix (ex: datasources:name:)
|
2022-05-02 09:29:30 +02:00
|
|
|
RegisterScopeAttributeResolver(scopePrefix string, resolver ScopeAttributeResolver)
|
2022-07-19 16:01:05 +02:00
|
|
|
|
|
|
|
|
DeleteUserPermissions(ctx context.Context, userID int64) error
|
2021-03-22 15:22:48 +03:00
|
|
|
}
|
2021-04-16 16:02:16 +03:00
|
|
|
|
2022-05-30 17:48:12 +02:00
|
|
|
type RoleRegistry interface {
|
|
|
|
|
// RegisterFixedRoles registers all roles declared to AccessControl
|
|
|
|
|
RegisterFixedRoles(ctx context.Context) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PermissionsStore interface {
|
2022-05-12 17:15:18 +02:00
|
|
|
// GetUserPermissions returns user permissions with only action and scope fields set.
|
2022-06-14 10:17:48 +02:00
|
|
|
GetUserPermissions(ctx context.Context, query GetUserPermissionsQuery) ([]Permission, error)
|
2022-07-19 16:01:05 +02:00
|
|
|
DeleteUserPermissions(ctx context.Context, userID int64) error
|
2021-11-11 14:02:53 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:48:47 +02:00
|
|
|
type TeamPermissionsService interface {
|
2022-08-10 11:56:48 +02:00
|
|
|
GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error)
|
2022-05-10 15:48:47 +02:00
|
|
|
SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type FolderPermissionsService interface {
|
|
|
|
|
PermissionsService
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type DashboardPermissionsService interface {
|
|
|
|
|
PermissionsService
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type DatasourcePermissionsService interface {
|
|
|
|
|
PermissionsService
|
2022-02-17 14:03:45 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-08 10:53:18 +01:00
|
|
|
type ServiceAccountPermissionsService interface {
|
|
|
|
|
PermissionsService
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-17 14:03:45 +01:00
|
|
|
type PermissionsService interface {
|
2021-12-20 09:52:24 +01:00
|
|
|
// GetPermissions returns all permissions for given resourceID
|
2022-08-10 11:56:48 +02:00
|
|
|
GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error)
|
2021-12-20 09:52:24 +01:00
|
|
|
// SetUserPermission sets permission on resource for a user
|
2022-02-03 15:27:05 +00:00
|
|
|
SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
|
2021-12-20 09:52:24 +01:00
|
|
|
// SetTeamPermission sets permission on resource for a team
|
2022-01-25 17:12:00 +01:00
|
|
|
SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*ResourcePermission, error)
|
2021-12-20 09:52:24 +01:00
|
|
|
// SetBuiltInRolePermission sets permission on resource for a built-in role (Admin, Editor, Viewer)
|
2022-01-25 17:12:00 +01:00
|
|
|
SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole string, resourceID string, permission string) (*ResourcePermission, error)
|
2022-02-07 17:04:32 +01:00
|
|
|
// SetPermissions sets several permissions on resource for either built-in role, team or user
|
|
|
|
|
SetPermissions(ctx context.Context, orgID int64, resourceID string, commands ...SetResourcePermissionCommand) ([]ResourcePermission, error)
|
2022-03-03 15:05:47 +01:00
|
|
|
// MapActions will map actions for a ResourcePermissions to it's "friendly" name configured in PermissionsToActions map.
|
|
|
|
|
MapActions(permission ResourcePermission) string
|
2021-11-11 14:02:53 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-03 15:27:05 +00:00
|
|
|
type User struct {
|
|
|
|
|
ID int64
|
|
|
|
|
IsExternal bool
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-15 12:08:15 +01:00
|
|
|
// Metadata contains user accesses for a given resource
|
|
|
|
|
// Ex: map[string]bool{"create":true, "delete": true}
|
|
|
|
|
type Metadata map[string]bool
|
|
|
|
|
|
2021-11-18 14:10:38 +01:00
|
|
|
// HasGlobalAccess checks user access with globally assigned permissions only
|
|
|
|
|
func HasGlobalAccess(ac AccessControl, c *models.ReqContext) func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
|
|
|
|
|
return func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
|
|
|
|
|
if ac.IsDisabled() {
|
|
|
|
|
return fallback(c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userCopy := *c.SignedInUser
|
2022-08-11 13:28:55 +02:00
|
|
|
userCopy.OrgID = GlobalOrgID
|
2021-11-18 14:10:38 +01:00
|
|
|
userCopy.OrgRole = ""
|
|
|
|
|
userCopy.OrgName = ""
|
|
|
|
|
hasAccess, err := ac.Evaluate(c.Req.Context(), &userCopy, evaluator)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.Logger.Error("Error from access control system", "error", err)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hasAccess
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-24 11:36:28 +02:00
|
|
|
func HasAccess(ac AccessControl, c *models.ReqContext) func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
|
|
|
|
|
return func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
|
2021-04-19 12:23:29 +03:00
|
|
|
if ac.IsDisabled() {
|
|
|
|
|
return fallback(c)
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-24 11:36:28 +02:00
|
|
|
hasAccess, err := ac.Evaluate(c.Req.Context(), c.SignedInUser, evaluator)
|
2021-04-19 12:23:29 +03:00
|
|
|
if err != nil {
|
|
|
|
|
c.Logger.Error("Error from access control system", "error", err)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hasAccess
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 15:05:47 +01:00
|
|
|
var ReqSignedIn = func(c *models.ReqContext) bool {
|
|
|
|
|
return c.IsSignedIn
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-19 12:23:29 +03:00
|
|
|
var ReqGrafanaAdmin = func(c *models.ReqContext) bool {
|
|
|
|
|
return c.IsGrafanaAdmin
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-10 11:56:48 +02:00
|
|
|
// ReqViewer returns true if the current user has org.RoleViewer. Note: this can be anonymous user as well
|
2022-05-19 09:22:26 -04:00
|
|
|
var ReqViewer = func(c *models.ReqContext) bool {
|
2022-08-10 11:56:48 +02:00
|
|
|
return c.OrgRole.Includes(org.RoleViewer)
|
2022-05-19 09:22:26 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 13:19:41 +03:00
|
|
|
var ReqOrgAdmin = func(c *models.ReqContext) bool {
|
2022-08-10 11:56:48 +02:00
|
|
|
return c.OrgRole == org.RoleAdmin
|
2021-04-22 13:19:41 +03:00
|
|
|
}
|
|
|
|
|
|
2022-03-03 15:05:47 +01:00
|
|
|
var ReqOrgAdminOrEditor = func(c *models.ReqContext) bool {
|
2022-08-10 11:56:48 +02:00
|
|
|
return c.OrgRole == org.RoleAdmin || c.OrgRole == org.RoleEditor
|
2022-03-03 15:05:47 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-14 10:17:48 +02:00
|
|
|
func BuildPermissionsMap(permissions []Permission) map[string]bool {
|
2021-04-22 13:19:41 +03:00
|
|
|
permissionsMap := make(map[string]bool)
|
2021-04-16 16:02:16 +03:00
|
|
|
for _, p := range permissions {
|
2021-04-22 13:19:41 +03:00
|
|
|
permissionsMap[p.Action] = true
|
2021-04-16 16:02:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return permissionsMap
|
|
|
|
|
}
|
2021-08-23 14:03:20 +02:00
|
|
|
|
2021-08-24 11:36:28 +02:00
|
|
|
// GroupScopesByAction will group scopes on action
|
2022-06-14 10:17:48 +02:00
|
|
|
func GroupScopesByAction(permissions []Permission) map[string][]string {
|
2021-12-14 16:05:59 +01:00
|
|
|
m := make(map[string][]string)
|
2021-08-24 11:36:28 +02:00
|
|
|
for _, p := range permissions {
|
2021-12-14 16:05:59 +01:00
|
|
|
m[p.Action] = append(m[p.Action], p.Scope)
|
2021-08-24 11:36:28 +02:00
|
|
|
}
|
|
|
|
|
return m
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-23 14:03:20 +02:00
|
|
|
func ValidateScope(scope string) bool {
|
|
|
|
|
prefix, last := scope[:len(scope)-1], scope[len(scope)-1]
|
|
|
|
|
// verify that last char is either ':' or '/' if last character of scope is '*'
|
|
|
|
|
if len(prefix) > 0 && last == '*' {
|
|
|
|
|
lastChar := prefix[len(prefix)-1]
|
|
|
|
|
if lastChar != ':' && lastChar != '/' {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return !strings.ContainsAny(prefix, "*?")
|
|
|
|
|
}
|
2021-12-15 12:08:15 +01:00
|
|
|
|
|
|
|
|
func addActionToMetadata(allMetadata map[string]Metadata, action, id string) map[string]Metadata {
|
|
|
|
|
metadata, initialized := allMetadata[id]
|
|
|
|
|
if !initialized {
|
|
|
|
|
metadata = Metadata{action: true}
|
|
|
|
|
} else {
|
|
|
|
|
metadata[action] = true
|
|
|
|
|
}
|
|
|
|
|
allMetadata[id] = metadata
|
|
|
|
|
return allMetadata
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetResourcesMetadata returns a map of accesscontrol metadata, listing for each resource, users available actions
|
2022-03-21 17:58:18 +01:00
|
|
|
func GetResourcesMetadata(ctx context.Context, permissions map[string][]string, prefix string, resourceIDs map[string]bool) map[string]Metadata {
|
|
|
|
|
rootPrefix, attributePrefix, ok := extractPrefixes(prefix)
|
|
|
|
|
if !ok {
|
|
|
|
|
return map[string]Metadata{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allScope := GetResourceAllScope(strings.TrimSuffix(rootPrefix, ":"))
|
|
|
|
|
allAttributeScope := Scope(strings.TrimSuffix(attributePrefix, ":"), "*")
|
2021-12-15 12:08:15 +01:00
|
|
|
|
2022-03-21 17:58:18 +01:00
|
|
|
// index of the attribute in the scope
|
|
|
|
|
attributeIndex := len(attributePrefix)
|
2021-12-15 12:08:15 +01:00
|
|
|
|
|
|
|
|
// Loop through permissions once
|
|
|
|
|
result := map[string]Metadata{}
|
2022-03-21 17:58:18 +01:00
|
|
|
|
2022-02-18 11:27:00 +01:00
|
|
|
for action, scopes := range permissions {
|
|
|
|
|
for _, scope := range scopes {
|
2022-03-21 17:58:18 +01:00
|
|
|
if scope == "*" || scope == allScope || scope == allAttributeScope {
|
2022-02-18 11:27:00 +01:00
|
|
|
// Add global action to all resources
|
2022-03-21 17:58:18 +01:00
|
|
|
for id := range resourceIDs {
|
2022-02-18 11:27:00 +01:00
|
|
|
result = addActionToMetadata(result, action, id)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-03-21 17:58:18 +01:00
|
|
|
if len(scope) > attributeIndex && strings.HasPrefix(scope, attributePrefix) && resourceIDs[scope[attributeIndex:]] {
|
2022-02-18 11:27:00 +01:00
|
|
|
// Add action to a specific resource
|
2022-03-21 17:58:18 +01:00
|
|
|
result = addActionToMetadata(result, action, scope[attributeIndex:])
|
2022-02-18 11:27:00 +01:00
|
|
|
}
|
2021-12-15 12:08:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 17:24:14 +01:00
|
|
|
return result
|
2021-12-15 12:08:15 +01:00
|
|
|
}
|
2022-03-16 10:43:44 +01:00
|
|
|
|
2022-05-17 15:51:44 +02:00
|
|
|
// MergeMeta will merge actions matching prefix of second metadata into first
|
|
|
|
|
func MergeMeta(prefix string, first Metadata, second Metadata) Metadata {
|
|
|
|
|
if first == nil {
|
|
|
|
|
first = Metadata{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for key := range second {
|
|
|
|
|
if strings.HasPrefix(key, prefix) {
|
|
|
|
|
first[key] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return first
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-16 10:43:44 +01:00
|
|
|
func ManagedUserRoleName(userID int64) string {
|
|
|
|
|
return fmt.Sprintf("managed:users:%d:permissions", userID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ManagedTeamRoleName(teamID int64) string {
|
|
|
|
|
return fmt.Sprintf("managed:teams:%d:permissions", teamID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ManagedBuiltInRoleName(builtInRole string) string {
|
|
|
|
|
return fmt.Sprintf("managed:builtins:%s:permissions", strings.ToLower(builtInRole))
|
|
|
|
|
}
|
2022-03-21 17:58:18 +01:00
|
|
|
|
|
|
|
|
func extractPrefixes(prefix string) (string, string, bool) {
|
|
|
|
|
parts := strings.Split(strings.TrimSuffix(prefix, ":"), ":")
|
|
|
|
|
if len(parts) != 2 {
|
|
|
|
|
return "", "", false
|
|
|
|
|
}
|
|
|
|
|
rootPrefix := parts[0] + ":"
|
|
|
|
|
attributePrefix := rootPrefix + parts[1] + ":"
|
|
|
|
|
return rootPrefix, attributePrefix, true
|
|
|
|
|
}
|
2022-05-05 16:31:14 +01:00
|
|
|
|
|
|
|
|
func IsDisabled(cfg *setting.Cfg) bool {
|
2022-05-16 03:45:41 -07:00
|
|
|
return !cfg.RBACEnabled
|
2022-05-05 16:31:14 +01:00
|
|
|
}
|
2022-06-02 16:10:41 +02:00
|
|
|
|
|
|
|
|
// GetOrgRoles returns legacy org roles for a user
|
2022-08-10 11:56:48 +02:00
|
|
|
func GetOrgRoles(cfg *setting.Cfg, user *user.SignedInUser) []string {
|
2022-06-02 16:10:41 +02:00
|
|
|
roles := []string{string(user.OrgRole)}
|
|
|
|
|
|
|
|
|
|
// With built-in role simplifying, inheritance is performed upon role registration.
|
|
|
|
|
if cfg.RBACBuiltInRoleAssignmentEnabled {
|
|
|
|
|
for _, br := range user.OrgRole.Children() {
|
|
|
|
|
roles = append(roles, string(br))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if user.IsGrafanaAdmin {
|
|
|
|
|
roles = append(roles, RoleGrafanaAdmin)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return roles
|
|
|
|
|
}
|