grafana/pkg/services/accesscontrol/roles.go

308 lines
7.7 KiB
Go
Raw Normal View History

package accesscontrol
import (
"fmt"
"strings"
"sync"
"github.com/grafana/grafana/pkg/models"
)
type RoleRegistry interface {
// RegisterFixedRoles registers all roles declared to AccessControl
RegisterFixedRoles() error
}
// Roles definition
var (
datasourcesExplorerRole = RoleDTO{
Version: 3,
Name: datasourcesExplorer,
DisplayName: "Data source explorer",
Description: "Enable the Explore feature. Data source permissions still apply; you can only query data sources for which you have query permissions.",
Group: "Data sources",
Permissions: []Permission{
{
Action: ActionDatasourcesExplore,
},
},
}
ldapReaderRole = RoleDTO{
Name: ldapReader,
DisplayName: "LDAP reader",
Description: "Read LDAP configuration and status.",
Group: "LDAP",
Version: 3,
Permissions: []Permission{
{
Action: ActionLDAPUsersRead,
},
{
Action: ActionLDAPStatusRead,
},
},
}
ldapWriterRole = RoleDTO{
Name: ldapWriter,
DisplayName: "LDAP writer",
Description: "Read and update LDAP configuration and read LDAP status.",
Group: "LDAP",
Version: 4,
Permissions: ConcatPermissions(ldapReaderRole.Permissions, []Permission{
{
Action: ActionLDAPUsersSync,
},
{
Action: ActionLDAPConfigReload,
},
}),
}
orgUsersWriterRole = RoleDTO{
Name: orgUsersWriter,
DisplayName: "Organization user writer",
Description: "Within a single organization, add a user, invite a user, read information about a user and their role, remove a user from that organization, or change the role of a user.",
Group: "User administration (organizational)",
Version: 3,
Permissions: ConcatPermissions(orgUsersReaderRole.Permissions, []Permission{
{
Action: ActionOrgUsersAdd,
Scope: ScopeUsersAll,
},
{
Action: ActionOrgUsersRoleUpdate,
Scope: ScopeUsersAll,
},
{
Action: ActionOrgUsersRemove,
Scope: ScopeUsersAll,
},
}),
}
orgUsersReaderRole = RoleDTO{
Name: orgUsersReader,
DisplayName: "Organization user reader",
Description: "Read users within a single organization.",
Group: "User administration (organizational)",
Version: 3,
Permissions: []Permission{
{
Action: ActionOrgUsersRead,
Scope: ScopeUsersAll,
},
},
}
settingsReaderRole = RoleDTO{
Version: 4,
DisplayName: "Setting reader",
Description: "Read Grafana instance settings.",
Group: "Settings",
Name: settingsReader,
Permissions: []Permission{
{
Action: ActionSettingsRead,
Scope: ScopeSettingsAll,
},
},
}
statsReaderRole = RoleDTO{
Version: 3,
Name: statsReader,
DisplayName: "Statistics reader",
Description: "Read Grafana instance statistics.",
Group: "Statistics",
Permissions: []Permission{
{
Action: ActionServerStatsRead,
},
},
}
usersReaderRole = RoleDTO{
Name: usersReader,
DisplayName: "User reader",
Description: "Read all users and their information, such as team memberships, authentication tokens, and quotas.",
Group: "User administration (global)",
Version: 3,
Permissions: []Permission{
{
Action: ActionUsersRead,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersTeamRead,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersAuthTokenList,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersQuotasList,
Scope: ScopeGlobalUsersAll,
},
},
}
usersWriterRole = RoleDTO{
Name: usersWriter,
DisplayName: "User writer",
Description: "Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a users authentication token, or update quotas for all users.",
Group: "User administration (global)",
Version: 3,
Permissions: ConcatPermissions(usersReaderRole.Permissions, []Permission{
{
Action: ActionUsersPasswordUpdate,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersCreate,
},
{
Action: ActionUsersWrite,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersDelete,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersEnable,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersDisable,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersPermissionsUpdate,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersLogout,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersAuthTokenUpdate,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersQuotasUpdate,
Scope: ScopeGlobalUsersAll,
},
}),
}
)
// Role names definitions
const (
datasourcesExplorer = "fixed:datasources:explorer"
ldapReader = "fixed:ldap:reader"
ldapWriter = "fixed:ldap:writer"
orgUsersReader = "fixed:org.users:reader"
orgUsersWriter = "fixed:org.users:writer"
settingsReader = "fixed:settings:reader"
statsReader = "fixed:stats:reader"
usersReader = "fixed:users:reader"
usersWriter = "fixed:users:writer"
AccessControl: Implement a way to register fixed roles (#35641) * AccessControl: Implement a way to register fixed roles * Add context to register func * Use FixedRoleGrantsMap instead of FixedRoleGrants * Removed FixedRoles map to sync.map * Wrote test for accesscontrol and provisioning * Use mutexes+map instead of sync maps * Create a sync map struct out of a Map and a Mutex * Create a sync map struct for grants as well * Validate builtin roles * Make validation public to access control * Handle errors consistently with what seeder does * Keep errors consistant amongst accesscontrol impl * Handle registration error * Reverse the registration direction thanks to a RoleRegistrant interface * Removed sync map in favor for simple maps since registration now happens during init * Work on the Registrant interface * Remove the Register Role from the interface to have services returning their registrations instead * Adding context to RegisterRegistrantsRoles and update descriptions * little bit of cosmetics * Making sure provisioning is ran after role registration * test for role registration * Change the accesscontrol interface to use a variadic * check if accesscontrol is enabled * Add a new test for RegisterFixedRoles and fix assign which was buggy * Moved RegistrationList def to roles.go * Change provisioning role's description * Better comment on RegisterFixedRoles * Correct comment on ValidateFixedRole * Simplify helper func to removeRoleHelper * Add log to saveFixedRole and assignFixedRole Co-authored-by: Vardan Torosyan <vardants@gmail.com> Co-authored-by: Jeremy Price <Jeremy.price@grafana.com>
2021-07-30 02:52:09 -05:00
)
var (
// FixedRoles provides a map of permission sets/roles which can be
// assigned to a set of users. When adding a new resource protected by
// Grafana access control the default permissions should be added to a
// new fixed role in this set so that users can access the new
// resource. FixedRoleGrants lists which built-in roles are
// assigned which fixed roles in this list.
FixedRoles = map[string]RoleDTO{
datasourcesExplorer: datasourcesExplorerRole,
ldapReader: ldapReaderRole,
ldapWriter: ldapWriterRole,
orgUsersReader: orgUsersReaderRole,
orgUsersWriter: orgUsersWriterRole,
settingsReader: settingsReaderRole,
statsReader: statsReaderRole,
usersReader: usersReaderRole,
usersWriter: usersWriterRole,
}
// FixedRoleGrants specifies which built-in roles are assigned
// to which set of FixedRoles by default. Alphabetically sorted.
FixedRoleGrants = map[string][]string{
RoleGrafanaAdmin: {
ldapReader,
ldapWriter,
orgUsersReader,
orgUsersWriter,
settingsReader,
statsReader,
usersReader,
usersWriter,
},
string(models.ROLE_ADMIN): {
orgUsersReader,
orgUsersWriter,
},
string(models.ROLE_EDITOR): {
datasourcesExplorer,
},
}
)
func ConcatPermissions(permissions ...[]Permission) []Permission {
if permissions == nil {
return nil
}
perms := make([]Permission, 0)
for _, p := range permissions {
pCopy := make([]Permission, 0, len(p))
copy(pCopy, p)
perms = append(perms, p...)
}
return perms
}
// ValidateFixedRole errors when a fixed role does not match expected pattern
func ValidateFixedRole(role RoleDTO) error {
if !strings.HasPrefix(role.Name, FixedRolePrefix) {
return ErrFixedRolePrefixMissing
}
return nil
}
// ValidateBuiltInRoles errors when a built-in role does not match expected pattern
func ValidateBuiltInRoles(builtInRoles []string) error {
for _, br := range builtInRoles {
if !models.RoleType(br).IsValid() && br != RoleGrafanaAdmin {
return fmt.Errorf("'%s' %w", br, ErrInvalidBuiltinRole)
}
}
return nil
}
type RegistrationList struct {
mx sync.RWMutex
registrations []RoleRegistration
}
func (m *RegistrationList) Append(regs ...RoleRegistration) {
m.mx.Lock()
defer m.mx.Unlock()
m.registrations = append(m.registrations, regs...)
}
func (m *RegistrationList) Range(f func(registration RoleRegistration) bool) {
m.mx.RLock()
defer m.mx.RUnlock()
for _, registration := range m.registrations {
if ok := f(registration); !ok {
return
}
}
}