aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/users/index.ts25
-rw-r--r--server/middlewares/oauth.ts1
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js10
-rw-r--r--server/tests/plugins/action-hooks.ts125
-rw-r--r--shared/extra-utils/users/users.ts3
-rw-r--r--shared/models/plugins/server-hook.model.ts18
6 files changed, 141 insertions, 41 deletions
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index 27351c1a9..b960e80c1 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -49,6 +49,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
49import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' 49import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
50import { UserRegister } from '../../../../shared/models/users/user-register.model' 50import { UserRegister } from '../../../../shared/models/users/user-register.model'
51import { MUser, MUserAccountDefault } from '@server/typings/models' 51import { MUser, MUserAccountDefault } from '@server/typings/models'
52import { Hooks } from '@server/lib/plugins/hooks'
52 53
53const auditLogger = auditLoggerFactory('users') 54const auditLogger = auditLoggerFactory('users')
54 55
@@ -172,7 +173,7 @@ usersRouter.post('/:id/verify-email',
172usersRouter.post('/token', 173usersRouter.post('/token',
173 loginRateLimiter, 174 loginRateLimiter,
174 token, 175 token,
175 success 176 tokenSuccess
176) 177)
177// TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route 178// TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route
178 179
@@ -198,11 +199,13 @@ async function createUser (req: express.Request, res: express.Response) {
198 adminFlags: body.adminFlags || UserAdminFlag.NONE 199 adminFlags: body.adminFlags || UserAdminFlag.NONE
199 }) as MUser 200 }) as MUser
200 201
201 const { user, account } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) 202 const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate })
202 203
203 auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) 204 auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
204 logger.info('User %s with its channel and account created.', body.username) 205 logger.info('User %s with its channel and account created.', body.username)
205 206
207 Hooks.runAction('action:api.user.created', { body, user, account, videoChannel })
208
206 return res.json({ 209 return res.json({
207 user: { 210 user: {
208 id: user.id, 211 id: user.id,
@@ -228,7 +231,7 @@ async function registerUser (req: express.Request, res: express.Response) {
228 emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null 231 emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
229 }) 232 })
230 233
231 const { user } = await createUserAccountAndChannelAndPlaylist({ 234 const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({
232 userToCreate: userToCreate, 235 userToCreate: userToCreate,
233 userDisplayName: body.displayName || undefined, 236 userDisplayName: body.displayName || undefined,
234 channelNames: body.channel 237 channelNames: body.channel
@@ -243,6 +246,8 @@ async function registerUser (req: express.Request, res: express.Response) {
243 246
244 Notifier.Instance.notifyOnNewUserRegistration(user) 247 Notifier.Instance.notifyOnNewUserRegistration(user)
245 248
249 Hooks.runAction('action:api.user.registered', { body, user, account, videoChannel })
250
246 return res.type('json').status(204).end() 251 return res.type('json').status(204).end()
247} 252}
248 253
@@ -251,6 +256,8 @@ async function unblockUser (req: express.Request, res: express.Response) {
251 256
252 await changeUserBlock(res, user, false) 257 await changeUserBlock(res, user, false)
253 258
259 Hooks.runAction('action:api.user.unblocked', { user })
260
254 return res.status(204).end() 261 return res.status(204).end()
255} 262}
256 263
@@ -260,6 +267,8 @@ async function blockUser (req: express.Request, res: express.Response) {
260 267
261 await changeUserBlock(res, user, true, reason) 268 await changeUserBlock(res, user, true, reason)
262 269
270 Hooks.runAction('action:api.user.blocked', { user })
271
263 return res.status(204).end() 272 return res.status(204).end()
264} 273}
265 274
@@ -286,6 +295,8 @@ async function removeUser (req: express.Request, res: express.Response) {
286 295
287 auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) 296 auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
288 297
298 Hooks.runAction('action:api.user.deleted', { user })
299
289 return res.sendStatus(204) 300 return res.sendStatus(204)
290} 301}
291 302
@@ -310,6 +321,8 @@ async function updateUser (req: express.Request, res: express.Response) {
310 321
311 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) 322 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
312 323
324 Hooks.runAction('action:api.user.updated', { user })
325
313 // Don't need to send this update to followers, these attributes are not federated 326 // Don't need to send this update to followers, these attributes are not federated
314 327
315 return res.sendStatus(204) 328 return res.sendStatus(204)
@@ -356,8 +369,10 @@ async function verifyUserEmail (req: express.Request, res: express.Response) {
356 return res.status(204).end() 369 return res.status(204).end()
357} 370}
358 371
359function success (req: express.Request, res: express.Response) { 372function tokenSuccess (req: express.Request) {
360 res.end() 373 const username = req.body.username
374
375 Hooks.runAction('action:api.user.oauth2-got-token', { username, ip: req.ip })
361} 376}
362 377
363async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) { 378async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) {
diff --git a/server/middlewares/oauth.ts b/server/middlewares/oauth.ts
index bb90dac47..749f5cccd 100644
--- a/server/middlewares/oauth.ts
+++ b/server/middlewares/oauth.ts
@@ -9,6 +9,7 @@ const oAuthServer = new OAuthServer({
9 useErrorHandler: true, 9 useErrorHandler: true,
10 accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN, 10 accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN,
11 refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN, 11 refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN,
12 continueMiddleware: true,
12 model: require('../lib/oauth-model') 13 model: require('../lib/oauth-model')
13}) 14})
14 15
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index 055884d29..69796ab07 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -9,7 +9,15 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
9 9
10 'action:api.video-thread.created', 10 'action:api.video-thread.created',
11 'action:api.video-comment-reply.created', 11 'action:api.video-comment-reply.created',
12 'action:api.video-comment.deleted' 12 'action:api.video-comment.deleted',
13
14 'action:api.user.blocked',
15 'action:api.user.unblocked',
16 'action:api.user.registered',
17 'action:api.user.created',
18 'action:api.user.deleted',
19 'action:api.user.updated',
20 'action:api.user.oauth2-got-token'
13 ] 21 ]
14 22
15 for (const h of actionHooks) { 23 for (const h of actionHooks) {
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts
index e28732cac..510ec3151 100644
--- a/server/tests/plugins/action-hooks.ts
+++ b/server/tests/plugins/action-hooks.ts
@@ -5,19 +5,26 @@ import 'mocha'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 flushAndRunMultipleServers, 7 flushAndRunMultipleServers,
8 flushAndRunServer, killallServers, reRunServer, 8 killallServers,
9 reRunServer,
9 ServerInfo, 10 ServerInfo,
10 waitUntilLog 11 waitUntilLog
11} from '../../../shared/extra-utils/server/servers' 12} from '../../../shared/extra-utils/server/servers'
12import { 13import {
13 addVideoCommentReply, 14 addVideoCommentReply,
14 addVideoCommentThread, deleteVideoComment, 15 addVideoCommentThread,
16 blockUser,
17 createUser,
18 deleteVideoComment,
15 getPluginTestPath, 19 getPluginTestPath,
16 installPlugin, removeVideo, 20 installPlugin, login,
21 registerUser, removeUser,
17 setAccessTokensToServers, 22 setAccessTokensToServers,
23 unblockUser, updateUser,
18 updateVideo, 24 updateVideo,
19 uploadVideo, 25 uploadVideo,
20 viewVideo 26 viewVideo,
27 userLogin
21} from '../../../shared/extra-utils' 28} from '../../../shared/extra-utils'
22 29
23const expect = chai.expect 30const expect = chai.expect
@@ -48,52 +55,104 @@ describe('Test plugin action hooks', function () {
48 await reRunServer(servers[0]) 55 await reRunServer(servers[0])
49 }) 56 })
50 57
51 it('Should run action:application.listening', async function () { 58 describe('Application hooks', function () {
52 await checkHook('action:application.listening') 59 it('Should run action:application.listening', async function () {
60 await checkHook('action:application.listening')
61 })
53 }) 62 })
54 63
55 it('Should run action:api.video.uploaded', async function () { 64 describe('Videos hooks', function () {
56 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 65 it('Should run action:api.video.uploaded', async function () {
57 videoUUID = res.body.video.uuid 66 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' })
67 videoUUID = res.body.video.uuid
58 68
59 await checkHook('action:api.video.uploaded') 69 await checkHook('action:api.video.uploaded')
60 }) 70 })
61 71
62 it('Should run action:api.video.updated', async function () { 72 it('Should run action:api.video.updated', async function () {
63 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video updated' }) 73 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video updated' })
64 74
65 await checkHook('action:api.video.updated') 75 await checkHook('action:api.video.updated')
66 }) 76 })
67 77
68 it('Should run action:api.video.viewed', async function () { 78 it('Should run action:api.video.viewed', async function () {
69 await viewVideo(servers[0].url, videoUUID) 79 await viewVideo(servers[0].url, videoUUID)
70 80
71 await checkHook('action:api.video.viewed') 81 await checkHook('action:api.video.viewed')
82 })
72 }) 83 })
73 84
74 it('Should run action:api.video-thread.created', async function () { 85 describe('Comments hooks', function () {
75 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') 86 it('Should run action:api.video-thread.created', async function () {
76 threadId = res.body.comment.id 87 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread')
88 threadId = res.body.comment.id
77 89
78 await checkHook('action:api.video-thread.created') 90 await checkHook('action:api.video-thread.created')
79 }) 91 })
80 92
81 it('Should run action:api.video-comment-reply.created', async function () { 93 it('Should run action:api.video-comment-reply.created', async function () {
82 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'reply') 94 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'reply')
83 95
84 await checkHook('action:api.video-comment-reply.created') 96 await checkHook('action:api.video-comment-reply.created')
85 }) 97 })
86 98
87 it('Should run action:api.video-comment.deleted', async function () { 99 it('Should run action:api.video-comment.deleted', async function () {
88 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 100 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId)
89 101
90 await checkHook('action:api.video-comment.deleted') 102 await checkHook('action:api.video-comment.deleted')
103 })
91 }) 104 })
92 105
93 it('Should run action:api.video.deleted', async function () { 106 describe('Users hooks', function () {
94 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) 107 let userId: number
108
109 it('Should run action:api.user.registered', async function () {
110 await registerUser(servers[0].url, 'registered_user', 'super_password')
95 111
96 await checkHook('action:api.video.deleted') 112 await checkHook('action:api.user.registered')
113 })
114
115 it('Should run action:api.user.created', async function () {
116 const res = await createUser({
117 url: servers[0].url,
118 accessToken: servers[0].accessToken,
119 username: 'created_user',
120 password: 'super_password'
121 })
122 userId = res.body.user.id
123
124 await checkHook('action:api.user.created')
125 })
126
127 it('Should run action:api.user.oauth2-got-token', async function () {
128 await userLogin(servers[0], { username: 'created_user', password: 'super_password' })
129
130 await checkHook('action:api.user.oauth2-got-token')
131 })
132
133 it('Should run action:api.user.blocked', async function () {
134 await blockUser(servers[0].url, userId, servers[0].accessToken)
135
136 await checkHook('action:api.user.blocked')
137 })
138
139 it('Should run action:api.user.unblocked', async function () {
140 await unblockUser(servers[0].url, userId, servers[0].accessToken)
141
142 await checkHook('action:api.user.unblocked')
143 })
144
145 it('Should run action:api.user.updated', async function () {
146 await updateUser({ url: servers[0].url, accessToken: servers[0].accessToken, userId, videoQuota: 50 })
147
148 await checkHook('action:api.user.updated')
149 })
150
151 it('Should run action:api.user.deleted', async function () {
152 await removeUser(servers[0].url, userId, servers[0].accessToken)
153
154 await checkHook('action:api.user.deleted')
155 })
97 }) 156 })
98 157
99 after(async function () { 158 after(async function () {
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts
index 9959fd074..2fe0e55c2 100644
--- a/shared/extra-utils/users/users.ts
+++ b/shared/extra-utils/users/users.ts
@@ -8,7 +8,8 @@ import { userLogin } from './login'
8import { UserUpdateMe } from '../../models/users' 8import { UserUpdateMe } from '../../models/users'
9import { omit } from 'lodash' 9import { omit } from 'lodash'
10 10
11type CreateUserArgs = { url: string, 11type CreateUserArgs = {
12 url: string,
12 accessToken: string, 13 accessToken: string,
13 username: string, 14 username: string,
14 password: string, 15 password: string,
diff --git a/shared/models/plugins/server-hook.model.ts b/shared/models/plugins/server-hook.model.ts
index 41ee28097..80ecd9e24 100644
--- a/shared/models/plugins/server-hook.model.ts
+++ b/shared/models/plugins/server-hook.model.ts
@@ -55,7 +55,23 @@ export const serverActionHookObject = {
55 // Fired when a reply to a thread is created 55 // Fired when a reply to a thread is created
56 'action:api.video-comment-reply.created': true, 56 'action:api.video-comment-reply.created': true,
57 // Fired when a comment (thread or reply) is deleted 57 // Fired when a comment (thread or reply) is deleted
58 'action:api.video-comment.deleted': true 58 'action:api.video-comment.deleted': true,
59
60 // Fired when a user is blocked (banned)
61 'action:api.user.blocked': true,
62 // Fired when a user is unblocked (unbanned)
63 'action:api.user.unblocked': true,
64 // Fired when a user registered on the instance
65 'action:api.user.registered': true,
66 // Fired when an admin/moderator created a user
67 'action:api.user.created': true,
68 // Fired when a user is removed by an admin/moderator
69 'action:api.user.deleted': true,
70 // Fired when a user is updated by an admin/moderator
71 'action:api.user.updated': true,
72
73 // Fired when a user got a new oauth2 token
74 'action:api.user.oauth2-got-token': true
59} 75}
60 76
61export type ServerActionHookName = keyof typeof serverActionHookObject 77export type ServerActionHookName = keyof typeof serverActionHookObject