From a875ee9f432b4e6cf2b40f97ae2a20e6c88ab9e8 Mon Sep 17 00:00:00 2001 From: kontrollanten <6680299+kontrollanten@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:34:28 +0100 Subject: [PATCH] feat: plugin support to filter email addresses Add support for plugins to filter user email addresses. --- .../models/src/plugins/server/server-hook.model.ts | 6 +++++- server/core/lib/auth/oauth.ts | 10 ++++++++-- .../validators/users/user-email-verification.ts | 11 ++++++++--- server/core/middlewares/validators/users/users.ts | 9 +++++++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/models/src/plugins/server/server-hook.model.ts b/packages/models/src/plugins/server/server-hook.model.ts index f2024d0c2..5cb7ef176 100644 --- a/packages/models/src/plugins/server/server-hook.model.ts +++ b/packages/models/src/plugins/server/server-hook.model.ts @@ -140,7 +140,11 @@ export const serverFilterHookObject = { // Peertube >= 5.2 'filter:feed.podcast.video.create-custom-tags.result': true, // Peertube >= 6.1 - 'filter:api.user.me.get.result': true + 'filter:api.user.me.get.result': true, + // Peertube >= 7.1 + 'filter:api.login.params': true, + 'filter:api.email-verification.ask-send-email.params': true, + 'filter:api.users.reset-password.params': true } export type ServerFilterHookName = keyof typeof serverFilterHookObject diff --git a/server/core/lib/auth/oauth.ts b/server/core/lib/auth/oauth.ts index d91bfc37a..4f5b60fd1 100644 --- a/server/core/lib/auth/oauth.ts +++ b/server/core/lib/auth/oauth.ts @@ -17,6 +17,7 @@ import { sha1 } from '@peertube/peertube-node-utils' import { HttpStatusCode, ServerErrorCode, UserRegistrationState } from '@peertube/peertube-models' import { OTP } from '../../initializers/constants.js' import { BypassLogin, getAccessToken, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model.js' +import { Hooks } from '../plugins/hooks.js' class MissingTwoFactorError extends Error { code = HttpStatusCode.UNAUTHORIZED_401 @@ -135,7 +136,12 @@ async function handlePasswordGrant (options: { client: MOAuthClient bypassLogin?: BypassLogin }) { - const { request, client, bypassLogin } = options + const { request, client } = options + const { bypassLogin, usernameOrEmail, password } = await Hooks.wrapObject({ + bypassLogin: options.bypassLogin, + usernameOrEmail: request.body.username, + password: request.body.password + }, 'filter:api.login.params') if (!request.body.username) { throw new InvalidRequestError('Missing parameter: `username`') @@ -145,7 +151,7 @@ async function handlePasswordGrant (options: { throw new InvalidRequestError('Missing parameter: `password`') } - const user = await getUser(request.body.username, request.body.password, bypassLogin) + const user = await getUser(usernameOrEmail, password, bypassLogin) if (!user) { const registration = await UserRegistrationModel.loadByEmailOrUsername(request.body.username) diff --git a/server/core/middlewares/validators/users/user-email-verification.ts b/server/core/middlewares/validators/users/user-email-verification.ts index 38f5e6586..1eb0de0e1 100644 --- a/server/core/middlewares/validators/users/user-email-verification.ts +++ b/server/core/middlewares/validators/users/user-email-verification.ts @@ -6,6 +6,7 @@ import { logger } from '../../../helpers/logger.js' import { Redis } from '../../../lib/redis.js' import { areValidationErrors, checkUserEmailExist, checkUserIdExist } from '../shared/index.js' import { checkRegistrationEmailExist, checkRegistrationIdExist } from './shared/user-registrations.js' +import { Hooks } from '@server/lib/plugins/hooks.js' const usersAskSendVerifyEmailValidator = [ body('email').isEmail().not().isEmpty().withMessage('Should have a valid email'), @@ -13,13 +14,17 @@ const usersAskSendVerifyEmailValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return + const { email } = await Hooks.wrapObject({ + email: req.body.email + }, 'filter:api.email-verification.ask-send-email.params') + const [ userExists, registrationExists ] = await Promise.all([ - checkUserEmailExist(req.body.email, res, false), - checkRegistrationEmailExist(req.body.email, res, false) + checkUserEmailExist(email, res, false), + checkRegistrationEmailExist(email, res, false) ]) if (!userExists && !registrationExists) { - logger.debug('User or registration with email %s does not exist (asking verify email).', req.body.email) + logger.debug('User or registration with email %s does not exist (asking verify email).', email) // Do not leak our emails return res.status(HttpStatusCode.NO_CONTENT_204).end() } diff --git a/server/core/middlewares/validators/users/users.ts b/server/core/middlewares/validators/users/users.ts index 030b5043c..40dc27841 100644 --- a/server/core/middlewares/validators/users/users.ts +++ b/server/core/middlewares/validators/users/users.ts @@ -39,6 +39,7 @@ import { doesVideoExist, isValidVideoIdParam } from '../shared/index.js' +import { Hooks } from '@server/lib/plugins/hooks.js' export const usersListValidator = [ query('blocked') @@ -334,9 +335,13 @@ export const usersAskResetPasswordValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return - const exists = await checkUserEmailExist(req.body.email, res, false) + const { email } = await Hooks.wrapObject({ + email: req.body.email + }, 'filter:api.users.reset-password.params') + + const exists = await checkUserEmailExist(email, res, false) if (!exists) { - logger.debug('User with email %s does not exist (asking reset password).', req.body.email) + logger.debug('User with email %s does not exist (asking reset password).', email) // Do not leak our emails return res.status(HttpStatusCode.NO_CONTENT_204).end() }