]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/users/index.ts
Update translations
[github/Chocobozzz/PeerTube.git] / server / controllers / api / users / index.ts
1 import express from 'express'
2 import { tokensRouter } from '@server/controllers/api/users/token'
3 import { Hooks } from '@server/lib/plugins/hooks'
4 import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
5 import { MUserAccountDefault } from '@server/types/models'
6 import { pick } from '@shared/core-utils'
7 import { HttpStatusCode, UserCreate, UserCreateResult, UserRight, UserUpdate } from '@shared/models'
8 import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
9 import { logger } from '../../../helpers/logger'
10 import { generateRandomString, getFormattedObjects } from '../../../helpers/utils'
11 import { WEBSERVER } from '../../../initializers/constants'
12 import { sequelizeTypescript } from '../../../initializers/database'
13 import { Emailer } from '../../../lib/emailer'
14 import { Redis } from '../../../lib/redis'
15 import { buildUser, createUserAccountAndChannelAndPlaylist } from '../../../lib/user'
16 import {
17 adminUsersSortValidator,
18 asyncMiddleware,
19 asyncRetryTransactionMiddleware,
20 authenticate,
21 ensureUserHasRight,
22 paginationValidator,
23 setDefaultPagination,
24 setDefaultSort,
25 userAutocompleteValidator,
26 usersAddValidator,
27 usersGetValidator,
28 usersListValidator,
29 usersRemoveValidator,
30 usersUpdateValidator
31 } from '../../../middlewares'
32 import {
33 ensureCanModerateUser,
34 usersAskResetPasswordValidator,
35 usersBlockingValidator,
36 usersResetPasswordValidator
37 } from '../../../middlewares/validators'
38 import { UserModel } from '../../../models/user/user'
39 import { emailVerificationRouter } from './email-verification'
40 import { meRouter } from './me'
41 import { myAbusesRouter } from './my-abuses'
42 import { myBlocklistRouter } from './my-blocklist'
43 import { myVideosHistoryRouter } from './my-history'
44 import { myNotificationsRouter } from './my-notifications'
45 import { mySubscriptionsRouter } from './my-subscriptions'
46 import { myVideoPlaylistsRouter } from './my-video-playlists'
47 import { registrationsRouter } from './registrations'
48 import { twoFactorRouter } from './two-factor'
49
50 const auditLogger = auditLoggerFactory('users')
51
52 const usersRouter = express.Router()
53 usersRouter.use('/', emailVerificationRouter)
54 usersRouter.use('/', registrationsRouter)
55 usersRouter.use('/', twoFactorRouter)
56 usersRouter.use('/', tokensRouter)
57 usersRouter.use('/', myNotificationsRouter)
58 usersRouter.use('/', mySubscriptionsRouter)
59 usersRouter.use('/', myBlocklistRouter)
60 usersRouter.use('/', myVideosHistoryRouter)
61 usersRouter.use('/', myVideoPlaylistsRouter)
62 usersRouter.use('/', myAbusesRouter)
63 usersRouter.use('/', meRouter)
64
65 usersRouter.get('/autocomplete',
66 userAutocompleteValidator,
67 asyncMiddleware(autocompleteUsers)
68 )
69
70 usersRouter.get('/',
71 authenticate,
72 ensureUserHasRight(UserRight.MANAGE_USERS),
73 paginationValidator,
74 adminUsersSortValidator,
75 setDefaultSort,
76 setDefaultPagination,
77 usersListValidator,
78 asyncMiddleware(listUsers)
79 )
80
81 usersRouter.post('/:id/block',
82 authenticate,
83 ensureUserHasRight(UserRight.MANAGE_USERS),
84 asyncMiddleware(usersBlockingValidator),
85 ensureCanModerateUser,
86 asyncMiddleware(blockUser)
87 )
88 usersRouter.post('/:id/unblock',
89 authenticate,
90 ensureUserHasRight(UserRight.MANAGE_USERS),
91 asyncMiddleware(usersBlockingValidator),
92 ensureCanModerateUser,
93 asyncMiddleware(unblockUser)
94 )
95
96 usersRouter.get('/:id',
97 authenticate,
98 ensureUserHasRight(UserRight.MANAGE_USERS),
99 asyncMiddleware(usersGetValidator),
100 getUser
101 )
102
103 usersRouter.post('/',
104 authenticate,
105 ensureUserHasRight(UserRight.MANAGE_USERS),
106 asyncMiddleware(usersAddValidator),
107 asyncRetryTransactionMiddleware(createUser)
108 )
109
110 usersRouter.put('/:id',
111 authenticate,
112 ensureUserHasRight(UserRight.MANAGE_USERS),
113 asyncMiddleware(usersUpdateValidator),
114 ensureCanModerateUser,
115 asyncMiddleware(updateUser)
116 )
117
118 usersRouter.delete('/:id',
119 authenticate,
120 ensureUserHasRight(UserRight.MANAGE_USERS),
121 asyncMiddleware(usersRemoveValidator),
122 ensureCanModerateUser,
123 asyncMiddleware(removeUser)
124 )
125
126 usersRouter.post('/ask-reset-password',
127 asyncMiddleware(usersAskResetPasswordValidator),
128 asyncMiddleware(askResetUserPassword)
129 )
130
131 usersRouter.post('/:id/reset-password',
132 asyncMiddleware(usersResetPasswordValidator),
133 asyncMiddleware(resetUserPassword)
134 )
135
136 // ---------------------------------------------------------------------------
137
138 export {
139 usersRouter
140 }
141
142 // ---------------------------------------------------------------------------
143
144 async function createUser (req: express.Request, res: express.Response) {
145 const body: UserCreate = req.body
146
147 const userToCreate = buildUser({
148 ...pick(body, [ 'username', 'password', 'email', 'role', 'videoQuota', 'videoQuotaDaily', 'adminFlags' ]),
149
150 emailVerified: null
151 })
152
153 // NB: due to the validator usersAddValidator, password==='' can only be true if we can send the mail.
154 const createPassword = userToCreate.password === ''
155 if (createPassword) {
156 userToCreate.password = await generateRandomString(20)
157 }
158
159 const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({
160 userToCreate,
161 channelNames: body.channelName && { name: body.channelName, displayName: body.channelName }
162 })
163
164 auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
165 logger.info('User %s with its channel and account created.', body.username)
166
167 if (createPassword) {
168 // this will send an email for newly created users, so then can set their first password.
169 logger.info('Sending to user %s a create password email', body.username)
170 const verificationString = await Redis.Instance.setCreatePasswordVerificationString(user.id)
171 const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString
172 Emailer.Instance.addPasswordCreateEmailJob(userToCreate.username, user.email, url)
173 }
174
175 Hooks.runAction('action:api.user.created', { body, user, account, videoChannel, req, res })
176
177 return res.json({
178 user: {
179 id: user.id,
180 account: {
181 id: account.id
182 }
183 } as UserCreateResult
184 })
185 }
186
187 async function unblockUser (req: express.Request, res: express.Response) {
188 const user = res.locals.user
189
190 await changeUserBlock(res, user, false)
191
192 Hooks.runAction('action:api.user.unblocked', { user, req, res })
193
194 return res.status(HttpStatusCode.NO_CONTENT_204).end()
195 }
196
197 async function blockUser (req: express.Request, res: express.Response) {
198 const user = res.locals.user
199 const reason = req.body.reason
200
201 await changeUserBlock(res, user, true, reason)
202
203 Hooks.runAction('action:api.user.blocked', { user, req, res })
204
205 return res.status(HttpStatusCode.NO_CONTENT_204).end()
206 }
207
208 function getUser (req: express.Request, res: express.Response) {
209 return res.json(res.locals.user.toFormattedJSON({ withAdminFlags: true }))
210 }
211
212 async function autocompleteUsers (req: express.Request, res: express.Response) {
213 const resultList = await UserModel.autoComplete(req.query.search as string)
214
215 return res.json(resultList)
216 }
217
218 async function listUsers (req: express.Request, res: express.Response) {
219 const resultList = await UserModel.listForAdminApi({
220 start: req.query.start,
221 count: req.query.count,
222 sort: req.query.sort,
223 search: req.query.search,
224 blocked: req.query.blocked
225 })
226
227 return res.json(getFormattedObjects(resultList.data, resultList.total, { withAdminFlags: true }))
228 }
229
230 async function removeUser (req: express.Request, res: express.Response) {
231 const user = res.locals.user
232
233 auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
234
235 await sequelizeTypescript.transaction(async t => {
236 // Use a transaction to avoid inconsistencies with hooks (account/channel deletion & federation)
237 await user.destroy({ transaction: t })
238 })
239
240 Hooks.runAction('action:api.user.deleted', { user, req, res })
241
242 return res.status(HttpStatusCode.NO_CONTENT_204).end()
243 }
244
245 async function updateUser (req: express.Request, res: express.Response) {
246 const body: UserUpdate = req.body
247 const userToUpdate = res.locals.user
248 const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON())
249 const roleChanged = body.role !== undefined && body.role !== userToUpdate.role
250
251 const keysToUpdate: (keyof UserUpdate)[] = [
252 'password',
253 'email',
254 'emailVerified',
255 'videoQuota',
256 'videoQuotaDaily',
257 'role',
258 'adminFlags',
259 'pluginAuth'
260 ]
261
262 for (const key of keysToUpdate) {
263 if (body[key] !== undefined) userToUpdate.set(key, body[key])
264 }
265
266 const user = await userToUpdate.save()
267
268 // Destroy user token to refresh rights
269 if (roleChanged || body.password !== undefined) await OAuthTokenModel.deleteUserToken(userToUpdate.id)
270
271 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
272
273 Hooks.runAction('action:api.user.updated', { user, req, res })
274
275 // Don't need to send this update to followers, these attributes are not federated
276
277 return res.status(HttpStatusCode.NO_CONTENT_204).end()
278 }
279
280 async function askResetUserPassword (req: express.Request, res: express.Response) {
281 const user = res.locals.user
282
283 const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id)
284 const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString
285 Emailer.Instance.addPasswordResetEmailJob(user.username, user.email, url)
286
287 return res.status(HttpStatusCode.NO_CONTENT_204).end()
288 }
289
290 async function resetUserPassword (req: express.Request, res: express.Response) {
291 const user = res.locals.user
292 user.password = req.body.password
293
294 await user.save()
295 await Redis.Instance.removePasswordVerificationString(user.id)
296
297 return res.status(HttpStatusCode.NO_CONTENT_204).end()
298 }
299
300 async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) {
301 const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
302
303 user.blocked = block
304 user.blockedReason = reason || null
305
306 await sequelizeTypescript.transaction(async t => {
307 await OAuthTokenModel.deleteUserToken(user.id, t)
308
309 await user.save({ transaction: t })
310 })
311
312 Emailer.Instance.addUserBlockJob(user, block, reason)
313
314 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
315 }