diff options
-rw-r--r-- | server/controllers/api/config.ts | 8 | ||||
-rw-r--r-- | server/helpers/signup.ts | 8 | ||||
-rw-r--r-- | server/middlewares/validators/users.ts | 17 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test/main.js | 11 | ||||
-rw-r--r-- | server/tests/plugins/filter-hooks.ts | 21 | ||||
-rw-r--r-- | 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' | |||
17 | import { CONFIG, reloadConfig } from '../../initializers/config' | 17 | import { CONFIG, reloadConfig } from '../../initializers/config' |
18 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 18 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
19 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' | 19 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' |
20 | import { Hooks } from '@server/lib/plugins/hooks' | ||
20 | 21 | ||
21 | const configRouter = express.Router() | 22 | const configRouter = express.Router() |
22 | 23 | ||
@@ -47,7 +48,12 @@ configRouter.delete('/custom', | |||
47 | let serverCommit: string | 48 | let serverCommit: string |
48 | 49 | ||
49 | async function getConfig (req: express.Request, res: express.Response) { | 50 | async function getConfig (req: express.Request, res: express.Response) { |
50 | const allowed = await isSignupAllowed() | 51 | const { allowed } = await Hooks.wrapPromiseFun( |
52 | isSignupAllowed, | ||
53 | {}, | ||
54 | 'filter:api.user.signup.allowed.result' | ||
55 | ) | ||
56 | |||
51 | const allowedForCurrentIP = isSignupAllowedForCurrentIP(req.ip) | 57 | const allowedForCurrentIP = isSignupAllowedForCurrentIP(req.ip) |
52 | const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) | 58 | const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) |
53 | 59 | ||
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' | |||
4 | 4 | ||
5 | const isCidr = require('is-cidr') | 5 | const isCidr = require('is-cidr') |
6 | 6 | ||
7 | async function isSignupAllowed () { | 7 | async function isSignupAllowed (): Promise<{ allowed: boolean, errorMessage?: string }> { |
8 | if (CONFIG.SIGNUP.ENABLED === false) { | 8 | if (CONFIG.SIGNUP.ENABLED === false) { |
9 | return false | 9 | return { allowed: false } |
10 | } | 10 | } |
11 | 11 | ||
12 | // No limit and signup is enabled | 12 | // No limit and signup is enabled |
13 | if (CONFIG.SIGNUP.LIMIT === -1) { | 13 | if (CONFIG.SIGNUP.LIMIT === -1) { |
14 | return true | 14 | return { allowed: true } |
15 | } | 15 | } |
16 | 16 | ||
17 | const totalUsers = await UserModel.countTotal() | 17 | const totalUsers = await UserModel.countTotal() |
18 | 18 | ||
19 | return totalUsers < CONFIG.SIGNUP.LIMIT | 19 | return { allowed: totalUsers < CONFIG.SIGNUP.LIMIT } |
20 | } | 20 | } |
21 | 21 | ||
22 | function isSignupAllowedForCurrentIP (ip: string) { | 22 | 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' | |||
35 | import { doesVideoExist } from '../../helpers/middlewares' | 35 | import { doesVideoExist } from '../../helpers/middlewares' |
36 | import { UserRole } from '../../../shared/models/users' | 36 | import { UserRole } from '../../../shared/models/users' |
37 | import { MUserDefault } from '@server/typings/models' | 37 | import { MUserDefault } from '@server/typings/models' |
38 | import { Hooks } from '@server/lib/plugins/hooks' | ||
39 | import { isLocalVideoAccepted } from '@server/lib/moderation' | ||
38 | 40 | ||
39 | const usersAddValidator = [ | 41 | const usersAddValidator = [ |
40 | body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), | 42 | body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), |
@@ -280,10 +282,19 @@ const usersVideoRatingValidator = [ | |||
280 | 282 | ||
281 | const ensureUserRegistrationAllowed = [ | 283 | const ensureUserRegistrationAllowed = [ |
282 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 284 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
283 | const allowed = await isSignupAllowed() | 285 | const allowedParams = { |
284 | if (allowed === false) { | 286 | body: req.body |
287 | } | ||
288 | |||
289 | const allowedResult = await Hooks.wrapPromiseFun( | ||
290 | isSignupAllowed, | ||
291 | allowedParams, | ||
292 | 'filter:api.user.signup.allowed.result' | ||
293 | ) | ||
294 | |||
295 | if (allowedResult.allowed === false) { | ||
285 | return res.status(403) | 296 | return res.status(403) |
286 | .json({ error: 'User registration is not enabled or user limit is reached.' }) | 297 | .json({ error: allowedResult.errorMessage || 'User registration is not enabled or user limit is reached.' }) |
287 | } | 298 | } |
288 | 299 | ||
289 | return next() | 300 | 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 | |||
86 | return false | 86 | return false |
87 | } | 87 | } |
88 | }) | 88 | }) |
89 | |||
90 | registerHook({ | ||
91 | target: 'filter:api.user.signup.allowed.result', | ||
92 | handler: (result, params) => { | ||
93 | if (params && params.body.email.includes('jma')) { | ||
94 | return { allowed: false, errorMessage: 'No jma' } | ||
95 | } | ||
96 | |||
97 | return result | ||
98 | } | ||
99 | }) | ||
89 | } | 100 | } |
90 | 101 | ||
91 | async function unregister () { | 102 | 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 { | |||
28 | getVideoWithToken, | 28 | getVideoWithToken, |
29 | setDefaultVideoChannel, | 29 | setDefaultVideoChannel, |
30 | waitJobs, | 30 | waitJobs, |
31 | doubleFollow | 31 | doubleFollow, getConfig, registerUser |
32 | } from '../../../shared/extra-utils' | 32 | } from '../../../shared/extra-utils' |
33 | import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' | 33 | import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' |
34 | import { VideoDetails } from '../../../shared/models/videos' | 34 | import { VideoDetails } from '../../../shared/models/videos' |
35 | import { getYoutubeVideoUrl, importVideo } from '../../../shared/extra-utils/videos/video-imports' | 35 | import { getYoutubeVideoUrl, importVideo } from '../../../shared/extra-utils/videos/video-imports' |
36 | import { ServerConfig } from '@shared/models' | ||
36 | 37 | ||
37 | const expect = chai.expect | 38 | const expect = chai.expect |
38 | 39 | ||
@@ -187,6 +188,24 @@ describe('Test plugin filter hooks', function () { | |||
187 | }) | 188 | }) |
188 | }) | 189 | }) |
189 | 190 | ||
191 | describe('Should run filter:api.user.signup.allowed.result', function () { | ||
192 | |||
193 | it('Should run on config endpoint', async function () { | ||
194 | const res = await getConfig(servers[0].url) | ||
195 | expect((res.body as ServerConfig).signup.allowed).to.be.true | ||
196 | }) | ||
197 | |||
198 | it('Should allow a signup', async function () { | ||
199 | await registerUser(servers[0].url, 'john', 'password') | ||
200 | }) | ||
201 | |||
202 | it('Should not allow a signup', async function () { | ||
203 | const res = await registerUser(servers[0].url, 'jma', 'password', 403) | ||
204 | |||
205 | expect(res.body.error).to.equal('No jma') | ||
206 | }) | ||
207 | }) | ||
208 | |||
190 | after(async function () { | 209 | after(async function () { |
191 | await cleanupTests(servers) | 210 | await cleanupTests(servers) |
192 | }) | 211 | }) |
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 = { | |||
29 | 29 | ||
30 | // Filter result used to check if we need to auto blacklist a video | 30 | // Filter result used to check if we need to auto blacklist a video |
31 | // (fired when a local or remote video is created or updated) | 31 | // (fired when a local or remote video is created or updated) |
32 | 'filter:video.auto-blacklist.result': true | 32 | 'filter:video.auto-blacklist.result': true, |
33 | |||
34 | // Filter result used to check if a user can register on the instance | ||
35 | 'filter:api.user.signup.allowed.result': true | ||
33 | } | 36 | } |
34 | 37 | ||
35 | export type ServerFilterHookName = keyof typeof serverFilterHookObject | 38 | export type ServerFilterHookName = keyof typeof serverFilterHookObject |