Add action hooks to user routes

This commit is contained in:
Chocobozzz 2019-12-06 15:59:12 +01:00
parent 349be1eaa9
commit 6f3fe96f40
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 141 additions and 41 deletions

View File

@ -49,6 +49,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
import { UserRegister } from '../../../../shared/models/users/user-register.model' import { UserRegister } from '../../../../shared/models/users/user-register.model'
import { MUser, MUserAccountDefault } from '@server/typings/models' import { MUser, MUserAccountDefault } from '@server/typings/models'
import { Hooks } from '@server/lib/plugins/hooks'
const auditLogger = auditLoggerFactory('users') const auditLogger = auditLoggerFactory('users')
@ -172,7 +173,7 @@ usersRouter.post('/:id/verify-email',
usersRouter.post('/token', usersRouter.post('/token',
loginRateLimiter, loginRateLimiter,
token, token,
success tokenSuccess
) )
// TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route
@ -198,11 +199,13 @@ async function createUser (req: express.Request, res: express.Response) {
adminFlags: body.adminFlags || UserAdminFlag.NONE adminFlags: body.adminFlags || UserAdminFlag.NONE
}) as MUser }) as MUser
const { user, account } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate })
auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
logger.info('User %s with its channel and account created.', body.username) logger.info('User %s with its channel and account created.', body.username)
Hooks.runAction('action:api.user.created', { body, user, account, videoChannel })
return res.json({ return res.json({
user: { user: {
id: user.id, id: user.id,
@ -228,7 +231,7 @@ async function registerUser (req: express.Request, res: express.Response) {
emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
}) })
const { user } = await createUserAccountAndChannelAndPlaylist({ const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({
userToCreate: userToCreate, userToCreate: userToCreate,
userDisplayName: body.displayName || undefined, userDisplayName: body.displayName || undefined,
channelNames: body.channel channelNames: body.channel
@ -243,6 +246,8 @@ async function registerUser (req: express.Request, res: express.Response) {
Notifier.Instance.notifyOnNewUserRegistration(user) Notifier.Instance.notifyOnNewUserRegistration(user)
Hooks.runAction('action:api.user.registered', { body, user, account, videoChannel })
return res.type('json').status(204).end() return res.type('json').status(204).end()
} }
@ -251,6 +256,8 @@ async function unblockUser (req: express.Request, res: express.Response) {
await changeUserBlock(res, user, false) await changeUserBlock(res, user, false)
Hooks.runAction('action:api.user.unblocked', { user })
return res.status(204).end() return res.status(204).end()
} }
@ -260,6 +267,8 @@ async function blockUser (req: express.Request, res: express.Response) {
await changeUserBlock(res, user, true, reason) await changeUserBlock(res, user, true, reason)
Hooks.runAction('action:api.user.blocked', { user })
return res.status(204).end() return res.status(204).end()
} }
@ -286,6 +295,8 @@ async function removeUser (req: express.Request, res: express.Response) {
auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
Hooks.runAction('action:api.user.deleted', { user })
return res.sendStatus(204) return res.sendStatus(204)
} }
@ -310,6 +321,8 @@ async function updateUser (req: express.Request, res: express.Response) {
auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
Hooks.runAction('action:api.user.updated', { user })
// Don't need to send this update to followers, these attributes are not federated // Don't need to send this update to followers, these attributes are not federated
return res.sendStatus(204) return res.sendStatus(204)
@ -356,8 +369,10 @@ async function verifyUserEmail (req: express.Request, res: express.Response) {
return res.status(204).end() return res.status(204).end()
} }
function success (req: express.Request, res: express.Response) { function tokenSuccess (req: express.Request) {
res.end() const username = req.body.username
Hooks.runAction('action:api.user.oauth2-got-token', { username, ip: req.ip })
} }
async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) { async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) {

View File

@ -9,6 +9,7 @@ const oAuthServer = new OAuthServer({
useErrorHandler: true, useErrorHandler: true,
accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN, accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN,
refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN, refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN,
continueMiddleware: true,
model: require('../lib/oauth-model') model: require('../lib/oauth-model')
}) })

View File

@ -9,7 +9,15 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
'action:api.video-thread.created', 'action:api.video-thread.created',
'action:api.video-comment-reply.created', 'action:api.video-comment-reply.created',
'action:api.video-comment.deleted' 'action:api.video-comment.deleted',
'action:api.user.blocked',
'action:api.user.unblocked',
'action:api.user.registered',
'action:api.user.created',
'action:api.user.deleted',
'action:api.user.updated',
'action:api.user.oauth2-got-token'
] ]
for (const h of actionHooks) { for (const h of actionHooks) {

View File

@ -5,19 +5,26 @@ import 'mocha'
import { import {
cleanupTests, cleanupTests,
flushAndRunMultipleServers, flushAndRunMultipleServers,
flushAndRunServer, killallServers, reRunServer, killallServers,
reRunServer,
ServerInfo, ServerInfo,
waitUntilLog waitUntilLog
} from '../../../shared/extra-utils/server/servers' } from '../../../shared/extra-utils/server/servers'
import { import {
addVideoCommentReply, addVideoCommentReply,
addVideoCommentThread, deleteVideoComment, addVideoCommentThread,
blockUser,
createUser,
deleteVideoComment,
getPluginTestPath, getPluginTestPath,
installPlugin, removeVideo, installPlugin, login,
registerUser, removeUser,
setAccessTokensToServers, setAccessTokensToServers,
unblockUser, updateUser,
updateVideo, updateVideo,
uploadVideo, uploadVideo,
viewVideo viewVideo,
userLogin
} from '../../../shared/extra-utils' } from '../../../shared/extra-utils'
const expect = chai.expect const expect = chai.expect
@ -48,10 +55,13 @@ describe('Test plugin action hooks', function () {
await reRunServer(servers[0]) await reRunServer(servers[0])
}) })
describe('Application hooks', function () {
it('Should run action:application.listening', async function () { it('Should run action:application.listening', async function () {
await checkHook('action:application.listening') await checkHook('action:application.listening')
}) })
})
describe('Videos hooks', function () {
it('Should run action:api.video.uploaded', async function () { it('Should run action:api.video.uploaded', async function () {
const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' })
videoUUID = res.body.video.uuid videoUUID = res.body.video.uuid
@ -70,7 +80,9 @@ describe('Test plugin action hooks', function () {
await checkHook('action:api.video.viewed') await checkHook('action:api.video.viewed')
}) })
})
describe('Comments hooks', function () {
it('Should run action:api.video-thread.created', async function () { it('Should run action:api.video-thread.created', async function () {
const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread')
threadId = res.body.comment.id threadId = res.body.comment.id
@ -89,11 +101,58 @@ describe('Test plugin action hooks', function () {
await checkHook('action:api.video-comment.deleted') await checkHook('action:api.video-comment.deleted')
}) })
})
it('Should run action:api.video.deleted', async function () { describe('Users hooks', function () {
await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) let userId: number
await checkHook('action:api.video.deleted') it('Should run action:api.user.registered', async function () {
await registerUser(servers[0].url, 'registered_user', 'super_password')
await checkHook('action:api.user.registered')
})
it('Should run action:api.user.created', async function () {
const res = await createUser({
url: servers[0].url,
accessToken: servers[0].accessToken,
username: 'created_user',
password: 'super_password'
})
userId = res.body.user.id
await checkHook('action:api.user.created')
})
it('Should run action:api.user.oauth2-got-token', async function () {
await userLogin(servers[0], { username: 'created_user', password: 'super_password' })
await checkHook('action:api.user.oauth2-got-token')
})
it('Should run action:api.user.blocked', async function () {
await blockUser(servers[0].url, userId, servers[0].accessToken)
await checkHook('action:api.user.blocked')
})
it('Should run action:api.user.unblocked', async function () {
await unblockUser(servers[0].url, userId, servers[0].accessToken)
await checkHook('action:api.user.unblocked')
})
it('Should run action:api.user.updated', async function () {
await updateUser({ url: servers[0].url, accessToken: servers[0].accessToken, userId, videoQuota: 50 })
await checkHook('action:api.user.updated')
})
it('Should run action:api.user.deleted', async function () {
await removeUser(servers[0].url, userId, servers[0].accessToken)
await checkHook('action:api.user.deleted')
})
}) })
after(async function () { after(async function () {

View File

@ -8,7 +8,8 @@ import { userLogin } from './login'
import { UserUpdateMe } from '../../models/users' import { UserUpdateMe } from '../../models/users'
import { omit } from 'lodash' import { omit } from 'lodash'
type CreateUserArgs = { url: string, type CreateUserArgs = {
url: string,
accessToken: string, accessToken: string,
username: string, username: string,
password: string, password: string,

View File

@ -55,7 +55,23 @@ export const serverActionHookObject = {
// Fired when a reply to a thread is created // Fired when a reply to a thread is created
'action:api.video-comment-reply.created': true, 'action:api.video-comment-reply.created': true,
// Fired when a comment (thread or reply) is deleted // Fired when a comment (thread or reply) is deleted
'action:api.video-comment.deleted': true 'action:api.video-comment.deleted': true,
// Fired when a user is blocked (banned)
'action:api.user.blocked': true,
// Fired when a user is unblocked (unbanned)
'action:api.user.unblocked': true,
// Fired when a user registered on the instance
'action:api.user.registered': true,
// Fired when an admin/moderator created a user
'action:api.user.created': true,
// Fired when a user is removed by an admin/moderator
'action:api.user.deleted': true,
// Fired when a user is updated by an admin/moderator
'action:api.user.updated': true,
// Fired when a user got a new oauth2 token
'action:api.user.oauth2-got-token': true
} }
export type ServerActionHookName = keyof typeof serverActionHookObject export type ServerActionHookName = keyof typeof serverActionHookObject