diff options
author | Chocobozzz <me@florianbigard.com> | 2019-02-11 09:30:29 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-02-11 10:37:27 +0100 |
commit | b426edd4854adc6e65844d8c54b8998e792b5778 (patch) | |
tree | b9ef4da0cdb2ab14c0aa1d67a883303f3ed0de14 /server | |
parent | 67b1d3fed765278bdc876cce393ef56d56942df0 (diff) | |
download | PeerTube-b426edd4854adc6e65844d8c54b8998e792b5778.tar.gz PeerTube-b426edd4854adc6e65844d8c54b8998e792b5778.tar.zst PeerTube-b426edd4854adc6e65844d8c54b8998e792b5778.zip |
Cleanup reset user password by admin
And add some tests
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/users/index.ts | 20 | ||||
-rw-r--r-- | server/controllers/api/users/me.ts | 2 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/lib/emailer.ts | 20 | ||||
-rw-r--r-- | server/middlewares/validators/users.ts | 2 | ||||
-rw-r--r-- | server/tests/api/check-params/users.ts | 18 | ||||
-rw-r--r-- | server/tests/api/users/users.ts | 16 |
7 files changed, 51 insertions, 29 deletions
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index beac6d8b1..e3533a7f6 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -3,7 +3,6 @@ import * as RateLimit from 'express-rate-limit' | |||
3 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' | 3 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { getFormattedObjects } from '../../../helpers/utils' | 5 | import { getFormattedObjects } from '../../../helpers/utils' |
6 | import { pseudoRandomBytesPromise } from '../../../helpers/core-utils' | ||
7 | import { CONFIG, RATES_LIMIT, sequelizeTypescript } from '../../../initializers' | 6 | import { CONFIG, RATES_LIMIT, sequelizeTypescript } from '../../../initializers' |
8 | import { Emailer } from '../../../lib/emailer' | 7 | import { Emailer } from '../../../lib/emailer' |
9 | import { Redis } from '../../../lib/redis' | 8 | import { Redis } from '../../../lib/redis' |
@@ -230,7 +229,7 @@ async function unblockUser (req: express.Request, res: express.Response, next: e | |||
230 | return res.status(204).end() | 229 | return res.status(204).end() |
231 | } | 230 | } |
232 | 231 | ||
233 | async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 232 | async function blockUser (req: express.Request, res: express.Response) { |
234 | const user: UserModel = res.locals.user | 233 | const user: UserModel = res.locals.user |
235 | const reason = req.body.reason | 234 | const reason = req.body.reason |
236 | 235 | ||
@@ -239,23 +238,23 @@ async function blockUser (req: express.Request, res: express.Response, next: exp | |||
239 | return res.status(204).end() | 238 | return res.status(204).end() |
240 | } | 239 | } |
241 | 240 | ||
242 | function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 241 | function getUser (req: express.Request, res: express.Response) { |
243 | return res.json((res.locals.user as UserModel).toFormattedJSON()) | 242 | return res.json((res.locals.user as UserModel).toFormattedJSON()) |
244 | } | 243 | } |
245 | 244 | ||
246 | async function autocompleteUsers (req: express.Request, res: express.Response, next: express.NextFunction) { | 245 | async function autocompleteUsers (req: express.Request, res: express.Response) { |
247 | const resultList = await UserModel.autoComplete(req.query.search as string) | 246 | const resultList = await UserModel.autoComplete(req.query.search as string) |
248 | 247 | ||
249 | return res.json(resultList) | 248 | return res.json(resultList) |
250 | } | 249 | } |
251 | 250 | ||
252 | async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) { | 251 | async function listUsers (req: express.Request, res: express.Response) { |
253 | const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search) | 252 | const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search) |
254 | 253 | ||
255 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 254 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
256 | } | 255 | } |
257 | 256 | ||
258 | async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 257 | async function removeUser (req: express.Request, res: express.Response) { |
259 | const user: UserModel = res.locals.user | 258 | const user: UserModel = res.locals.user |
260 | 259 | ||
261 | await user.destroy() | 260 | await user.destroy() |
@@ -265,12 +264,13 @@ async function removeUser (req: express.Request, res: express.Response, next: ex | |||
265 | return res.sendStatus(204) | 264 | return res.sendStatus(204) |
266 | } | 265 | } |
267 | 266 | ||
268 | async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 267 | async function updateUser (req: express.Request, res: express.Response) { |
269 | const body: UserUpdate = req.body | 268 | const body: UserUpdate = req.body |
270 | const userToUpdate = res.locals.user as UserModel | 269 | const userToUpdate = res.locals.user as UserModel |
271 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) | 270 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) |
272 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role | 271 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role |
273 | 272 | ||
273 | if (body.password !== undefined) userToUpdate.password = body.password | ||
274 | if (body.email !== undefined) userToUpdate.email = body.email | 274 | if (body.email !== undefined) userToUpdate.email = body.email |
275 | if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified | 275 | if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified |
276 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota | 276 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota |
@@ -280,11 +280,11 @@ async function updateUser (req: express.Request, res: express.Response, next: ex | |||
280 | const user = await userToUpdate.save() | 280 | const user = await userToUpdate.save() |
281 | 281 | ||
282 | // Destroy user token to refresh rights | 282 | // Destroy user token to refresh rights |
283 | if (roleChanged) await deleteUserToken(userToUpdate.id) | 283 | if (roleChanged || body.password !== undefined) await deleteUserToken(userToUpdate.id) |
284 | 284 | ||
285 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) | 285 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) |
286 | 286 | ||
287 | // Don't need to send this update to followers, these attributes are not propagated | 287 | // Don't need to send this update to followers, these attributes are not federated |
288 | 288 | ||
289 | return res.sendStatus(204) | 289 | return res.sendStatus(204) |
290 | } | 290 | } |
@@ -294,7 +294,7 @@ async function askResetUserPassword (req: express.Request, res: express.Response | |||
294 | 294 | ||
295 | const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id) | 295 | const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id) |
296 | const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString | 296 | const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString |
297 | await Emailer.Instance.addForgetPasswordEmailJob(user.email, url) | 297 | await Emailer.Instance.addPasswordResetEmailJob(user.email, url) |
298 | 298 | ||
299 | return res.status(204).end() | 299 | return res.status(204).end() |
300 | } | 300 | } |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 94a2b8732..d5e154869 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -167,7 +167,7 @@ async function deleteMe (req: express.Request, res: express.Response) { | |||
167 | return res.sendStatus(204) | 167 | return res.sendStatus(204) |
168 | } | 168 | } |
169 | 169 | ||
170 | async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) { | 170 | async function updateMe (req: express.Request, res: express.Response) { |
171 | const body: UserUpdateMe = req.body | 171 | const body: UserUpdateMe = req.body |
172 | 172 | ||
173 | const user: UserModel = res.locals.oauth.token.user | 173 | const user: UserModel = res.locals.oauth.token.user |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 98f8f8694..e5c4c4e63 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -711,6 +711,8 @@ if (isTestInstance() === true) { | |||
711 | CACHE.VIDEO_CAPTIONS.MAX_AGE = 3000 | 711 | CACHE.VIDEO_CAPTIONS.MAX_AGE = 3000 |
712 | MEMOIZE_TTL.OVERVIEWS_SAMPLE = 1 | 712 | MEMOIZE_TTL.OVERVIEWS_SAMPLE = 1 |
713 | ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS = '0ms' | 713 | ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS = '0ms' |
714 | |||
715 | RATES_LIMIT.LOGIN.MAX = 20 | ||
714 | } | 716 | } |
715 | 717 | ||
716 | updateWebserverUrls() | 718 | updateWebserverUrls() |
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 7681164b3..672414cc0 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -101,22 +101,6 @@ class Emailer { | |||
101 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 101 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
102 | } | 102 | } |
103 | 103 | ||
104 | addForceResetPasswordEmailJob (to: string, resetPasswordUrl: string) { | ||
105 | const text = `Hi dear user,\n\n` + | ||
106 | `Your password has been reset on ${CONFIG.WEBSERVER.HOST}! ` + | ||
107 | `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + | ||
108 | `Cheers,\n` + | ||
109 | `PeerTube.` | ||
110 | |||
111 | const emailPayload: EmailPayload = { | ||
112 | to: [ to ], | ||
113 | subject: 'Reset of your PeerTube password', | ||
114 | text | ||
115 | } | ||
116 | |||
117 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
118 | } | ||
119 | |||
120 | addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { | 104 | addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { |
121 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() | 105 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() |
122 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() | 106 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() |
@@ -312,9 +296,9 @@ class Emailer { | |||
312 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 296 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
313 | } | 297 | } |
314 | 298 | ||
315 | addForgetPasswordEmailJob (to: string, resetPasswordUrl: string) { | 299 | addPasswordResetEmailJob (to: string, resetPasswordUrl: string) { |
316 | const text = `Hi dear user,\n\n` + | 300 | const text = `Hi dear user,\n\n` + |
317 | `It seems you forgot your password on ${CONFIG.WEBSERVER.HOST}! ` + | 301 | `A reset password procedure for your account ${to} has been requested on ${CONFIG.WEBSERVER.HOST} ` + |
318 | `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + | 302 | `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + |
319 | `If you are not the person who initiated this request, please ignore this email.\n\n` + | 303 | `If you are not the person who initiated this request, please ignore this email.\n\n` + |
320 | `Cheers,\n` + | 304 | `Cheers,\n` + |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 1bb0bfb1b..a52e3060a 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -113,6 +113,7 @@ const deleteMeValidator = [ | |||
113 | 113 | ||
114 | const usersUpdateValidator = [ | 114 | const usersUpdateValidator = [ |
115 | param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), | 115 | param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), |
116 | body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'), | ||
116 | body('email').optional().isEmail().withMessage('Should have a valid email attribute'), | 117 | body('email').optional().isEmail().withMessage('Should have a valid email attribute'), |
117 | body('emailVerified').optional().isBoolean().withMessage('Should have a valid email verified attribute'), | 118 | body('emailVerified').optional().isBoolean().withMessage('Should have a valid email verified attribute'), |
118 | body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), | 119 | body('videoQuota').optional().custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), |
@@ -233,6 +234,7 @@ const usersAskResetPasswordValidator = [ | |||
233 | logger.debug('Checking usersAskResetPassword parameters', { parameters: req.body }) | 234 | logger.debug('Checking usersAskResetPassword parameters', { parameters: req.body }) |
234 | 235 | ||
235 | if (areValidationErrors(req, res)) return | 236 | if (areValidationErrors(req, res)) return |
237 | |||
236 | const exists = await checkUserEmailExist(req.body.email, res, false) | 238 | const exists = await checkUserEmailExist(req.body.email, res, false) |
237 | if (!exists) { | 239 | if (!exists) { |
238 | logger.debug('User with email %s does not exist (asking reset password).', req.body.email) | 240 | logger.debug('User with email %s does not exist (asking reset password).', req.body.email) |
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index a3e8e2e9c..13be8b460 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts | |||
@@ -464,6 +464,24 @@ describe('Test users API validators', function () { | |||
464 | await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields }) | 464 | await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields }) |
465 | }) | 465 | }) |
466 | 466 | ||
467 | it('Should fail with a too small password', async function () { | ||
468 | const fields = { | ||
469 | currentPassword: 'my super password', | ||
470 | password: 'bla' | ||
471 | } | ||
472 | |||
473 | await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields }) | ||
474 | }) | ||
475 | |||
476 | it('Should fail with a too long password', async function () { | ||
477 | const fields = { | ||
478 | currentPassword: 'my super password', | ||
479 | password: 'super'.repeat(61) | ||
480 | } | ||
481 | |||
482 | await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields }) | ||
483 | }) | ||
484 | |||
467 | it('Should fail with an non authenticated user', async function () { | 485 | it('Should fail with an non authenticated user', async function () { |
468 | const fields = { | 486 | const fields = { |
469 | videoQuota: 42 | 487 | videoQuota: 42 |
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index ad98ab1c7..c4465d541 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -501,6 +501,22 @@ describe('Test users', function () { | |||
501 | accessTokenUser = await userLogin(server, user) | 501 | accessTokenUser = await userLogin(server, user) |
502 | }) | 502 | }) |
503 | 503 | ||
504 | it('Should be able to update another user password', async function () { | ||
505 | await updateUser({ | ||
506 | url: server.url, | ||
507 | userId, | ||
508 | accessToken, | ||
509 | password: 'password updated' | ||
510 | }) | ||
511 | |||
512 | await getMyUserVideoQuotaUsed(server.url, accessTokenUser, 401) | ||
513 | |||
514 | await userLogin(server, user, 400) | ||
515 | |||
516 | user.password = 'password updated' | ||
517 | accessTokenUser = await userLogin(server, user) | ||
518 | }) | ||
519 | |||
504 | it('Should be able to list video blacklist by a moderator', async function () { | 520 | it('Should be able to list video blacklist by a moderator', async function () { |
505 | await getBlacklistedVideosList(server.url, accessTokenUser) | 521 | await getBlacklistedVideosList(server.url, accessTokenUser) |
506 | }) | 522 | }) |