]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/users/index.ts
add user account email verificiation (#977)
[github/Chocobozzz/PeerTube.git] / server / controllers / api / users / index.ts
1 import * as express from 'express'
2 import * as RateLimit from 'express-rate-limit'
3 import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared'
4 import { logger } from '../../../helpers/logger'
5 import { getFormattedObjects } from '../../../helpers/utils'
6 import { CONFIG, RATES_LIMIT, sequelizeTypescript } from '../../../initializers'
7 import { Emailer } from '../../../lib/emailer'
8 import { Redis } from '../../../lib/redis'
9 import { createUserAccountAndChannel } from '../../../lib/user'
10 import {
11 asyncMiddleware,
12 asyncRetryTransactionMiddleware,
13 authenticate,
14 ensureUserHasRight,
15 ensureUserRegistrationAllowed,
16 ensureUserRegistrationAllowedForIP,
17 paginationValidator,
18 setDefaultPagination,
19 setDefaultSort,
20 token,
21 usersAddValidator,
22 usersGetValidator,
23 usersRegisterValidator,
24 usersRemoveValidator,
25 usersSortValidator,
26 usersUpdateValidator
27 } from '../../../middlewares'
28 import {
29 usersAskResetPasswordValidator, usersBlockingValidator, usersResetPasswordValidator,
30 usersAskSendVerifyEmailValidator, usersVerifyEmailValidator
31 } from '../../../middlewares/validators'
32 import { UserModel } from '../../../models/account/user'
33 import { OAuthTokenModel } from '../../../models/oauth/oauth-token'
34 import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger'
35 import { meRouter } from './me'
36
37 const auditLogger = auditLoggerFactory('users')
38
39 const loginRateLimiter = new RateLimit({
40 windowMs: RATES_LIMIT.LOGIN.WINDOW_MS,
41 max: RATES_LIMIT.LOGIN.MAX,
42 delayMs: 0
43 })
44
45 const usersRouter = express.Router()
46 usersRouter.use('/', meRouter)
47
48 usersRouter.get('/',
49 authenticate,
50 ensureUserHasRight(UserRight.MANAGE_USERS),
51 paginationValidator,
52 usersSortValidator,
53 setDefaultSort,
54 setDefaultPagination,
55 asyncMiddleware(listUsers)
56 )
57
58 usersRouter.post('/:id/block',
59 authenticate,
60 ensureUserHasRight(UserRight.MANAGE_USERS),
61 asyncMiddleware(usersBlockingValidator),
62 asyncMiddleware(blockUser)
63 )
64 usersRouter.post('/:id/unblock',
65 authenticate,
66 ensureUserHasRight(UserRight.MANAGE_USERS),
67 asyncMiddleware(usersBlockingValidator),
68 asyncMiddleware(unblockUser)
69 )
70
71 usersRouter.get('/:id',
72 authenticate,
73 ensureUserHasRight(UserRight.MANAGE_USERS),
74 asyncMiddleware(usersGetValidator),
75 getUser
76 )
77
78 usersRouter.post('/',
79 authenticate,
80 ensureUserHasRight(UserRight.MANAGE_USERS),
81 asyncMiddleware(usersAddValidator),
82 asyncRetryTransactionMiddleware(createUser)
83 )
84
85 usersRouter.post('/register',
86 asyncMiddleware(ensureUserRegistrationAllowed),
87 ensureUserRegistrationAllowedForIP,
88 asyncMiddleware(usersRegisterValidator),
89 asyncRetryTransactionMiddleware(registerUser)
90 )
91
92 usersRouter.put('/:id',
93 authenticate,
94 ensureUserHasRight(UserRight.MANAGE_USERS),
95 asyncMiddleware(usersUpdateValidator),
96 asyncMiddleware(updateUser)
97 )
98
99 usersRouter.delete('/:id',
100 authenticate,
101 ensureUserHasRight(UserRight.MANAGE_USERS),
102 asyncMiddleware(usersRemoveValidator),
103 asyncMiddleware(removeUser)
104 )
105
106 usersRouter.post('/ask-reset-password',
107 asyncMiddleware(usersAskResetPasswordValidator),
108 asyncMiddleware(askResetUserPassword)
109 )
110
111 usersRouter.post('/:id/reset-password',
112 asyncMiddleware(usersResetPasswordValidator),
113 asyncMiddleware(resetUserPassword)
114 )
115
116 usersRouter.post('/ask-send-verify-email',
117 loginRateLimiter,
118 asyncMiddleware(usersAskSendVerifyEmailValidator),
119 asyncMiddleware(askSendVerifyUserEmail)
120 )
121
122 usersRouter.post('/:id/verify-email',
123 asyncMiddleware(usersVerifyEmailValidator),
124 asyncMiddleware(verifyUserEmail)
125 )
126
127 usersRouter.post('/token',
128 loginRateLimiter,
129 token,
130 success
131 )
132 // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route
133
134 // ---------------------------------------------------------------------------
135
136 export {
137 usersRouter
138 }
139
140 // ---------------------------------------------------------------------------
141
142 async function createUser (req: express.Request, res: express.Response) {
143 const body: UserCreate = req.body
144 const userToCreate = new UserModel({
145 username: body.username,
146 password: body.password,
147 email: body.email,
148 nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
149 autoPlayVideo: true,
150 role: body.role,
151 videoQuota: body.videoQuota,
152 videoQuotaDaily: body.videoQuotaDaily
153 })
154
155 const { user, account } = await createUserAccountAndChannel(userToCreate)
156
157 auditLogger.create(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON()))
158 logger.info('User %s with its channel and account created.', body.username)
159
160 return res.json({
161 user: {
162 id: user.id,
163 account: {
164 id: account.id,
165 uuid: account.Actor.uuid
166 }
167 }
168 }).end()
169 }
170
171 async function registerUser (req: express.Request, res: express.Response) {
172 const body: UserCreate = req.body
173
174 const userToCreate = new UserModel({
175 username: body.username,
176 password: body.password,
177 email: body.email,
178 nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
179 autoPlayVideo: true,
180 role: UserRole.USER,
181 videoQuota: CONFIG.USER.VIDEO_QUOTA,
182 videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
183 emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
184 })
185
186 const { user } = await createUserAccountAndChannel(userToCreate)
187
188 auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON()))
189 logger.info('User %s with its channel and account registered.', body.username)
190
191 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
192 await sendVerifyUserEmail(user)
193 }
194
195 return res.type('json').status(204).end()
196 }
197
198 async function unblockUser (req: express.Request, res: express.Response, next: express.NextFunction) {
199 const user: UserModel = res.locals.user
200
201 await changeUserBlock(res, user, false)
202
203 return res.status(204).end()
204 }
205
206 async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) {
207 const user: UserModel = res.locals.user
208 const reason = req.body.reason
209
210 await changeUserBlock(res, user, true, reason)
211
212 return res.status(204).end()
213 }
214
215 function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
216 return res.json((res.locals.user as UserModel).toFormattedJSON())
217 }
218
219 async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
220 const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort)
221
222 return res.json(getFormattedObjects(resultList.data, resultList.total))
223 }
224
225 async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
226 const user: UserModel = res.locals.user
227
228 await user.destroy()
229
230 auditLogger.delete(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON()))
231
232 return res.sendStatus(204)
233 }
234
235 async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
236 const body: UserUpdate = req.body
237 const userToUpdate = res.locals.user as UserModel
238 const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON())
239 const roleChanged = body.role !== undefined && body.role !== userToUpdate.role
240
241 if (body.email !== undefined) userToUpdate.email = body.email
242 if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota
243 if (body.videoQuotaDaily !== undefined) userToUpdate.videoQuotaDaily = body.videoQuotaDaily
244 if (body.role !== undefined) userToUpdate.role = body.role
245
246 const user = await userToUpdate.save()
247
248 // Destroy user token to refresh rights
249 if (roleChanged) {
250 await OAuthTokenModel.deleteUserToken(userToUpdate.id)
251 }
252
253 auditLogger.update(
254 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
255 new UserAuditView(user.toFormattedJSON()),
256 oldUserAuditView
257 )
258
259 // Don't need to send this update to followers, these attributes are not propagated
260
261 return res.sendStatus(204)
262 }
263
264 async function askResetUserPassword (req: express.Request, res: express.Response, next: express.NextFunction) {
265 const user = res.locals.user as UserModel
266
267 const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id)
268 const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString
269 await Emailer.Instance.addForgetPasswordEmailJob(user.email, url)
270
271 return res.status(204).end()
272 }
273
274 async function resetUserPassword (req: express.Request, res: express.Response, next: express.NextFunction) {
275 const user = res.locals.user as UserModel
276 user.password = req.body.password
277
278 await user.save()
279
280 return res.status(204).end()
281 }
282
283 async function sendVerifyUserEmail (user: UserModel) {
284 const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
285 const url = CONFIG.WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString
286 await Emailer.Instance.addVerifyEmailJob(user.email, url)
287 return
288 }
289
290 async function askSendVerifyUserEmail (req: express.Request, res: express.Response, next: express.NextFunction) {
291 const user = res.locals.user as UserModel
292
293 await sendVerifyUserEmail(user)
294
295 return res.status(204).end()
296 }
297
298 async function verifyUserEmail (req: express.Request, res: express.Response, next: express.NextFunction) {
299 const user = res.locals.user as UserModel
300 user.emailVerified = true
301
302 await user.save()
303
304 return res.status(204).end()
305 }
306
307 function success (req: express.Request, res: express.Response, next: express.NextFunction) {
308 res.end()
309 }
310
311 async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) {
312 const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
313
314 user.blocked = block
315 user.blockedReason = reason || null
316
317 await sequelizeTypescript.transaction(async t => {
318 await OAuthTokenModel.deleteUserToken(user.id, t)
319
320 await user.save({ transaction: t })
321 })
322
323 await Emailer.Instance.addUserBlockJob(user, block, reason)
324
325 auditLogger.update(
326 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
327 new UserAuditView(user.toFormattedJSON()),
328 oldUserAuditView
329 )
330 }