diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 076761a2..41859609 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -28,7 +28,8 @@ "react-icons": "^5.0.1", "react-router-dom": "^6.22.3", "react-scripts": "^5.0.1", - "web-vitals": "^3.5.1" + "web-vitals": "^3.5.1", + "zxcvbn": "^4.4.2" }, "devDependencies": { "axios-mock-adapter": "^1.22.0", @@ -28453,6 +28454,11 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==" } } } diff --git a/webapp/package.json b/webapp/package.json index 2ea16917..55422652 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -23,7 +23,8 @@ "react-icons": "^5.0.1", "react-router-dom": "^6.22.3", "react-scripts": "^5.0.1", - "web-vitals": "^3.5.1" + "web-vitals": "^3.5.1", + "zxcvbn": "^4.4.2" }, "scripts": { "start": "react-scripts start", diff --git a/webapp/src/components/loginAndRegistration/AddUser.js b/webapp/src/components/loginAndRegistration/AddUser.js index 2324ecab..0714bd67 100644 --- a/webapp/src/components/loginAndRegistration/AddUser.js +++ b/webapp/src/components/loginAndRegistration/AddUser.js @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"; import axios from 'axios'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import zxcvbn from "zxcvbn"; const AddUser = () => { @@ -14,26 +15,87 @@ const AddUser = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [repeatPassword, setRepeatPassword] = useState(''); + const [passwordStrength, setPasswordStrength] = useState(undefined); + const [passwordStrengthText, setPasswordStrengthText] = useState(''); + const [submitError, setSubmitError] = useState(''); const handleSubmit = async (event) => { event.preventDefault(); try { - //TODO: Add more validations - if(password === repeatPassword){ //User put the same password + //Validations + //TODO: email validation + if(password !== repeatPassword){ + //User put the same password + setSubmitError("addUser.error_passwords_no_match"); + } else if(/\s/.test(password)){ + //User put spaces in password + setSubmitError("addUser.error_password_spaces"); + } else if(password.length < 8){ + //Password too short + setSubmitError("addUser.error_password_minimum_length"); + } else if(password.length > 64){ + //Password too long + setSubmitError("addUser.error_password_maximum_length"); + } else if(/\s/.test(username)){ + //Spaces in username + setSubmitError("addUser.error_username_spaces"); + } else{ + //Continue + setSubmitError(''); const response = await axios.post(apiUrl, { username, password }); console.log("Registered user: " + response.data.username); navigate('/login'); } - else{ - //TODO: Show some errors to the user - } } catch (error) { + if(error.response.data.error === "Username already in use"){ //TODO: Improve + setSubmitError("addUser.error_username_in_use"); + } console.error('Error adding user:', error); } }; + //Possible email validation + /** + const validateEmail = (email) => { + return String(email) + .toLowerCase() + .match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + ); + }; + */ + + const handlePasswordChange = (e) => { + const newPassword = e.target.value; + setPassword(newPassword); + + const newStrength = zxcvbn(newPassword); + + switch(newStrength.score){ + case 0: + setPasswordStrengthText("addUser.very_weak_password"); + break; + case 1: + setPasswordStrengthText("addUser.very_weak_password"); + break; + case 2: + setPasswordStrengthText("addUser.weak_password"); + break; + case 3: + setPasswordStrengthText("addUser.good_password"); + break; + case 4: + setPasswordStrengthText("addUser.strong_password"); + break; + default: + setPasswordStrengthText("addUser.very_weak_password"); + break; + } + setPasswordStrength(newStrength); + }; + return (
{t("addUser.password_placeholder")}:
{ placeholder={t("addUser.password_placeholder")} required value={password} - onChange={(e) => setPassword(e.target.value)} + onChange={handlePasswordChange} />{t("addUser.repeat_password_placeholder")}:
{ onChange={(e) => setRepeatPassword(e.target.value)} />{t(submitError)}
}