diff options
-rw-r--r-- | client/src/app/login/login.component.ts | 6 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | server/controllers/api/users.ts | 48 | ||||
-rw-r--r-- | server/helpers/custom-validators/users.ts | 13 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/migrations/0245-user-blocked.ts | 40 | ||||
-rw-r--r-- | server/lib/oauth-model.ts | 7 | ||||
-rw-r--r-- | server/middlewares/oauth.ts | 2 | ||||
-rw-r--r-- | server/middlewares/validators/users.ts | 21 | ||||
-rw-r--r-- | server/models/account/user.ts | 7 | ||||
-rw-r--r-- | server/models/oauth/oauth-token.ts | 8 | ||||
-rw-r--r-- | server/tests/api/check-params/users.ts | 16 | ||||
-rw-r--r-- | server/tests/api/users/users.ts | 31 | ||||
-rw-r--r-- | server/tests/utils/users/users.ts | 22 | ||||
-rw-r--r-- | yarn.lock | 121 |
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 { | |||
32 | import { | 32 | import { |
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 | ||
112 | usersRouter.post('/:id/block', | ||
113 | authenticate, | ||
114 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
115 | asyncMiddleware(usersBlockingValidator), | ||
116 | asyncMiddleware(blockUser) | ||
117 | ) | ||
118 | usersRouter.post('/:id/unblock', | ||
119 | authenticate, | ||
120 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
121 | asyncMiddleware(usersBlockingValidator), | ||
122 | asyncMiddleware(unblockUser) | ||
123 | ) | ||
124 | |||
111 | usersRouter.get('/:id', | 125 | usersRouter.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 | ||
295 | async 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 | |||
303 | async 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 | |||
281 | function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 311 | function 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 | |||
423 | function success (req: express.Request, res: express.Response, next: express.NextFunction) { | 453 | function success (req: express.Request, res: express.Response, next: express.NextFunction) { |
424 | res.end() | 454 | res.end() |
425 | } | 455 | } |
456 | |||
457 | async 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' | |||
2 | import * as validator from 'validator' | 2 | import * as validator from 'validator' |
3 | import { UserRole } from '../../../shared' | 3 | import { UserRole } from '../../../shared' |
4 | import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers' | 4 | import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers' |
5 | import { exists, isFileValid } from './misc' | 5 | import { exists, isFileValid, isBooleanValid } from './misc' |
6 | import { values } from 'lodash' | 6 | import { values } from 'lodash' |
7 | 7 | ||
8 | const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS | 8 | const 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 | ||
32 | function isBoolean (value: any) { | ||
33 | return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value)) | ||
34 | } | ||
35 | |||
36 | const nsfwPolicies = values(NSFW_POLICY_TYPES) | 32 | const nsfwPolicies = values(NSFW_POLICY_TYPES) |
37 | function isUserNSFWPolicyValid (value: any) { | 33 | function isUserNSFWPolicyValid (value: any) { |
38 | return exists(value) && nsfwPolicies.indexOf(value) !== -1 | 34 | return exists(value) && nsfwPolicies.indexOf(value) !== -1 |
39 | } | 35 | } |
40 | 36 | ||
41 | function isUserAutoPlayVideoValid (value: any) { | 37 | function isUserAutoPlayVideoValid (value: any) { |
42 | return isBoolean(value) | 38 | return isBooleanValid(value) |
39 | } | ||
40 | |||
41 | function isUserBlockedValid (value: any) { | ||
42 | return isBooleanValid(value) | ||
43 | } | 43 | } |
44 | 44 | ||
45 | function isUserRoleValid (value: any) { | 45 | function isUserRoleValid (value: any) { |
@@ -57,6 +57,7 @@ function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } | | |||
57 | // --------------------------------------------------------------------------- | 57 | // --------------------------------------------------------------------------- |
58 | 58 | ||
59 | export { | 59 | export { |
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 | ||
18 | const LAST_MIGRATION_VERSION = 240 | 18 | const 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { createClient } from 'redis' | ||
3 | import { CONFIG } from '../constants' | ||
4 | import { JobQueue } from '../../lib/job-queue' | ||
5 | import { initDatabaseModels } from '../database' | ||
6 | |||
7 | async 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 | |||
36 | function down (options) { | ||
37 | throw new Error('Not implemented.') | ||
38 | } | ||
39 | |||
40 | export { 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 @@ | |||
1 | import { AccessDeniedError} from 'oauth2-server' | ||
1 | import { logger } from '../helpers/logger' | 2 | import { logger } from '../helpers/logger' |
2 | import { UserModel } from '../models/account/user' | 3 | import { UserModel } from '../models/account/user' |
3 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 4 | import { 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 | ||
77 | const 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 | |||
77 | const deleteMeValidator = [ | 97 | const 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' | |||
21 | import { User, UserRole } from '../../../shared/models/users' | 21 | import { User, UserRole } from '../../../shared/models/users' |
22 | import { | 22 | import { |
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' | |||
3 | import { AccountModel } from '../account/account' | 3 | import { AccountModel } from '../account/account' |
4 | import { UserModel } from '../account/user' | 4 | import { UserModel } from '../account/user' |
5 | import { OAuthClientModel } from './oauth-client' | 5 | import { OAuthClientModel } from './oauth-client' |
6 | import { Transaction } from 'sequelize' | ||
6 | 7 | ||
7 | export type OAuthTokenInfo = { | 8 | export 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' | |||
8 | import { | 8 | import { |
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' |
13 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' | 13 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' |
14 | import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../utils/videos/video-imports' | 14 | import { 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' |
12 | import { follow } from '../../utils/server/follows' | 12 | import { follow } from '../../utils/server/follows' |
13 | import { setAccessTokensToServers } from '../../utils/users/login' | 13 | import { 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 | ||
137 | function 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 | |||
147 | function 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 | |||
137 | function updateMyUser (options: { | 157 | function 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 |
@@ -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 | ||
2716 | fstream@^1.0.0, fstream@^1.0.2: | 2722 | fstream-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 | |||
2730 | fstream@^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 | ||
3230 | hawk@~3.1.3: | 3244 | hawk@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 | ||
4118 | libxmljs@0.19.1: | 4132 | libxmljs@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 | ||
4126 | lint-staged@^7.1.0: | 4140 | lint-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 | ||
4962 | node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2: | 4976 | node-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 | ||
4991 | node-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 | |||
4977 | node-sass@^4.9.0: | 5007 | node-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 | ||
5136 | oauth2-server@3.0.0: | 5166 | oauth2-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 | ||
5841 | rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: | 5871 | rc@^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 | ||
6047 | request@2.87.0, request@^2.81.0, request@^2.83.0: | 6077 | request@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 | ||
6104 | request@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 | |||
6099 | require-directory@^2.1.1: | 6129 | require-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 | ||
7114 | tar-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 | |||
7084 | tar-stream@^1.1.2: | 7127 | tar-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 | ||
7096 | tar@^2.0.0: | 7139 | tar@^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 | ||
7500 | uid-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 | |||
7457 | uint64be@^1.0.1: | 7504 | uint64be@^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" |