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 {
|
||||
Admin = 'Admin',
|
||||
Editor = 'Editor',
|
||||
None = 'None',
|
||||
Viewer = 'Viewer',
|
||||
Editor = 'Editor',
|
||||
Admin = 'Admin',
|
||||
}
|
||||
|
@ -192,7 +192,8 @@ func (s *SocialAzureAD) extractRoleAndAdmin(claims *azureClaims) (org.RoleType,
|
||||
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 {
|
||||
if found := hasRole(claims.Roles, role); found {
|
||||
if role == RoleGrafanaAdmin {
|
||||
|
@ -20,8 +20,8 @@ import (
|
||||
|
||||
type AuthOptions struct {
|
||||
ReqGrafanaAdmin bool
|
||||
ReqSignedIn bool
|
||||
ReqNoAnonynmous bool
|
||||
ReqSignedIn bool
|
||||
}
|
||||
|
||||
func accessForbidden(c *contextmodel.ReqContext) {
|
||||
|
@ -9,47 +9,58 @@ import (
|
||||
type RoleType string
|
||||
|
||||
const (
|
||||
RoleNone RoleType = "None"
|
||||
RoleViewer RoleType = "Viewer"
|
||||
RoleEditor RoleType = "Editor"
|
||||
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 {
|
||||
return r == RoleViewer || r == RoleAdmin || r == RoleEditor
|
||||
_, ok := rolePrecedence[r]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r RoleType) Includes(other RoleType) bool {
|
||||
if r == RoleAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
if r == RoleEditor {
|
||||
return other != RoleAdmin
|
||||
}
|
||||
|
||||
return r == other
|
||||
return rolePrecedence[r] >= rolePrecedence[other]
|
||||
}
|
||||
|
||||
func (r RoleType) Children() []RoleType {
|
||||
switch r {
|
||||
case RoleAdmin:
|
||||
return []RoleType{RoleEditor, RoleViewer}
|
||||
case RoleEditor:
|
||||
return []RoleType{RoleViewer}
|
||||
default:
|
||||
return nil
|
||||
children := make([]RoleType, 0, 3)
|
||||
|
||||
for _, role := range roleOrder {
|
||||
if rolePrecedence[r] > rolePrecedence[role] {
|
||||
children = append(children, role)
|
||||
}
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
func (r RoleType) Parents() []RoleType {
|
||||
switch r {
|
||||
case RoleEditor:
|
||||
return []RoleType{RoleAdmin}
|
||||
case RoleViewer:
|
||||
return []RoleType{RoleEditor, RoleAdmin}
|
||||
default:
|
||||
return nil
|
||||
parents := make([]RoleType, 0, 3)
|
||||
|
||||
for _, role := range roleOrder {
|
||||
if rolePrecedence[r] < rolePrecedence[role] {
|
||||
parents = append(parents, role)
|
||||
}
|
||||
}
|
||||
|
||||
return parents
|
||||
}
|
||||
|
||||
func (r *RoleType) UnmarshalText(data []byte) error {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
var (
|
||||
ErrFixedRolePrefixMissing = errors.New("fixed role should be prefixed with '" + FixedRolePrefix + "'")
|
||||
ErrInvalidBuiltinRole = errors.New("built-in role is not valid")
|
||||
ErrNoneRoleAssignment = errors.New("none role cannot receive permissions")
|
||||
ErrInvalidScope = errors.New("invalid scope")
|
||||
ErrResolverNotFound = errors.New("no resolver found")
|
||||
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
|
||||
func ValidateBuiltInRoles(builtInRoles []string) error {
|
||||
for _, br := range builtInRoles {
|
||||
if org.RoleType(br) == org.RoleNone {
|
||||
return ErrNoneRoleAssignment
|
||||
}
|
||||
if !org.RoleType(br).IsValid() && br != RoleGrafanaAdmin {
|
||||
return fmt.Errorf("'%s' %w", br, ErrInvalidBuiltinRole)
|
||||
}
|
||||
@ -327,6 +330,17 @@ func BuildBasicRoleDefinitions() map[string]*RoleDTO {
|
||||
Permissions: []Permission{},
|
||||
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: {
|
||||
Name: BasicRolePrefix + "grafana_admin",
|
||||
UID: BasicRoleUIDPrefix + "grafana_admin",
|
||||
|
@ -47,9 +47,10 @@ type OrgUser struct {
|
||||
type RoleType = roletype.RoleType
|
||||
|
||||
const (
|
||||
RoleViewer RoleType = "Viewer"
|
||||
RoleEditor RoleType = "Editor"
|
||||
RoleAdmin RoleType = "Admin"
|
||||
RoleNone RoleType = roletype.RoleNone
|
||||
RoleViewer RoleType = roletype.RoleViewer
|
||||
RoleEditor RoleType = roletype.RoleEditor
|
||||
RoleAdmin RoleType = roletype.RoleAdmin
|
||||
)
|
||||
|
||||
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) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
|
@ -17,13 +17,14 @@ import (
|
||||
type roleType string
|
||||
|
||||
const (
|
||||
RoleNone roleType = "None"
|
||||
RoleViewer roleType = "Viewer"
|
||||
RoleEditor roleType = "Editor"
|
||||
RoleAdmin roleType = "Admin"
|
||||
)
|
||||
|
||||
func (r roleType) IsValid() bool {
|
||||
return r == RoleViewer || r == RoleAdmin || r == RoleEditor
|
||||
return r == RoleViewer || r == RoleAdmin || r == RoleEditor || r == RoleNone
|
||||
}
|
||||
|
||||
type permissionType int
|
||||
|
@ -87,7 +87,9 @@ export const AddPermission = ({
|
||||
{target === PermissionTarget.BuiltInRole && (
|
||||
<Select
|
||||
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 || '')}
|
||||
width="auto"
|
||||
/>
|
||||
|
@ -29,7 +29,7 @@ interface RolesCollectionEntry {
|
||||
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) => ({
|
||||
label: r,
|
||||
value: r,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { OrgRole } from '@grafana/data';
|
||||
|
||||
export enum TeamPermissionLevel {
|
||||
Admin = 4,
|
||||
Editor = 2,
|
||||
@ -5,11 +7,7 @@ export enum TeamPermissionLevel {
|
||||
Viewer = 1,
|
||||
}
|
||||
|
||||
export enum OrgRole {
|
||||
Viewer = 'Viewer',
|
||||
Editor = 'Editor',
|
||||
Admin = 'Admin',
|
||||
}
|
||||
export { OrgRole as OrgRole };
|
||||
|
||||
export interface DashboardAclDTO {
|
||||
id?: number;
|
||||
|
Reference in New Issue
Block a user