fixed more error handling for password checks & translations

This commit is contained in:
Joel Biddle
2023-08-22 22:42:02 +10:00
parent 00089a6bba
commit 1f60a3d73e
12 changed files with 294 additions and 163 deletions

View File

@@ -234,9 +234,12 @@
"validate-base": "Password should contain:",
"validate-too-short": "at least 14 characters",
"validate-too-long": "at most 100 characters",
"validate-uppercase": "at least 1 uppercase character",
"validate-lowercase": "at least 1 lowercase character",
"validate-number": "at least 1 number",
"validate-case": "at least 1 lowercase character",
"validate-breached": "The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password."
"validate-special-char": "at least 1 special character",
"validate-repeated-char": "at most 2 repeated consecutive characters",
"validate-is-breached": "The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password."
},
"token": {
"service-tokens": "Service Tokens",

View File

@@ -229,11 +229,14 @@
"current-wrong": "La contraseña actual puede puede que sea incorrecta",
"new": "Nueva contraseña",
"validate-base": "La contraseña debe contener:",
"validate-too-short": "como mínimo 14 caracteres",
"validate-too-short": "al menos 14 caracteres",
"validate-too-long": "como máximo 100 caracteres",
"validate-number": "como mínimo 1 número",
"validate-case": "como mínimo 1 letra en minúsculas",
"validate-breached": "La contraseña que proporcionó se encuentra en una lista de contraseñas comúnmente utilizadas en otros sitios web. Inténtelo de nuevo con una contraseña más segura."
"validate-uppercase": "al menos 1 carácter en mayúscula",
"validate-lowercase": "al menos 1 carácter en minúsculas",
"validate-number": "al menos 1 número",
"validate-special-char": "al menos 1 carácter especial",
"validate-repeated-char": "au plus 2 caracteres consecutivos repetidos",
"validate-breached": "La contraseña que proporcionó se encuentra en una lista de contraseñas comúnmente utilizadas en otros sitios web. Vuelva a intentarlo con una contraseña más segura."
},
"token": {
"service-tokens": "Tokens de servicio",

View File

@@ -218,9 +218,12 @@
"validate-base": "Le mot de passe doit contenir:",
"validate-too-short": "au moins 14 caractères",
"validate-too-long": "au maximum 100 caractères",
"validate-uppercase": "au moins 1 caractère miniscule",
"validate-lowercase": "au moins 1 caractère majuscule",
"validate-number": "au moins 1 chiffre",
"validate-case": "au moins 1 caractère miniscule",
"validate-breached": "Le mot de passe que vous avez fourni figure dans une liste de mots de passe couramment utilisés sur d'autres sites Web. Veuillez réessayer avec un mot de passe plus fort."
"validate-special-char": "au moins 1 caractère spécial",
"validate-repeated-char": "au plus 2 caractères consécutifs répétés",
"validate-is-breached": "Le mot de passe que vous avez fourni figure dans une liste de mots de passe couramment utilisés sur d'autres sites Web. Veuillez réessayer avec un mot de passe plus fort."
},
"token": {
"service-tokens": "Jetons de service",

View File

@@ -183,10 +183,13 @@
"new": "새 비밀번호",
"current-wrong": "현재 비밀번호가 잘못되었어요",
"validate-base": "비밀번호는 다음 조건을 만족해야 합니다:",
"validate-too-short": "14 글자 이상",
"validate-too-long": "100 자 이하",
"validate-number": "1개 이상의 숫자",
"validate-case": "1개 이상의 소문자",
"validate-too-short": "최소 14자",
"validate-too-long": "최대 100자",
"validate-uppercase": "최소 1개의 대문자",
"validate-lowercase": "최소 1개의 소문자",
"validate-number": "숫자 1개 이상",
"validate-special-char": "특수 문자 1개 이상",
"validate-repeated-char": "최대 2개의 반복되는 연속 문자",
"validate-breached": "귀하가 제공한 비밀번호는 다른 웹사이트에서 일반적으로 사용되는 비밀번호 목록에 포함되어 있습니다. 더 강력한 비밀번호로 다시 시도해 주세요."
},
"token": {

View File

@@ -213,8 +213,11 @@
"validate-base": "A senha deve conter:",
"validate-too-short": "pelo menos 14 caracteres",
"validate-too-long": "no máximo 100 caracteres",
"validate-uppercase": "pelo menos 1 caractere maiúsculo",
"validate-lowercase": "pelo menos 1 caractere minúsculo",
"validate-number": "pelo menos 1 número",
"validate-case": "pelo menos 1 caractere minúsculo",
"validate-special-char": "pelo menos 1 caractere especial",
"validate-repeated-char": "au plus 2 caractères consécutifs répétés",
"validate-breached": "A senha que você forneceu está em uma lista de senhas comumente usadas em outros sites. Tente novamente com uma senha mais forte."
},
"token": {

View File

@@ -231,8 +231,11 @@
"validate-base": "Şifre kısıtlamaları:",
"validate-too-short": "en az 14 karakter",
"validate-too-long": "en fazla 100 karakter",
"validate-number": "en az 1 rakam",
"validate-case": "en az 1 küçük harf",
"validate-uppercase": "en az 1 büyük harf karakter",
"validate-lowercase": "en az 1 küçük harf karakter",
"validate-number": "en az 1 sayı",
"validate-special-char": "en az 1 özel karakter",
"validate-repeated-char": "veya artı 2 karakter ardışık tekrar",
"validate-breached": "Sağladığınız şifre, diğer web sitelerinde yaygın olarak kullanılan şifreler listesinde yer almaktadır. Lütfen daha güçlü bir şifre ile tekrar deneyiniz."
},
"token": {

View File

@@ -39,14 +39,15 @@ interface UserInfoStepProps {
}
type Errors = {
tooShort?: string,
tooLong?: string,
upperCase?: string,
lowerCase?: string,
number?: string,
specialChar?: string,
repeatedChar?: string,
breachedPassword?: string
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
repeatedChar?: string;
isBeachedPassword?: string;
isCommonPassword?: string;
};
/**
@@ -73,7 +74,7 @@ export default function UserInfoStep({
setOrganizationName,
attributionSource,
setAttributionSource,
providerAuthToken,
providerAuthToken
}: UserInfoStepProps): JSX.Element {
const { data: commonPasswords } = useGetCommonPasswords();
const [nameError, setNameError] = useState(false);
@@ -101,7 +102,7 @@ export default function UserInfoStep({
} else {
setOrganizationNameError(false);
}
errorCheck = await checkPassword({
password,
commonPasswords,
@@ -176,7 +177,7 @@ export default function UserInfoStep({
salt: result.salt,
verifier: result.verifier,
organizationName,
attributionSource,
attributionSource
});
// unset signup JWT token and set JWT token
@@ -193,7 +194,7 @@ export default function UserInfoStep({
});
const userOrgs = await fetchOrganizations();
const orgId = userOrgs[0]?._id;
const project = await ProjectService.initProject({
organizationId: orgId,
@@ -217,13 +218,15 @@ export default function UserInfoStep({
};
return (
<div className="h-full mx-auto mb-36 w-max rounded-xl md:px-8 md:mb-16">
<p className="mx-8 mb-6 flex justify-center text-xl font-bold text-medium md:mx-16 text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<div className="mx-auto mb-36 h-full w-max rounded-xl md:mb-16 md:px-8">
<p className="text-medium mx-8 mb-6 flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-xl font-bold text-transparent md:mx-16">
{t("signup.step3-message")}
</p>
<div className="h-full mx-auto mb-36 w-max rounded-xl py-6 md:px-8 md:mb-16 md:border md:border-mineshaft-600 md:bg-mineshaft-800">
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Your Name</p>
<div className="mx-auto mb-36 h-full w-max rounded-xl py-6 md:mb-16 md:border md:border-mineshaft-600 md:bg-mineshaft-800 md:px-8">
<div className="relative z-0 flex w-1/4 w-full min-w-[20rem] flex-col items-center justify-end rounded-lg py-2 lg:w-1/6">
<p className="mb-1 ml-1 w-full text-left text-sm font-medium text-bunker-300">
Your Name
</p>
<Input
placeholder="Jane Doe"
onChange={(e) => setName(e.target.value)}
@@ -232,10 +235,16 @@ export default function UserInfoStep({
autoComplete="given-name"
className="h-12"
/>
{nameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your name</p>}
{nameError && (
<p className="mt-1 ml-1 w-full text-left text-xs text-red-600">
Please, specify your name
</p>
)}
</div>
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Organization Name</p>
<div className="relative z-0 flex w-1/4 w-full min-w-[20rem] flex-col items-center justify-end rounded-lg py-2 lg:w-1/6">
<p className="mb-1 ml-1 w-full text-left text-sm font-medium text-bunker-300">
Organization Name
</p>
<Input
placeholder="Infisical"
onChange={(e) => setOrganizationName(e.target.value)}
@@ -243,10 +252,16 @@ export default function UserInfoStep({
isRequired
className="h-12"
/>
{organizationNameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your organization name</p>}
{organizationNameError && (
<p className="mt-1 ml-1 w-full text-left text-xs text-red-600">
Please, specify your organization name
</p>
)}
</div>
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Where did you hear about us? <span className="font-light">(optional)</span></p>
<div className="relative z-0 flex w-1/4 w-full min-w-[20rem] flex-col items-center justify-end rounded-lg py-2 lg:w-1/6">
<p className="mb-1 ml-1 w-full text-left text-sm font-medium text-bunker-300">
Where did you hear about us? <span className="font-light">(optional)</span>
</p>
<Input
placeholder=""
onChange={(e) => setAttributionSource(e.target.value)}
@@ -254,7 +269,7 @@ export default function UserInfoStep({
className="h-12"
/>
</div>
<div className="mt-2 flex lg:w-1/6 w-1/4 min-w-[20rem] max-h-60 w-full flex-col items-center justify-center rounded-lg py-2">
<div className="mt-2 flex max-h-60 w-1/4 w-full min-w-[20rem] flex-col items-center justify-center rounded-lg py-2 lg:w-1/6">
<InputField
label={t("section.password.password")}
onChangeHandler={(pass: string) => {
@@ -274,23 +289,20 @@ export default function UserInfoStep({
/>
{Object.keys(errors).length > 0 && (
<div className="mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-2 py-2">
<div className="mb-2 text-sm text-gray-400">{t("section.password.validate-base")}</div>
<div className="mb-2 text-sm text-gray-400">
{t("section.password.validate-base")}
</div>
{Object.keys(errors).map((key) => {
if (errors[key as keyof Errors]) {
return (
<div
className="ml-1 flex flex-row items-top justify-start"
key={key}
>
<div className="items-top ml-1 flex flex-row justify-start" key={key}>
<div>
<FontAwesomeIcon
icon={faXmark}
className="text-md text-red ml-0.5 mr-2.5"
<FontAwesomeIcon
icon={faXmark}
className="text-md ml-0.5 mr-2.5 text-red"
/>
</div>
<p className="text-gray-400 text-sm">
{errors[key as keyof Errors]}
</p>
<p className="text-sm text-gray-400">{errors[key as keyof Errors]}</p>
</div>
);
}
@@ -300,18 +312,21 @@ export default function UserInfoStep({
</div>
)}
</div>
<div className="flex flex-col items-center justify-center lg:w-[19%] w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
<div className="text-l py-1 text-lg w-full">
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-[19%]">
<div className="text-l w-full py-1 text-lg">
<Button
type="submit"
onClick={signupErrorCheck}
size="sm"
isFullWidth
className='h-14'
className="h-14"
colorSchema="primary"
variant="outline_bg"
isLoading={isLoading}
> {String(t("signup.signup"))} </Button>
>
{" "}
{String(t("signup.signup"))}{" "}
</Button>
</div>
</div>
</div>

View File

@@ -6,9 +6,12 @@ interface PasswordCheckProps {
errorCheck: boolean;
setPasswordErrorTooShort: (value: boolean) => void;
setPasswordErrorTooLong: (value: boolean) => void;
setPasswordErrorNumber: (value: boolean) => void;
setPasswordErrorUpperCase: (value: boolean) => void;
setPasswordErrorLowerCase: (value: boolean) => void;
setPasswordErrorIsBreached: (value: boolean) => void;
setPasswordErrorNumber: (value: boolean) => void;
setPasswordErrorSpecialChar: (value: boolean) => void;
setPasswordErrorRepeatedChar: (value: boolean) => void;
setPasswordErrorIsBreachedPassword: (value: boolean) => void;
}
/**
@@ -18,11 +21,15 @@ const passwordCheck = async ({
password,
setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorNumber,
setPasswordErrorUpperCase,
setPasswordErrorLowerCase,
setPasswordErrorIsBreached,
setPasswordErrorNumber,
setPasswordErrorSpecialChar,
setPasswordErrorRepeatedChar,
setPasswordErrorIsBreachedPassword,
errorCheck
}: PasswordCheckProps) => {
// tooShort
if (!password || password.length < 14) {
setPasswordErrorTooShort(true);
errorCheck = true;
@@ -30,6 +37,7 @@ const passwordCheck = async ({
setPasswordErrorTooShort(false);
}
// tooLong
if (password.length > 100) {
setPasswordErrorTooLong(true);
errorCheck = true;
@@ -37,51 +45,54 @@ const passwordCheck = async ({
setPasswordErrorTooLong(false);
}
if (!/\d/.test(password)) {
// upperCase
if (!/[A-Z]/.test(password)) {
setPasswordErrorUpperCase(true);
errorCheck = true;
} else {
setPasswordErrorUpperCase(false);
}
// lowerCase
if (!/[a-z]/.test(password)) {
setPasswordErrorLowerCase(true);
errorCheck = true;
} else {
setPasswordErrorLowerCase(false);
}
// number
if (!/[0-9]/.test(password)) {
setPasswordErrorNumber(true);
errorCheck = true;
} else {
setPasswordErrorNumber(false);
}
if (!/[a-z]/.test(password)) {
setPasswordErrorLowerCase(true);
// specialChar
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
setPasswordErrorSpecialChar(true);
errorCheck = true;
// } else if (/(.)(?:(?!\1).){1,2}/.test(password)) {
// console.log(111)
// setPasswordError(true);
// setPasswordErrorMessage("Password should not contain repeating characters.");
// errorCheck = true;
// } else if (RegExp(`[${email}]`).test(password)) {
// console.log(222)
// setPasswordError(true);
// setPasswordErrorMessage("Password should not contain your email.");
// errorCheck = true;
} else {
setPasswordErrorLowerCase(false);
setPasswordErrorSpecialChar(false);
}
// repeatedChar
if (/([A-Za-z0-9])\1\1\1/.test(password)) {
setPasswordErrorRepeatedChar(true);
errorCheck = true;
} else {
setPasswordErrorRepeatedChar(false);
}
// breachedPassword
if (await checkIsPasswordBreached(password)) {
setPasswordErrorIsBreached(true);
setPasswordErrorIsBreachedPassword(true);
errorCheck = true;
} else {
setPasswordErrorIsBreached(false);
setPasswordErrorIsBreachedPassword(false);
}
// if (!/[A-Z]/.test(password)) {
// setPasswordErrorUpperCase(true);
// errorCheck = true;
// } else {
// setPasswordErrorUpperCase(false);
// }
// if (!/(?=.*[!@#$%^&*])/.test(password)) {
// setPasswordErrorSpecialChar(true);
// // "Please add at least 1 special character (*, !, #, %)."
// errorCheck = true;
// } else {
// setPasswordErrorSpecialChar(false);
// }
return errorCheck;
};

View File

@@ -8,8 +8,8 @@ type Errors = {
number?: string;
specialChar?: string;
repeatedChar?: string;
commonPassword?: string;
breachedPassword?: string;
isBreachedPassword?: string;
isCommonPassword?: string;
};
interface CheckPasswordParams {
@@ -20,12 +20,15 @@ interface CheckPasswordParams {
/**
* Validate that the password [password]:
* - Contains at least 14 characters long
* - Contains at most 100 characters long
* - Contains at least 14 characters
* - Contains at most 100 characters
* - Contains at least 1 uppercase character (A-Z)
* - Contains at least 1 lowercase character (a-z)
* - Contains at least 1 number (0-9)
* - Contains at least 1 special character
* - Does not contain 3 repeat, consecutive characters
* - Is not in a database of breached passwords
* - Is not in a list of common passwords
*
* The function returns whether or not the password [password]
* passes the minimum requirements above. It sets errors on
@@ -42,41 +45,50 @@ const checkPassword = async ({
}: CheckPasswordParams): Promise<boolean> => {
const errors: Errors = {};
// tooShort
if (password.length < 14) {
errors.tooShort = "at least 14 characters";
}
// toolong
if (password.length > 100) {
errors.tooLong = "at most 100 characters";
}
// upperCase
if (!/[A-Z]/.test(password)) {
errors.upperCase = "at least 1 uppercase character (A-Z)";
}
// lowerCase
if (!/[a-z]/.test(password)) {
errors.lowerCase = "at least 1 lowercase character (a-z)";
}
// number
if (!/[0-9]/.test(password)) {
errors.number = "at least 1 number (0-9)";
}
// specialChar
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
errors.specialChar = "at least 1 special character (!@#$%^&*(),.?)";
}
// repeatedChar
if (/([A-Za-z0-9])\1\1\1/.test(password)) {
errors.repeatedChar = "No 3 repeat, consecutive characters";
}
// breachedPassword
if (await checkIsPasswordBreached(password)) {
errors.breachedPassword =
errors.isBreachedPassword =
"The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password.";
}
// commonPassword
if (commonPasswords.includes(password)) {
errors.commonPassword = "No common passwords";
errors.isCommonPassword = "No common passwords";
}
setErrors(errors);

View File

@@ -30,9 +30,12 @@ export default function PasswordReset() {
const [backupKeyError, setBackupKeyError] = useState(false);
const [passwordErrorTooShort, setPasswordErrorTooShort] = useState(false);
const [passwordErrorTooLong, setPasswordErrorTooLong] = useState(false);
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false);
const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false);
const [passwordErrorIsBreached, setPasswordErrorIsBreached] = useState(false);
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] = useState(false);
const [passwordErrorRepeatedChar, setPasswordErrorRepeatedChar] = useState(false);
const [passwordErrorIsBreachedPassword, setPasswordErrorIsBreachedPassword] = useState(false);
const router = useRouter();
@@ -43,7 +46,7 @@ export default function PasswordReset() {
const token = parsedUrl.token as string;
const email = (parsedUrl.to as string)?.replace(" ", "+").trim();
// Unencrypt the private key with a backup key
// Decrypt the private key with a backup key
const getEncryptedKeyHandler = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
@@ -71,9 +74,12 @@ export default function PasswordReset() {
password: newPassword,
setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorNumber,
setPasswordErrorUpperCase,
setPasswordErrorLowerCase,
setPasswordErrorIsBreached,
setPasswordErrorNumber,
setPasswordErrorSpecialChar,
setPasswordErrorRepeatedChar,
setPasswordErrorIsBreachedPassword,
errorCheck: false
});
@@ -229,9 +235,12 @@ export default function PasswordReset() {
password,
setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorNumber,
setPasswordErrorUpperCase,
setPasswordErrorLowerCase,
setPasswordErrorIsBreached,
setPasswordErrorNumber,
setPasswordErrorSpecialChar,
setPasswordErrorRepeatedChar,
setPasswordErrorIsBreachedPassword,
errorCheck: false
});
}}
@@ -241,9 +250,12 @@ export default function PasswordReset() {
error={
passwordErrorTooShort &&
passwordErrorTooLong &&
passwordErrorNumber &&
passwordErrorUpperCase &&
passwordErrorLowerCase &&
passwordErrorIsBreached
passwordErrorNumber &&
passwordErrorSpecialChar &&
passwordErrorRepeatedChar &&
passwordErrorIsBreachedPassword
}
autoComplete="new-password"
id="new-password"
@@ -251,9 +263,12 @@ export default function PasswordReset() {
</div>
{passwordErrorTooShort ||
passwordErrorTooLong ||
passwordErrorNumber ||
passwordErrorUpperCase ||
passwordErrorLowerCase ||
passwordErrorIsBreached ? (
passwordErrorNumber ||
passwordErrorSpecialChar ||
passwordErrorRepeatedChar ||
passwordErrorIsBreachedPassword ? (
<div className="mx-2 mt-3 mb-2 flex w-full max-w-md flex-col items-start rounded-md bg-white/5 px-2 py-2">
<div className="mb-1 text-sm text-gray-400">Password should contain:</div>
<div className="ml-1 flex flex-row items-center justify-start">
@@ -277,13 +292,15 @@ export default function PasswordReset() {
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorNumber ? (
{passwordErrorUpperCase ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>
at least 1 number
<div
className={`${passwordErrorUpperCase ? "text-gray-400" : "text-gray-600"} text-sm`}
>
at least 1 uppercase character
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
@@ -297,14 +314,54 @@ export default function PasswordReset() {
>
at least 1 lowercase character
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorNumber ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>
at least 1 number
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorIsBreached ? (
{passwordErrorSpecialChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${passwordErrorIsBreached ? "text-gray-400" : "text-gray-600"} text-sm`}
className={`${
passwordErrorSpecialChar ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
at least 1 special character
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorRepeatedChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${
passwordErrorRepeatedChar ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
at most 2 repeated characters
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorIsBreachedPassword ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${
passwordErrorIsBreachedPassword ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
The password you provided is in a list of passwords commonly used on other websites.
Please try again with a stronger password.

View File

@@ -21,8 +21,8 @@ type Errors = {
number?: string;
specialChar?: string;
repeatedChar?: string;
commonPassword?: string;
breachedPassword?: string;
isBreachedPassword?: string;
isCommonPassword?: string;
};
const schema = yup

View File

@@ -1,4 +1,3 @@
import crypto from "crypto";
import React, { useEffect, useState } from "react";
@@ -25,24 +24,25 @@ import ProjectService from "@app/services/ProjectService";
const client = new jsrp.client();
type Props = {
setStep: (step: number) => void;
email: string;
password: string;
setPassword: (value: string) => void;
name: string;
providerOrganizationName: string;
providerAuthToken?: string;
}
setStep: (step: number) => void;
email: string;
password: string;
setPassword: (value: string) => void;
name: string;
providerOrganizationName: string;
providerAuthToken?: string;
};
type Errors = {
tooShort?: string,
tooLong?: string,
upperCase?: string,
lowerCase?: string,
number?: string,
specialChar?: string,
repeatedChar?: string,
breachedPassword?: string
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
repeatedChar?: string;
isBeachedPassword?: string;
isCommonPassword?: string;
};
/**
@@ -65,7 +65,7 @@ export const UserInfoSSOStep = ({
password,
setPassword,
setStep,
providerAuthToken,
providerAuthToken
}: Props) => {
const { data: commonPasswords } = useGetCommonPasswords();
const [nameError, setNameError] = useState(false);
@@ -99,7 +99,7 @@ export const UserInfoSSOStep = ({
} else {
setOrganizationNameError(false);
}
errorCheck = await checkPassword({
password,
commonPasswords,
@@ -212,15 +212,17 @@ export const UserInfoSSOStep = ({
setIsLoading(false);
}
};
return (
<div className="h-full mx-auto mb-36 w-max rounded-xl md:px-8 md:mb-16">
<p className="mx-8 mb-6 flex justify-center text-xl font-bold text-medium md:mx-16 text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<div className="mx-auto mb-36 h-full w-max rounded-xl md:mb-16 md:px-8">
<p className="text-medium mx-8 mb-6 flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-xl font-bold text-transparent md:mx-16">
{t("signup.step3-message")}
</p>
<div className="h-full mx-auto mb-36 w-max rounded-xl py-6 md:px-8 md:mb-16 md:border md:border-mineshaft-600 md:bg-mineshaft-800">
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Your Name</p>
<div className="mx-auto mb-36 h-full w-max rounded-xl py-6 md:mb-16 md:border md:border-mineshaft-600 md:bg-mineshaft-800 md:px-8">
<div className="relative z-0 flex w-1/4 w-full min-w-[20rem] flex-col items-center justify-end rounded-lg py-2 lg:w-1/6">
<p className="mb-1 ml-1 w-full text-left text-sm font-medium text-bunker-300">
Your Name
</p>
<Input
placeholder="Jane Doe"
value={name}
@@ -229,11 +231,17 @@ export const UserInfoSSOStep = ({
autoComplete="given-name"
className="h-12"
/>
{nameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your name</p>}
{nameError && (
<p className="mt-1 ml-1 w-full text-left text-xs text-red-600">
Please, specify your name
</p>
)}
</div>
{providerOrganizationName === undefined && (
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Organization Name</p>
<div className="relative z-0 flex w-1/4 w-full min-w-[20rem] flex-col items-center justify-end rounded-lg py-2 lg:w-1/6">
<p className="mb-1 ml-1 w-full text-left text-sm font-medium text-bunker-300">
Organization Name
</p>
<Input
placeholder="Infisical"
value={organizationName}
@@ -242,12 +250,18 @@ export const UserInfoSSOStep = ({
className="h-12"
disabled
/>
{organizationNameError && <p className='text-left w-full text-xs text-red-600 mt-1 ml-1'>Please, specify your organization name</p>}
{organizationNameError && (
<p className="mt-1 ml-1 w-full text-left text-xs text-red-600">
Please, specify your organization name
</p>
)}
</div>
)}
{providerOrganizationName === undefined && (
<div className="relative z-0 lg:w-1/6 w-1/4 min-w-[20rem] flex flex-col items-center justify-end w-full py-2 rounded-lg">
<p className='text-left w-full text-sm text-bunker-300 mb-1 ml-1 font-medium'>Where did you hear about us? <span className="font-light">(optional)</span></p>
<div className="relative z-0 flex w-1/4 w-full min-w-[20rem] flex-col items-center justify-end rounded-lg py-2 lg:w-1/6">
<p className="mb-1 ml-1 w-full text-left text-sm font-medium text-bunker-300">
Where did you hear about us? <span className="font-light">(optional)</span>
</p>
<Input
placeholder=""
onChange={(e) => setAttributionSource(e.target.value)}
@@ -256,12 +270,12 @@ export const UserInfoSSOStep = ({
/>
</div>
)}
<div className="mt-2 flex lg:w-1/6 w-1/4 min-w-[20rem] max-h-60 w-full flex-col items-center justify-center rounded-lg py-2">
<div className="mt-2 flex max-h-60 w-1/4 w-full min-w-[20rem] flex-col items-center justify-center rounded-lg py-2 lg:w-1/6">
<InputField
label="Infisical Password"
onChangeHandler={(pass: string) => {
onChangeHandler={async (pass: string) => {
setPassword(pass);
checkPassword({
await checkPassword({
password: pass,
commonPasswords,
setErrors
@@ -274,26 +288,27 @@ export const UserInfoSSOStep = ({
autoComplete="new-password"
id="new-password"
/>
<div className="mt-2 w-min min-w-[20rem] max-h-60 flex-col items-center justify-center rounded-md px-1.5 bg-mineshaft-500 text-mineshaft-300 text-xs p-1.5"><FontAwesomeIcon icon={faInfoCircle} className="mr-1.5" />Infisical Password is used as part of the encryption mechanism so that even the authentication provider is not able to access your secrets.</div>
<div className="mt-2 max-h-60 w-min min-w-[20rem] flex-col items-center justify-center rounded-md bg-mineshaft-500 p-1.5 px-1.5 text-xs text-mineshaft-300">
<FontAwesomeIcon icon={faInfoCircle} className="mr-1.5" />
Infisical Password is used as part of the encryption mechanism so that even the
authentication provider is not able to access your secrets.
</div>
{Object.keys(errors).length > 0 && (
<div className="mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-2 py-2">
<div className="mb-2 text-sm text-gray-400">{t("section.password.validate-base")}</div>
<div className="mb-2 text-sm text-gray-400">
{t("section.password.validate-base")}
</div>
{Object.keys(errors).map((key) => {
if (errors[key as keyof Errors]) {
return (
<div
className="ml-1 flex flex-row items-top justify-start"
key={key}
>
<div className="items-top ml-1 flex flex-row justify-start" key={key}>
<div>
<FontAwesomeIcon
icon={faXmark}
className="text-md text-red ml-0.5 mr-2.5"
<FontAwesomeIcon
icon={faXmark}
className="text-md ml-0.5 mr-2.5 text-red"
/>
</div>
<p className="text-gray-400 text-sm">
{errors[key as keyof Errors]}
</p>
<p className="text-sm text-gray-400">{errors[key as keyof Errors]}</p>
</div>
);
}
@@ -303,21 +318,24 @@ export const UserInfoSSOStep = ({
</div>
)}
</div>
<div className="flex flex-col items-center justify-center lg:w-[19%] w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
<div className="text-l py-1 text-lg w-full">
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-[19%]">
<div className="text-l w-full py-1 text-lg">
<Button
type="submit"
onClick={signupErrorCheck}
size="sm"
isFullWidth
className='h-12'
className="h-12"
colorSchema="primary"
variant="outline_bg"
isLoading={isLoading}
> {String(t("signup.signup"))} </Button>
>
{" "}
{String(t("signup.signup"))}{" "}
</Button>
</div>
</div>
</div>
</div>
);
}
};