diff --git a/cypress/e2e/SignUp.cy.ts b/cypress/e2e/SignUp.cy.ts index df74f22f..ba1bf839 100644 --- a/cypress/e2e/SignUp.cy.ts +++ b/cypress/e2e/SignUp.cy.ts @@ -4,6 +4,8 @@ import { API_ROUTES } from '@graasp/query-client'; import { SIGN_UP_PATH } from '../../src/config/paths'; import { + EMAIL_SIGN_UP_FIELD_ID, + NAME_SIGN_UP_FIELD_ID, SIGN_UP_BUTTON_ID, SIGN_UP_SAVE_ACTIONS_ID, SUCCESS_CONTENT_ID, @@ -101,6 +103,24 @@ describe('SignUp', () => { cy.visit(`${SIGN_UP_PATH}?${search.toString()}`); cy.get(`#${SIGN_UP_BUTTON_ID}`).should('be.visible'); }); + + it('Username can not contain special characters', () => { + const badUsername = '<<div>%^\'"'; + + cy.visit(SIGN_UP_PATH); + cy.get(`#${NAME_SIGN_UP_FIELD_ID}`).clear(); + cy.get(`#${NAME_SIGN_UP_FIELD_ID}`).type(badUsername); + cy.get(`#${EMAIL_SIGN_UP_FIELD_ID}`).clear(); + cy.get(`#${EMAIL_SIGN_UP_FIELD_ID}`).type('test@test.lol'); + cy.agreeWithAllTerms(); + cy.get(`#${SIGN_UP_BUTTON_ID}`).click(); + + // The helper text should display the message about special characters + cy.get('[id$=-helper-text]').should( + 'have.text', + 'User name must not contain " ", ", <, >, ^, %, \\', + ); + }); }); describe('Defining Analytics On Sign Up', () => { diff --git a/package.json b/package.json index e41f4a35..7fa302c5 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@emotion/react": "11.13.3", "@emotion/styled": "11.13.0", "@graasp/query-client": "3.26.0", - "@graasp/sdk": "4.31.0", + "@graasp/sdk": "4.32.0", "@graasp/stylis-plugin-rtl": "2.2.0", "@graasp/translations": "1.39.0", "@graasp/ui": "5.2.1", diff --git a/src/langs/constants.ts b/src/langs/constants.ts index 6be82224..64f37c69 100644 --- a/src/langs/constants.ts +++ b/src/langs/constants.ts @@ -38,6 +38,7 @@ export const AUTH = { PRIVACY_POLICY_LINK: 'PRIVACY_POLICY_LINK', INVITATIONS_LOADING_MESSAGE: 'INVITATIONS_LOADING_MESSAGE', USERNAME_TOO_SHORT_ERROR: 'USERNAME_TOO_SHORT_ERROR', + USERNAME_SPECIAL_CHARACTERS_ERROR: 'USERNAME_SPECIAL_CHARACTERS_ERROR', USERNAME_TOO_LONG_ERROR: 'USERNAME_TOO_LONG_ERROR', INVALID_EMAIL_ERROR: 'INVALID_EMAIL_ERROR', EMPTY_EMAIL_ERROR: 'EMPTY_EMAIL_ERROR', diff --git a/src/langs/de.json b/src/langs/de.json index b5ea5569..a096cc5b 100644 --- a/src/langs/de.json +++ b/src/langs/de.json @@ -35,6 +35,7 @@ "INVITATIONS_LOADING_MESSAGE": "Wir warten auf Ihre Einladung, bitte warten …", "USERNAME_TOO_SHORT_ERROR": "Bitte geben Sie einen Benutzernamen mit mehr als {{min}} Zeichen ein", "USERNAME_TOO_LONG_ERROR": "Bitte geben Sie einen Benutzernamen mit weniger als {{max}} Zeichen ein.", + "USERNAME_SPECIAL_CHARACTERS_ERROR": "Der Benutzername darf nicht enthalten \" \", \", <, >, ^, %, \\", "INVALID_EMAIL_ERROR": "Dies scheint keine gültige E-Mail-Adresse zu sein", "EMPTY_EMAIL_ERROR": "Eine E-Mail-Adresse ist erforderlich, dieses Feld darf nicht leer sein", "PASSWORD_EMPTY_ERROR": "Das Passwort darf nicht leer sein" diff --git a/src/langs/en.json b/src/langs/en.json index bbc92173..f85097f7 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -39,6 +39,7 @@ "INVITATIONS_LOADING_MESSAGE": "We are looking for your invitation, please stand by…", "USERNAME_TOO_SHORT_ERROR": "Please enter a username with more than {{min}} characters", "USERNAME_TOO_LONG_ERROR": "Please enter a username under {{max}} characters", + "USERNAME_SPECIAL_CHARACTERS_ERROR": "User name must not contain \" \", \", <, >, ^, %, \\", "INVALID_EMAIL_ERROR": "This does not look like a valid email address", "EMPTY_EMAIL_ERROR": "An email address is required, this field can not be empty", "REQUIRED_FIELD_ERROR": "This field is required", diff --git a/src/langs/es.json b/src/langs/es.json index 970de063..ec0d6621 100644 --- a/src/langs/es.json +++ b/src/langs/es.json @@ -35,6 +35,7 @@ "INVITATIONS_LOADING_MESSAGE": "Estamos esperando su invitación, por favor esperen...", "USERNAME_TOO_SHORT_ERROR": "Por favor ingrese un nombre de usuario con más de {{min}} caracteres", "USERNAME_TOO_LONG_ERROR": "Por favor ingrese un nombre de usuario con menos de {{max}} caracteres", + "USERNAME_SPECIAL_CHARACTERS_ERROR": "El nombre de usuario no debe contener \" \", \", <, >, ^, %, \\", "INVALID_EMAIL_ERROR": "Esto no parece una dirección de correo electrónico válida.", "EMPTY_EMAIL_ERROR": "Se requiere una dirección de correo electrónico, este campo no puede estar vacío", "PASSWORD_EMPTY_ERROR": "La contraseña no puede estar vacía" diff --git a/src/langs/fr.json b/src/langs/fr.json index 1c494232..e745433c 100644 --- a/src/langs/fr.json +++ b/src/langs/fr.json @@ -35,6 +35,7 @@ "INVITATIONS_LOADING_MESSAGE": "Nous recherchons votre invitation, veuillez patienter quelques instants…", "USERNAME_TOO_SHORT_ERROR": "Veuillez saisir un nom d'utilisateur comportant au moins {{min}} caractères", "USERNAME_TOO_LONG_ERROR": "Veuillez saisir un nom d'utilisateur de moins de {{max}} caractères", + "USERNAME_SPECIAL_CHARACTERS_ERROR": "Le nom d'utilisateur ne doit pas contenir \" \", \", <, >, ^, %, \\", "INVALID_EMAIL_ERROR": "Cela ne ressemble pas à une adresse e-mail valide", "EMPTY_EMAIL_ERROR": "Une adresse email est obligatoire, ce champ ne peut pas être vide", "PASSWORD_EMPTY_ERROR": "Le mot de passe ne peut pas être vide", diff --git a/src/langs/it.json b/src/langs/it.json index 5fea5692..a708e91a 100644 --- a/src/langs/it.json +++ b/src/langs/it.json @@ -22,5 +22,6 @@ "SIGN_UP_SAVE_ACTIONS_LABEL": "Attivare il salvataggio degli insight guidati dai dati per migliorare i cruscotti analitici", "SIGN_UP_SAVE_ACTIONS_TOOLTIP": "Raccogliamo dati analitici per migliorare l'esperienza dell'utente durante la navigazione su Graasp.", "SIGN_UP_SUCCESS_TITLE": "Benvenuto!", - "SWITCH_ACCOUNT_TEXT": "Passa a un altro account" + "SWITCH_ACCOUNT_TEXT": "Passa a un altro account", + "USERNAME_SPECIAL_CHARACTERS_ERROR": "Il nome utente non deve contenere \" \", \", <, >, ^, %, \\" } diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 8c56ca2c..5b853d77 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -4,6 +4,7 @@ import validator from 'validator'; import { MAX_USERNAME_LENGTH, MIN_USERNAME_LENGTH, + MemberConstants, isPasswordStrong, } from '@graasp/sdk'; @@ -12,6 +13,7 @@ import { AUTH } from '../langs/constants'; const { USERNAME_TOO_LONG_ERROR, USERNAME_TOO_SHORT_ERROR, + USERNAME_SPECIAL_CHARACTERS_ERROR, INVALID_EMAIL_ERROR, PASSWORD_EMPTY_ERROR, PASSWORD_WEAK_ERROR, @@ -19,6 +21,8 @@ const { EMPTY_EMAIL_ERROR, } = AUTH; +const USER_NAME_REGEX = MemberConstants.USERNAME_FORBIDDEN_CHARS_REGEX; + export const nameValidator = (name: string) => { const trimmedName = name.trim(); if (trimmedName.length > MAX_USERNAME_LENGTH) { @@ -27,6 +31,9 @@ export const nameValidator = (name: string) => { if (trimmedName.length < MIN_USERNAME_LENGTH) { return USERNAME_TOO_SHORT_ERROR; } + if (USER_NAME_REGEX.test(trimmedName)) { + return USERNAME_SPECIAL_CHARACTERS_ERROR; + } return null; }; diff --git a/yarn.lock b/yarn.lock index 6734ad6a..c7701931 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2574,9 +2574,9 @@ __metadata: languageName: node linkType: hard -"@graasp/sdk@npm:4.31.0": - version: 4.31.0 - resolution: "@graasp/sdk@npm:4.31.0" +"@graasp/sdk@npm:4.32.0": + version: 4.32.0 + resolution: "@graasp/sdk@npm:4.32.0" dependencies: "@faker-js/faker": "npm:9.0.1" filesize: "npm:10.1.6" @@ -2585,7 +2585,7 @@ __metadata: peerDependencies: date-fns: ^3 || ^4.0.0 uuid: ^9 || ^10 - checksum: 10/9b2bf85a51cc12b6f2bdefeb7bbc0c615db9ea3188ace6d460b14e61503763aeab13fd2aa1c4135cee602c2c58465895569b2845b91d942982f96f5594dfd1d4 + checksum: 10/76f56a3e10e61ee59d8f778903126ff9e07b7a0512c0b42ff63a8735c2bd263c0ee5abb1a74320ab3c8632689b8e359ad63e9b9dbaed63dbeffeb0bc2066d676 languageName: node linkType: hard @@ -7132,7 +7132,7 @@ __metadata: "@emotion/react": "npm:11.13.3" "@emotion/styled": "npm:11.13.0" "@graasp/query-client": "npm:3.26.0" - "@graasp/sdk": "npm:4.31.0" + "@graasp/sdk": "npm:4.32.0" "@graasp/stylis-plugin-rtl": "npm:2.2.0" "@graasp/translations": "npm:1.39.0" "@graasp/ui": "npm:5.2.1"