From ab287b7cf95daacd9e92dc552567ab3523562289 Mon Sep 17 00:00:00 2001 From: Daniele Guido Date: Mon, 16 Dec 2024 18:45:26 +0100 Subject: [PATCH] create class and service, add basic password validation (#475) * create class and service, add basic password validation * update user row with the new password --- .../change-password/change-password.class.ts | 54 +++++++++++++++++++ .../change-password.service.ts | 50 +++++++++++++++++ src/services/index.ts | 1 + 3 files changed, 105 insertions(+) create mode 100644 src/services/change-password/change-password.class.ts create mode 100644 src/services/change-password/change-password.service.ts diff --git a/src/services/change-password/change-password.class.ts b/src/services/change-password/change-password.class.ts new file mode 100644 index 00000000..a8595eea --- /dev/null +++ b/src/services/change-password/change-password.class.ts @@ -0,0 +1,54 @@ +import type { Sequelize } from 'sequelize' +import initDebug from 'debug' +import type { ImpressoApplication } from '../../types' +import User from '../../models/users.model' +import { NotAuthenticated } from '@feathersjs/errors' + +const debug = initDebug('impresso:services/change-password') + +export interface ServiceOptions { + app: ImpressoApplication + name: string +} + +export class Service { + app: ImpressoApplication + name: string + sequelizeClient?: Sequelize + constructor({ app, name }: ServiceOptions) { + this.app = app + this.name = name + + this.sequelizeClient = app.get('sequelizeClient') + debug('change-password initialized.') + } + + async create(data: any, params: { user: { id: number } }) { + if (!this.sequelizeClient) { + throw new Error('Sequelize client not available') + } + debug('change-password.create', params.user.id) + const userSequelize = User.sequelize(this.sequelizeClient) + const user = await userSequelize.findOne({ + where: { + id: params.user.id, + }, + }) + if (!user || user.get('id') !== params.user.id) { + throw new NotAuthenticated('User not found') + } + const updated = await userSequelize.update( + { + password: User.buildPassword({ password: data.sanitized.password }), + }, + { + // criteria + where: { id: params.user.id }, + } + ) + debug('change-password.create', updated) + return { + response: 'ok', + } + } +} diff --git a/src/services/change-password/change-password.service.ts b/src/services/change-password/change-password.service.ts new file mode 100644 index 00000000..221f69dc --- /dev/null +++ b/src/services/change-password/change-password.service.ts @@ -0,0 +1,50 @@ +import { Service } from './change-password.class' +import { ImpressoApplication } from '../../types' +import { HookContext, ServiceOptions } from '@feathersjs/feathers' +import { authenticateAround } from '../../hooks/authenticate' +import { REGEX_PASSWORD, validate } from '../../hooks/params' +import { BadRequest } from '@feathersjs/errors' + +export default (app: ImpressoApplication) => { + app.use( + '/change-password', + new Service({ + app, + name: 'change-password', + }), + { + events: [], + } as ServiceOptions + ) + const service = app.service('change-password') + service.hooks({ + around: { + all: [authenticateAround()], + }, + before: { + create: [ + // validate a JWT token using a regular expression + validate( + { + password: { + required: true, + regex: REGEX_PASSWORD, + }, + verifyPassword: { + required: true, + regex: REGEX_PASSWORD, + }, + }, + 'POST' + ), + (context: HookContext) => { + const { password, verifyPassword } = context.data + if (password !== verifyPassword) { + throw new BadRequest('Passwords do not match') + } + return context + }, + ], + }, + }) +} diff --git a/src/services/index.ts b/src/services/index.ts index ae412fce..23f986d3 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -61,6 +61,7 @@ const internalApiServices = [ 'subscriptions', 'text-reuse-connected-clusters', 'password-reset', + 'change-password', 'terms-of-use', 'user-requests', 'user-requests-reviews',