Username field (#2135)

This commit is contained in:
Jenny 2022-02-23 09:46:56 -05:00 committed by GitHub
parent 2d55809caf
commit ecd65b3ce7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 18 deletions

View file

@ -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", () => {

View file

@ -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>
); );
}; };

View file

@ -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.",
}, },
}; };

View file

@ -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!",

View file

@ -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;
} }

View file

@ -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"
> >

View file

@ -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