fix: add utf8 email validation support (#6239)
Co-authored-by: Thomas <thomas@ajnart.fr>
This commit is contained in:
1 parent
7c152cf4fe
commit
7d93df04dd
6 files changed
+43
-13
No files matched your search
+2
-1
@@ -17,6 +17,7 @@ import { showErrorNotification } from "@homarr/notifications";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { UserAvatar } from "@homarr/ui";
|
||||
import { createCustomErrorParams } from "@homarr/validation/form/i18n";
|
||||
import { optionalEmailSchema } from "@homarr/validation/email";
|
||||
import { userPasswordSchema } from "@homarr/validation/user";
|
||||
|
||||
import { GroupSelectModal } from "~/components/access/group-select-modal";
|
||||
@@ -60,7 +61,7 @@ export const UserCreateStepperComponent = ({ initialGroups }: UserCreateStepperC
|
||||
const generalForm = useZodForm(
|
||||
z.object({
|
||||
username: z.string().min(1),
|
||||
email: z.string().email().or(z.string().length(0).optional()),
|
||||
email: optionalEmailSchema,
|
||||
}),
|
||||
{
|
||||
initialValues: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CredentialsSignin } from "@auth/core/errors";
|
||||
import { z } from "zod/v4";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import { createId } from "@homarr/common";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
@@ -7,6 +7,7 @@ import type { Database, InferInsertModel } from "@homarr/db";
|
||||
import { and, eq } from "@homarr/db";
|
||||
import { users } from "@homarr/db/schema";
|
||||
import type { ldapSignInSchema } from "@homarr/validation/user";
|
||||
import { utf8EmailSchema } from "@homarr/validation/email";
|
||||
|
||||
import { env } from "../../../env";
|
||||
import { LdapClient } from "../ldap-client";
|
||||
@@ -52,8 +53,7 @@ export const authorizeWithLdapCredentialsAsync = async (
|
||||
throw new CredentialsSignin(`User not found in LDAP username="${credentials.name}"`);
|
||||
}
|
||||
|
||||
// Validate email
|
||||
const mailResult = await z.string().email().safeParseAsync(ldapUser[env.AUTH_LDAP_USER_MAIL_ATTRIBUTE]);
|
||||
const mailResult = await utf8EmailSchema.safeParseAsync(ldapUser[env.AUTH_LDAP_USER_MAIL_ATTRIBUTE]);
|
||||
|
||||
if (!mailResult.success) {
|
||||
logger.error("User found in LDAP but with invalid or non-existing Email", {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { requiredNullableEmailSchema } from "@homarr/validation/email";
|
||||
|
||||
const regexEncryptedSchema = z.string().regex(/^[a-f0-9]+\.[a-f0-9]+$/g);
|
||||
|
||||
const encryptedSchema = z.custom<`${string}.${string}`>((value) => regexEncryptedSchema.safeParse(value).success);
|
||||
@@ -7,7 +9,7 @@ const encryptedSchema = z.custom<`${string}.${string}`>((value) => regexEncrypte
|
||||
export const oldmarrImportUserSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string().email().nullable(),
|
||||
email: requiredNullableEmailSchema,
|
||||
emailVerified: z.date().nullable(),
|
||||
image: z.string().nullable(),
|
||||
isAdmin: z.boolean(),
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const utf8EmailSchema = z.email({ pattern: z.regexes.unicodeEmail });
|
||||
|
||||
export const optionalEmailSchema = utf8EmailSchema.or(z.string().length(0)).optional();
|
||||
|
||||
export const nullableEmailSchema = utf8EmailSchema
|
||||
.or(z.literal(""))
|
||||
.transform((value) => (value === "" ? null : value))
|
||||
.optional()
|
||||
.nullable();
|
||||
|
||||
export const requiredNullableEmailSchema = utf8EmailSchema
|
||||
.or(z.literal(""))
|
||||
.transform((value) => (value === "" ? null : value))
|
||||
.nullable();
|
||||
@@ -4,6 +4,7 @@ import { z } from "zod/v4";
|
||||
import type { TranslationFunction } from "@homarr/translation";
|
||||
|
||||
import { createCustomErrorParams, zodErrorMap } from "./i18n";
|
||||
import { utf8EmailSchema } from "../email";
|
||||
|
||||
const expectError = (error: z.core.$ZodIssue, key: string) => {
|
||||
expect(error.message).toContain(key);
|
||||
@@ -111,4 +112,19 @@ describe("i18n", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
expectError(result.error.issues[0]!, "boardAlreadyExists");
|
||||
});
|
||||
|
||||
test("should validate UTF8SMTP email with umlauts", () => {
|
||||
const result = utf8EmailSchema.safeParse("jörg@example.com");
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test("should validate UTF8SMTP email with unicode domain", () => {
|
||||
const result = utf8EmailSchema.safeParse("user@münchen.de");
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test("should validate standard ASCII emails", () => {
|
||||
const result = utf8EmailSchema.safeParse("user@example.com");
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import type { TranslationObject } from "@homarr/translation";
|
||||
|
||||
import { zodEnumFromArray } from "./enums";
|
||||
import { createCustomErrorParams } from "./form/i18n";
|
||||
import { nullableEmailSchema, optionalEmailSchema } from "./email";
|
||||
|
||||
// We always want the lowercase version of the username to compare it in a case-insensitive way
|
||||
export const usernameSchema = z.string().trim().toLowerCase().min(3).max(255);
|
||||
@@ -42,7 +43,7 @@ export const userBaseCreateSchema = z.object({
|
||||
username: usernameSchema,
|
||||
password: userPasswordSchema,
|
||||
confirmPassword: z.string(),
|
||||
email: z.string().email().or(z.string().length(0)).optional(),
|
||||
email: optionalEmailSchema,
|
||||
});
|
||||
|
||||
export const userCreateSchema = addConfirmPasswordRefinement(userBaseCreateSchema).and(
|
||||
@@ -85,13 +86,7 @@ export const userRegistrationApiSchema = userRegistrationSchema.and(
|
||||
export const userEditProfileSchema = z.object({
|
||||
id: z.string(),
|
||||
name: usernameSchema,
|
||||
email: z
|
||||
.string()
|
||||
.email()
|
||||
.or(z.literal(""))
|
||||
.transform((value) => (value === "" ? null : value))
|
||||
.optional()
|
||||
.nullable(),
|
||||
email: nullableEmailSchema,
|
||||
});
|
||||
|
||||
const baseChangePasswordSchema = z.object({
|
||||
|
||||
Reference in new issue
Block a user