fix: signup password hint validation (#10454)

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
Mehul
2023-08-29 13:56:45 +02:00
committed by GitHub
co-authored by Udit Takkar
parent b285f27d00
commit 79f092673a
6 changed files with 49 additions and 58 deletions
+9 -27
View File
@@ -57,12 +57,10 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
}),
});
const formMethods = useForm<{
username: string;
email_address: string;
full_name: string;
password: string;
}>({
type formSchemaType = z.infer<typeof formSchema>;
const formMethods = useForm<formSchemaType>({
mode: "onChange",
resolver: zodResolver(formSchema),
});
@@ -70,7 +68,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
props.onError();
};
const onSubmit = formMethods.handleSubmit(async (data: z.infer<typeof formSchema>) => {
const onSubmit = formMethods.handleSubmit(async (data) => {
props.onSubmit();
const response = await fetch("/api/auth/setup", {
method: "POST",
@@ -130,11 +128,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
className={classNames("my-0", longWebsiteUrl && "rounded-t-none")}
onBlur={onBlur}
name="username"
onChange={async (e) => {
onChange(e.target.value);
formMethods.setValue("username", e.target.value);
await formMethods.trigger("username");
}}
onChange={(e) => onChange(e.target.value)}
/>
</>
)}
@@ -148,11 +142,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<TextField
value={value || ""}
onBlur={onBlur}
onChange={async (e) => {
onChange(e.target.value);
formMethods.setValue("full_name", e.target.value);
await formMethods.trigger("full_name");
}}
onChange={(e) => onChange(e.target.value)}
color={formMethods.formState.errors.full_name ? "warn" : ""}
type="text"
name="full_name"
@@ -172,11 +162,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<EmailField
value={value || ""}
onBlur={onBlur}
onChange={async (e) => {
onChange(e.target.value);
formMethods.setValue("email_address", e.target.value);
await formMethods.trigger("email_address");
}}
onChange={(e) => onChange(e.target.value)}
className="my-0"
name="email_address"
/>
@@ -191,11 +177,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<PasswordField
value={value || ""}
onBlur={onBlur}
onChange={async (e) => {
onChange(e.target.value);
formMethods.setValue("password", e.target.value);
await formMethods.trigger("password");
}}
onChange={(e) => onChange(e.target.value)}
hintErrors={["caplow", "admin_min", "num"]}
name="password"
className="my-0"
+1 -11
View File
@@ -1,5 +1,4 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { checkPremiumUsername } from "@calcom/ee/common/lib/checkPremiumUsername";
@@ -11,18 +10,9 @@ import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager";
import { validateUsernameInTeam, validateUsername } from "@calcom/lib/validateUsername";
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
import { signupSchema } from "@calcom/prisma/zod-utils";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
const signupSchema = z.object({
username: z.string().refine((value) => !value.includes("+"), {
message: "String should not contain a plus symbol (+).",
}),
email: z.string().email(),
password: z.string().min(7),
language: z.string().optional(),
token: z.string().optional(),
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
return res.status(405).end();
+3 -8
View File
@@ -17,6 +17,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import slugify from "@calcom/lib/slugify";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { signupSchema as apiSignupSchema } from "@calcom/prisma/zod-utils";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import { Alert, Button, EmailField, HeadSeo, PasswordField, TextField } from "@calcom/ui";
@@ -25,14 +26,7 @@ import PageWrapper from "@components/PageWrapper";
import { IS_GOOGLE_LOGIN_ENABLED } from "../server/lib/constants";
import { ssrInit } from "../server/lib/ssr";
const signupSchema = z.object({
username: z.string().refine((value) => !value.includes("+"), {
message: "String should not contain a plus symbol (+).",
}),
email: z.string().email(),
password: z.string().min(7),
language: z.string().optional(),
token: z.string().optional(),
const signupSchema = apiSignupSchema.extend({
apiError: z.string().optional(), // Needed to display API errors doesnt get passed to the API
});
@@ -46,6 +40,7 @@ export default function Signup({ prepopulateFormValues, token, orgSlug }: Signup
const { t, i18n } = useLocale();
const flags = useFlagMap();
const methods = useForm<FormValues>({
mode: "onChange",
resolver: zodResolver(signupSchema),
defaultValues: prepopulateFormValues,
});
@@ -10,15 +10,11 @@ export function isPasswordValid(password: string, breakdown?: boolean, strict?:
num = false, // At least one number
min = false, // Eight characters, or fifteen in strict mode.
admin_min = false;
if (password.length > 7 && (!strict || password.length > 14)) min = true;
if (password.length >= 7 && (!strict || password.length > 14)) min = true;
if (strict && password.length > 14) admin_min = true;
for (let i = 0; i < password.length; i++) {
if (!isNaN(parseInt(password[i]))) num = true;
else {
if (password[i] === password[i].toUpperCase()) cap = true;
if (password[i] === password[i].toLowerCase()) low = true;
}
}
if (password.match(/\d/)) num = true;
if (password.match(/[a-z]/)) low = true;
if (password.match(/[A-Z]/)) cap = true;
if (!breakdown) return cap && low && num && min && (strict ? admin_min : true);
+24
View File
@@ -14,6 +14,7 @@ import type {
import { appDataSchemas } from "@calcom/app-store/apps.schemas.generated";
import dayjs from "@calcom/dayjs";
import { isPasswordValid } from "@calcom/features/auth/lib/isPasswordValid";
import type { FieldType as FormBuilderFieldType } from "@calcom/features/form-builder/schema";
import { fieldsSchema as formBuilderFieldsSchema } from "@calcom/features/form-builder/schema";
import { isSupportedTimeZone } from "@calcom/lib/date-fns";
@@ -602,6 +603,28 @@ export const emailSchemaRefinement = (value: string) => {
return emailRegex.test(value);
};
export const signupSchema = z.object({
username: z.string().refine((value) => !value.includes("+"), {
message: "String should not contain a plus symbol (+).",
}),
email: z.string().email(),
password: z.string().superRefine((data, ctx) => {
const isStrict = false;
const result = isPasswordValid(data, true, isStrict);
Object.keys(result).map((key: string) => {
if (!result[key as keyof typeof result]) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [key],
message: key,
});
}
});
}),
language: z.string().optional(),
token: z.string().optional(),
});
export const ZVerifyCodeInputSchema = z.object({
email: z.string().email(),
code: z.string(),
@@ -610,3 +633,4 @@ export const ZVerifyCodeInputSchema = z.object({
export type ZVerifyCodeInputSchema = z.infer<typeof ZVerifyCodeInputSchema>;
export const coerceToDate = z.coerce.date();
@@ -3,17 +3,21 @@ import { useFormContext } from "react-hook-form";
import { Check, Circle, Info, X } from "../../icon";
export function HintsOrErrors<T extends FieldValues = FieldValues>(props: {
type hintsOrErrorsProps = {
hintErrors?: string[];
fieldName: string;
t: (key: string) => string;
}) {
};
export function HintsOrErrors<T extends FieldValues = FieldValues>({
hintErrors,
fieldName,
t,
}: hintsOrErrorsProps) {
const methods = useFormContext() as ReturnType<typeof useFormContext> | null;
/* If there's no methods it means we're using these components outside a React Hook Form context */
if (!methods) return null;
const { formState } = methods;
const { hintErrors, fieldName, t } = props;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const fieldErrors: FieldErrors<T> | undefined = formState.errors[fieldName];