mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 07:32:57 +08:00
Auth: Add empty role definition (#64694)
* Allow setting role as None Co-authored-by: gamab <gabi.mabs@gmail.com> Seeking for places where role.None would be used Co-authored-by: Jguer <joao.guerreiro@grafana.com> Adding None role to the frontend Co-authored-by: Jguer <joao.guerreiro@grafana.com> unify org role declaration and remove from add permission fix backend test fix backend lint * remove role none from frontend * Simplify checks Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * nits --------- Co-authored-by: Kalle Persson <kalle.persson@grafana.com>
This commit is contained in:
@ -5,7 +5,8 @@ export interface UserOrgDTO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum OrgRole {
|
export enum OrgRole {
|
||||||
Admin = 'Admin',
|
None = 'None',
|
||||||
Editor = 'Editor',
|
|
||||||
Viewer = 'Viewer',
|
Viewer = 'Viewer',
|
||||||
|
Editor = 'Editor',
|
||||||
|
Admin = 'Admin',
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,8 @@ func (s *SocialAzureAD) extractRoleAndAdmin(claims *azureClaims) (org.RoleType,
|
|||||||
return s.defaultRole(false), false
|
return s.defaultRole(false), false
|
||||||
}
|
}
|
||||||
|
|
||||||
roleOrder := []org.RoleType{RoleGrafanaAdmin, org.RoleAdmin, org.RoleEditor, org.RoleViewer}
|
roleOrder := []org.RoleType{RoleGrafanaAdmin, org.RoleAdmin, org.RoleEditor,
|
||||||
|
org.RoleViewer, org.RoleNone}
|
||||||
for _, role := range roleOrder {
|
for _, role := range roleOrder {
|
||||||
if found := hasRole(claims.Roles, role); found {
|
if found := hasRole(claims.Roles, role); found {
|
||||||
if role == RoleGrafanaAdmin {
|
if role == RoleGrafanaAdmin {
|
||||||
|
@ -20,8 +20,8 @@ import (
|
|||||||
|
|
||||||
type AuthOptions struct {
|
type AuthOptions struct {
|
||||||
ReqGrafanaAdmin bool
|
ReqGrafanaAdmin bool
|
||||||
ReqSignedIn bool
|
|
||||||
ReqNoAnonynmous bool
|
ReqNoAnonynmous bool
|
||||||
|
ReqSignedIn bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func accessForbidden(c *contextmodel.ReqContext) {
|
func accessForbidden(c *contextmodel.ReqContext) {
|
||||||
|
@ -9,47 +9,58 @@ import (
|
|||||||
type RoleType string
|
type RoleType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
RoleNone RoleType = "None"
|
||||||
RoleViewer RoleType = "Viewer"
|
RoleViewer RoleType = "Viewer"
|
||||||
RoleEditor RoleType = "Editor"
|
RoleEditor RoleType = "Editor"
|
||||||
RoleAdmin RoleType = "Admin"
|
RoleAdmin RoleType = "Admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var rolePrecedence = map[RoleType]int{
|
||||||
|
RoleNone: 10,
|
||||||
|
RoleViewer: 20,
|
||||||
|
RoleEditor: 30,
|
||||||
|
RoleAdmin: 40,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed to keep stable order
|
||||||
|
var roleOrder = [...]RoleType{
|
||||||
|
RoleNone,
|
||||||
|
RoleViewer,
|
||||||
|
RoleEditor,
|
||||||
|
RoleAdmin,
|
||||||
|
}
|
||||||
|
|
||||||
func (r RoleType) IsValid() bool {
|
func (r RoleType) IsValid() bool {
|
||||||
return r == RoleViewer || r == RoleAdmin || r == RoleEditor
|
_, ok := rolePrecedence[r]
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RoleType) Includes(other RoleType) bool {
|
func (r RoleType) Includes(other RoleType) bool {
|
||||||
if r == RoleAdmin {
|
return rolePrecedence[r] >= rolePrecedence[other]
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == RoleEditor {
|
|
||||||
return other != RoleAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
return r == other
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RoleType) Children() []RoleType {
|
func (r RoleType) Children() []RoleType {
|
||||||
switch r {
|
children := make([]RoleType, 0, 3)
|
||||||
case RoleAdmin:
|
|
||||||
return []RoleType{RoleEditor, RoleViewer}
|
for _, role := range roleOrder {
|
||||||
case RoleEditor:
|
if rolePrecedence[r] > rolePrecedence[role] {
|
||||||
return []RoleType{RoleViewer}
|
children = append(children, role)
|
||||||
default:
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RoleType) Parents() []RoleType {
|
func (r RoleType) Parents() []RoleType {
|
||||||
switch r {
|
parents := make([]RoleType, 0, 3)
|
||||||
case RoleEditor:
|
|
||||||
return []RoleType{RoleAdmin}
|
for _, role := range roleOrder {
|
||||||
case RoleViewer:
|
if rolePrecedence[r] < rolePrecedence[role] {
|
||||||
return []RoleType{RoleEditor, RoleAdmin}
|
parents = append(parents, role)
|
||||||
default:
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return parents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RoleType) UnmarshalText(data []byte) error {
|
func (r *RoleType) UnmarshalText(data []byte) error {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrFixedRolePrefixMissing = errors.New("fixed role should be prefixed with '" + FixedRolePrefix + "'")
|
ErrFixedRolePrefixMissing = errors.New("fixed role should be prefixed with '" + FixedRolePrefix + "'")
|
||||||
ErrInvalidBuiltinRole = errors.New("built-in role is not valid")
|
ErrInvalidBuiltinRole = errors.New("built-in role is not valid")
|
||||||
|
ErrNoneRoleAssignment = errors.New("none role cannot receive permissions")
|
||||||
ErrInvalidScope = errors.New("invalid scope")
|
ErrInvalidScope = errors.New("invalid scope")
|
||||||
ErrResolverNotFound = errors.New("no resolver found")
|
ErrResolverNotFound = errors.New("no resolver found")
|
||||||
ErrPluginIDRequired = errors.New("plugin ID is required")
|
ErrPluginIDRequired = errors.New("plugin ID is required")
|
||||||
|
@ -264,6 +264,9 @@ func ValidateFixedRole(role RoleDTO) error {
|
|||||||
// ValidateBuiltInRoles errors when a built-in role does not match expected pattern
|
// ValidateBuiltInRoles errors when a built-in role does not match expected pattern
|
||||||
func ValidateBuiltInRoles(builtInRoles []string) error {
|
func ValidateBuiltInRoles(builtInRoles []string) error {
|
||||||
for _, br := range builtInRoles {
|
for _, br := range builtInRoles {
|
||||||
|
if org.RoleType(br) == org.RoleNone {
|
||||||
|
return ErrNoneRoleAssignment
|
||||||
|
}
|
||||||
if !org.RoleType(br).IsValid() && br != RoleGrafanaAdmin {
|
if !org.RoleType(br).IsValid() && br != RoleGrafanaAdmin {
|
||||||
return fmt.Errorf("'%s' %w", br, ErrInvalidBuiltinRole)
|
return fmt.Errorf("'%s' %w", br, ErrInvalidBuiltinRole)
|
||||||
}
|
}
|
||||||
@ -327,6 +330,17 @@ func BuildBasicRoleDefinitions() map[string]*RoleDTO {
|
|||||||
Permissions: []Permission{},
|
Permissions: []Permission{},
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
},
|
},
|
||||||
|
string(org.RoleNone): {
|
||||||
|
Name: BasicRolePrefix + "none",
|
||||||
|
UID: BasicRoleUIDPrefix + "none",
|
||||||
|
OrgID: GlobalOrgID,
|
||||||
|
Version: 1,
|
||||||
|
DisplayName: string(org.RoleNone),
|
||||||
|
Description: "None role",
|
||||||
|
Group: "Basic",
|
||||||
|
Permissions: []Permission{},
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
RoleGrafanaAdmin: {
|
RoleGrafanaAdmin: {
|
||||||
Name: BasicRolePrefix + "grafana_admin",
|
Name: BasicRolePrefix + "grafana_admin",
|
||||||
UID: BasicRoleUIDPrefix + "grafana_admin",
|
UID: BasicRoleUIDPrefix + "grafana_admin",
|
||||||
|
@ -47,9 +47,10 @@ type OrgUser struct {
|
|||||||
type RoleType = roletype.RoleType
|
type RoleType = roletype.RoleType
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RoleViewer RoleType = "Viewer"
|
RoleNone RoleType = roletype.RoleNone
|
||||||
RoleEditor RoleType = "Editor"
|
RoleViewer RoleType = roletype.RoleViewer
|
||||||
RoleAdmin RoleType = "Admin"
|
RoleEditor RoleType = roletype.RoleEditor
|
||||||
|
RoleAdmin RoleType = roletype.RoleAdmin
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateOrgCommand struct {
|
type CreateOrgCommand struct {
|
||||||
|
@ -77,6 +77,40 @@ func TestStore_CreateServiceAccount(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStore_CreateServiceAccountRoleNone(t *testing.T) {
|
||||||
|
_, store := setupTestDatabase(t)
|
||||||
|
orgQuery := &org.CreateOrgCommand{Name: orgimpl.MainOrgName}
|
||||||
|
orgResult, err := store.orgService.CreateWithMember(context.Background(), orgQuery)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
serviceAccountName := "new Service Account"
|
||||||
|
serviceAccountOrgId := orgResult.ID
|
||||||
|
serviceAccountRole := org.RoleNone
|
||||||
|
saForm := serviceaccounts.CreateServiceAccountForm{
|
||||||
|
Name: serviceAccountName,
|
||||||
|
Role: &serviceAccountRole,
|
||||||
|
IsDisabled: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
saDTO, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "sa-new-service-account", saDTO.Login)
|
||||||
|
assert.Equal(t, serviceAccountName, saDTO.Name)
|
||||||
|
assert.Equal(t, 0, int(saDTO.Tokens))
|
||||||
|
|
||||||
|
retrieved, err := store.RetrieveServiceAccount(context.Background(), serviceAccountOrgId, saDTO.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "sa-new-service-account", retrieved.Login)
|
||||||
|
assert.Equal(t, serviceAccountName, retrieved.Name)
|
||||||
|
assert.Equal(t, serviceAccountOrgId, retrieved.OrgId)
|
||||||
|
assert.Equal(t, string(serviceAccountRole), retrieved.Role)
|
||||||
|
|
||||||
|
retrievedId, err := store.RetrieveServiceAccountIdByName(context.Background(), serviceAccountOrgId, serviceAccountName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, saDTO.Id, retrievedId)
|
||||||
|
assert.Equal(t, saDTO.Role, string(org.RoleNone))
|
||||||
|
}
|
||||||
|
|
||||||
func TestStore_DeleteServiceAccount(t *testing.T) {
|
func TestStore_DeleteServiceAccount(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -17,13 +17,14 @@ import (
|
|||||||
type roleType string
|
type roleType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
RoleNone roleType = "None"
|
||||||
RoleViewer roleType = "Viewer"
|
RoleViewer roleType = "Viewer"
|
||||||
RoleEditor roleType = "Editor"
|
RoleEditor roleType = "Editor"
|
||||||
RoleAdmin roleType = "Admin"
|
RoleAdmin roleType = "Admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r roleType) IsValid() bool {
|
func (r roleType) IsValid() bool {
|
||||||
return r == RoleViewer || r == RoleAdmin || r == RoleEditor
|
return r == RoleViewer || r == RoleAdmin || r == RoleEditor || r == RoleNone
|
||||||
}
|
}
|
||||||
|
|
||||||
type permissionType int
|
type permissionType int
|
||||||
|
@ -87,7 +87,9 @@ export const AddPermission = ({
|
|||||||
{target === PermissionTarget.BuiltInRole && (
|
{target === PermissionTarget.BuiltInRole && (
|
||||||
<Select
|
<Select
|
||||||
aria-label={'Built-in role picker'}
|
aria-label={'Built-in role picker'}
|
||||||
options={Object.values(OrgRole).map((r) => ({ value: r, label: r }))}
|
options={Object.values(OrgRole)
|
||||||
|
.filter((r) => r !== OrgRole.None)
|
||||||
|
.map((r) => ({ value: r, label: r }))}
|
||||||
onChange={(r) => setBuiltinRole(r.value || '')}
|
onChange={(r) => setBuiltinRole(r.value || '')}
|
||||||
width="auto"
|
width="auto"
|
||||||
/>
|
/>
|
||||||
|
@ -29,7 +29,7 @@ interface RolesCollectionEntry {
|
|||||||
roles: Role[];
|
roles: Role[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasicRoles = Object.values(OrgRole);
|
const BasicRoles = Object.values(OrgRole).filter((r) => r !== OrgRole.None);
|
||||||
const BasicRoleOption: Array<SelectableValue<OrgRole>> = BasicRoles.map((r) => ({
|
const BasicRoleOption: Array<SelectableValue<OrgRole>> = BasicRoles.map((r) => ({
|
||||||
label: r,
|
label: r,
|
||||||
value: r,
|
value: r,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { OrgRole } from '@grafana/data';
|
||||||
|
|
||||||
export enum TeamPermissionLevel {
|
export enum TeamPermissionLevel {
|
||||||
Admin = 4,
|
Admin = 4,
|
||||||
Editor = 2,
|
Editor = 2,
|
||||||
@ -5,11 +7,7 @@ export enum TeamPermissionLevel {
|
|||||||
Viewer = 1,
|
Viewer = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum OrgRole {
|
export { OrgRole as OrgRole };
|
||||||
Viewer = 'Viewer',
|
|
||||||
Editor = 'Editor',
|
|
||||||
Admin = 'Admin',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DashboardAclDTO {
|
export interface DashboardAclDTO {
|
||||||
id?: number;
|
id?: number;
|
||||||
|
Reference in New Issue
Block a user