From 4ce7eb71ba28a563336c07d10c182ff89461c72b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 25 Oct 2019 13:54:32 +0200 Subject: [PATCH] Add plugin hook on registration --- server/controllers/api/config.ts | 8 ++++++- server/helpers/signup.ts | 8 +++---- server/middlewares/validators/users.ts | 17 ++++++++++++--- .../fixtures/peertube-plugin-test/main.js | 11 ++++++++++ server/tests/plugins/filter-hooks.ts | 21 ++++++++++++++++++- shared/models/plugins/server-hook.model.ts | 5 ++++- 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 39a124fc5..113c1e9db 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -17,6 +17,7 @@ import { objectConverter } from '../../helpers/core-utils' import { CONFIG, reloadConfig } from '../../initializers/config' import { PluginManager } from '../../lib/plugins/plugin-manager' import { getThemeOrDefault } from '../../lib/plugins/theme-utils' +import { Hooks } from '@server/lib/plugins/hooks' const configRouter = express.Router() @@ -47,7 +48,12 @@ configRouter.delete('/custom', let serverCommit: string async function getConfig (req: express.Request, res: express.Response) { - const allowed = await isSignupAllowed() + const { allowed } = await Hooks.wrapPromiseFun( + isSignupAllowed, + {}, + 'filter:api.user.signup.allowed.result' + ) + const allowedForCurrentIP = isSignupAllowedForCurrentIP(req.ip) const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) diff --git a/server/helpers/signup.ts b/server/helpers/signup.ts index 5eb56b3cf..7c73f7c5c 100644 --- a/server/helpers/signup.ts +++ b/server/helpers/signup.ts @@ -4,19 +4,19 @@ import { CONFIG } from '../initializers/config' const isCidr = require('is-cidr') -async function isSignupAllowed () { +async function isSignupAllowed (): Promise<{ allowed: boolean, errorMessage?: string }> { if (CONFIG.SIGNUP.ENABLED === false) { - return false + return { allowed: false } } // No limit and signup is enabled if (CONFIG.SIGNUP.LIMIT === -1) { - return true + return { allowed: true } } const totalUsers = await UserModel.countTotal() - return totalUsers < CONFIG.SIGNUP.LIMIT + return { allowed: totalUsers < CONFIG.SIGNUP.LIMIT } } function isSignupAllowedForCurrentIP (ip: string) { diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 804d1410e..8615de406 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -35,6 +35,8 @@ import { isThemeRegistered } from '../../lib/plugins/theme-utils' import { doesVideoExist } from '../../helpers/middlewares' import { UserRole } from '../../../shared/models/users' import { MUserDefault } from '@server/typings/models' +import { Hooks } from '@server/lib/plugins/hooks' +import { isLocalVideoAccepted } from '@server/lib/moderation' const usersAddValidator = [ body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), @@ -280,10 +282,19 @@ const usersVideoRatingValidator = [ const ensureUserRegistrationAllowed = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { - const allowed = await isSignupAllowed() - if (allowed === false) { + const allowedParams = { + body: req.body + } + + const allowedResult = await Hooks.wrapPromiseFun( + isSignupAllowed, + allowedParams, + 'filter:api.user.signup.allowed.result' + ) + + if (allowedResult.allowed === false) { return res.status(403) - .json({ error: 'User registration is not enabled or user limit is reached.' }) + .json({ error: allowedResult.errorMessage || 'User registration is not enabled or user limit is reached.' }) } return next() diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js index 7c53f6afe..055884d29 100644 --- a/server/tests/fixtures/peertube-plugin-test/main.js +++ b/server/tests/fixtures/peertube-plugin-test/main.js @@ -86,6 +86,17 @@ async function register ({ registerHook, registerSetting, settingsManager, stora return false } }) + + registerHook({ + target: 'filter:api.user.signup.allowed.result', + handler: (result, params) => { + if (params && params.body.email.includes('jma')) { + return { allowed: false, errorMessage: 'No jma' } + } + + return result + } + }) } async function unregister () { diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index ec0679b04..b2436fb9e 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts @@ -28,11 +28,12 @@ import { getVideoWithToken, setDefaultVideoChannel, waitJobs, - doubleFollow + doubleFollow, getConfig, registerUser } from '../../../shared/extra-utils' import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' import { VideoDetails } from '../../../shared/models/videos' import { getYoutubeVideoUrl, importVideo } from '../../../shared/extra-utils/videos/video-imports' +import { ServerConfig } from '@shared/models' const expect = chai.expect @@ -187,6 +188,24 @@ describe('Test plugin filter hooks', function () { }) }) + describe('Should run filter:api.user.signup.allowed.result', function () { + + it('Should run on config endpoint', async function () { + const res = await getConfig(servers[0].url) + expect((res.body as ServerConfig).signup.allowed).to.be.true + }) + + it('Should allow a signup', async function () { + await registerUser(servers[0].url, 'john', 'password') + }) + + it('Should not allow a signup', async function () { + const res = await registerUser(servers[0].url, 'jma', 'password', 403) + + expect(res.body.error).to.equal('No jma') + }) + }) + after(async function () { await cleanupTests(servers) }) diff --git a/shared/models/plugins/server-hook.model.ts b/shared/models/plugins/server-hook.model.ts index 32c7f4688..41ee28097 100644 --- a/shared/models/plugins/server-hook.model.ts +++ b/shared/models/plugins/server-hook.model.ts @@ -29,7 +29,10 @@ export const serverFilterHookObject = { // Filter result used to check if we need to auto blacklist a video // (fired when a local or remote video is created or updated) - 'filter:video.auto-blacklist.result': true + 'filter:video.auto-blacklist.result': true, + + // Filter result used to check if a user can register on the instance + 'filter:api.user.signup.allowed.result': true } export type ServerFilterHookName = keyof typeof serverFilterHookObject