diff --git a/backend/src/constants/registration.js b/backend/src/constants/registration.js new file mode 100644 index 000000000..9e63e478e --- /dev/null +++ b/backend/src/constants/registration.js @@ -0,0 +1,5 @@ +// this file is duplicated in `backend/src/config/metadata.js` and `webapp/constants/metadata.js` +export default { + NONCE_LENGTH: 5, + INVITE_CODE_LENGTH: 6, +} diff --git a/backend/src/schema/resolvers/emails.spec.js b/backend/src/schema/resolvers/emails.spec.js index 60f5ae899..39b70ac0b 100644 --- a/backend/src/schema/resolvers/emails.spec.js +++ b/backend/src/schema/resolvers/emails.spec.js @@ -158,7 +158,7 @@ describe('VerifyEmailAddress', () => { ` beforeEach(() => { - variables = { ...variables, email: 'to-be-verified@example.org', nonce: '123456' } + variables = { ...variables, email: 'to-be-verified@example.org', nonce: '12345' } }) describe('unauthenticated', () => { diff --git a/backend/src/schema/resolvers/helpers/generateInviteCode.js b/backend/src/schema/resolvers/helpers/generateInviteCode.js index 70e122d26..99c752eb0 100644 --- a/backend/src/schema/resolvers/helpers/generateInviteCode.js +++ b/backend/src/schema/resolvers/helpers/generateInviteCode.js @@ -1,8 +1,13 @@ +import CONSTANTS_REGISTRATION from './../../../constants/registration' + export default function generateInviteCode() { // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) - return Array.from({ length: 6 }, (n = Math.floor(Math.random() * 36)) => { - // n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65 - // else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48 - return String.fromCharCode(n > 9 ? n + 55 : n + 48) - }).join('') + return Array.from( + { length: CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH }, + (n = Math.floor(Math.random() * 36)) => { + // n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65 + // else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48 + return String.fromCharCode(n > 9 ? n + 55 : n + 48) + }, + ).join('') } diff --git a/backend/src/schema/resolvers/helpers/generateNonce.js b/backend/src/schema/resolvers/helpers/generateNonce.js index 6da40b5c2..50aa8489e 100644 --- a/backend/src/schema/resolvers/helpers/generateNonce.js +++ b/backend/src/schema/resolvers/helpers/generateNonce.js @@ -1,5 +1,11 @@ +import CONSTANTS_REGISTRATION from './../../../constants/registration' + +// TODO: why this is not used in resolver 'requestPasswordReset'? export default function generateNonce() { - return Array.from({ length: 5 }, (n = Math.floor(Math.random() * 10)) => { - return String.fromCharCode(n + 48) - }).join('') + return Array.from( + { length: CONSTANTS_REGISTRATION.NONCE_LENGTH }, + (n = Math.floor(Math.random() * 10)) => { + return String.fromCharCode(n + 48) + }, + ).join('') } diff --git a/backend/src/schema/resolvers/inviteCodes.spec.js b/backend/src/schema/resolvers/inviteCodes.spec.js index 094716871..72e2a2492 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.js +++ b/backend/src/schema/resolvers/inviteCodes.spec.js @@ -3,6 +3,7 @@ import { getDriver } from '../../db/neo4j' import { gql } from '../../helpers/jest' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' +import CONSTANTS_REGISTRATION from './../../constants/registration' let user let query @@ -107,7 +108,11 @@ describe('inviteCodes', () => { errors: undefined, data: { GenerateInviteCode: { - code: expect.stringMatching(/^[0-9A-Z]{6,6}$/), + code: expect.stringMatching( + new RegExp( + `^[0-9A-Z]{${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH},${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH}}$`, + ), + ), expiresAt: null, createdAt: expect.any(String), }, @@ -129,7 +134,11 @@ describe('inviteCodes', () => { errors: undefined, data: { GenerateInviteCode: { - code: expect.stringMatching(/^[0-9A-Z]{6,6}$/), + code: expect.stringMatching( + new RegExp( + `^[0-9A-Z]{${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH},${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH}}$`, + ), + ), expiresAt: nextWeek.toISOString(), createdAt: expect.any(String), }, diff --git a/backend/src/schema/resolvers/passwordReset.js b/backend/src/schema/resolvers/passwordReset.js index d1f49876b..6fea020dd 100644 --- a/backend/src/schema/resolvers/passwordReset.js +++ b/backend/src/schema/resolvers/passwordReset.js @@ -1,11 +1,13 @@ import { v4 as uuid } from 'uuid' import bcrypt from 'bcryptjs' +import CONSTANTS_REGISTRATION from './../../constants/registration' import createPasswordReset from './helpers/createPasswordReset' export default { Mutation: { requestPasswordReset: async (_parent, { email }, { driver }) => { - const nonce = uuid().substring(0, 6) + // TODO: why this is generated differntly from 'backend/src/schema/resolvers/helpers/generateNonce.js'? + const nonce = uuid().substring(0, CONSTANTS_REGISTRATION.NONCE_LENGTH) return createPasswordReset({ driver, nonce, email }) }, resetPassword: async (_parent, { email, nonce, newPassword }, { driver }) => { diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index 3e55ee6fd..bc2a48bc8 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -1,6 +1,7 @@ import Factory, { cleanDatabase } from '../../db/factories' import { gql } from '../../helpers/jest' import { getNeode, getDriver } from '../../db/neo4j' +import CONSTANTS_REGISTRATION from './../../constants/registration' import createPasswordReset from './helpers/createPasswordReset' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' @@ -109,7 +110,7 @@ describe('passwordReset', () => { const resets = await getAllPasswordResets() const [reset] = resets const { nonce } = reset.properties - expect(nonce).toHaveLength(6) + expect(nonce).toHaveLength(CONSTANTS_REGISTRATION.NONCE_LENGTH) }) }) }) @@ -118,7 +119,7 @@ describe('passwordReset', () => { describe('resetPassword', () => { const setup = async (options = {}) => { - const { email = 'user@example.org', issuedAt = new Date(), nonce = 'abcde' } = options + const { email = 'user@example.org', issuedAt = new Date(), nonce = '12345' } = options await createPasswordReset({ driver, email, issuedAt, nonce }) } @@ -148,7 +149,7 @@ describe('resetPassword', () => { describe('invalid email', () => { it('resolves to false', async () => { await setup() - variables = { ...variables, email: 'non-existent@example.org', nonce: 'abcde' } + variables = { ...variables, email: 'non-existent@example.org', nonce: '12345' } await expect(mutate({ mutation, variables })).resolves.toMatchObject({ data: { resetPassword: false }, }) @@ -162,7 +163,7 @@ describe('resetPassword', () => { describe('but invalid nonce', () => { beforeEach(() => { - variables = { ...variables, nonce: 'slkdjf' } + variables = { ...variables, nonce: 'slkdj' } }) it('resolves to false', async () => { @@ -177,7 +178,7 @@ describe('resetPassword', () => { beforeEach(() => { variables = { ...variables, - nonce: 'abcde', + nonce: '12345', } }) diff --git a/webapp/components/Registration/RegistrationSlideInvite.vue b/webapp/components/Registration/RegistrationSlideInvite.vue index 5a3aee777..f9ce15ee9 100644 --- a/webapp/components/Registration/RegistrationSlideInvite.vue +++ b/webapp/components/Registration/RegistrationSlideInvite.vue @@ -22,6 +22,7 @@