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

391 lines
11 KiB

package accesscontrol
import (
"encoding/json"
"strings"
"time"
"github.com/grafana/grafana/pkg/services/annotations"
)
// RoleRegistration stores a role and its assignments to built-in roles
// (Viewer, Editor, Admin, Grafana Admin)
type RoleRegistration struct {
Role RoleDTO
Grants []string
}
// Role is the model for Role in RBAC.
type Role struct {
ID int64 `json:"-" xorm:"pk autoincr 'id'"`
OrgID int64 `json:"-" xorm:"org_id"`
Version int64 `json:"version"`
UID string `xorm:"uid" json:"uid"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Group string `xorm:"group_name" json:"group"`
Description string `json:"description"`
Hidden bool `json:"hidden"`
Updated time.Time `json:"updated"`
Created time.Time `json:"created"`
}
func (r Role) Global() bool {
return r.OrgID == GlobalOrgID
}
func (r Role) IsFixed() bool {
return strings.HasPrefix(r.Name, FixedRolePrefix)
}
func (r Role) GetDisplayName() string {
if r.IsFixed() && r.DisplayName == "" {
r.DisplayName = fallbackDisplayName(r.Name)
}
return r.DisplayName
}
func (r Role) MarshalJSON() ([]byte, error) {
type Alias Role
r.DisplayName = r.GetDisplayName()
return json.Marshal(&struct {
Alias
Global bool `json:"global" xorm:"-"`
}{
Alias: (Alias)(r),
Global: r.Global(),
})
}
type RoleDTO struct {
Version int64 `json:"version"`
UID string `xorm:"uid" json:"uid"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Group string `xorm:"group_name" json:"group"`
Permissions []Permission `json:"permissions,omitempty"`
Delegatable *bool `json:"delegatable,omitempty"`
Hidden bool `json:"hidden,omitempty"`
ID int64 `json:"-" xorm:"pk autoincr 'id'"`
OrgID int64 `json:"-" xorm:"org_id"`
Updated time.Time `json:"updated"`
Created time.Time `json:"created"`
}
func (r RoleDTO) Role() Role {
return Role{
ID: r.ID,
OrgID: r.OrgID,
UID: r.UID,
Name: r.Name,
DisplayName: r.DisplayName,
Group: r.Group,
Description: r.Description,
Hidden: r.Hidden,
Updated: r.Updated,
Created: r.Created,
}
}
func (r RoleDTO) Global() bool {
return r.OrgID == GlobalOrgID
}
func (r RoleDTO) IsFixed() bool {
return strings.HasPrefix(r.Name, FixedRolePrefix)
}
func (r RoleDTO) GetDisplayName() string {
if r.IsFixed() && r.DisplayName == "" {
r.DisplayName = fallbackDisplayName(r.Name)
}
if r.DisplayName == "" {
return r.Name
}
return r.DisplayName
}
func (r RoleDTO) MarshalJSON() ([]byte, error) {
type Alias RoleDTO
r.DisplayName = r.GetDisplayName()
return json.Marshal(&struct {
Alias
Global bool `json:"global" xorm:"-"`
}{
Alias: (Alias)(r),
Global: r.Global(),
})
}
// fallbackDisplayName provides a fallback name for role
// that can be displayed in the ui for better readability
// example: currently this would give:
// fixed:datasources:name -> datasources name
// datasources:admin -> datasources admin
func fallbackDisplayName(rName string) string {
// removing prefix for fixed roles
rNameWithoutPrefix := strings.Replace(rName, FixedRolePrefix, "", 1)
return strings.TrimSpace(strings.Replace(rNameWithoutPrefix, ":", " ", -1))
}
type TeamRole struct {
ID int64 `json:"id" xorm:"pk autoincr 'id'"`
OrgID int64 `json:"orgId" xorm:"org_id"`
RoleID int64 `json:"roleId" xorm:"role_id"`
TeamID int64 `json:"teamId" xorm:"team_id"`
Created time.Time
}
type UserRole struct {
ID int64 `json:"id" xorm:"pk autoincr 'id'"`
OrgID int64 `json:"orgId" xorm:"org_id"`
RoleID int64 `json:"roleId" xorm:"role_id"`
UserID int64 `json:"userId" xorm:"user_id"`
Created time.Time
}
type BuiltinRole struct {
ID int64 `json:"id" xorm:"pk autoincr 'id'"`
RoleID int64 `json:"roleId" xorm:"role_id"`
OrgID int64 `json:"orgId" xorm:"org_id"`
Role string
Updated time.Time
Created time.Time
}
// Permission is the model for access control permissions.
type Permission struct {
ID int64 `json:"-" xorm:"pk autoincr 'id'"`
RoleID int64 `json:"-" xorm:"role_id"`
Action string `json:"action"`
Scope string `json:"scope"`
Updated time.Time `json:"updated"`
Created time.Time `json:"created"`
}
func (p Permission) OSSPermission() Permission {
return Permission{
Action: p.Action,
Scope: p.Scope,
}
}
type GetUserPermissionsQuery struct {
OrgID int64 `json:"-"`
UserID int64 `json:"userId"`
Roles []string
Actions []string
}
// ScopeParams holds the parameters used to fill in scope templates
type ScopeParams struct {
OrgID int64
URLParams map[string]string
}
// ResourcePermission is structure that holds all actions that either a team / user / builtin-role
// can perform against specific resource.
type ResourcePermission struct {
ID int64
RoleName string
Actions []string
Scope string
UserId int64
UserLogin string
UserEmail string
TeamId int64
TeamEmail string
Team string
BuiltInRole string
IsManaged bool
Created time.Time
Updated time.Time
}
func (p *ResourcePermission) Contains(targetActions []string) bool {
if len(p.Actions) < len(targetActions) {
return false
}
var contain = func(arr []string, s string) bool {
for _, item := range arr {
if item == s {
return true
}
}
return false
}
for _, a := range targetActions {
if !contain(p.Actions, a) {
return false
}
}
return true
}
type SetResourcePermissionCommand struct {
UserID int64
TeamID int64
BuiltinRole string
Permission string
}
const (
GlobalOrgID = 0
GeneralFolderUID = "general"
// Permission actions
ActionAPIKeyRead = "apikeys:read"
ActionAPIKeyCreate = "apikeys:create"
ActionAPIKeyDelete = "apikeys:delete"
// Users actions
ActionUsersRead = "users:read"
ActionUsersWrite = "users:write"
ActionUsersTeamRead = "users.teams:read"
// We can ignore gosec G101 since this does not contain any credentials.
// nolint:gosec
ActionUsersAuthTokenList = "users.authtoken:list"
// We can ignore gosec G101 since this does not contain any credentials.
// nolint:gosec
ActionUsersAuthTokenUpdate = "users.authtoken:update"
// We can ignore gosec G101 since this does not contain any credentials.
// nolint:gosec
ActionUsersPasswordUpdate = "users.password:update"
ActionUsersDelete = "users:delete"
ActionUsersCreate = "users:create"
ActionUsersEnable = "users:enable"
ActionUsersDisable = "users:disable"
ActionUsersPermissionsUpdate = "users.permissions:update"
ActionUsersLogout = "users:logout"
ActionUsersQuotasList = "users.quotas:list"
ActionUsersQuotasUpdate = "users.quotas:update"
// Org actions
ActionOrgUsersRead = "org.users:read"
ActionOrgUsersAdd = "org.users:add"
ActionOrgUsersRemove = "org.users:remove"
ActionOrgUsersRoleUpdate = "org.users.role:update"
// LDAP actions
ActionLDAPUsersRead = "ldap.user:read"
ActionLDAPUsersSync = "ldap.user:sync"
ActionLDAPStatusRead = "ldap.status:read"
ActionLDAPConfigReload = "ldap.config:reload"
// Server actions
ActionServerStatsRead = "server.stats:read"
// Settings actions
ActionSettingsRead = "settings:read"
// Datasources actions
ActionDatasourcesExplore = "datasources:explore"
// Plugin actions
ActionPluginsManage = "plugins:manage"
// Global Scopes
ScopeGlobalUsersAll = "global.users:*"
// APIKeys scope
ScopeAPIKeysAll = "apikeys:*"
// Users scope
ScopeUsersAll = "users:*"
// Settings scope
ScopeSettingsAll = "settings:*"
AccessControl: Implement teams resource service (#43951) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * moving resourceservice to the main wire file pt2 * move team related actions so that they can be reused * PR feedback * fix * typo * Access Control: adding hooks for team member endpoints (#43991) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * add access control to list and add team member endpoint, and hooks for adding team members * member permission type is 0 * add ID scope for team permission checks * add more team actions, use Member for member permission name * protect team member update endpoint with FGAC permissions * update SQL functions for teams and the corresponding tests * also protect team member removal endpoint with FGAC permissions and add a hook to permission service * a few small fixes, provide team permission service to test setup * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * remove resource services from wireexts * remove unneeded actions * linting fix * remove comments * feedback fixes * feedback * simplifying * remove team member within the same transaction * fix a mistake with the error * call the correct sql fction * linting * Access control: tests for team member endpoints (#44177) * tests for team member endpoints * clean up and fix the tests * fixing tests take 2 * don't import enterprise test license * don't import enterprise test license * remove unused variable Co-authored-by: gamab <gabi.mabs@gmail.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
3 years ago
// Team related actions
ActionTeamsCreate = "teams:create"
ActionTeamsDelete = "teams:delete"
ActionTeamsRead = "teams:read"
ActionTeamsWrite = "teams:write"
ActionTeamsPermissionsRead = "teams.permissions:read"
ActionTeamsPermissionsWrite = "teams.permissions:write"
// Team related scopes
ScopeTeamsAll = "teams:*"
// Annotations related actions
ActionAnnotationsCreate = "annotations:create"
ActionAnnotationsDelete = "annotations:delete"
ActionAnnotationsRead = "annotations:read"
ActionAnnotationsWrite = "annotations:write"
Access control: Use access control for dashboard and folder (#44702) * Add actions and scopes * add resource service for dashboard and folder * Add dashboard guardian with fgac permission evaluation * Add CanDelete function to guardian interface * Add CanDelete property to folder and dashboard dto and set values * change to correct function name * Add accesscontrol to folder endpoints * add access control to dashboard endpoints * check access for nav links * Add fixed roles for dashboard and folders * use correct package * add hack to override guardian Constructor if accesscontrol is enabled * Add services * Add function to handle api backward compatability * Add permissionServices to HttpServer * Set permission when new dashboard is created * Add default permission when creating new dashboard * Set default permission when creating folder and dashboard * Add access control filter for dashboard search * Add to accept list * Add accesscontrol to dashboardimport * Disable access control in tests * Add check to see if user is allow to create a dashboard * Use SetPermissions * Use function to set several permissions at once * remove permissions for folder and dashboard on delete * update required permission * set permission for provisioning * Add CanCreate to dashboard guardian and set correct permisisons for provisioning * Dont set admin on folder / dashboard creation * Add dashboard and folder permission migrations * Add tests for CanCreate * Add roles and update descriptions * Solve uid to id for dashboard and folder permissions * Add folder and dashboard actions to permission filter * Handle viewer_can_edit flag * set folder and dashboard permissions services * Add dashboard permissions when importing a new dashboard * Set access control permissions on provisioning * Pass feature flags and only set permissions if access control is enabled * only add default permissions for folders and dashboards without folders * Batch create permissions in migrations * Remove `dashboards:edit` action * Remove unused function from interface * Update pkg/services/guardian/accesscontrol_guardian_test.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
3 years ago
// Dashboard actions
ActionDashboardsCreate = "dashboards:create"
ActionDashboardsRead = "dashboards:read"
ActionDashboardsWrite = "dashboards:write"
ActionDashboardsDelete = "dashboards:delete"
ActionDashboardsPermissionsRead = "dashboards.permissions:read"
ActionDashboardsPermissionsWrite = "dashboards.permissions:write"
// Dashboard scopes
ScopeDashboardsAll = "dashboards:*"
// Alert scopes are divided into two groups. The internal (to Grafana) and the external ones.
// For the Grafana ones, given we have ACID control we're able to provide better granularity by defining CRUD options.
// For the external ones, we only have read and write permissions due to the lack of atomicity control of the external system.
// Alerting rules actions
ActionAlertingRuleCreate = "alert.rules:create"
ActionAlertingRuleRead = "alert.rules:read"
ActionAlertingRuleUpdate = "alert.rules:update"
ActionAlertingRuleDelete = "alert.rules:delete"
// Alerting instances (+silences) actions
ActionAlertingInstanceCreate = "alert.instances:create"
ActionAlertingInstanceUpdate = "alert.instances:update"
ActionAlertingInstanceRead = "alert.instances:read"
// Alerting Notification policies actions
ActionAlertingNotificationsCreate = "alert.notifications:create"
ActionAlertingNotificationsRead = "alert.notifications:read"
ActionAlertingNotificationsUpdate = "alert.notifications:update"
ActionAlertingNotificationsDelete = "alert.notifications:delete"
// External alerting rule actions. We can only narrow it down to writes or reads, as we don't control the atomicity in the external system.
ActionAlertingRuleExternalWrite = "alert.rules.external:write"
ActionAlertingRuleExternalRead = "alert.rules.external:read"
// External alerting instances actions. We can only narrow it down to writes or reads, as we don't control the atomicity in the external system.
ActionAlertingInstancesExternalWrite = "alert.instances.external:write"
ActionAlertingInstancesExternalRead = "alert.instances.external:read"
// External alerting notifications actions. We can only narrow it down to writes or reads, as we don't control the atomicity in the external system.
ActionAlertingNotificationsExternalWrite = "alert.notifications.external:write"
ActionAlertingNotificationsExternalRead = "alert.notifications.external:read"
AccessControl: Implement teams resource service (#43951) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * moving resourceservice to the main wire file pt2 * move team related actions so that they can be reused * PR feedback * fix * typo * Access Control: adding hooks for team member endpoints (#43991) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * add access control to list and add team member endpoint, and hooks for adding team members * member permission type is 0 * add ID scope for team permission checks * add more team actions, use Member for member permission name * protect team member update endpoint with FGAC permissions * update SQL functions for teams and the corresponding tests * also protect team member removal endpoint with FGAC permissions and add a hook to permission service * a few small fixes, provide team permission service to test setup * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * remove resource services from wireexts * remove unneeded actions * linting fix * remove comments * feedback fixes * feedback * simplifying * remove team member within the same transaction * fix a mistake with the error * call the correct sql fction * linting * Access control: tests for team member endpoints (#44177) * tests for team member endpoints * clean up and fix the tests * fixing tests take 2 * don't import enterprise test license * don't import enterprise test license * remove unused variable Co-authored-by: gamab <gabi.mabs@gmail.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
3 years ago
)
var (
// Team scope
ScopeTeamsID = Scope("teams", "id", Parameter(":teamId"))
// Annotation scopes
ScopeAnnotationsRoot = "annotations"
ScopeAnnotationsProvider = NewScopeProvider(ScopeAnnotationsRoot)
ScopeAnnotationsAll = ScopeAnnotationsProvider.GetResourceAllScope()
ScopeAnnotationsID = Scope(ScopeAnnotationsRoot, "id", Parameter(":annotationId"))
ScopeAnnotationsTypeDashboard = ScopeAnnotationsProvider.GetResourceScopeType(annotations.Dashboard.String())
ScopeAnnotationsTypeOrganization = ScopeAnnotationsProvider.GetResourceScopeType(annotations.Organization.String())
)
const RoleGrafanaAdmin = "Grafana Admin"
const FixedRolePrefix = "fixed:"