Allow adding permitted groups to new users with fine-grained (#3179)

permissions.
This commit is contained in:
Stan Silvert 2022-08-25 16:25:34 -04:00 committed by GitHub
parent 7e7c23abe7
commit 298411261a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 15 additions and 10 deletions

View file

@ -27,6 +27,7 @@ export type GroupPickerDialogProps = {
type: "selectOne" | "selectMany"; type: "selectOne" | "selectMany";
filterGroups?: string[]; filterGroups?: string[];
text: { title: string; ok: string }; text: { title: string; ok: string };
canBrowse?: boolean;
onConfirm: (groups: GroupRepresentation[] | undefined) => void; onConfirm: (groups: GroupRepresentation[] | undefined) => void;
onClose: () => void; onClose: () => void;
}; };
@ -40,6 +41,7 @@ export const GroupPickerDialog = ({
type, type,
filterGroups, filterGroups,
text, text,
canBrowse = true,
onClose, onClose,
onConfirm, onConfirm,
}: GroupPickerDialogProps) => { }: GroupPickerDialogProps) => {
@ -282,7 +284,7 @@ export const GroupPickerDialog = ({
aria-label={t("groupName")} aria-label={t("groupName")}
isPlainButtonAction isPlainButtonAction
> >
{((hasSubgroups(group) && filter === "") || {((hasSubgroups(group) && filter === "" && canBrowse) ||
type === "selectOne") && ( type === "selectOne") && (
<Button <Button
isDisabled isDisabled

View file

@ -28,6 +28,7 @@ import useFormatDate from "../utils/useFormatDate";
import { GroupPickerDialog } from "../components/group/GroupPickerDialog"; import { GroupPickerDialog } from "../components/group/GroupPickerDialog";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import type RequiredActionProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/requiredActionProviderRepresentation"; import type RequiredActionProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/requiredActionProviderRepresentation";
import { useAccess } from "../context/access/Access";
export type BruteForced = { export type BruteForced = {
isBruteForceProtected?: boolean; isBruteForceProtected?: boolean;
@ -61,6 +62,8 @@ export const UserForm = ({
const navigate = useNavigate(); const navigate = useNavigate();
const { adminClient } = useAdminClient(); const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts(); const { addAlert, addError } = useAlerts();
const { hasAccess } = useAccess();
const isManager = hasAccess("manage-users");
const { const {
handleSubmit, handleSubmit,
@ -144,7 +147,7 @@ export const UserForm = ({
<FormAccess <FormAccess
isHorizontal isHorizontal
onSubmit={handleSubmit(save)} onSubmit={handleSubmit(save)}
role="manage-users" role="query-users"
fineGrainedAccess={user?.access?.manage} fineGrainedAccess={user?.access?.manage}
className="pf-u-mt-lg" className="pf-u-mt-lg"
> >
@ -155,6 +158,7 @@ export const UserForm = ({
title: "users:selectGroups", title: "users:selectGroups",
ok: "users:join", ok: "users:join",
}} }}
canBrowse={isManager}
onConfirm={(groups) => { onConfirm={(groups) => {
user?.id ? addGroups(groups || []) : addChips(groups || []); user?.id ? addGroups(groups || []) : addChips(groups || []);
setOpen(false); setOpen(false);

View file

@ -21,6 +21,7 @@ import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { useAdminClient } from "../context/auth/AdminClient"; import { useAdminClient } from "../context/auth/AdminClient";
import { emptyFormatter } from "../util"; import { emptyFormatter } from "../util";
import { useAccess } from "../context/access/Access";
type UserGroupsProps = { type UserGroupsProps = {
user: UserRepresentation; user: UserRepresentation;
@ -45,6 +46,9 @@ export const UserGroups = ({ user }: UserGroupsProps) => {
const { enabled } = useHelp(); const { enabled } = useHelp();
const { hasAccess } = useAccess();
const isManager = hasAccess("manage-users");
const { adminClient } = useAdminClient(); const { adminClient } = useAdminClient();
const alphabetize = (groupsList: GroupRepresentation[]) => { const alphabetize = (groupsList: GroupRepresentation[]) => {
return sortBy(groupsList, (group) => group.path?.toUpperCase()); return sortBy(groupsList, (group) => group.path?.toUpperCase());
@ -254,6 +258,7 @@ export const UserGroups = ({ user }: UserGroupsProps) => {
title: t("joinGroupsFor", { username: user.username }), title: t("joinGroupsFor", { username: user.username }),
ok: "users:join", ok: "users:join",
}} }}
canBrowse={isManager}
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
onConfirm={(groups) => { onConfirm={(groups) => {
addGroups(groups || []); addGroups(groups || []);

View file

@ -94,15 +94,9 @@ const UsersTabs = () => {
addAlert(t("userSaved"), AlertVariant.success); addAlert(t("userSaved"), AlertVariant.success);
refresh(); refresh();
} else { } else {
user.groups = addedGroups.map((group) => group.path!);
const createdUser = await adminClient.users.create(user); const createdUser = await adminClient.users.create(user);
addedGroups.forEach(async (group) => {
await adminClient.users.addToGroup({
id: createdUser.id!,
groupId: group.id!,
});
});
addAlert(t("userCreated"), AlertVariant.success); addAlert(t("userCreated"), AlertVariant.success);
navigate(toUser({ id: createdUser.id, realm, tab: "settings" })); navigate(toUser({ id: createdUser.id, realm, tab: "settings" }));
} }

View file

@ -10,7 +10,7 @@ export const AddUserRoute: RouteDef = {
path: "/:realm/users/add-user", path: "/:realm/users/add-user",
component: lazy(() => import("../UsersTabs")), component: lazy(() => import("../UsersTabs")),
breadcrumb: (t) => t("users:createUser"), breadcrumb: (t) => t("users:createUser"),
access: "manage-users", access: ["query-users", "query-groups"],
}; };
export const toAddUser = (params: AddUserParams): Partial<Path> => ({ export const toAddUser = (params: AddUserParams): Partial<Path> => ({