grafana/pkg/services/accesscontrol/roles.go
Gabriel MABILLE 88c11f1cc0
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 09:52:09 +02:00

281 lines
5.9 KiB
Go

package accesscontrol
import (
"fmt"
"strings"
"sync"
"github.com/grafana/grafana/pkg/models"
)
// Roles definition
var (
datasourcesEditorReadRole = RoleDTO{
Version: 1,
Name: datasourcesEditorRead,
Permissions: []Permission{
{
Action: ActionDatasourcesExplore,
},
},
}
ldapAdminReadRole = RoleDTO{
Name: ldapAdminRead,
Version: 1,
Permissions: []Permission{
{
Action: ActionLDAPUsersRead,
},
{
Action: ActionLDAPStatusRead,
},
},
}
ldapAdminEditRole = RoleDTO{
Name: ldapAdminEdit,
Version: 2,
Permissions: ConcatPermissions(ldapAdminReadRole.Permissions, []Permission{
{
Action: ActionLDAPUsersSync,
},
{
Action: ActionLDAPConfigReload,
},
}),
}
serverAdminReadRole = RoleDTO{
Version: 1,
Name: serverAdminRead,
Permissions: []Permission{
{
Action: ActionServerStatsRead,
},
},
}
settingsAdminReadRole = RoleDTO{
Version: 1,
Name: settingsAdminRead,
Permissions: []Permission{
{
Action: ActionSettingsRead,
Scope: ScopeSettingsAll,
},
},
}
usersOrgReadRole = RoleDTO{
Name: usersOrgRead,
Version: 1,
Permissions: []Permission{
{
Action: ActionOrgUsersRead,
Scope: ScopeUsersAll,
},
},
}
usersOrgEditRole = RoleDTO{
Name: usersOrgEdit,
Version: 1,
Permissions: ConcatPermissions(usersOrgReadRole.Permissions, []Permission{
{
Action: ActionOrgUsersAdd,
Scope: ScopeUsersAll,
},
{
Action: ActionOrgUsersRoleUpdate,
Scope: ScopeUsersAll,
},
{
Action: ActionOrgUsersRemove,
Scope: ScopeUsersAll,
},
}),
}
usersAdminReadRole = RoleDTO{
Name: usersAdminRead,
Version: 1,
Permissions: []Permission{
{
Action: ActionUsersRead,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersTeamRead,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersAuthTokenList,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersQuotasList,
Scope: ScopeGlobalUsersAll,
},
},
}
usersAdminEditRole = RoleDTO{
Name: usersAdminEdit,
Version: 1,
Permissions: ConcatPermissions(usersAdminReadRole.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 (
datasourcesEditorRead = "fixed:datasources:editor:read"
serverAdminRead = "fixed:server:admin:read"
settingsAdminRead = "fixed:settings:admin:read"
usersAdminEdit = "fixed:users:admin:edit"
usersAdminRead = "fixed:users:admin:read"
usersOrgEdit = "fixed:users:org:edit"
usersOrgRead = "fixed:users:org:read"
ldapAdminEdit = "fixed:ldap:admin:edit"
ldapAdminRead = "fixed:ldap:admin:read"
)
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{
datasourcesEditorRead: datasourcesEditorReadRole,
usersAdminEdit: usersAdminEditRole,
usersAdminRead: usersAdminReadRole,
usersOrgEdit: usersOrgEditRole,
usersOrgRead: usersOrgReadRole,
ldapAdminEdit: ldapAdminEditRole,
ldapAdminRead: ldapAdminReadRole,
serverAdminRead: serverAdminReadRole,
settingsAdminRead: settingsAdminReadRole,
}
// FixedRoleGrants specifies which built-in roles are assigned
// to which set of FixedRoles by default. Alphabetically sorted.
FixedRoleGrants = map[string][]string{
RoleGrafanaAdmin: {
ldapAdminEdit,
ldapAdminRead,
serverAdminRead,
settingsAdminRead,
usersAdminEdit,
usersAdminRead,
usersOrgEdit,
usersOrgRead,
},
string(models.ROLE_ADMIN): {
usersOrgEdit,
usersOrgRead,
},
string(models.ROLE_EDITOR): {
datasourcesEditorRead,
},
}
)
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
}
}
}