Username field (#2135)
This commit is contained in:
parent
2d55809caf
commit
ecd65b3ce7
7 changed files with 87 additions and 18 deletions
|
@ -178,7 +178,7 @@ describe("User creation", () => {
|
||||||
// Go to user groups
|
// Go to user groups
|
||||||
userGroupsPage.goToGroupsTab();
|
userGroupsPage.goToGroupsTab();
|
||||||
cy.findByTestId(`leave-${groupsList[0]}`).click();
|
cy.findByTestId(`leave-${groupsList[0]}`).click();
|
||||||
cy.findByTestId("confirm").click();
|
cy.findByTestId("confirm").click({ force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Go to user consents test", () => {
|
it("Go to user consents test", () => {
|
||||||
|
|
|
@ -34,7 +34,10 @@ export const RealmSettingsLoginTab = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<FormPanel className="kc-login-screen" title="Login screen customization">
|
<FormPanel
|
||||||
|
className="kc-login-screen"
|
||||||
|
title={t("loginScreenCustomization")}
|
||||||
|
>
|
||||||
<FormAccess isHorizontal role="manage-realm">
|
<FormAccess isHorizontal role="manage-realm">
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("userRegistration")}
|
label={t("userRegistration")}
|
||||||
|
@ -128,7 +131,7 @@ export const RealmSettingsLoginTab = ({
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
</FormPanel>
|
</FormPanel>
|
||||||
<FormPanel className="kc-email-settings" title="Email settings">
|
<FormPanel className="kc-email-settings" title={t("emailSettings")}>
|
||||||
<FormAccess isHorizontal role="manage-realm">
|
<FormAccess isHorizontal role="manage-realm">
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("emailAsUsername")}
|
label={t("emailAsUsername")}
|
||||||
|
@ -268,6 +271,43 @@ export const RealmSettingsLoginTab = ({
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
</FormPanel>
|
</FormPanel>
|
||||||
|
<FormPanel
|
||||||
|
className="kc-user-info-settings"
|
||||||
|
title={t("userInfoSettings")}
|
||||||
|
>
|
||||||
|
<FormAccess isHorizontal role="manage-realm">
|
||||||
|
<FormGroup
|
||||||
|
label={t("editUsername")}
|
||||||
|
fieldId="kc-edit-username"
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
helpText="realm-settings-help:editUsername"
|
||||||
|
fieldLabelId="realm-settings:editUsername"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
hasNoPaddingTop
|
||||||
|
>
|
||||||
|
<Controller
|
||||||
|
name="editUsernameAllowed"
|
||||||
|
defaultValue={realm.editUsernameAllowed}
|
||||||
|
control={form.control}
|
||||||
|
render={({ onChange, value }) => (
|
||||||
|
<Switch
|
||||||
|
id="kc-edit-username-switch"
|
||||||
|
data-testid="edit-username-switch"
|
||||||
|
name="editUsernameAllowed"
|
||||||
|
label={t("common:on")}
|
||||||
|
labelOff={t("common:off")}
|
||||||
|
isChecked={value}
|
||||||
|
onChange={(value) => {
|
||||||
|
updateSwitchValue(onChange, value, "editUsernameAllowed");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</FormAccess>
|
||||||
|
</FormPanel>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -146,5 +146,7 @@ export default {
|
||||||
"The condition is checked during client registration/update requests and it evaluates to true if the entity (usually user), who is creating/updating client is member of the specified role. For reference the realm role, you can use the realm role name like 'my_realm_role' . For reference client role, you can use the client_id.role_name for example 'my_client.my_client_role' will refer to client role 'my_client_role' of client 'my_client'. ",
|
"The condition is checked during client registration/update requests and it evaluates to true if the entity (usually user), who is creating/updating client is member of the specified role. For reference the realm role, you can use the realm role name like 'my_realm_role' . For reference client role, you can use the client_id.role_name for example 'my_client.my_client_role' will refer to client role 'my_client_role' of client 'my_client'. ",
|
||||||
defaultGroups:
|
defaultGroups:
|
||||||
"Default groups allow you to automatically assign groups membership whenever any new user is created or imported through <1>identity brokering</1>.",
|
"Default groups allow you to automatically assign groups membership whenever any new user is created or imported through <1>identity brokering</1>.",
|
||||||
|
editUsername:
|
||||||
|
"If enabled, the username field is editable, readonly otherwise.",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,6 +102,7 @@ export default {
|
||||||
noKeys: "No keys",
|
noKeys: "No keys",
|
||||||
noKeysDescription: "You haven't created any ",
|
noKeysDescription: "You haven't created any ",
|
||||||
certificate: "Certificate",
|
certificate: "Certificate",
|
||||||
|
loginScreenCustomization: "Login screen customization",
|
||||||
userRegistration: "User registration",
|
userRegistration: "User registration",
|
||||||
userRegistrationHelpText:
|
userRegistrationHelpText:
|
||||||
"Enable/disable the registration page. A link for registration will show on login page too.",
|
"Enable/disable the registration page. A link for registration will show on login page too.",
|
||||||
|
@ -111,6 +112,7 @@ export default {
|
||||||
rememberMe: "Remember me",
|
rememberMe: "Remember me",
|
||||||
rememberMeHelpText:
|
rememberMeHelpText:
|
||||||
"Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.",
|
"Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.",
|
||||||
|
emailSettings: "Email settings",
|
||||||
emailAsUsername: "Email as username",
|
emailAsUsername: "Email as username",
|
||||||
emailAsUsernameHelpText: "Allow users to set email as username.",
|
emailAsUsernameHelpText: "Allow users to set email as username.",
|
||||||
loginWithEmail: "Login with email",
|
loginWithEmail: "Login with email",
|
||||||
|
@ -124,6 +126,8 @@ export default {
|
||||||
verifyEmail: "Verify email",
|
verifyEmail: "Verify email",
|
||||||
verifyEmailHelpText:
|
verifyEmailHelpText:
|
||||||
"Require user to verify their email address after initial login or after address changes are submitted.",
|
"Require user to verify their email address after initial login or after address changes are submitted.",
|
||||||
|
userInfoSettings: "User info settings",
|
||||||
|
editUsername: "Edit username",
|
||||||
testConnection: "Test connection",
|
testConnection: "Test connection",
|
||||||
testConnectionSuccess:
|
testConnectionSuccess:
|
||||||
"Success! SMTP connection successful. E-mail was sent!",
|
"Success! SMTP connection successful. E-mail was sent!",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
.pf-c-card.pf-m-flat.kc-login-screen,
|
.pf-c-card.pf-m-flat.kc-login-screen,
|
||||||
.pf-c-card.pf-m-flat.kc-email-settings,
|
.pf-c-card.pf-m-flat.kc-email-settings,
|
||||||
|
.pf-c-card.pf-m-flat.kc-user-info-settings,
|
||||||
.pf-c-card.pf-m-flat.kc-email-template,
|
.pf-c-card.pf-m-flat.kc-email-template,
|
||||||
.pf-c-card.pf-m-flat.kc-email-connection,
|
.pf-c-card.pf-m-flat.kc-email-connection,
|
||||||
.pf-c-card.pf-m-flat.kc-message-bundles,
|
.pf-c-card.pf-m-flat.kc-message-bundles,
|
||||||
|
@ -24,14 +25,16 @@ article.pf-c-card.pf-m-flat.kc-email-connection
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-login-screen > div.pf-c-card__header.kc-form-panel__header,
|
.kc-login-screen > div.pf-c-card__header.kc-form-panel__header,
|
||||||
.kc-email-settings > div.pf-c-card__header.kc-form-panel__header {
|
.kc-email-settings > div.pf-c-card__header.kc-form-panel__header,
|
||||||
|
.kc-user-info-settings > div.pf-c-card__header.kc-form-panel__header {
|
||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-email-settings > div.pf-c-card__body.kc-form-panel__body,
|
.kc-email-settings > div.pf-c-card__body.kc-form-panel__body,
|
||||||
.kc-login-screen > div.pf-c-card__body.kc-form-panel__body {
|
.kc-login-screen > div.pf-c-card__body.kc-form-panel__body,
|
||||||
|
.kc-user-info-settings > div.pf-c-card__body.kc-form-panel__body {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-bottom: var(--pf-global--spacer--2xl);
|
padding-bottom: var(--pf-global--spacer--2xl);
|
||||||
}
|
}
|
||||||
|
@ -219,7 +222,8 @@ article.pf-c-card.pf-m-flat.kc-login-settings-template
|
||||||
padding: 0 var(--pf-global--spacer--md) 0 var(--pf-global--spacer--md);
|
padding: 0 var(--pf-global--spacer--md) 0 var(--pf-global--spacer--md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-backToPolicies, .kc-backToPolicies {
|
.kc-backToPolicies,
|
||||||
|
.kc-backToPolicies {
|
||||||
width: 5rem;
|
width: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,20 +232,20 @@ article.pf-c-card.pf-m-flat.kc-login-settings-template
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-executor-trash-icon {
|
.kc-executor-trash-icon {
|
||||||
margin-left: .5rem;
|
margin-left: 0.5rem;
|
||||||
color: var(--pf-global--Color--200);
|
color: var(--pf-global--Color--200);
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-executor-trash-icon:hover {
|
.kc-executor-trash-icon:hover {
|
||||||
filter: brightness(55%);
|
filter: brightness(55%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-condition-link {
|
.kc-condition-link {
|
||||||
margin-right: 0.625rem;
|
margin-right: 0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kc-conditionType-trash-icon {
|
.kc-conditionType-trash-icon {
|
||||||
margin-left: .5rem;
|
margin-left: 0.5rem;
|
||||||
color: var(--pf-global--Color--400);
|
color: var(--pf-global--Color--400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +259,7 @@ article.pf-c-card.pf-m-flat.kc-login-settings-template
|
||||||
|
|
||||||
input#kc-scopes {
|
input#kc-scopes {
|
||||||
width: 630px;
|
width: 630px;
|
||||||
|
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,13 @@ import { FormAccess } from "../components/form-access/FormAccess";
|
||||||
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
||||||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||||
import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation";
|
import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { emailRegexPattern } from "../util";
|
import { emailRegexPattern } from "../util";
|
||||||
import { GroupPickerDialog } from "../components/group/GroupPickerDialog";
|
import { GroupPickerDialog } from "../components/group/GroupPickerDialog";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||||
|
|
||||||
export type BruteForced = {
|
export type BruteForced = {
|
||||||
isBruteForceProtected?: boolean;
|
isBruteForceProtected?: boolean;
|
||||||
|
@ -49,7 +50,7 @@ export const UserForm = ({
|
||||||
onGroupsUpdate,
|
onGroupsUpdate,
|
||||||
}: UserFormProps) => {
|
}: UserFormProps) => {
|
||||||
const { t } = useTranslation("users");
|
const { t } = useTranslation("users");
|
||||||
const { realm } = useRealm();
|
const { realm: realmName } = useRealm();
|
||||||
|
|
||||||
const [
|
const [
|
||||||
isRequiredUserActionsDropdownOpen,
|
isRequiredUserActionsDropdownOpen,
|
||||||
|
@ -67,6 +68,21 @@ export const UserForm = ({
|
||||||
);
|
);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [locked, setLocked] = useState(isLocked);
|
const [locked, setLocked] = useState(isLocked);
|
||||||
|
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||||
|
|
||||||
|
useFetch(
|
||||||
|
async () => {
|
||||||
|
const realm = await adminClient.realms.findOne({ realm: realmName });
|
||||||
|
if (!realm) {
|
||||||
|
throw new Error(t("common:notFound"));
|
||||||
|
}
|
||||||
|
return realm;
|
||||||
|
},
|
||||||
|
(realm) => {
|
||||||
|
setRealm(realm);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const unLockUser = async () => {
|
const unLockUser = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -148,7 +164,7 @@ export const UserForm = ({
|
||||||
filterGroups={selectedGroups.map((group) => group.name!)}
|
filterGroups={selectedGroups.map((group) => group.name!)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{user?.id ? (
|
{user?.id && (
|
||||||
<>
|
<>
|
||||||
<FormGroup label={t("common:id")} fieldId="kc-id" isRequired>
|
<FormGroup label={t("common:id")} fieldId="kc-id" isRequired>
|
||||||
<TextInput id={user.id} value={user.id} type="text" isReadOnly />
|
<TextInput id={user.id} value={user.id} type="text" isReadOnly />
|
||||||
|
@ -165,7 +181,8 @@ export const UserForm = ({
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</>
|
</>
|
||||||
) : (
|
)}
|
||||||
|
{!realm?.registrationEmailAsUsername && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("username")}
|
label={t("username")}
|
||||||
fieldId="kc-username"
|
fieldId="kc-username"
|
||||||
|
@ -178,7 +195,7 @@ export const UserForm = ({
|
||||||
type="text"
|
type="text"
|
||||||
id="kc-username"
|
id="kc-username"
|
||||||
name="username"
|
name="username"
|
||||||
isReadOnly={!!user?.id}
|
isReadOnly={!!user?.id && !realm?.editUsernameAllowed}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
|
@ -401,7 +418,7 @@ export const UserForm = ({
|
||||||
<Button
|
<Button
|
||||||
data-testid="cancel-create-user"
|
data-testid="cancel-create-user"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
user?.id ? reset(user) : history.push(`/${realm}/users`)
|
user?.id ? reset(user) : history.push(`/${realmName}/users`)
|
||||||
}
|
}
|
||||||
variant="link"
|
variant="link"
|
||||||
>
|
>
|
||||||
|
|
|
@ -70,7 +70,7 @@ const UsersTabs = () => {
|
||||||
setBruteForced(bruteForced);
|
setBruteForced(bruteForced);
|
||||||
user && setupForm(user);
|
user && setupForm(user);
|
||||||
},
|
},
|
||||||
[]
|
[user?.username]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setupForm = (user: UserRepresentation) => {
|
const setupForm = (user: UserRepresentation) => {
|
||||||
|
@ -152,7 +152,9 @@ const UsersTabs = () => {
|
||||||
<ImpersonateConfirm />
|
<ImpersonateConfirm />
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
<ViewHeader
|
<ViewHeader
|
||||||
titleKey={user?.username || t("createUser")}
|
titleKey={
|
||||||
|
userForm.getValues().username || user?.username || t("createUser")
|
||||||
|
}
|
||||||
divider={!id}
|
divider={!id}
|
||||||
dropdownItems={[
|
dropdownItems={[
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
|
|
Loading…
Reference in a new issue