Fine-grained permissions for user section. (#2451)

This commit is contained in:
Stan Silvert 2022-04-21 11:03:48 -04:00 committed by GitHub
parent d5f6aca82e
commit 1c296a1641
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 39 additions and 12 deletions

View file

@ -16,9 +16,15 @@ export type AttributesFormProps = {
form: UseFormMethods<AttributeForm>; form: UseFormMethods<AttributeForm>;
save?: (model: AttributeForm) => void; save?: (model: AttributeForm) => void;
reset?: () => 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 { t } = useTranslation("roles");
const noSaveCancelButtons = !save && !reset; const noSaveCancelButtons = !save && !reset;
const { const {
@ -30,6 +36,7 @@ export const AttributesForm = ({ form, reset, save }: AttributesFormProps) => {
<FormAccess <FormAccess
role="manage-realm" role="manage-realm"
onSubmit={save ? handleSubmit(save) : undefined} onSubmit={save ? handleSubmit(save) : undefined}
fineGrainedAccess={fineGrainedAccess}
> >
<FormProvider {...form}> <FormProvider {...form}>
<KeyValueInput name="attributes" /> <KeyValueInput name="attributes" />

View file

@ -56,6 +56,7 @@ export const UserAttributes = ({ user: defaultUser }: UserAttributesProps) => {
<AttributesForm <AttributesForm
form={form} form={form}
save={save} save={save}
fineGrainedAccess={user.access?.manage}
reset={() => reset={() =>
form.reset({ form.reset({
attributes: convertAttributes(), attributes: convertAttributes(),

View file

@ -153,6 +153,7 @@ export const UserForm = ({
isHorizontal isHorizontal
onSubmit={handleSubmit(save)} onSubmit={handleSubmit(save)}
role="manage-users" role="manage-users"
fineGrainedAccess={user?.access?.manage}
className="pf-u-mt-lg" className="pf-u-mt-lg"
> >
{open && ( {open && (

View file

@ -216,6 +216,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => {
data-testid={`leave-${group.name}`} data-testid={`leave-${group.name}`}
onClick={() => leave([group])} onClick={() => leave([group])}
variant="link" variant="link"
isDisabled={!user.access?.manageGroupMembership}
> >
{t("leave")} {t("leave")}
</Button> </Button>
@ -286,6 +287,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => {
className="kc-join-group-button" className="kc-join-group-button"
onClick={toggleModal} onClick={toggleModal}
data-testid="add-group-button" data-testid="add-group-button"
isDisabled={!user.access?.manageGroupMembership}
> >
{t("joinGroup")} {t("joinGroup")}
</Button> </Button>

View file

@ -23,6 +23,7 @@ import {
SearchIcon, SearchIcon,
WarningTriangleIcon, WarningTriangleIcon,
} from "@patternfly/react-icons"; } from "@patternfly/react-icons";
import type { IRowData } from "@patternfly/react-table";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
@ -113,7 +114,10 @@ export default function UsersSection() {
} }
try { try {
const users = await adminClient.users.find({ ...params }); const users = await adminClient.users.find({
briefRepresentation: true,
...params,
});
if (realm?.bruteForceProtected) { if (realm?.bruteForceProtected) {
const brutes = await Promise.all( const brutes = await Promise.all(
users.map((user: BruteUser) => users.map((user: BruteUser) =>
@ -344,15 +348,20 @@ export default function UsersSection() {
) )
} }
toolbarItem={toolbar} toolbarItem={toolbar}
actions={[ actionResolver={(rowData: IRowData) => {
{ const user: UserRepresentation = rowData.data;
title: t("common:delete"), if (!user.access?.manage) return [];
onRowClick: (user) => {
setSelectedRows([user]); return [
toggleDeleteDialog(); {
title: t("common:delete"),
onClick: () => {
setSelectedRows([user]);
toggleDeleteDialog();
},
}, },
}, ];
]} }}
columns={[ columns={[
{ {
name: "username", name: "username",

View file

@ -157,11 +157,16 @@ const UsersTabs = () => {
dropdownItems={[ dropdownItems={[
<DropdownItem <DropdownItem
key="impersonate" key="impersonate"
isDisabled={!user?.access?.impersonate}
onClick={() => toggleImpersonateDialog()} onClick={() => toggleImpersonateDialog()}
> >
{t("impersonate")} {t("impersonate")}
</DropdownItem>, </DropdownItem>,
<DropdownItem key="delete" onClick={() => toggleDeleteDialog()}> <DropdownItem
key="delete"
isDisabled={!user?.access?.manage}
onClick={() => toggleDeleteDialog()}
>
{t("common:delete")} {t("common:delete")}
</DropdownItem>, </DropdownItem>,
]} ]}
@ -196,6 +201,7 @@ const UsersTabs = () => {
<Tab <Tab
eventKey="credentials" eventKey="credentials"
data-testid="credentials" data-testid="credentials"
isHidden={!user.access?.manage}
title={<TabTitleText>{t("common:credentials")}</TabTitleText>} title={<TabTitleText>{t("common:credentials")}</TabTitleText>}
> >
<UserCredentials user={user} /> <UserCredentials user={user} />
@ -203,6 +209,7 @@ const UsersTabs = () => {
<Tab <Tab
eventKey="role-mapping" eventKey="role-mapping"
data-testid="role-mapping-tab" data-testid="role-mapping-tab"
isHidden={!user.access?.mapRoles}
title={<TabTitleText>{t("roleMapping")}</TabTitleText>} title={<TabTitleText>{t("roleMapping")}</TabTitleText>}
> >
<UserRoleMapping id={id} name={user.username!} /> <UserRoleMapping id={id} name={user.username!} />

View file

@ -15,7 +15,7 @@ export const UserRoute: RouteDef = {
path: "/:realm/users/:id/:tab", path: "/:realm/users/:id/:tab",
component: lazy(() => import("../UsersTabs")), component: lazy(() => import("../UsersTabs")),
breadcrumb: (t) => t("users:userDetails"), breadcrumb: (t) => t("users:userDetails"),
access: "manage-users", access: "view-users",
}; };
export const toUser = (params: UserParams): LocationDescriptorObject => ({ export const toUser = (params: UserParams): LocationDescriptorObject => ({