Merge pull request #1810 from veenone/feat/ldap-signin-by-username

Feat/ldap signin by username
This commit is contained in:
ignacionelson
2026-10-05 16:12:13 -03:00
29 changed files with 394 additions and 42 deletions
+12 -5
View File
@@ -26,9 +26,11 @@ interface LoginProps {
status?: string;
canResetPassword: boolean;
canRegister: boolean;
/** A directory username attribute is configured, so the field also takes a username. */
usernameSignIn: boolean;
}
export default function Login({ status, canResetPassword, canRegister }: LoginProps) {
export default function Login({ status, canResetPassword, canRegister, usernameSignIn }: LoginProps) {
const { t } = useTranslation();
const { flash } = usePage<SharedData>().props;
const captcha = useRef<CaptchaHandle>(null);
@@ -59,7 +61,12 @@ export default function Login({ status, canResetPassword, canRegister }: LoginPr
};
return (
<AuthLayout title={t('Log in to your account')} description={t('Enter your email and password below to log in')}>
<AuthLayout
title={t('Log in to your account')}
description={
usernameSignIn ? t('Enter your email or username and password below to log in') : t('Enter your email and password below to log in')
}
>
<Head title={t('Log in')} />
{/* The app-wide Toaster lives in the authenticated layout, so a
@@ -75,14 +82,14 @@ export default function Login({ status, canResetPassword, canRegister }: LoginPr
<form className="flex flex-col gap-6" onSubmit={submit}>
<div className="grid gap-6">
<div className="grid gap-2">
<Label htmlFor="email">{t('Email address')}</Label>
<Label htmlFor="email">{usernameSignIn ? t('Email or username') : t('Email address')}</Label>
<Input
id="email"
type="email"
type={usernameSignIn ? 'text' : 'email'}
required
autoFocus
tabIndex={1}
autoComplete="email"
autoComplete={usernameSignIn ? 'username' : 'email'}
value={data.email}
onChange={(e) => setData('email', e.target.value)}
placeholder="email@example.com"
@@ -33,6 +33,7 @@ interface LdapSettings {
user_filter: string | null;
email_attribute: string;
name_attribute: string;
username_attribute: string | null;
auto_provision: boolean;
auto_approve: boolean;
}
@@ -68,6 +69,7 @@ export default function LdapSettingsPage({ ldap, encryptions, extension_availabl
user_filter: ldap.user_filter ?? '',
email_attribute: ldap.email_attribute,
name_attribute: ldap.name_attribute,
username_attribute: ldap.username_attribute ?? '',
auto_provision: ldap.auto_provision,
auto_approve: ldap.auto_approve,
});
@@ -268,6 +270,23 @@ export default function LdapSettingsPage({ ldap, encryptions, extension_availabl
<InputError message={form.errors.name_attribute} />
</div>
<div className="grid gap-2">
<Label htmlFor="username_attribute">{t('Username attribute (optional)')}</Label>
<Input
id="username_attribute"
className="max-w-64"
value={form.data.username_attribute}
placeholder="uid"
onChange={(e) => form.setData('username_attribute', e.target.value)}
/>
<p className="text-muted-foreground text-sm">
{t(
'Lets people sign in with their directory username as well as their address: uid, cn or sAMAccountName, for example. Leave it empty to sign in by address only.',
)}
</p>
<InputError message={form.errors.username_attribute} />
</div>
<div className="grid gap-2">
<Label htmlFor="user_filter">{t('Additional filter (optional)')}</Label>
<Input