aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/login/login.component.ts6
-rw-r--r--package.json2
-rw-r--r--server/controllers/api/users.ts48
-rw-r--r--server/helpers/custom-validators/users.ts13
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0245-user-blocked.ts40
-rw-r--r--server/lib/oauth-model.ts7
-rw-r--r--server/middlewares/oauth.ts2
-rw-r--r--server/middlewares/validators/users.ts21
-rw-r--r--server/models/account/user.ts7
-rw-r--r--server/models/oauth/oauth-token.ts8
-rw-r--r--server/tests/api/check-params/users.ts16
-rw-r--r--server/tests/api/users/users.ts31
-rw-r--r--server/tests/utils/users/users.ts22
-rw-r--r--yarn.lock121
15 files changed, 287 insertions, 59 deletions
diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts
index 56f992b5d..9a68c12fa 100644
--- a/client/src/app/login/login.component.ts
+++ b/client/src/app/login/login.component.ts
@@ -55,7 +55,11 @@ export class LoginComponent extends FormReactive implements OnInit {
55 .subscribe( 55 .subscribe(
56 () => this.redirectService.redirectToHomepage(), 56 () => this.redirectService.redirectToHomepage(),
57 57
58 err => this.error = err.message 58 err => {
59 if (err.message.indexOf('credentials are invalid') !== -1) this.error = this.i18n('Incorrect username or password.')
60 else if (err.message.indexOf('blocked') !== -1) this.error = this.i18n('You account is blocked.')
61 else this.error = err.message
62 }
59 ) 63 )
60 } 64 }
61 65
diff --git a/package.json b/package.json
index 6348bbb6a..ba539bd31 100644
--- a/package.json
+++ b/package.json
@@ -78,6 +78,7 @@
78 "@types/bluebird": "3.5.21" 78 "@types/bluebird": "3.5.21"
79 }, 79 },
80 "dependencies": { 80 "dependencies": {
81 "@types/oauth2-server": "^3.0.8",
81 "async": "^2.0.0", 82 "async": "^2.0.0",
82 "async-lock": "^1.1.2", 83 "async-lock": "^1.1.2",
83 "async-lru": "^1.1.1", 84 "async-lru": "^1.1.1",
@@ -113,6 +114,7 @@
113 "morgan": "^1.5.3", 114 "morgan": "^1.5.3",
114 "multer": "^1.1.0", 115 "multer": "^1.1.0",
115 "nodemailer": "^4.4.2", 116 "nodemailer": "^4.4.2",
117 "oauth2-server": "^3.0.0",
116 "parse-torrent": "^6.0.0", 118 "parse-torrent": "^6.0.0",
117 "password-generator": "^2.0.2", 119 "password-generator": "^2.0.2",
118 "pem": "^1.12.3", 120 "pem": "^1.12.3",
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index 3d2586c3a..8f429d0b5 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -32,6 +32,7 @@ import {
32import { 32import {
33 deleteMeValidator, 33 deleteMeValidator,
34 usersAskResetPasswordValidator, 34 usersAskResetPasswordValidator,
35 usersBlockingValidator,
35 usersResetPasswordValidator, 36 usersResetPasswordValidator,
36 videoImportsSortValidator, 37 videoImportsSortValidator,
37 videosSortValidator 38 videosSortValidator
@@ -108,6 +109,19 @@ usersRouter.get('/',
108 asyncMiddleware(listUsers) 109 asyncMiddleware(listUsers)
109) 110)
110 111
112usersRouter.post('/:id/block',
113 authenticate,
114 ensureUserHasRight(UserRight.MANAGE_USERS),
115 asyncMiddleware(usersBlockingValidator),
116 asyncMiddleware(blockUser)
117)
118usersRouter.post('/:id/unblock',
119 authenticate,
120 ensureUserHasRight(UserRight.MANAGE_USERS),
121 asyncMiddleware(usersBlockingValidator),
122 asyncMiddleware(unblockUser)
123)
124
111usersRouter.get('/:id', 125usersRouter.get('/:id',
112 authenticate, 126 authenticate,
113 ensureUserHasRight(UserRight.MANAGE_USERS), 127 ensureUserHasRight(UserRight.MANAGE_USERS),
@@ -278,6 +292,22 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons
278 return res.json(data) 292 return res.json(data)
279} 293}
280 294
295async function unblockUser (req: express.Request, res: express.Response, next: express.NextFunction) {
296 const user: UserModel = res.locals.user
297
298 await changeUserBlock(res, user, false)
299
300 return res.status(204).end()
301}
302
303async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) {
304 const user: UserModel = res.locals.user
305
306 await changeUserBlock(res, user, true)
307
308 return res.status(204).end()
309}
310
281function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { 311function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
282 return res.json((res.locals.user as UserModel).toFormattedJSON()) 312 return res.json((res.locals.user as UserModel).toFormattedJSON())
283} 313}
@@ -423,3 +453,21 @@ async function resetUserPassword (req: express.Request, res: express.Response, n
423function success (req: express.Request, res: express.Response, next: express.NextFunction) { 453function success (req: express.Request, res: express.Response, next: express.NextFunction) {
424 res.end() 454 res.end()
425} 455}
456
457async function changeUserBlock (res: express.Response, user: UserModel, block: boolean) {
458 const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
459
460 user.blocked = block
461
462 await sequelizeTypescript.transaction(async t => {
463 await OAuthTokenModel.deleteUserToken(user.id, t)
464
465 await user.save({ transaction: t })
466 })
467
468 auditLogger.update(
469 res.locals.oauth.token.User.Account.Actor.getIdentifier(),
470 new UserAuditView(user.toFormattedJSON()),
471 oldUserAuditView
472 )
473}
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts
index ce1323e94..4a0d79ae5 100644
--- a/server/helpers/custom-validators/users.ts
+++ b/server/helpers/custom-validators/users.ts
@@ -2,7 +2,7 @@ import 'express-validator'
2import * as validator from 'validator' 2import * as validator from 'validator'
3import { UserRole } from '../../../shared' 3import { UserRole } from '../../../shared'
4import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers' 4import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers'
5import { exists, isFileValid } from './misc' 5import { exists, isFileValid, isBooleanValid } from './misc'
6import { values } from 'lodash' 6import { values } from 'lodash'
7 7
8const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS 8const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
@@ -29,17 +29,17 @@ function isUserDescriptionValid (value: string) {
29 return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION)) 29 return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION))
30} 30}
31 31
32function isBoolean (value: any) {
33 return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
34}
35
36const nsfwPolicies = values(NSFW_POLICY_TYPES) 32const nsfwPolicies = values(NSFW_POLICY_TYPES)
37function isUserNSFWPolicyValid (value: any) { 33function isUserNSFWPolicyValid (value: any) {
38 return exists(value) && nsfwPolicies.indexOf(value) !== -1 34 return exists(value) && nsfwPolicies.indexOf(value) !== -1
39} 35}
40 36
41function isUserAutoPlayVideoValid (value: any) { 37function isUserAutoPlayVideoValid (value: any) {
42 return isBoolean(value) 38 return isBooleanValid(value)
39}
40
41function isUserBlockedValid (value: any) {
42 return isBooleanValid(value)
43} 43}
44 44
45function isUserRoleValid (value: any) { 45function isUserRoleValid (value: any) {
@@ -57,6 +57,7 @@ function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } |
57// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
58 58
59export { 59export {
60 isUserBlockedValid,
60 isUserPasswordValid, 61 isUserPasswordValid,
61 isUserRoleValid, 62 isUserRoleValid,
62 isUserVideoQuotaValid, 63 isUserVideoQuotaValid,
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 80eb3f1e7..0a651beed 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -15,7 +15,7 @@ let config: IConfig = require('config')
15 15
16// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
17 17
18const LAST_MIGRATION_VERSION = 240 18const LAST_MIGRATION_VERSION = 245
19 19
20// --------------------------------------------------------------------------- 20// ---------------------------------------------------------------------------
21 21
diff --git a/server/initializers/migrations/0245-user-blocked.ts b/server/initializers/migrations/0245-user-blocked.ts
new file mode 100644
index 000000000..67afea5ed
--- /dev/null
+++ b/server/initializers/migrations/0245-user-blocked.ts
@@ -0,0 +1,40 @@
1import * as Sequelize from 'sequelize'
2import { createClient } from 'redis'
3import { CONFIG } from '../constants'
4import { JobQueue } from '../../lib/job-queue'
5import { initDatabaseModels } from '../database'
6
7async function up (utils: {
8 transaction: Sequelize.Transaction
9 queryInterface: Sequelize.QueryInterface
10 sequelize: Sequelize.Sequelize
11}): Promise<any> {
12 {
13 const data = {
14 type: Sequelize.BOOLEAN,
15 allowNull: true,
16 defaultValue: null
17 }
18 await utils.queryInterface.addColumn('user', 'blocked', data)
19 }
20
21 {
22 const query = 'UPDATE "user" SET "blocked" = false'
23 await utils.sequelize.query(query)
24 }
25
26 {
27 const data = {
28 type: Sequelize.BOOLEAN,
29 allowNull: false,
30 defaultValue: null
31 }
32 await utils.queryInterface.changeColumn('user', 'blocked', data)
33 }
34}
35
36function down (options) {
37 throw new Error('Not implemented.')
38}
39
40export { up, down }
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts
index 3adcce7b0..f13c25795 100644
--- a/server/lib/oauth-model.ts
+++ b/server/lib/oauth-model.ts
@@ -1,3 +1,4 @@
1import { AccessDeniedError} from 'oauth2-server'
1import { logger } from '../helpers/logger' 2import { logger } from '../helpers/logger'
2import { UserModel } from '../models/account/user' 3import { UserModel } from '../models/account/user'
3import { OAuthClientModel } from '../models/oauth/oauth-client' 4import { OAuthClientModel } from '../models/oauth/oauth-client'
@@ -34,6 +35,8 @@ async function getUser (usernameOrEmail: string, password: string) {
34 const passwordMatch = await user.isPasswordMatch(password) 35 const passwordMatch = await user.isPasswordMatch(password)
35 if (passwordMatch === false) return null 36 if (passwordMatch === false) return null
36 37
38 if (user.blocked) throw new AccessDeniedError('User is blocked.')
39
37 return user 40 return user
38} 41}
39 42
@@ -67,9 +70,7 @@ async function saveToken (token: TokenInfo, client: OAuthClientModel, user: User
67 } 70 }
68 71
69 const tokenCreated = await OAuthTokenModel.create(tokenToCreate) 72 const tokenCreated = await OAuthTokenModel.create(tokenToCreate)
70 const tokenToReturn = Object.assign(tokenCreated, { client, user }) 73 return Object.assign(tokenCreated, { client, user })
71
72 return tokenToReturn
73} 74}
74 75
75// --------------------------------------------------------------------------- 76// ---------------------------------------------------------------------------
diff --git a/server/middlewares/oauth.ts b/server/middlewares/oauth.ts
index a6f28dd5b..5233b66bd 100644
--- a/server/middlewares/oauth.ts
+++ b/server/middlewares/oauth.ts
@@ -39,7 +39,7 @@ function token (req: express.Request, res: express.Response, next: express.NextF
39 if (err) { 39 if (err) {
40 return res.status(err.status) 40 return res.status(err.status)
41 .json({ 41 .json({
42 error: 'Authentication failed.', 42 error: err.message,
43 code: err.name 43 code: err.name
44 }) 44 })
45 .end() 45 .end()
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 3c207c81f..94d8ab53b 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -74,6 +74,26 @@ const usersRemoveValidator = [
74 } 74 }
75] 75]
76 76
77const usersBlockingValidator = [
78 param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
79
80 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
81 logger.debug('Checking usersRemove parameters', { parameters: req.params })
82
83 if (areValidationErrors(req, res)) return
84 if (!await checkUserIdExist(req.params.id, res)) return
85
86 const user = res.locals.user
87 if (user.username === 'root') {
88 return res.status(400)
89 .send({ error: 'Cannot block the root user' })
90 .end()
91 }
92
93 return next()
94 }
95]
96
77const deleteMeValidator = [ 97const deleteMeValidator = [
78 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 98 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
79 const user: UserModel = res.locals.oauth.token.User 99 const user: UserModel = res.locals.oauth.token.User
@@ -230,6 +250,7 @@ export {
230 usersAddValidator, 250 usersAddValidator,
231 deleteMeValidator, 251 deleteMeValidator,
232 usersRegisterValidator, 252 usersRegisterValidator,
253 usersBlockingValidator,
233 usersRemoveValidator, 254 usersRemoveValidator,
234 usersUpdateValidator, 255 usersUpdateValidator,
235 usersUpdateMeValidator, 256 usersUpdateMeValidator,
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 1b1fc5ee8..ea6d63312 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -21,6 +21,7 @@ import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
21import { User, UserRole } from '../../../shared/models/users' 21import { User, UserRole } from '../../../shared/models/users'
22import { 22import {
23 isUserAutoPlayVideoValid, 23 isUserAutoPlayVideoValid,
24 isUserBlockedValid,
24 isUserNSFWPolicyValid, 25 isUserNSFWPolicyValid,
25 isUserPasswordValid, 26 isUserPasswordValid,
26 isUserRoleValid, 27 isUserRoleValid,
@@ -101,6 +102,12 @@ export class UserModel extends Model<UserModel> {
101 autoPlayVideo: boolean 102 autoPlayVideo: boolean
102 103
103 @AllowNull(false) 104 @AllowNull(false)
105 @Default(false)
106 @Is('UserBlocked', value => throwIfNotValid(value, isUserBlockedValid, 'blocked boolean'))
107 @Column
108 blocked: boolean
109
110 @AllowNull(false)
104 @Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role')) 111 @Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role'))
105 @Column 112 @Column
106 role: number 113 role: number
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index 026c30135..4c53848dc 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -3,6 +3,7 @@ import { logger } from '../../helpers/logger'
3import { AccountModel } from '../account/account' 3import { AccountModel } from '../account/account'
4import { UserModel } from '../account/user' 4import { UserModel } from '../account/user'
5import { OAuthClientModel } from './oauth-client' 5import { OAuthClientModel } from './oauth-client'
6import { Transaction } from 'sequelize'
6 7
7export type OAuthTokenInfo = { 8export type OAuthTokenInfo = {
8 refreshToken: string 9 refreshToken: string
@@ -125,7 +126,7 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
125 } as OAuthTokenInfo 126 } as OAuthTokenInfo
126 }) 127 })
127 .catch(err => { 128 .catch(err => {
128 logger.info('getRefreshToken error.', { err }) 129 logger.error('getRefreshToken error.', { err })
129 throw err 130 throw err
130 }) 131 })
131 } 132 }
@@ -163,11 +164,12 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
163 }) 164 })
164 } 165 }
165 166
166 static deleteUserToken (userId: number) { 167 static deleteUserToken (userId: number, t?: Transaction) {
167 const query = { 168 const query = {
168 where: { 169 where: {
169 userId 170 userId
170 } 171 },
172 transaction: t
171 } 173 }
172 174
173 return OAuthTokenModel.destroy(query) 175 return OAuthTokenModel.destroy(query)
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 60165ae22..b3fb61f6c 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -8,7 +8,7 @@ import { UserRole, VideoImport, VideoImportState } from '../../../../shared'
8import { 8import {
9 createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest, 9 createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
10 makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers, 10 makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
11 updateUser, uploadVideo, userLogin, deleteMe 11 updateUser, uploadVideo, userLogin, deleteMe, unblockUser, blockUser
12} from '../../utils' 12} from '../../utils'
13import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' 13import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
14import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../utils/videos/video-imports' 14import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../utils/videos/video-imports'
@@ -455,17 +455,29 @@ describe('Test users API validators', function () {
455 }) 455 })
456 }) 456 })
457 457
458 describe('When removing an user', function () { 458 describe('When blocking/unblocking/removing user', function () {
459 it('Should fail with an incorrect id', async function () { 459 it('Should fail with an incorrect id', async function () {
460 await removeUser(server.url, 'blabla', server.accessToken, 400) 460 await removeUser(server.url, 'blabla', server.accessToken, 400)
461 await blockUser(server.url, 'blabla', server.accessToken, 400)
462 await unblockUser(server.url, 'blabla', server.accessToken, 400)
461 }) 463 })
462 464
463 it('Should fail with the root user', async function () { 465 it('Should fail with the root user', async function () {
464 await removeUser(server.url, rootId, server.accessToken, 400) 466 await removeUser(server.url, rootId, server.accessToken, 400)
467 await blockUser(server.url, rootId, server.accessToken, 400)
468 await unblockUser(server.url, rootId, server.accessToken, 400)
465 }) 469 })
466 470
467 it('Should return 404 with a non existing id', async function () { 471 it('Should return 404 with a non existing id', async function () {
468 await removeUser(server.url, 4545454, server.accessToken, 404) 472 await removeUser(server.url, 4545454, server.accessToken, 404)
473 await blockUser(server.url, 4545454, server.accessToken, 404)
474 await unblockUser(server.url, 4545454, server.accessToken, 404)
475 })
476
477 it('Should fail with a non admin user', async function () {
478 await removeUser(server.url, userId, userAccessToken, 403)
479 await blockUser(server.url, userId, userAccessToken, 403)
480 await unblockUser(server.url, userId, userAccessToken, 403)
469 }) 481 })
470 }) 482 })
471 483
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index c9e8eb6f9..77aa00f60 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -7,7 +7,7 @@ import {
7 createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating, 7 createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating,
8 getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo, 8 getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo,
9 registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin, 9 registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin,
10 deleteMe 10 deleteMe, blockUser, unblockUser
11} from '../../utils/index' 11} from '../../utils/index'
12import { follow } from '../../utils/server/follows' 12import { follow } from '../../utils/server/follows'
13import { setAccessTokensToServers } from '../../utils/users/login' 13import { setAccessTokensToServers } from '../../utils/users/login'
@@ -45,28 +45,28 @@ describe('Test users', function () {
45 const client = { id: 'client', secret: server.client.secret } 45 const client = { id: 'client', secret: server.client.secret }
46 const res = await login(server.url, client, server.user, 400) 46 const res = await login(server.url, client, server.user, 400)
47 47
48 expect(res.body.error).to.equal('Authentication failed.') 48 expect(res.body.error).to.contain('client is invalid')
49 }) 49 })
50 50
51 it('Should not login with an invalid client secret', async function () { 51 it('Should not login with an invalid client secret', async function () {
52 const client = { id: server.client.id, secret: 'coucou' } 52 const client = { id: server.client.id, secret: 'coucou' }
53 const res = await login(server.url, client, server.user, 400) 53 const res = await login(server.url, client, server.user, 400)
54 54
55 expect(res.body.error).to.equal('Authentication failed.') 55 expect(res.body.error).to.contain('client is invalid')
56 }) 56 })
57 57
58 it('Should not login with an invalid username', async function () { 58 it('Should not login with an invalid username', async function () {
59 const user = { username: 'captain crochet', password: server.user.password } 59 const user = { username: 'captain crochet', password: server.user.password }
60 const res = await login(server.url, server.client, user, 400) 60 const res = await login(server.url, server.client, user, 400)
61 61
62 expect(res.body.error).to.equal('Authentication failed.') 62 expect(res.body.error).to.contain('credentials are invalid')
63 }) 63 })
64 64
65 it('Should not login with an invalid password', async function () { 65 it('Should not login with an invalid password', async function () {
66 const user = { username: server.user.username, password: 'mew_three' } 66 const user = { username: server.user.username, password: 'mew_three' }
67 const res = await login(server.url, server.client, user, 400) 67 const res = await login(server.url, server.client, user, 400)
68 68
69 expect(res.body.error).to.equal('Authentication failed.') 69 expect(res.body.error).to.contain('credentials are invalid')
70 }) 70 })
71 71
72 it('Should not be able to upload a video', async function () { 72 it('Should not be able to upload a video', async function () {
@@ -493,6 +493,27 @@ describe('Test users', function () {
493 } 493 }
494 }) 494 })
495 495
496 it('Should block and unblock a user', async function () {
497 const user16 = {
498 username: 'user_16',
499 password: 'my super password'
500 }
501 const resUser = await createUser(server.url, server.accessToken, user16.username, user16.password)
502 const user16Id = resUser.body.user.id
503
504 accessToken = await userLogin(server, user16)
505
506 await getMyUserInformation(server.url, accessToken, 200)
507 await blockUser(server.url, user16Id, server.accessToken)
508
509 await getMyUserInformation(server.url, accessToken, 401)
510 await userLogin(server, user16, 400)
511
512 await unblockUser(server.url, user16Id, server.accessToken)
513 accessToken = await userLogin(server, user16)
514 await getMyUserInformation(server.url, accessToken, 200)
515 })
516
496 after(async function () { 517 after(async function () {
497 killallServers([ server ]) 518 killallServers([ server ])
498 519
diff --git a/server/tests/utils/users/users.ts b/server/tests/utils/users/users.ts
index e24e721bd..7e15fc86e 100644
--- a/server/tests/utils/users/users.ts
+++ b/server/tests/utils/users/users.ts
@@ -134,6 +134,26 @@ function removeUser (url: string, userId: number | string, accessToken: string,
134 .expect(expectedStatus) 134 .expect(expectedStatus)
135} 135}
136 136
137function blockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
138 const path = '/api/v1/users'
139
140 return request(url)
141 .post(path + '/' + userId + '/block')
142 .set('Accept', 'application/json')
143 .set('Authorization', 'Bearer ' + accessToken)
144 .expect(expectedStatus)
145}
146
147function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
148 const path = '/api/v1/users'
149
150 return request(url)
151 .post(path + '/' + userId + '/unblock')
152 .set('Accept', 'application/json')
153 .set('Authorization', 'Bearer ' + accessToken)
154 .expect(expectedStatus)
155}
156
137function updateMyUser (options: { 157function updateMyUser (options: {
138 url: string 158 url: string
139 accessToken: string, 159 accessToken: string,
@@ -234,6 +254,8 @@ export {
234 updateUser, 254 updateUser,
235 updateMyUser, 255 updateMyUser,
236 getUserInformation, 256 getUserInformation,
257 blockUser,
258 unblockUser,
237 askResetPassword, 259 askResetPassword,
238 resetPassword, 260 resetPassword,
239 updateMyAvatar 261 updateMyAvatar
diff --git a/yarn.lock b/yarn.lock
index 206700a87..c1a3d6e88 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -191,6 +191,12 @@
191 "@types/events" "*" 191 "@types/events" "*"
192 "@types/node" "*" 192 "@types/node" "*"
193 193
194"@types/oauth2-server@^3.0.8":
195 version "3.0.8"
196 resolved "https://registry.yarnpkg.com/@types/oauth2-server/-/oauth2-server-3.0.8.tgz#0b7f5083790732ea00bf8c5e0b04b9fa1f22f22c"
197 dependencies:
198 "@types/express" "*"
199
194"@types/parse-torrent-file@*": 200"@types/parse-torrent-file@*":
195 version "4.0.1" 201 version "4.0.1"
196 resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.1.tgz#056a6c18f3fac0cd7c6c74540f00496a3225976b" 202 resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.1.tgz#056a6c18f3fac0cd7c6c74540f00496a3225976b"
@@ -2713,7 +2719,15 @@ fsevents@^1.2.2:
2713 nan "^2.9.2" 2719 nan "^2.9.2"
2714 node-pre-gyp "^0.10.0" 2720 node-pre-gyp "^0.10.0"
2715 2721
2716fstream@^1.0.0, fstream@^1.0.2: 2722fstream-ignore@^1.0.5:
2723 version "1.0.5"
2724 resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
2725 dependencies:
2726 fstream "^1.0.0"
2727 inherits "2"
2728 minimatch "^3.0.0"
2729
2730fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
2717 version "1.0.11" 2731 version "1.0.11"
2718 resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" 2732 resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
2719 dependencies: 2733 dependencies:
@@ -3227,7 +3241,7 @@ hashish@~0.0.4:
3227 dependencies: 3241 dependencies:
3228 traverse ">=0.2.4" 3242 traverse ">=0.2.4"
3229 3243
3230hawk@~3.1.3: 3244hawk@3.1.3, hawk@~3.1.3:
3231 version "3.1.3" 3245 version "3.1.3"
3232 resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" 3246 resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
3233 dependencies: 3247 dependencies:
@@ -4115,13 +4129,13 @@ levn@^0.3.0, levn@~0.3.0:
4115 prelude-ls "~1.1.2" 4129 prelude-ls "~1.1.2"
4116 type-check "~0.3.2" 4130 type-check "~0.3.2"
4117 4131
4118libxmljs@0.19.1: 4132libxmljs@0.19.0:
4119 version "0.19.1" 4133 version "0.19.0"
4120 resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.1.tgz#bc7a62822c4392363feaab49b116b4786b2d5ada" 4134 resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.0.tgz#dd0e635ce752af7701492ceb8c565ab74d494473"
4121 dependencies: 4135 dependencies:
4122 bindings "~1.3.0" 4136 bindings "~1.3.0"
4123 nan "~2.10.0" 4137 nan "~2.10.0"
4124 node-pre-gyp "~0.10.2" 4138 node-pre-gyp "~0.6.37"
4125 4139
4126lint-staged@^7.1.0: 4140lint-staged@^7.1.0:
4127 version "7.2.0" 4141 version "7.2.0"
@@ -4701,7 +4715,7 @@ minimalistic-assert@^1.0.1:
4701 version "1.0.1" 4715 version "1.0.1"
4702 resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" 4716 resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
4703 4717
4704"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: 4718"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
4705 version "3.0.4" 4719 version "3.0.4"
4706 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 4720 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
4707 dependencies: 4721 dependencies:
@@ -4959,7 +4973,7 @@ node-pre-gyp@0.10.2:
4959 semver "^5.3.0" 4973 semver "^5.3.0"
4960 tar "^4" 4974 tar "^4"
4961 4975
4962node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2: 4976node-pre-gyp@^0.10.0:
4963 version "0.10.3" 4977 version "0.10.3"
4964 resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" 4978 resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
4965 dependencies: 4979 dependencies:
@@ -4974,6 +4988,22 @@ node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2:
4974 semver "^5.3.0" 4988 semver "^5.3.0"
4975 tar "^4" 4989 tar "^4"
4976 4990
4991node-pre-gyp@~0.6.37:
4992 version "0.6.39"
4993 resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
4994 dependencies:
4995 detect-libc "^1.0.2"
4996 hawk "3.1.3"
4997 mkdirp "^0.5.1"
4998 nopt "^4.0.1"
4999 npmlog "^4.0.2"
5000 rc "^1.1.7"
5001 request "2.81.0"
5002 rimraf "^2.6.1"
5003 semver "^5.3.0"
5004 tar "^2.2.1"
5005 tar-pack "^3.4.0"
5006
4977node-sass@^4.9.0: 5007node-sass@^4.9.0:
4978 version "4.9.2" 5008 version "4.9.2"
4979 resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.2.tgz#5e63fe6bd0f2ae3ac9d6c14ede8620e2b8bdb437" 5009 resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.2.tgz#5e63fe6bd0f2ae3ac9d6c14ede8620e2b8bdb437"
@@ -5133,7 +5163,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
5133 version "0.8.2" 5163 version "0.8.2"
5134 resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" 5164 resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
5135 5165
5136oauth2-server@3.0.0: 5166oauth2-server@3.0.0, oauth2-server@^3.0.0:
5137 version "3.0.0" 5167 version "3.0.0"
5138 resolved "https://registry.yarnpkg.com/oauth2-server/-/oauth2-server-3.0.0.tgz#c46276b74c3d28634d59ee981f76b58a6459cc28" 5168 resolved "https://registry.yarnpkg.com/oauth2-server/-/oauth2-server-3.0.0.tgz#c46276b74c3d28634d59ee981f76b58a6459cc28"
5139 dependencies: 5169 dependencies:
@@ -5838,7 +5868,7 @@ raw-body@~1.1.0:
5838 bytes "1" 5868 bytes "1"
5839 string_decoder "0.10" 5869 string_decoder "0.10"
5840 5870
5841rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: 5871rc@^1.0.1, rc@^1.1.6, rc@^1.1.7, rc@^1.2.7:
5842 version "1.2.8" 5872 version "1.2.8"
5843 resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" 5873 resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
5844 dependencies: 5874 dependencies:
@@ -6044,32 +6074,7 @@ repeating@^2.0.0:
6044 dependencies: 6074 dependencies:
6045 is-finite "^1.0.0" 6075 is-finite "^1.0.0"
6046 6076
6047request@2.87.0, request@^2.81.0, request@^2.83.0: 6077request@2.81.0, "request@>=2.9.0 <2.82.0":
6048 version "2.87.0"
6049 resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
6050 dependencies:
6051 aws-sign2 "~0.7.0"
6052 aws4 "^1.6.0"
6053 caseless "~0.12.0"
6054 combined-stream "~1.0.5"
6055 extend "~3.0.1"
6056 forever-agent "~0.6.1"
6057 form-data "~2.3.1"
6058 har-validator "~5.0.3"
6059 http-signature "~1.2.0"
6060 is-typedarray "~1.0.0"
6061 isstream "~0.1.2"
6062 json-stringify-safe "~5.0.1"
6063 mime-types "~2.1.17"
6064 oauth-sign "~0.8.2"
6065 performance-now "^2.1.0"
6066 qs "~6.5.1"
6067 safe-buffer "^5.1.1"
6068 tough-cookie "~2.3.3"
6069 tunnel-agent "^0.6.0"
6070 uuid "^3.1.0"
6071
6072"request@>=2.9.0 <2.82.0":
6073 version "2.81.0" 6078 version "2.81.0"
6074 resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" 6079 resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
6075 dependencies: 6080 dependencies:
@@ -6096,6 +6101,31 @@ request@2.87.0, request@^2.81.0, request@^2.83.0:
6096 tunnel-agent "^0.6.0" 6101 tunnel-agent "^0.6.0"
6097 uuid "^3.0.0" 6102 uuid "^3.0.0"
6098 6103
6104request@2.87.0, request@^2.81.0, request@^2.83.0:
6105 version "2.87.0"
6106 resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
6107 dependencies:
6108 aws-sign2 "~0.7.0"
6109 aws4 "^1.6.0"
6110 caseless "~0.12.0"
6111 combined-stream "~1.0.5"
6112 extend "~3.0.1"
6113 forever-agent "~0.6.1"
6114 form-data "~2.3.1"
6115 har-validator "~5.0.3"
6116 http-signature "~1.2.0"
6117 is-typedarray "~1.0.0"
6118 isstream "~0.1.2"
6119 json-stringify-safe "~5.0.1"
6120 mime-types "~2.1.17"
6121 oauth-sign "~0.8.2"
6122 performance-now "^2.1.0"
6123 qs "~6.5.1"
6124 safe-buffer "^5.1.1"
6125 tough-cookie "~2.3.3"
6126 tunnel-agent "^0.6.0"
6127 uuid "^3.1.0"
6128
6099require-directory@^2.1.1: 6129require-directory@^2.1.1:
6100 version "2.1.1" 6130 version "2.1.1"
6101 resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 6131 resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -7081,6 +7111,19 @@ tar-fs@^1.13.0:
7081 pump "^1.0.0" 7111 pump "^1.0.0"
7082 tar-stream "^1.1.2" 7112 tar-stream "^1.1.2"
7083 7113
7114tar-pack@^3.4.0:
7115 version "3.4.1"
7116 resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
7117 dependencies:
7118 debug "^2.2.0"
7119 fstream "^1.0.10"
7120 fstream-ignore "^1.0.5"
7121 once "^1.3.3"
7122 readable-stream "^2.1.4"
7123 rimraf "^2.5.1"
7124 tar "^2.2.1"
7125 uid-number "^0.0.6"
7126
7084tar-stream@^1.1.2: 7127tar-stream@^1.1.2:
7085 version "1.6.1" 7128 version "1.6.1"
7086 resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395" 7129 resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
@@ -7093,7 +7136,7 @@ tar-stream@^1.1.2:
7093 to-buffer "^1.1.0" 7136 to-buffer "^1.1.0"
7094 xtend "^4.0.0" 7137 xtend "^4.0.0"
7095 7138
7096tar@^2.0.0: 7139tar@^2.0.0, tar@^2.2.1:
7097 version "2.2.1" 7140 version "2.2.1"
7098 resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" 7141 resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
7099 dependencies: 7142 dependencies:
@@ -7454,6 +7497,10 @@ uglify-to-browserify@~1.0.0:
7454 version "1.0.2" 7497 version "1.0.2"
7455 resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" 7498 resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
7456 7499
7500uid-number@^0.0.6:
7501 version "0.0.6"
7502 resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
7503
7457uint64be@^1.0.1: 7504uint64be@^1.0.1:
7458 version "1.0.1" 7505 version "1.0.1"
7459 resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-1.0.1.tgz#1f7154202f2a1b8af353871dda651bf34ce93e95" 7506 resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-1.0.1.tgz#1f7154202f2a1b8af353871dda651bf34ce93e95"