From c5e2ca12e1b091bc6196555927bd8b013966ef3b Mon Sep 17 00:00:00 2001
From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com>
Date: Tue, 7 Apr 2026 16:57:40 -0700
Subject: [PATCH] fix: make edit dialogs read-only when user lacks manage
permission
When a user doesn't have MANAGE_ROLES or MANAGE_GROUPS capability,
the name/description fields are now disabled and read-only,
permission toggles are disabled, and the save button is disabled.
---
src/components/access/EditGroupDialog.tsx | 10 +++++++---
src/components/access/EditRoleDialog.tsx | 10 +++++++---
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/src/components/access/EditGroupDialog.tsx b/src/components/access/EditGroupDialog.tsx
index abea92e..e7fb923 100644
--- a/src/components/access/EditGroupDialog.tsx
+++ b/src/components/access/EditGroupDialog.tsx
@@ -162,8 +162,10 @@ export function EditGroupDialog({ group, canManage, onClose }: t.EditGroupDialog
value={name}
onChange={(e) => setName(e.target.value)}
placeholder={localize('com_access_group_name_placeholder')}
+ disabled={!canManage}
+ readOnly={!canManage}
autoFocus
- className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled)"
+ className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled) disabled:cursor-not-allowed disabled:opacity-50"
/>
@@ -179,7 +181,9 @@ export function EditGroupDialog({ group, canManage, onClose }: t.EditGroupDialog
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder={localize('com_access_group_desc_placeholder')}
- className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled)"
+ disabled={!canManage}
+ readOnly={!canManage}
+ className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled) disabled:cursor-not-allowed disabled:opacity-50"
/>
@@ -250,7 +254,7 @@ export function EditGroupDialog({ group, canManage, onClose }: t.EditGroupDialog
diff --git a/src/components/access/EditRoleDialog.tsx b/src/components/access/EditRoleDialog.tsx
index bd22b7e..841b238 100644
--- a/src/components/access/EditRoleDialog.tsx
+++ b/src/components/access/EditRoleDialog.tsx
@@ -213,7 +213,8 @@ export function EditRoleDialog({ role, canManage, onClose }: t.EditRoleDialogPro
value={name}
onChange={(e) => setName(e.target.value)}
placeholder={localize('com_access_role_name_placeholder')}
- disabled={role?.isSystemRole}
+ disabled={role?.isSystemRole || !canManage}
+ readOnly={!canManage}
autoFocus
className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled) disabled:cursor-not-allowed disabled:opacity-50"
/>
@@ -231,7 +232,9 @@ export function EditRoleDialog({ role, canManage, onClose }: t.EditRoleDialogPro
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder={localize('com_access_role_desc_placeholder')}
- className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled)"
+ disabled={!canManage}
+ readOnly={!canManage}
+ className="rounded-lg border border-(--cui-color-stroke-default) bg-(--cui-color-background-default) px-3 py-2 text-sm text-(--cui-color-text-default) placeholder:text-(--cui-color-text-disabled) disabled:cursor-not-allowed disabled:opacity-50"
/>
@@ -260,7 +263,7 @@ export function EditRoleDialog({ role, canManage, onClose }: t.EditRoleDialogPro
);
})()}
@@ -333,6 +336,7 @@ export function EditRoleDialog({ role, canManage, onClose }: t.EditRoleDialogPro
type="primary"
label={localize('com_ui_save')}
disabled={
+ !canManage ||
!name.trim() ||
(permissionsDirty && permissions === null) ||
(!detailsDirty && !permissionsDirty && !membersDirty) ||