2021-09-02 13:44:31 +00:00
|
|
|
import React, { useState } from "react";
|
2021-03-11 20:23:08 +00:00
|
|
|
import {
|
|
|
|
AlertVariant,
|
2021-09-03 13:54:23 +00:00
|
|
|
ButtonVariant,
|
|
|
|
DropdownItem,
|
2021-03-11 20:23:08 +00:00
|
|
|
PageSection,
|
|
|
|
Tab,
|
|
|
|
TabTitleText,
|
|
|
|
} from "@patternfly/react-core";
|
2021-03-03 13:53:42 +00:00
|
|
|
import { useTranslation } from "react-i18next";
|
2021-09-02 13:44:31 +00:00
|
|
|
import { FormProvider, useForm } from "react-hook-form";
|
2021-03-03 13:53:42 +00:00
|
|
|
|
2021-08-26 08:39:35 +00:00
|
|
|
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
2021-09-02 13:44:31 +00:00
|
|
|
import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation";
|
|
|
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
|
|
|
import { BruteForced, UserForm } from "./UserForm";
|
2021-03-03 21:17:01 +00:00
|
|
|
import { useAlerts } from "../components/alert/Alerts";
|
2021-09-02 13:44:31 +00:00
|
|
|
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
2021-06-07 12:58:47 +00:00
|
|
|
import { useHistory, useParams } from "react-router-dom";
|
2021-03-11 20:23:08 +00:00
|
|
|
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
2021-03-23 19:02:27 +00:00
|
|
|
import { UserGroups } from "./UserGroups";
|
2021-04-14 18:39:21 +00:00
|
|
|
import { UserConsents } from "./UserConsents";
|
2021-06-07 12:58:47 +00:00
|
|
|
import { useRealm } from "../context/realm-context/RealmContext";
|
2021-08-31 08:13:25 +00:00
|
|
|
import { UserIdentityProviderLinks } from "./UserIdentityProviderLinks";
|
2021-09-03 13:54:23 +00:00
|
|
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
2021-09-02 13:44:31 +00:00
|
|
|
import { toUser } from "./routes/User";
|
2021-09-03 13:54:23 +00:00
|
|
|
import { toUsers } from "./routes/Users";
|
2021-09-03 14:40:59 +00:00
|
|
|
import { UserRoleMapping } from "./UserRoleMapping";
|
2021-10-25 14:38:54 +00:00
|
|
|
import { UserAttributes } from "./UserAttributes";
|
2021-11-24 15:37:30 +00:00
|
|
|
import { UserCredentials } from "./UserCredentials";
|
2022-05-18 09:16:20 +00:00
|
|
|
import { UserSessions } from "./UserSessions";
|
2021-11-18 16:45:56 +00:00
|
|
|
import { useAccess } from "../context/access/Access";
|
2022-02-02 11:44:52 +00:00
|
|
|
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
2021-03-03 13:53:42 +00:00
|
|
|
|
2021-10-27 14:15:30 +00:00
|
|
|
const UsersTabs = () => {
|
2021-09-03 13:54:23 +00:00
|
|
|
const { t } = useTranslation("users");
|
2021-07-28 12:01:42 +00:00
|
|
|
const { addAlert, addError } = useAlerts();
|
2021-03-04 18:49:05 +00:00
|
|
|
const history = useHistory();
|
2021-06-07 12:58:47 +00:00
|
|
|
const { realm } = useRealm();
|
2021-11-18 16:45:56 +00:00
|
|
|
const { hasAccess } = useAccess();
|
2021-03-04 18:49:05 +00:00
|
|
|
|
2021-03-03 21:17:01 +00:00
|
|
|
const adminClient = useAdminClient();
|
2021-04-14 18:39:21 +00:00
|
|
|
const userForm = useForm<UserRepresentation>({ mode: "onChange" });
|
2021-03-11 20:23:08 +00:00
|
|
|
const { id } = useParams<{ id: string }>();
|
2021-09-02 13:44:31 +00:00
|
|
|
const [user, setUser] = useState<UserRepresentation>();
|
|
|
|
const [bruteForced, setBruteForced] = useState<BruteForced>();
|
2021-04-20 12:22:36 +00:00
|
|
|
const [addedGroups, setAddedGroups] = useState<GroupRepresentation[]>([]);
|
2021-03-23 19:02:27 +00:00
|
|
|
|
2021-09-02 13:44:31 +00:00
|
|
|
useFetch(
|
|
|
|
async () => {
|
2021-03-23 19:02:27 +00:00
|
|
|
if (id) {
|
2021-09-02 13:44:31 +00:00
|
|
|
const user = await adminClient.users.findOne({ id });
|
2021-09-30 08:58:48 +00:00
|
|
|
if (!user) {
|
|
|
|
throw new Error(t("common:notFound"));
|
|
|
|
}
|
|
|
|
|
|
|
|
const isBruteForceProtected = (await adminClient.realms.findOne({
|
|
|
|
realm,
|
|
|
|
}))!.bruteForceProtected;
|
|
|
|
const bruteForce = await adminClient.attackDetection.findOne({
|
|
|
|
id: user.id!,
|
|
|
|
});
|
2021-09-02 13:44:31 +00:00
|
|
|
const isLocked: boolean =
|
2021-09-30 08:58:48 +00:00
|
|
|
isBruteForceProtected && bruteForce && bruteForce.disabled;
|
2021-09-02 13:44:31 +00:00
|
|
|
return { user, bruteForced: { isBruteForceProtected, isLocked } };
|
2021-03-23 19:02:27 +00:00
|
|
|
}
|
2021-09-02 13:44:31 +00:00
|
|
|
return { user: undefined };
|
|
|
|
},
|
|
|
|
({ user, bruteForced }) => {
|
|
|
|
setUser(user);
|
|
|
|
setBruteForced(bruteForced);
|
|
|
|
user && setupForm(user);
|
|
|
|
},
|
2022-02-23 14:46:56 +00:00
|
|
|
[user?.username]
|
2021-09-02 13:44:31 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const setupForm = (user: UserRepresentation) => {
|
|
|
|
userForm.reset(user);
|
|
|
|
};
|
2021-03-03 21:17:01 +00:00
|
|
|
|
2021-04-20 12:22:36 +00:00
|
|
|
const updateGroups = (groups: GroupRepresentation[]) => {
|
|
|
|
setAddedGroups(groups);
|
|
|
|
};
|
|
|
|
|
2021-03-03 21:17:01 +00:00
|
|
|
const save = async (user: UserRepresentation) => {
|
2021-12-06 16:32:52 +00:00
|
|
|
user.username = user.username?.trim();
|
|
|
|
|
2021-03-03 21:17:01 +00:00
|
|
|
try {
|
2021-03-11 20:23:08 +00:00
|
|
|
if (id) {
|
2021-06-16 11:35:03 +00:00
|
|
|
await adminClient.users.update({ id }, user);
|
2021-09-03 13:54:23 +00:00
|
|
|
addAlert(t("userSaved"), AlertVariant.success);
|
2021-03-11 20:23:08 +00:00
|
|
|
} else {
|
2021-06-07 12:58:47 +00:00
|
|
|
const createdUser = await adminClient.users.create(user);
|
2021-04-20 12:22:36 +00:00
|
|
|
|
|
|
|
addedGroups.forEach(async (group) => {
|
|
|
|
await adminClient.users.addToGroup({
|
2021-06-07 12:58:47 +00:00
|
|
|
id: createdUser.id!,
|
2021-04-20 12:22:36 +00:00
|
|
|
groupId: group.id!,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-09-03 13:54:23 +00:00
|
|
|
addAlert(t("userCreated"), AlertVariant.success);
|
2021-09-02 13:44:31 +00:00
|
|
|
history.push(toUser({ id: createdUser.id, realm, tab: "settings" }));
|
2021-03-11 20:23:08 +00:00
|
|
|
}
|
2021-03-03 21:17:01 +00:00
|
|
|
} catch (error) {
|
2021-12-06 16:32:52 +00:00
|
|
|
addError("users:userCreateError", error);
|
2021-03-03 21:17:01 +00:00
|
|
|
}
|
|
|
|
};
|
2021-03-03 13:53:42 +00:00
|
|
|
|
2021-09-03 13:54:23 +00:00
|
|
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
|
|
|
titleKey: "users:deleteConfirm",
|
|
|
|
messageKey: "users:deleteConfirmCurrentUser",
|
|
|
|
continueButtonLabel: "common:delete",
|
|
|
|
continueButtonVariant: ButtonVariant.danger,
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
await adminClient.users.del({ id });
|
|
|
|
addAlert(t("userDeletedSuccess"), AlertVariant.success);
|
|
|
|
history.push(toUsers({ realm }));
|
|
|
|
} catch (error) {
|
|
|
|
addError("users:userDeletedError", error);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const [toggleImpersonateDialog, ImpersonateConfirm] = useConfirmDialog({
|
|
|
|
titleKey: "users:impersonateConfirm",
|
|
|
|
messageKey: "users:impersonateConfirmDialog",
|
|
|
|
continueButtonLabel: "users:impersonate",
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
const data = await adminClient.users.impersonation(
|
|
|
|
{ id },
|
|
|
|
{ user: id, realm }
|
|
|
|
);
|
|
|
|
if (data.sameRealm) {
|
|
|
|
window.location = data.redirect;
|
|
|
|
} else {
|
|
|
|
window.open(data.redirect, "_blank");
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
addError("users:impersonateError", error);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-02-02 11:44:52 +00:00
|
|
|
if (id && !user) {
|
|
|
|
return <KeycloakSpinner />;
|
|
|
|
}
|
|
|
|
|
2021-03-03 13:53:42 +00:00
|
|
|
return (
|
|
|
|
<>
|
2021-09-03 13:54:23 +00:00
|
|
|
<ImpersonateConfirm />
|
|
|
|
<DeleteConfirm />
|
2021-09-02 13:44:31 +00:00
|
|
|
<ViewHeader
|
2022-03-28 19:05:33 +00:00
|
|
|
titleKey={user?.id ? user.username! : t("createUser")}
|
2021-09-02 13:44:31 +00:00
|
|
|
divider={!id}
|
2021-09-03 13:54:23 +00:00
|
|
|
dropdownItems={[
|
|
|
|
<DropdownItem
|
|
|
|
key="impersonate"
|
2022-04-21 15:03:48 +00:00
|
|
|
isDisabled={!user?.access?.impersonate}
|
2021-09-03 13:54:23 +00:00
|
|
|
onClick={() => toggleImpersonateDialog()}
|
|
|
|
>
|
|
|
|
{t("impersonate")}
|
|
|
|
</DropdownItem>,
|
2022-04-21 15:03:48 +00:00
|
|
|
<DropdownItem
|
|
|
|
key="delete"
|
|
|
|
isDisabled={!user?.access?.manage}
|
|
|
|
onClick={() => toggleDeleteDialog()}
|
|
|
|
>
|
2021-09-03 13:54:23 +00:00
|
|
|
{t("common:delete")}
|
|
|
|
</DropdownItem>,
|
|
|
|
]}
|
2021-09-02 13:44:31 +00:00
|
|
|
/>
|
2021-06-07 12:58:47 +00:00
|
|
|
<PageSection variant="light" className="pf-u-p-0">
|
2021-09-02 13:44:31 +00:00
|
|
|
<FormProvider {...userForm}>
|
|
|
|
{id && user && (
|
2021-11-18 16:45:56 +00:00
|
|
|
<KeycloakTabs isBox mountOnEnter>
|
2021-09-02 13:44:31 +00:00
|
|
|
<Tab
|
|
|
|
eventKey="settings"
|
|
|
|
data-testid="user-details-tab"
|
2021-09-03 13:54:23 +00:00
|
|
|
title={<TabTitleText>{t("common:details")}</TabTitleText>}
|
2021-09-02 13:44:31 +00:00
|
|
|
>
|
|
|
|
<PageSection variant="light">
|
|
|
|
{bruteForced && (
|
|
|
|
<UserForm
|
|
|
|
onGroupsUpdate={updateGroups}
|
|
|
|
save={save}
|
|
|
|
user={user}
|
|
|
|
bruteForce={bruteForced}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</PageSection>
|
|
|
|
</Tab>
|
2021-10-25 14:38:54 +00:00
|
|
|
<Tab
|
|
|
|
eventKey="attributes"
|
|
|
|
data-testid="attributes"
|
|
|
|
title={<TabTitleText>{t("common:attributes")}</TabTitleText>}
|
|
|
|
>
|
|
|
|
<UserAttributes user={user} />
|
|
|
|
</Tab>
|
2021-11-24 15:37:30 +00:00
|
|
|
<Tab
|
|
|
|
eventKey="credentials"
|
|
|
|
data-testid="credentials"
|
2022-04-21 15:03:48 +00:00
|
|
|
isHidden={!user.access?.manage}
|
2021-11-24 15:37:30 +00:00
|
|
|
title={<TabTitleText>{t("common:credentials")}</TabTitleText>}
|
|
|
|
>
|
|
|
|
<UserCredentials user={user} />
|
|
|
|
</Tab>
|
2022-03-14 10:15:28 +00:00
|
|
|
<Tab
|
|
|
|
eventKey="role-mapping"
|
|
|
|
data-testid="role-mapping-tab"
|
2022-04-21 15:03:48 +00:00
|
|
|
isHidden={!user.access?.mapRoles}
|
2022-03-14 10:15:28 +00:00
|
|
|
title={<TabTitleText>{t("roleMapping")}</TabTitleText>}
|
|
|
|
>
|
|
|
|
<UserRoleMapping id={id} name={user.username!} />
|
|
|
|
</Tab>
|
2021-09-02 13:44:31 +00:00
|
|
|
<Tab
|
|
|
|
eventKey="groups"
|
|
|
|
data-testid="user-groups-tab"
|
2021-09-03 13:54:23 +00:00
|
|
|
title={<TabTitleText>{t("common:groups")}</TabTitleText>}
|
2021-09-02 13:44:31 +00:00
|
|
|
>
|
|
|
|
<UserGroups user={user} />
|
|
|
|
</Tab>
|
|
|
|
<Tab
|
|
|
|
eventKey="consents"
|
|
|
|
data-testid="user-consents-tab"
|
2021-09-03 13:54:23 +00:00
|
|
|
title={<TabTitleText>{t("consents")}</TabTitleText>}
|
2021-09-02 13:44:31 +00:00
|
|
|
>
|
|
|
|
<UserConsents />
|
|
|
|
</Tab>
|
2021-11-18 16:45:56 +00:00
|
|
|
{hasAccess("view-identity-providers") && (
|
|
|
|
<Tab
|
|
|
|
eventKey="identity-provider-links"
|
|
|
|
data-testid="identity-provider-links-tab"
|
|
|
|
title={
|
|
|
|
<TabTitleText>{t("identityProviderLinks")}</TabTitleText>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<UserIdentityProviderLinks />
|
|
|
|
</Tab>
|
|
|
|
)}
|
2022-05-18 09:16:20 +00:00
|
|
|
<Tab
|
|
|
|
eventKey="sessions"
|
|
|
|
data-testid="user-sessions-tab"
|
|
|
|
title={<TabTitleText>{t("sessions")}</TabTitleText>}
|
|
|
|
>
|
|
|
|
<UserSessions />
|
|
|
|
</Tab>
|
2021-09-02 13:44:31 +00:00
|
|
|
</KeycloakTabs>
|
|
|
|
)}
|
|
|
|
{!id && (
|
|
|
|
<PageSection variant="light">
|
|
|
|
<UserForm onGroupsUpdate={updateGroups} save={save} />
|
|
|
|
</PageSection>
|
|
|
|
)}
|
|
|
|
</FormProvider>
|
2021-03-03 13:53:42 +00:00
|
|
|
</PageSection>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|
2021-10-27 14:15:30 +00:00
|
|
|
|
|
|
|
export default UsersTabs;
|