diff --git a/src/api/auth-groups.tsx b/src/api/auth-groups.tsx index d664f4c5cc..85990bcf8d 100644 --- a/src/api/auth-groups.tsx +++ b/src/api/auth-groups.tsx @@ -3,7 +3,7 @@ import type { LxdApiResponse } from "types/apiResponse"; import type { LxdGroup } from "types/permissions"; import { withEntitlementsQuery } from "util/entitlements/api"; -export const groupEntitlements = ["can_edit", "can_delete"]; +export const groupEntitlements = ["can_delete", "can_edit"]; export const fetchGroups = ( isFineGrained: boolean | null, diff --git a/src/api/auth-identities.tsx b/src/api/auth-identities.tsx index 4244f0e922..c23e713628 100644 --- a/src/api/auth-identities.tsx +++ b/src/api/auth-identities.tsx @@ -3,7 +3,7 @@ import type { LxdApiResponse } from "types/apiResponse"; import type { LxdIdentity, TlsIdentityTokenDetail } from "types/permissions"; import { withEntitlementsQuery } from "util/entitlements/api"; -export const identitiesEntitlements = ["can_edit", "can_delete"]; +export const identitiesEntitlements = ["can_delete", "can_edit"]; export const fetchIdentities = ( isFineGrained: boolean | null, diff --git a/src/api/auth-idp-groups.tsx b/src/api/auth-idp-groups.tsx index a4b5934bca..cee9538ea0 100644 --- a/src/api/auth-idp-groups.tsx +++ b/src/api/auth-idp-groups.tsx @@ -3,7 +3,7 @@ import type { LxdApiResponse } from "types/apiResponse"; import type { IdpGroup } from "types/permissions"; import { withEntitlementsQuery } from "util/entitlements/api"; -const idpGroupEntitlements = ["can_edit", "can_delete"]; +const idpGroupEntitlements = ["can_delete", "can_edit"]; export const fetchIdpGroups = ( isFineGrained: boolean | null, diff --git a/src/components/SelectableMainTable.tsx b/src/components/SelectableMainTable.tsx index b8fdd2f42e..2cd26780c4 100644 --- a/src/components/SelectableMainTable.tsx +++ b/src/components/SelectableMainTable.tsx @@ -19,14 +19,14 @@ interface SelectableMainTableProps { parentName: string; selectedNames: string[]; setSelectedNames: (val: string[], isUnselectAll?: boolean) => void; - processingNames: string[]; + disabledNames: string[]; rows: MainTableRow[]; indeterminateNames?: string[]; disableSelect?: boolean; onToggleRow?: (rowName: string) => void; hideContextualMenu?: boolean; defaultSortKey?: string; - disableHeaderCheckbox?: boolean; + disableSelectAll?: boolean; } type Props = SelectableMainTableProps & MainTableProps; @@ -37,7 +37,7 @@ const SelectableMainTable: FC = ({ parentName, selectedNames, setSelectedNames, - processingNames, + disabledNames, rows, headers, indeterminateNames = [], @@ -45,7 +45,7 @@ const SelectableMainTable: FC = ({ onToggleRow, hideContextualMenu, defaultSortKey, - disableHeaderCheckbox, + disableSelectAll, ...props }: Props) => { const [currentSelectedIndex, setCurrentSelectedIndex] = useState(); @@ -93,7 +93,7 @@ const SelectableMainTable: FC = ({ checked={isAllSelected} indeterminate={isSomeSelected && !isAllSelected} onChange={isSomeSelected ? selectNone : selectPage} - disabled={disableSelect || disableHeaderCheckbox} + disabled={disableSelect || disableSelectAll} /> {!hideContextualMenu && ( = ({ ]; const selectedNamesLookup = new Set(selectedNames); - const processingNamesLookup = new Set(processingNames); + const disabledNamesLookup = new Set(disabledNames); const indeterminateNamesLookup = new Set(indeterminateNames); const rowsWithCheckbox = rows.map((row, rowIndex) => { const isRowSelected = selectedNamesLookup.has(row.name ?? ""); - const isRowProcessing = processingNamesLookup.has(row.name ?? ""); + const isRowDisabled = disabledNamesLookup.has(row.name ?? ""); const isRowIndeterminate = indeterminateNamesLookup.has(row.name ?? ""); const toggleRow = (event: PointerEvent) => { @@ -185,7 +185,7 @@ const SelectableMainTable: FC = ({ labelClassName="u-no-margin--bottom" checked={isRowSelected} onChange={toggleRow} - disabled={isRowProcessing || !row.name || disableSelect} + disabled={isRowDisabled || !row.name || disableSelect} indeterminate={isRowIndeterminate && !isRowSelected} /> ), @@ -197,7 +197,7 @@ const SelectableMainTable: FC = ({ const className = classnames(row.className, { "selected-row": isRowSelected, - "processing-row": isRowProcessing, + "processing-row": isRowDisabled, }); const key = row.key ?? row.name; diff --git a/src/context/useIdentities.tsx b/src/context/useIdentities.tsx index e523226a19..615756013d 100644 --- a/src/context/useIdentities.tsx +++ b/src/context/useIdentities.tsx @@ -21,7 +21,7 @@ export const useIdentity = ( ): UseQueryResult => { const { isFineGrained } = useAuth(); return useQuery({ - queryKey: [queryKeys.identities, id], + queryKey: [queryKeys.identities, authMethod, id], queryFn: () => fetchIdentity(id, authMethod, isFineGrained), enabled: (enabled ?? true) && isFineGrained !== null, }); diff --git a/src/pages/cluster/ClusterGroupForm.tsx b/src/pages/cluster/ClusterGroupForm.tsx index 595cda1760..cf52d243ae 100644 --- a/src/pages/cluster/ClusterGroupForm.tsx +++ b/src/pages/cluster/ClusterGroupForm.tsx @@ -170,7 +170,7 @@ const ClusterGroupForm: FC = ({ group }) => { setSelectedNames={(newMembers: string[]) => void formik.setFieldValue("members", newMembers) } - processingNames={[]} + disabledNames={[]} /> diff --git a/src/pages/images/ImageList.tsx b/src/pages/images/ImageList.tsx index dcfc204dd1..73b6e0c4dd 100644 --- a/src/pages/images/ImageList.tsx +++ b/src/pages/images/ImageList.tsx @@ -288,7 +288,7 @@ const ImageList: FC = () => { itemName="image" parentName="project" filteredNames={filteredImages.map((item) => item.fingerprint)} - processingNames={processingNames} + disabledNames={processingNames} rows={[]} disableSelect={!deletableImages.length} /> diff --git a/src/pages/instances/InstanceList.tsx b/src/pages/instances/InstanceList.tsx index 7fcf01d86c..8f73764bb6 100644 --- a/src/pages/instances/InstanceList.tsx +++ b/src/pages/instances/InstanceList.tsx @@ -699,7 +699,7 @@ const InstanceList: FC = () => { parentName="project" selectedNames={selectedNames} setSelectedNames={setSelectedNames} - processingNames={processingNames} + disabledNames={processingNames} filteredNames={filteredInstances.map( (instance) => instance.name, )} @@ -716,7 +716,7 @@ const InstanceList: FC = () => { parentName="project" selectedNames={selectedNames} setSelectedNames={setSelectedNames} - processingNames={processingNames} + disabledNames={processingNames} filteredNames={filteredInstances.map( (instance) => instance.name, )} diff --git a/src/pages/instances/InstanceSnapshots.tsx b/src/pages/instances/InstanceSnapshots.tsx index 13744285f7..6eba368e5f 100644 --- a/src/pages/instances/InstanceSnapshots.tsx +++ b/src/pages/instances/InstanceSnapshots.tsx @@ -278,7 +278,7 @@ const InstanceSnapshots = (props: Props) => { parentName="instance" selectedNames={selectedNames} setSelectedNames={setSelectedNames} - processingNames={processingNames} + disabledNames={processingNames} filteredNames={filteredSnapshots.map( (snapshot) => snapshot.name, )} diff --git a/src/pages/permissions/PermissionGroups.tsx b/src/pages/permissions/PermissionGroups.tsx index be37c60e1e..bbb14dd7a6 100644 --- a/src/pages/permissions/PermissionGroups.tsx +++ b/src/pages/permissions/PermissionGroups.tsx @@ -209,7 +209,7 @@ const PermissionGroups: FC = () => { parentName="" selectedNames={selectedGroupNames} setSelectedNames={setSelectedGroupNames} - processingNames={[]} + disabledNames={[]} filteredNames={filteredGroups.map((item) => item.name)} disableSelect={!!panelParams.panel} /> diff --git a/src/pages/permissions/PermissionIdentities.tsx b/src/pages/permissions/PermissionIdentities.tsx index a5b4d4a67e..312b21a426 100644 --- a/src/pages/permissions/PermissionIdentities.tsx +++ b/src/pages/permissions/PermissionIdentities.tsx @@ -302,7 +302,7 @@ const PermissionIdentities: FC = () => { parentName="" selectedNames={selectedIdentityIds} setSelectedNames={setSelectedIdentityIds} - processingNames={[]} + disabledNames={[]} filteredNames={fineGrainedIdentities.map( (identity) => identity.id, )} diff --git a/src/pages/permissions/PermissionIdpGroups.tsx b/src/pages/permissions/PermissionIdpGroups.tsx index 6036c36b1e..e33e996699 100644 --- a/src/pages/permissions/PermissionIdpGroups.tsx +++ b/src/pages/permissions/PermissionIdpGroups.tsx @@ -41,7 +41,7 @@ const PermissionIdpGroups: FC = () => { const { data: settings } = useSettings(); const hasCustomClaim = settings?.config?.["oidc.groups.claim"]; const { canCreateIdpGroups } = useServerEntitlements(); - const { canEditGroup } = useIdpGroupEntitlements(); + const { canEditIdpGroup } = useIdpGroupEntitlements(); if (error) { notify.failure("Loading provider groups failed", error); @@ -94,7 +94,7 @@ const PermissionIdpGroups: FC = () => { title: idpGroup.name, }, { - content: canEditGroup(idpGroup) ? ( + content: canEditIdpGroup(idpGroup) ? ( , @@ -232,7 +232,7 @@ const PermissionIdpGroups: FC = () => { parentName="" selectedNames={selectedGroupNames} setSelectedNames={setSelectedGroupNames} - processingNames={[]} + disabledNames={[]} filteredNames={filteredGroups.map((item) => item.name)} disableSelect={!!panelParams.panel} /> diff --git a/src/pages/permissions/actions/BulkDeleteIdentitiesBtn.tsx b/src/pages/permissions/actions/BulkDeleteIdentitiesBtn.tsx index eb57c22a18..5bb853f58f 100644 --- a/src/pages/permissions/actions/BulkDeleteIdentitiesBtn.tsx +++ b/src/pages/permissions/actions/BulkDeleteIdentitiesBtn.tsx @@ -83,7 +83,7 @@ const BulkDeleteIdentitiesBtn: FC = ({ ))} You do not have permission to delete the following{" "} - {pluralize("identity", deletableIdentities.length)}: + {pluralize("identity", restrictedIdentities.length)}:
    {restrictedIdentities.map((identity) => (
  • {identity.name}
  • diff --git a/src/pages/permissions/actions/BulkDeleteIdpGroupsBtn.tsx b/src/pages/permissions/actions/BulkDeleteIdpGroupsBtn.tsx index 053c511bb1..86f3fa60d8 100644 --- a/src/pages/permissions/actions/BulkDeleteIdpGroupsBtn.tsx +++ b/src/pages/permissions/actions/BulkDeleteIdpGroupsBtn.tsx @@ -12,8 +12,8 @@ interface Props { const BulkDeleteIdpGroupsBtn: FC = ({ idpGroups, className }) => { const [confirming, setConfirming] = useState(false); - const { canDeleteGroup } = useIdpGroupEntitlements(); - const deletableGroups = idpGroups.filter(canDeleteGroup); + const { canEditIdpGroup } = useIdpGroupEntitlements(); + const deletableGroups = idpGroups.filter(canEditIdpGroup); const handleConfirmDelete = () => { setConfirming(true); diff --git a/src/pages/permissions/actions/DeleteIdpGroupBtn.tsx b/src/pages/permissions/actions/DeleteIdpGroupBtn.tsx index a7066923bd..3cffa46f66 100644 --- a/src/pages/permissions/actions/DeleteIdpGroupBtn.tsx +++ b/src/pages/permissions/actions/DeleteIdpGroupBtn.tsx @@ -11,7 +11,7 @@ interface Props { const DeleteIdpGroupBtn: FC = ({ idpGroup }) => { const { openPortal, closePortal, isOpen, Portal } = usePortal(); - const { canDeleteGroup } = useIdpGroupEntitlements(); + const { canDeleteIdpGroup } = useIdpGroupEntitlements(); return ( <> @@ -23,11 +23,11 @@ const DeleteIdpGroupBtn: FC = ({ idpGroup }) => { type="button" aria-label="Delete IDP group" title={ - canDeleteGroup() + canDeleteIdpGroup() ? "Delete IDP group" : "You do not have permission to delete this IDP group" } - disabled={!canDeleteGroup(idpGroup)} + disabled={!canDeleteIdpGroup(idpGroup)} > diff --git a/src/pages/permissions/actions/DeleteIdpGroupsModal.tsx b/src/pages/permissions/actions/DeleteIdpGroupsModal.tsx index d5dba19756..1e20b2ed6c 100644 --- a/src/pages/permissions/actions/DeleteIdpGroupsModal.tsx +++ b/src/pages/permissions/actions/DeleteIdpGroupsModal.tsx @@ -23,12 +23,12 @@ const DeleteIdpGroupsModal: FC = ({ idpGroups, close }) => { const notify = useNotify(); const toastNotify = useToastNotification(); const [submitting, setSubmitting] = useState(false); - const { canDeleteGroup } = useIdpGroupEntitlements(); + const { canDeleteIdpGroup } = useIdpGroupEntitlements(); const restrictedGroups: IdpGroup[] = []; const deletableGroups: IdpGroup[] = []; idpGroups.forEach((group) => { - if (canDeleteGroup(group)) { + if (canDeleteIdpGroup(group)) { deletableGroups.push(group); } else { restrictedGroups.push(group); diff --git a/src/pages/permissions/actions/EditIdentityGroupsBtn.tsx b/src/pages/permissions/actions/EditIdentityGroupsBtn.tsx index 10e3da0e62..35280b0256 100644 --- a/src/pages/permissions/actions/EditIdentityGroupsBtn.tsx +++ b/src/pages/permissions/actions/EditIdentityGroupsBtn.tsx @@ -27,10 +27,10 @@ const EditIdentityGroupsBtn: FC = ({ ); const getRestrictedWarning = () => { - const test = restrictedIdentities + const restrictedList = restrictedIdentities .map((identity) => `\n- ${identity.name}`) .join(""); - return `You do not have permission to modify ${restrictedIdentities.length > 1 ? "some of the selected" : "the selected"} ${pluralize("identity", restrictedIdentities.length)}:${test}`; + return `You do not have permission to modify ${restrictedIdentities.length > 1 ? "some of the selected" : "the selected"} ${pluralize("identity", restrictedIdentities.length)}:${restrictedList}`; }; return ( diff --git a/src/pages/permissions/panels/EditGroupIdentitiesPanel.tsx b/src/pages/permissions/panels/EditGroupIdentitiesPanel.tsx index d5caee4f11..6e2aec574e 100644 --- a/src/pages/permissions/panels/EditGroupIdentitiesPanel.tsx +++ b/src/pages/permissions/panels/EditGroupIdentitiesPanel.tsx @@ -252,15 +252,6 @@ const EditGroupIdentitiesPanel: FC = ({ groups }) => { defaultSort: "name", }); - const confirmButtonText = modifiedIdentities.size - ? `Apply ${modifiedIdentities.size} identity ${pluralize("change", modifiedIdentities.size)}` - : "Modify identities"; - - const panelTitle = - groups.length > 1 - ? `Change identities for ${groups.length} groups` - : `Change identities for ${groups[0]?.name}`; - const content = ( = ({ groups }) => { parentName="server" selectedNames={Array.from(selectedIdentities)} setSelectedNames={modifyIdentities} - processingNames={restrictedIdentities.map((identity) => identity.id)} + disabledNames={restrictedIdentities.map((identity) => identity.id)} filteredNames={fineGrainedIdentities.map((identity) => identity.id)} indeterminateNames={Array.from(indeterminateIdentities)} onToggleRow={toggleRow} hideContextualMenu - disableHeaderCheckbox={!!restrictedIdentities.length} + disableSelectAll={!!restrictedIdentities.length} /> ); + const confirmButtonText = modifiedIdentities.size + ? `Apply ${modifiedIdentities.size} identity ${pluralize("change", modifiedIdentities.size)}` + : "Modify identities"; + + const panelTitle = + groups.length > 1 + ? `Change identities for ${groups.length} groups` + : `Change identities for ${groups[0]?.name}`; + return ( <> diff --git a/src/pages/permissions/panels/EditGroupPermissionsForm.tsx b/src/pages/permissions/panels/EditGroupPermissionsForm.tsx index 1f18f20f2a..81ab801671 100644 --- a/src/pages/permissions/panels/EditGroupPermissionsForm.tsx +++ b/src/pages/permissions/panels/EditGroupPermissionsForm.tsx @@ -42,10 +42,21 @@ const EditGroupPermissionsForm: FC = ({ const [search, setSearch] = useState(""); const { canViewPermissions } = useServerEntitlements(); const { canEditGroup } = useGroupEntitlements(); - const permissionEditRestriction = - !canViewPermissions() || !canEditGroup(group) - ? "You do not have permission to edit entitlements for this group" - : ""; + const getPermissionEditRestriction = () => { + if (!canViewPermissions() && !canEditGroup(group)) { + return "You do not have permission to view entitlements or edit this group"; + } + + if (!canViewPermissions()) { + return "You do not have permission to view entitlements"; + } + + if (!canEditGroup(group)) { + return "You do not have permission to edit this group"; + } + + return ""; + }; const addPermission = (newPermission: FormPermission) => { const permissionExists = permissions.find( @@ -178,9 +189,9 @@ const EditGroupPermissionsForm: FC = ({ onClick={() => deletePermission(permission.id ?? "")} type="button" aria-label="Delete permission" - title={permissionEditRestriction ?? "Delete permission"} + title={getPermissionEditRestriction() ?? "Delete permission"} className="u-no-margin--right" - disabled={!!permissionEditRestriction} + disabled={!!getPermissionEditRestriction()} > @@ -223,7 +234,7 @@ const EditGroupPermissionsForm: FC = ({ diff --git a/src/pages/permissions/panels/EditIdentitiesForm.tsx b/src/pages/permissions/panels/EditIdentitiesForm.tsx index b59d0ac628..5e959929c4 100644 --- a/src/pages/permissions/panels/EditIdentitiesForm.tsx +++ b/src/pages/permissions/panels/EditIdentitiesForm.tsx @@ -171,14 +171,12 @@ const EditIdentitiesForm: FC = ({ .filter((id) => !id.isRemoved) .map((identity) => identity.id)} setSelectedNames={bulkSelect} - processingNames={restrictedIdentities.map( - (identity) => identity.name, - )} + disabledNames={restrictedIdentities.map((identity) => identity.name)} filteredNames={fineGrainedIdentities.map((identity) => identity.id)} indeterminateNames={[]} onToggleRow={toggleRow} hideContextualMenu - disableHeaderCheckbox={!!restrictedIdentities.length} + disableSelectAll={!!restrictedIdentities.length} /> diff --git a/src/pages/permissions/panels/GroupSelection.tsx b/src/pages/permissions/panels/GroupSelection.tsx index 822f21c54c..dabb2c48b7 100644 --- a/src/pages/permissions/panels/GroupSelection.tsx +++ b/src/pages/permissions/panels/GroupSelection.tsx @@ -145,7 +145,7 @@ const GroupSelection: FC = ({ parentName="" selectedNames={Array.from(selectedGroups)} setSelectedNames={setSelectedGroups} - processingNames={[]} + disabledNames={[]} filteredNames={groups.map((group) => group.name)} indeterminateNames={Array.from(indeterminateGroups ?? new Set())} onToggleRow={toggleGroup} diff --git a/src/pages/storage/StorageVolumeSnapshots.tsx b/src/pages/storage/StorageVolumeSnapshots.tsx index 5fbd64a09e..a1e769e6f7 100644 --- a/src/pages/storage/StorageVolumeSnapshots.tsx +++ b/src/pages/storage/StorageVolumeSnapshots.tsx @@ -276,7 +276,7 @@ const StorageVolumeSnapshots: FC = ({ volume }) => { parentName="instance" selectedNames={selectedNames} setSelectedNames={setSelectedNames} - processingNames={processingNames} + disabledNames={processingNames} filteredNames={filteredSnapshots.map( (snapshot) => snapshot.name, )} diff --git a/src/util/entitlements/groups.tsx b/src/util/entitlements/groups.tsx index 1556ddf873..46f7d5e5be 100644 --- a/src/util/entitlements/groups.tsx +++ b/src/util/entitlements/groups.tsx @@ -5,12 +5,12 @@ import { LxdGroup } from "types/permissions"; export const useGroupEntitlements = () => { const { isFineGrained } = useAuth(); - const canEditGroup = (group?: LxdGroup) => - hasEntitlement(isFineGrained, "can_edit", group?.access_entitlements); - const canDeleteGroup = (group?: LxdGroup) => hasEntitlement(isFineGrained, "can_delete", group?.access_entitlements); + const canEditGroup = (group?: LxdGroup) => + hasEntitlement(isFineGrained, "can_edit", group?.access_entitlements); + return { canDeleteGroup, canEditGroup, diff --git a/src/util/entitlements/identities.tsx b/src/util/entitlements/identities.tsx index a7394ab4f3..56eaa4b1d6 100644 --- a/src/util/entitlements/identities.tsx +++ b/src/util/entitlements/identities.tsx @@ -5,12 +5,12 @@ import { LxdIdentity } from "types/permissions"; export const useIdentityEntitlements = () => { const { isFineGrained } = useAuth(); - const canEditIdentity = (identity?: LxdIdentity) => - hasEntitlement(isFineGrained, "can_edit", identity?.access_entitlements); - const canDeleteIdentity = (identity?: LxdIdentity) => hasEntitlement(isFineGrained, "can_delete", identity?.access_entitlements); + const canEditIdentity = (identity?: LxdIdentity) => + hasEntitlement(isFineGrained, "can_edit", identity?.access_entitlements); + return { canDeleteIdentity, canEditIdentity, diff --git a/src/util/entitlements/idp-groups.tsx b/src/util/entitlements/idp-groups.tsx index 0a637ffb2d..a55d5e614a 100644 --- a/src/util/entitlements/idp-groups.tsx +++ b/src/util/entitlements/idp-groups.tsx @@ -5,14 +5,14 @@ import { IdpGroup } from "types/permissions"; export const useIdpGroupEntitlements = () => { const { isFineGrained } = useAuth(); - const canEditGroup = (group?: IdpGroup) => - hasEntitlement(isFineGrained, "can_edit", group?.access_entitlements); - - const canDeleteGroup = (group?: IdpGroup) => + const canDeleteIdpGroup = (group?: IdpGroup) => hasEntitlement(isFineGrained, "can_delete", group?.access_entitlements); + const canEditIdpGroup = (group?: IdpGroup) => + hasEntitlement(isFineGrained, "can_edit", group?.access_entitlements); + return { - canDeleteGroup, - canEditGroup, + canDeleteIdpGroup, + canEditIdpGroup, }; };