Now uses the role mapping component (#3184)
This commit is contained in:
parent
7a556a2e1e
commit
05a660b681
13 changed files with 114 additions and 391 deletions
|
@ -458,7 +458,6 @@ describe("Clients test", () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
keycloakBefore();
|
||||
loginPage.logIn();
|
||||
commonPage.sidebar().goToClients();
|
||||
commonPage.tableToolbarUtils().searchItem(client);
|
||||
|
@ -591,24 +590,18 @@ describe("Clients test", () => {
|
|||
commonPage.tableToolbarUtils().searchItem(itemId, false);
|
||||
commonPage.tableUtils().clickRowItemLink(itemId);
|
||||
rolesTab.goToAssociatedRolesTab();
|
||||
commonPage.tableUtils().selectRowItemAction("create-realm", "Remove");
|
||||
commonPage.tableUtils().selectRowItemAction("create-realm", "Unassign");
|
||||
commonPage.sidebar().waitForPageLoad();
|
||||
commonPage
|
||||
.modalUtils()
|
||||
.checkModalTitle("Remove associated role?")
|
||||
.confirmModal();
|
||||
commonPage.modalUtils().checkModalTitle("Remove mapping?").confirmModal();
|
||||
commonPage.sidebar().waitForPageLoad();
|
||||
|
||||
commonPage
|
||||
.masthead()
|
||||
.checkNotificationMessage("Associated roles have been removed", true);
|
||||
.checkNotificationMessage("Scope mapping successfully removed", true);
|
||||
|
||||
commonPage.tableUtils().selectRowItemAction("manage-consent", "Remove");
|
||||
commonPage.tableUtils().selectRowItemAction("manage-consent", "Unassign");
|
||||
commonPage.sidebar().waitForPageLoad();
|
||||
commonPage
|
||||
.modalUtils()
|
||||
.checkModalTitle("Remove associated role?")
|
||||
.confirmModal();
|
||||
commonPage.modalUtils().checkModalTitle("Remove mapping?").confirmModal();
|
||||
});
|
||||
|
||||
it("Should delete associated role from search bar test", () => {
|
||||
|
@ -617,7 +610,7 @@ describe("Clients test", () => {
|
|||
commonPage.sidebar().waitForPageLoad();
|
||||
rolesTab.goToAssociatedRolesTab();
|
||||
|
||||
cy.get('td[data-label="Role name"]')
|
||||
cy.get('td[data-label="Name"]')
|
||||
.contains("manage-account")
|
||||
.parent()
|
||||
.within(() => {
|
||||
|
@ -627,15 +620,12 @@ describe("Clients test", () => {
|
|||
associatedRolesPage.removeAssociatedRoles();
|
||||
|
||||
commonPage.sidebar().waitForPageLoad();
|
||||
commonPage
|
||||
.modalUtils()
|
||||
.checkModalTitle("Remove associated roles?")
|
||||
.confirmModal();
|
||||
commonPage.modalUtils().checkModalTitle("Remove mapping?").confirmModal();
|
||||
commonPage.sidebar().waitForPageLoad();
|
||||
|
||||
commonPage
|
||||
.masthead()
|
||||
.checkNotificationMessage("Associated roles have been removed", true);
|
||||
.checkNotificationMessage("Scope mapping successfully removed", true);
|
||||
});
|
||||
|
||||
it("Should delete client role test", () => {
|
||||
|
|
|
@ -156,11 +156,11 @@ describe("Realm roles test", () => {
|
|||
rolesTab.goToAssociatedRolesTab();
|
||||
listingPage.removeItem("create-realm");
|
||||
sidebarPage.waitForPageLoad();
|
||||
modalUtils.checkModalTitle("Remove associated role?").confirmModal();
|
||||
modalUtils.checkModalTitle("Remove mapping?").confirmModal();
|
||||
sidebarPage.waitForPageLoad();
|
||||
|
||||
masthead.checkNotificationMessage(
|
||||
"Associated roles have been removed",
|
||||
"Scope mapping successfully removed",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
@ -175,11 +175,11 @@ describe("Realm roles test", () => {
|
|||
associatedRolesPage.removeAssociatedRoles();
|
||||
|
||||
sidebarPage.waitForPageLoad();
|
||||
modalUtils.checkModalTitle("Remove associated roles?").confirmModal();
|
||||
modalUtils.checkModalTitle("Remove mapping?").confirmModal();
|
||||
sidebarPage.waitForPageLoad();
|
||||
|
||||
masthead.checkNotificationMessage(
|
||||
"Associated roles have been removed",
|
||||
"Scope mapping successfully removed",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
@ -206,20 +206,20 @@ describe("Realm roles test", () => {
|
|||
// delete associated roles from list
|
||||
listingPage.removeItem("create-realm");
|
||||
sidebarPage.waitForPageLoad();
|
||||
modalUtils.checkModalTitle("Remove associated role?").confirmModal();
|
||||
modalUtils.checkModalTitle("Remove mapping?").confirmModal();
|
||||
sidebarPage.waitForPageLoad();
|
||||
|
||||
masthead.checkNotificationMessage(
|
||||
"Associated roles have been removed",
|
||||
"Scope mapping successfully removed",
|
||||
true
|
||||
);
|
||||
listingPage.removeItem("offline_access");
|
||||
sidebarPage.waitForPageLoad();
|
||||
modalUtils.checkModalTitle("Remove associated role?").confirmModal();
|
||||
modalUtils.checkModalTitle("Remove mapping?").confirmModal();
|
||||
sidebarPage.waitForPageLoad();
|
||||
|
||||
masthead.checkNotificationMessage(
|
||||
"Associated roles have been removed",
|
||||
"Scope mapping successfully removed",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
|
|
@ -45,16 +45,14 @@ describe("Realm settings - User registration tab", () => {
|
|||
|
||||
it("Remove admin role", () => {
|
||||
const role = "admin";
|
||||
listingPage.markItemRow(role).removeMarkedItems();
|
||||
listingPage.markItemRow(role).removeMarkedItems("Unassign");
|
||||
sidebarPage.waitForPageLoad();
|
||||
modalUtils
|
||||
.checkModalTitle("Remove associated roles?")
|
||||
.checkModalMessage(
|
||||
"This action will remove the associated roles of default-roles-master. Users who have permission to default-roles-master will no longer have access to these roles."
|
||||
)
|
||||
.checkModalTitle("Remove mapping?")
|
||||
.checkModalMessage("Are you sure you want to remove this mapping?")
|
||||
.checkConfirmButtonText("Remove")
|
||||
.confirmModal();
|
||||
masthead.checkNotificationMessage("Associated roles have been removed");
|
||||
masthead.checkNotificationMessage("Scope mapping successfully removed");
|
||||
});
|
||||
|
||||
it("Add default group", () => {
|
||||
|
|
|
@ -162,8 +162,8 @@ export default class ListingPage extends CommonElements {
|
|||
return this;
|
||||
}
|
||||
|
||||
removeMarkedItems() {
|
||||
cy.get(this.listHeaderSecondaryBtn).contains("Remove").click();
|
||||
removeMarkedItems(name: string = "Remove") {
|
||||
cy.get(this.listHeaderSecondaryBtn).contains(name).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -246,7 +246,7 @@ export default class ListingPage extends CommonElements {
|
|||
|
||||
removeItem(itemName: string) {
|
||||
this.clickRowDetails(itemName);
|
||||
this.clickDetailMenu("Remove");
|
||||
this.clickDetailMenu("Unassign");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ enum ClientRolesTabItems {
|
|||
export default class ClientRolesTab extends CommonPage {
|
||||
private createRoleBtn = "create-role";
|
||||
private createRoleEmptyStateBtn = "no-roles-for-this-client-empty-action";
|
||||
private hideInheritedRolesChkBox = "#kc-hide-inherited-roles-checkbox";
|
||||
private hideInheritedRolesChkBox = "#hideInheritedRoles";
|
||||
private rolesTab = "rolesTab";
|
||||
private associatedRolesTab = ".kc-associated-roles-tab > button";
|
||||
|
||||
|
|
|
@ -28,9 +28,11 @@ export default class DedicatedScopesMappersTab extends CommonPage {
|
|||
}
|
||||
|
||||
addPredefinedMapper() {
|
||||
this.emptyState()
|
||||
.checkIfExists(true)
|
||||
.clickSecondaryBtn(mapperTypeEmptyState.AddPredefinedMapper, true);
|
||||
this.emptyState().checkIfExists(true);
|
||||
this.emptyState().clickSecondaryBtn(
|
||||
mapperTypeEmptyState.AddPredefinedMapper,
|
||||
true
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
export default class AssociatedRolesPage {
|
||||
private actionDropdown = "action-dropdown";
|
||||
private addRolesDropdownItem = "add-roles";
|
||||
private addRoleToolbarButton = "add-role-button";
|
||||
private addRoleToolbarButton = "assignRole";
|
||||
private addAssociatedRolesModalButton = "assign";
|
||||
private compositeRoleBadge = "composite-role-badge";
|
||||
private filterTypeDropdown = "filter-type-dropdown";
|
||||
private filterTypeDropdownItem = "roles";
|
||||
private usersPage = "users-page";
|
||||
private removeRolesButton = "removeRoles";
|
||||
private addRoleTable = 'td[data-label="Name"]';
|
||||
private removeRolesButton = "unAssignRole";
|
||||
private addRoleTable = '[aria-label="Roles"] td[data-label="Name"]';
|
||||
|
||||
addAssociatedRealmRole(roleName: string) {
|
||||
cy.findByTestId(this.actionDropdown).last().click();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default class UserRegistration {
|
||||
private userRegistrationTab = "rs-userRegistration-tab";
|
||||
private defaultGroupTab = "#pf-tab-20-groups";
|
||||
private addRoleBtn = "add-role-button";
|
||||
private addRoleBtn = "assignRole";
|
||||
private addDefaultGroupBtn = "no-default-groups-empty-action";
|
||||
private namesColumn = 'tbody td[data-label="Name"]:visible';
|
||||
private addBtn = "assign";
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
"roleCreateError": "Could not create role: {{error}}",
|
||||
"roleImportSuccess": "Role import successful",
|
||||
"roleDeleteConfirm": "Delete role?",
|
||||
"roleDeleteConfirmDialog": "This action will permanently delete the role {{selectedRoleName}} and cannot be undone.",
|
||||
"roleDeleteConfirmDialog": "This action will permanently delete the role \"{{selectedRoleName}}\" and cannot be undone.",
|
||||
"roleDeletedSuccess": "The role has been deleted",
|
||||
"roleDeleteError": "Could not delete role: {{error}}",
|
||||
"defaultRole": "This role serves as a container for both realm and client default roles. It cannot be removed.",
|
||||
|
|
|
@ -13,10 +13,8 @@ export type ResourcesKey = keyof KeycloakAdminClient;
|
|||
|
||||
type DeleteFunctions =
|
||||
| keyof Pick<Groups, "delClientRoleMappings" | "delRealmRoleMappings">
|
||||
| keyof Pick<
|
||||
ClientScopes,
|
||||
"delClientScopeMappings" | "delRealmScopeMappings"
|
||||
>;
|
||||
| keyof Pick<ClientScopes, "delClientScopeMappings" | "delRealmScopeMappings">
|
||||
| keyof Pick<Roles, "delCompositeRoles">;
|
||||
|
||||
type ListEffectiveFunction =
|
||||
| keyof Pick<Groups, "listRoleMappings" | "listAvailableRealmRoleMappings">
|
||||
|
@ -26,7 +24,7 @@ type ListEffectiveFunction =
|
|||
| "listAvailableRealmScopeMappings"
|
||||
| "listCompositeClientScopeMappings"
|
||||
>
|
||||
| keyof Pick<Roles, "getCompositeRoles">
|
||||
| keyof Pick<Roles, "getCompositeRoles" | "getCompositeRolesForClient">
|
||||
| keyof Pick<
|
||||
Users,
|
||||
"listCompositeClientRoleMappings" | "listCompositeRealmRoleMappings"
|
||||
|
@ -83,8 +81,12 @@ const mapping: ResourceMapping = {
|
|||
clientScopes: clientFunctions,
|
||||
clients: clientFunctions,
|
||||
roles: {
|
||||
delete: [],
|
||||
listEffective: ["getCompositeRoles", "getCompositeRoles"],
|
||||
delete: ["delCompositeRoles", "delCompositeRoles"],
|
||||
listEffective: [
|
||||
"getCompositeRoles",
|
||||
"getCompositeRoles",
|
||||
"getCompositeRolesForClient",
|
||||
],
|
||||
listAvailable: ["listRoles", "find"],
|
||||
},
|
||||
};
|
||||
|
@ -140,7 +142,28 @@ export const getMapping = async (
|
|||
id: string
|
||||
): Promise<MappingsRepresentation> => {
|
||||
const query = mapping[type]!.listEffective[0];
|
||||
return applyQuery(adminClient, type, query, { id }) as MappingsRepresentation;
|
||||
const result = applyQuery(adminClient, type, query, { id });
|
||||
if (type !== "roles") {
|
||||
return result as MappingsRepresentation;
|
||||
}
|
||||
const roles = await result;
|
||||
const clientRoles = await Promise.all(
|
||||
roles
|
||||
.filter((r) => r.clientRole)
|
||||
.map(async (role) => {
|
||||
const client = await adminClient.clients.findOne({
|
||||
id: role.containerId!,
|
||||
});
|
||||
|
||||
role.containerId = client?.clientId;
|
||||
return { ...client, mappings: [role] };
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
clientMappings: clientRoles,
|
||||
realmMappings: roles.filter((r) => !r.clientRole),
|
||||
};
|
||||
};
|
||||
|
||||
export const getEffectiveRoles = async (
|
||||
|
@ -149,9 +172,18 @@ export const getEffectiveRoles = async (
|
|||
id: string
|
||||
): Promise<Row[]> => {
|
||||
const query = mapping[type]!.listEffective[1];
|
||||
return (await applyQuery(adminClient, type, query, { id })).map((role) => ({
|
||||
role,
|
||||
}));
|
||||
if (type !== "roles") {
|
||||
return (await applyQuery(adminClient, type, query, { id })).map((role) => ({
|
||||
role,
|
||||
}));
|
||||
}
|
||||
const roles = await applyQuery(adminClient, type, query, { id });
|
||||
const parentRoles = await Promise.all(
|
||||
roles
|
||||
.filter((r) => r.composite)
|
||||
.map((r) => applyQuery(adminClient, type, query, { id: r.id }))
|
||||
);
|
||||
return [...roles, ...parentRoles.flat()].map((role) => ({ role }));
|
||||
};
|
||||
|
||||
export const getAvailableRoles = async (
|
||||
|
|
|
@ -1,326 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { useParams, useRouteMatch } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom-v5-compat";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
Checkbox,
|
||||
Label,
|
||||
PageSection,
|
||||
ToolbarItem,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import {
|
||||
ClientRoleParams,
|
||||
ClientRoleRoute,
|
||||
toClientRole,
|
||||
} from "./routes/ClientRole";
|
||||
import {
|
||||
RealmSettingsParams,
|
||||
RealmSettingsRoute,
|
||||
} from "../realm-settings/routes/RealmSettings";
|
||||
import { RealmRoleParams, RealmRoleTab, toRealmRole } from "./routes/RealmRole";
|
||||
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { emptyFormatter } from "../util";
|
||||
import { AddRoleMappingModal } from "../components/role-mapping/AddRoleMappingModal";
|
||||
import { useAdminClient } from "../context/auth/AdminClient";
|
||||
import type { AttributeForm } from "../components/key-value-form/AttributeForm";
|
||||
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
|
||||
|
||||
type AssociatedRolesTabProps = {
|
||||
parentRole: AttributeForm;
|
||||
client?: ClientRepresentation;
|
||||
refresh: () => void;
|
||||
};
|
||||
|
||||
type Role = RoleRepresentation & {
|
||||
inherited?: string;
|
||||
};
|
||||
|
||||
export const AssociatedRolesTab = ({
|
||||
parentRole,
|
||||
refresh: refreshParent,
|
||||
}: AssociatedRolesTabProps) => {
|
||||
const { t } = useTranslation("roles");
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const { id, realm } = useParams<RealmRoleParams>();
|
||||
|
||||
const [key, setKey] = useState(0);
|
||||
const refresh = () => setKey(new Date().getTime());
|
||||
|
||||
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
||||
const [isInheritedHidden, setIsInheritedHidden] = useState(false);
|
||||
const [count, setCount] = useState(0);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { adminClient } = useAdminClient();
|
||||
const clientRoleRouteMatch = useRouteMatch<ClientRoleParams>(
|
||||
ClientRoleRoute.path
|
||||
);
|
||||
|
||||
const realmSettingsMatch = useRouteMatch<RealmSettingsParams>(
|
||||
RealmSettingsRoute.path
|
||||
);
|
||||
|
||||
const subRoles = async (result: Role[], roles: Role[]): Promise<Role[]> => {
|
||||
const promises = roles.map(async (r) => {
|
||||
if (result.find((o) => o.id === r.id)) return result;
|
||||
result.push(r);
|
||||
if (r.composite) {
|
||||
const subList = (await adminClient.roles.getCompositeRoles({
|
||||
id: r.id!,
|
||||
})) as Role[];
|
||||
subList.map((o) => (o.inherited = r.name));
|
||||
result.concat(await subRoles(result, subList));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
await Promise.all(promises);
|
||||
return [...result];
|
||||
};
|
||||
|
||||
const loader = async (first?: number, max?: number, search?: string) => {
|
||||
const compositeRoles = await adminClient.roles.getCompositeRoles({
|
||||
id: parentRole.id!,
|
||||
first: first,
|
||||
max: max!,
|
||||
search: search,
|
||||
});
|
||||
setCount(compositeRoles.length);
|
||||
|
||||
if (!isInheritedHidden) {
|
||||
const children = await subRoles([], compositeRoles);
|
||||
compositeRoles.splice(0, compositeRoles.length);
|
||||
compositeRoles.push(...children);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
compositeRoles.map(async (role) => {
|
||||
if (role.clientRole) {
|
||||
role.containerId = (
|
||||
await adminClient.clients.findOne({
|
||||
id: role.containerId!,
|
||||
})
|
||||
)?.clientId;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return compositeRoles;
|
||||
};
|
||||
|
||||
const toRolesTab = (tab: RealmRoleTab | undefined = "associated-roles") => {
|
||||
const to = clientRoleRouteMatch
|
||||
? toClientRole({ ...clientRoleRouteMatch.params, tab })
|
||||
: !realmSettingsMatch
|
||||
? toRealmRole({
|
||||
realm,
|
||||
id,
|
||||
tab,
|
||||
})
|
||||
: undefined;
|
||||
if (to) navigate(to);
|
||||
};
|
||||
|
||||
const AliasRenderer = ({ id, name, clientRole, containerId }: Role) => {
|
||||
return (
|
||||
<>
|
||||
{clientRole && (
|
||||
<Label color="blue" key={`label-${id}`}>
|
||||
{containerId}
|
||||
</Label>
|
||||
)}{" "}
|
||||
{name}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const toggleModal = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
if (selectedRows.length >= count) {
|
||||
refreshParent();
|
||||
toRolesTab("details");
|
||||
} else {
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "roles:roleRemoveAssociatedRoleConfirm",
|
||||
messageKey: t("roles:roleRemoveAssociatedText", {
|
||||
role: selectedRows.map((r) => r.name),
|
||||
roleName: parentRole.name,
|
||||
}),
|
||||
continueButtonLabel: "common:remove",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.roles.delCompositeRoles(
|
||||
{ id: parentRole.id! },
|
||||
selectedRows
|
||||
);
|
||||
reload();
|
||||
setSelectedRows([]);
|
||||
|
||||
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("roles:roleDeleteError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [toggleDeleteAssociatedRolesDialog, DeleteAssociatedRolesConfirm] =
|
||||
useConfirmDialog({
|
||||
titleKey: t("roles:removeAssociatedRoles") + "?",
|
||||
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
|
||||
name: parentRole.name || t("createRole"),
|
||||
}),
|
||||
continueButtonLabel: "common:remove",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.roles.delCompositeRoles(
|
||||
{ id: parentRole.id! },
|
||||
selectedRows
|
||||
);
|
||||
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
||||
reload();
|
||||
} catch (error) {
|
||||
addError("roles:roleDeleteError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const addComposites = async (composites: RoleRepresentation[]) => {
|
||||
const compositeArray = composites;
|
||||
|
||||
try {
|
||||
await adminClient.roles.createComposite(
|
||||
{ roleId: parentRole.id!, realm },
|
||||
compositeArray
|
||||
);
|
||||
toRolesTab();
|
||||
refresh();
|
||||
addAlert(t("addAssociatedRolesSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("roles:addAssociatedRolesError", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PageSection variant="light" padding={{ default: "noPadding" }}>
|
||||
<DeleteConfirm />
|
||||
<DeleteAssociatedRolesConfirm />
|
||||
{open && (
|
||||
<AddRoleMappingModal
|
||||
id={parentRole.id!}
|
||||
type="roles"
|
||||
name={parentRole.name}
|
||||
onAssign={(rows) => addComposites(rows.map((r) => r.role))}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<KeycloakDataTable
|
||||
key={key}
|
||||
loader={loader}
|
||||
ariaLabelKey="roles:roleList"
|
||||
searchPlaceholderKey="roles:searchFor"
|
||||
canSelectAll
|
||||
isPaginated
|
||||
onSelect={(rows) => {
|
||||
setSelectedRows([
|
||||
...rows.map((r) => {
|
||||
delete r.inherited;
|
||||
return r;
|
||||
}),
|
||||
]);
|
||||
}}
|
||||
toolbarItem={
|
||||
<>
|
||||
<ToolbarItem>
|
||||
<Checkbox
|
||||
label="Hide inherited roles"
|
||||
key="associated-roles-check"
|
||||
id="kc-hide-inherited-roles-checkbox"
|
||||
onChange={() => {
|
||||
setIsInheritedHidden(!isInheritedHidden);
|
||||
refresh();
|
||||
}}
|
||||
isChecked={isInheritedHidden}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
<ToolbarItem>
|
||||
<Button
|
||||
key="add-role-button"
|
||||
onClick={() => toggleModal()}
|
||||
data-testid="add-role-button"
|
||||
>
|
||||
{t("addRole")}
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
<ToolbarItem>
|
||||
<Button
|
||||
variant="link"
|
||||
isDisabled={selectedRows.length === 0}
|
||||
key="remove-role-button"
|
||||
data-testid="removeRoles"
|
||||
onClick={() => {
|
||||
toggleDeleteAssociatedRolesDialog();
|
||||
}}
|
||||
>
|
||||
{t("removeRoles")}
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
</>
|
||||
}
|
||||
actions={[
|
||||
{
|
||||
title: t("common:remove"),
|
||||
onRowClick: (role) => {
|
||||
setSelectedRows([role]);
|
||||
toggleDeleteDialog();
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
name: "name",
|
||||
displayKey: "roles:roleName",
|
||||
cellRenderer: AliasRenderer,
|
||||
cellFormatters: [emptyFormatter()],
|
||||
},
|
||||
{
|
||||
name: "inherited",
|
||||
displayKey: "roles:inheritedFrom",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
displayKey: "common:description",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
},
|
||||
]}
|
||||
emptyState={
|
||||
<ListEmptyState
|
||||
hasIcon
|
||||
message={t("noRolesAssociated")}
|
||||
instructions={t("noRolesAssociatedInstructions")}
|
||||
primaryActionText={t("addRole")}
|
||||
onPrimaryAction={() => setOpen(true)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</PageSection>
|
||||
);
|
||||
};
|
|
@ -31,7 +31,6 @@ import { RealmRoleForm } from "./RealmRoleForm";
|
|||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { AddRoleMappingModal } from "../components/role-mapping/AddRoleMappingModal";
|
||||
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
||||
import { AssociatedRolesTab } from "./AssociatedRolesTab";
|
||||
import { UsersInRoleTab } from "./UsersInRoleTab";
|
||||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import { toRealmRole } from "./routes/RealmRole";
|
||||
|
@ -41,6 +40,7 @@ import {
|
|||
toClientRole,
|
||||
} from "./routes/ClientRole";
|
||||
import { PermissionsTab } from "../components/permission-tab/PermissionTab";
|
||||
import { RoleMapping } from "../components/role-mapping/RoleMapping";
|
||||
|
||||
export default function RealmRoleTabs() {
|
||||
const { t } = useTranslation("roles");
|
||||
|
@ -59,10 +59,10 @@ export default function RealmRoleTabs() {
|
|||
|
||||
const { realm: realmName } = useRealm();
|
||||
|
||||
const [key, setKey] = useState("");
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
const refresh = () => {
|
||||
setKey(`${new Date().getTime()}`);
|
||||
setKey(key + 1);
|
||||
};
|
||||
|
||||
const { addAlert, addError } = useAlerts();
|
||||
|
@ -184,7 +184,7 @@ export default function RealmRoleTabs() {
|
|||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "roles:roleDeleteConfirm",
|
||||
messageKey: t("roles:roleDeleteConfirmDialog", {
|
||||
name: role?.name || t("createRole"),
|
||||
selectedRoleName: role?.name || t("createRole"),
|
||||
}),
|
||||
continueButtonLabel: "common:delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
|
@ -385,7 +385,13 @@ export default function RealmRoleTabs() {
|
|||
className="kc-associated-roles-tab"
|
||||
title={<TabTitleText>{t("associatedRolesText")}</TabTitleText>}
|
||||
>
|
||||
<AssociatedRolesTab parentRole={role} refresh={refresh} />
|
||||
<RoleMapping
|
||||
name={role.name!}
|
||||
id={role.id!}
|
||||
type="roles"
|
||||
isManager
|
||||
save={(rows) => addComposites(rows.map((r) => r.role))}
|
||||
/>
|
||||
</Tab>
|
||||
)}
|
||||
{!isDefaultRole(role.name!) && (
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tab, Tabs, TabTitleText } from "@patternfly/react-core";
|
||||
import { AlertVariant, Tab, Tabs, TabTitleText } from "@patternfly/react-core";
|
||||
|
||||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { AssociatedRolesTab } from "../realm-roles/AssociatedRolesTab";
|
||||
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { RoleMapping } from "../components/role-mapping/RoleMapping";
|
||||
import { DefaultsGroupsTab } from "./DefaultGroupsTab";
|
||||
|
||||
export const UserRegistration = () => {
|
||||
|
@ -16,6 +18,7 @@ export const UserRegistration = () => {
|
|||
const [key, setKey] = useState(0);
|
||||
|
||||
const { adminClient } = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const { realm: realmName } = useRealm();
|
||||
|
||||
useFetch(
|
||||
|
@ -28,6 +31,21 @@ export const UserRegistration = () => {
|
|||
return <KeycloakSpinner />;
|
||||
}
|
||||
|
||||
const addComposites = async (composites: RoleRepresentation[]) => {
|
||||
const compositeArray = composites;
|
||||
|
||||
try {
|
||||
await adminClient.roles.createComposite(
|
||||
{ roleId: realm.defaultRole!.id!, realm: realmName },
|
||||
compositeArray
|
||||
);
|
||||
setKey(key + 1);
|
||||
addAlert(t("roles:addAssociatedRolesSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("roles:addAssociatedRolesError", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
|
@ -39,9 +57,12 @@ export const UserRegistration = () => {
|
|||
eventKey={10}
|
||||
title={<TabTitleText>{t("defaultRoles")}</TabTitleText>}
|
||||
>
|
||||
<AssociatedRolesTab
|
||||
parentRole={{ ...realm.defaultRole, attributes: [] }}
|
||||
refresh={() => setKey(key + 1)}
|
||||
<RoleMapping
|
||||
name={realm.defaultRole!.name!}
|
||||
id={realm.defaultRole!.id!}
|
||||
type="roles"
|
||||
isManager
|
||||
save={(rows) => addComposites(rows.map((r) => r.role))}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
|
|
Loading…
Reference in a new issue