Fine-grained permissions for user section. (#2451)
This commit is contained in:
parent
d5f6aca82e
commit
1c296a1641
7 changed files with 39 additions and 12 deletions
|
@ -16,9 +16,15 @@ export type AttributesFormProps = {
|
|||
form: UseFormMethods<AttributeForm>;
|
||||
save?: (model: AttributeForm) => void;
|
||||
reset?: () => void;
|
||||
fineGrainedAccess?: boolean;
|
||||
};
|
||||
|
||||
export const AttributesForm = ({ form, reset, save }: AttributesFormProps) => {
|
||||
export const AttributesForm = ({
|
||||
form,
|
||||
reset,
|
||||
save,
|
||||
fineGrainedAccess,
|
||||
}: AttributesFormProps) => {
|
||||
const { t } = useTranslation("roles");
|
||||
const noSaveCancelButtons = !save && !reset;
|
||||
const {
|
||||
|
@ -30,6 +36,7 @@ export const AttributesForm = ({ form, reset, save }: AttributesFormProps) => {
|
|||
<FormAccess
|
||||
role="manage-realm"
|
||||
onSubmit={save ? handleSubmit(save) : undefined}
|
||||
fineGrainedAccess={fineGrainedAccess}
|
||||
>
|
||||
<FormProvider {...form}>
|
||||
<KeyValueInput name="attributes" />
|
||||
|
|
|
@ -56,6 +56,7 @@ export const UserAttributes = ({ user: defaultUser }: UserAttributesProps) => {
|
|||
<AttributesForm
|
||||
form={form}
|
||||
save={save}
|
||||
fineGrainedAccess={user.access?.manage}
|
||||
reset={() =>
|
||||
form.reset({
|
||||
attributes: convertAttributes(),
|
||||
|
|
|
@ -153,6 +153,7 @@ export const UserForm = ({
|
|||
isHorizontal
|
||||
onSubmit={handleSubmit(save)}
|
||||
role="manage-users"
|
||||
fineGrainedAccess={user?.access?.manage}
|
||||
className="pf-u-mt-lg"
|
||||
>
|
||||
{open && (
|
||||
|
|
|
@ -216,6 +216,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => {
|
|||
data-testid={`leave-${group.name}`}
|
||||
onClick={() => leave([group])}
|
||||
variant="link"
|
||||
isDisabled={!user.access?.manageGroupMembership}
|
||||
>
|
||||
{t("leave")}
|
||||
</Button>
|
||||
|
@ -286,6 +287,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => {
|
|||
className="kc-join-group-button"
|
||||
onClick={toggleModal}
|
||||
data-testid="add-group-button"
|
||||
isDisabled={!user.access?.manageGroupMembership}
|
||||
>
|
||||
{t("joinGroup")}
|
||||
</Button>
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
SearchIcon,
|
||||
WarningTriangleIcon,
|
||||
} from "@patternfly/react-icons";
|
||||
import type { IRowData } from "@patternfly/react-table";
|
||||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
||||
|
@ -113,7 +114,10 @@ export default function UsersSection() {
|
|||
}
|
||||
|
||||
try {
|
||||
const users = await adminClient.users.find({ ...params });
|
||||
const users = await adminClient.users.find({
|
||||
briefRepresentation: true,
|
||||
...params,
|
||||
});
|
||||
if (realm?.bruteForceProtected) {
|
||||
const brutes = await Promise.all(
|
||||
users.map((user: BruteUser) =>
|
||||
|
@ -344,15 +348,20 @@ export default function UsersSection() {
|
|||
)
|
||||
}
|
||||
toolbarItem={toolbar}
|
||||
actions={[
|
||||
{
|
||||
title: t("common:delete"),
|
||||
onRowClick: (user) => {
|
||||
setSelectedRows([user]);
|
||||
toggleDeleteDialog();
|
||||
actionResolver={(rowData: IRowData) => {
|
||||
const user: UserRepresentation = rowData.data;
|
||||
if (!user.access?.manage) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
title: t("common:delete"),
|
||||
onClick: () => {
|
||||
setSelectedRows([user]);
|
||||
toggleDeleteDialog();
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
];
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
name: "username",
|
||||
|
|
|
@ -157,11 +157,16 @@ const UsersTabs = () => {
|
|||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key="impersonate"
|
||||
isDisabled={!user?.access?.impersonate}
|
||||
onClick={() => toggleImpersonateDialog()}
|
||||
>
|
||||
{t("impersonate")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem key="delete" onClick={() => toggleDeleteDialog()}>
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
isDisabled={!user?.access?.manage}
|
||||
onClick={() => toggleDeleteDialog()}
|
||||
>
|
||||
{t("common:delete")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
|
@ -196,6 +201,7 @@ const UsersTabs = () => {
|
|||
<Tab
|
||||
eventKey="credentials"
|
||||
data-testid="credentials"
|
||||
isHidden={!user.access?.manage}
|
||||
title={<TabTitleText>{t("common:credentials")}</TabTitleText>}
|
||||
>
|
||||
<UserCredentials user={user} />
|
||||
|
@ -203,6 +209,7 @@ const UsersTabs = () => {
|
|||
<Tab
|
||||
eventKey="role-mapping"
|
||||
data-testid="role-mapping-tab"
|
||||
isHidden={!user.access?.mapRoles}
|
||||
title={<TabTitleText>{t("roleMapping")}</TabTitleText>}
|
||||
>
|
||||
<UserRoleMapping id={id} name={user.username!} />
|
||||
|
|
|
@ -15,7 +15,7 @@ export const UserRoute: RouteDef = {
|
|||
path: "/:realm/users/:id/:tab",
|
||||
component: lazy(() => import("../UsersTabs")),
|
||||
breadcrumb: (t) => t("users:userDetails"),
|
||||
access: "manage-users",
|
||||
access: "view-users",
|
||||
};
|
||||
|
||||
export const toUser = (params: UserParams): LocationDescriptorObject => ({
|
||||
|
|
Loading…
Reference in a new issue