From e69219184b1a3262ec5e617d30337b6431c9840c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 8 Aug 2018 14:58:21 +0200 Subject: Implement user blocking on server side --- client/src/app/login/login.component.ts | 6 +- package.json | 2 + server/controllers/api/users.ts | 48 ++++++++ server/helpers/custom-validators/users.ts | 13 ++- server/initializers/constants.ts | 2 +- .../initializers/migrations/0245-user-blocked.ts | 40 +++++++ server/lib/oauth-model.ts | 7 +- server/middlewares/oauth.ts | 2 +- server/middlewares/validators/users.ts | 21 ++++ server/models/account/user.ts | 7 ++ server/models/oauth/oauth-token.ts | 8 +- server/tests/api/check-params/users.ts | 16 ++- server/tests/api/users/users.ts | 31 +++++- server/tests/utils/users/users.ts | 22 ++++ yarn.lock | 121 ++++++++++++++------- 15 files changed, 287 insertions(+), 59 deletions(-) create mode 100644 server/initializers/migrations/0245-user-blocked.ts 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 { .subscribe( () => this.redirectService.redirectToHomepage(), - err => this.error = err.message + err => { + if (err.message.indexOf('credentials are invalid') !== -1) this.error = this.i18n('Incorrect username or password.') + else if (err.message.indexOf('blocked') !== -1) this.error = this.i18n('You account is blocked.') + else this.error = err.message + } ) } diff --git a/package.json b/package.json index 6348bbb6a..ba539bd31 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@types/bluebird": "3.5.21" }, "dependencies": { + "@types/oauth2-server": "^3.0.8", "async": "^2.0.0", "async-lock": "^1.1.2", "async-lru": "^1.1.1", @@ -113,6 +114,7 @@ "morgan": "^1.5.3", "multer": "^1.1.0", "nodemailer": "^4.4.2", + "oauth2-server": "^3.0.0", "parse-torrent": "^6.0.0", "password-generator": "^2.0.2", "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 { import { deleteMeValidator, usersAskResetPasswordValidator, + usersBlockingValidator, usersResetPasswordValidator, videoImportsSortValidator, videosSortValidator @@ -108,6 +109,19 @@ usersRouter.get('/', asyncMiddleware(listUsers) ) +usersRouter.post('/:id/block', + authenticate, + ensureUserHasRight(UserRight.MANAGE_USERS), + asyncMiddleware(usersBlockingValidator), + asyncMiddleware(blockUser) +) +usersRouter.post('/:id/unblock', + authenticate, + ensureUserHasRight(UserRight.MANAGE_USERS), + asyncMiddleware(usersBlockingValidator), + asyncMiddleware(unblockUser) +) + usersRouter.get('/:id', authenticate, ensureUserHasRight(UserRight.MANAGE_USERS), @@ -278,6 +292,22 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons return res.json(data) } +async function unblockUser (req: express.Request, res: express.Response, next: express.NextFunction) { + const user: UserModel = res.locals.user + + await changeUserBlock(res, user, false) + + return res.status(204).end() +} + +async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) { + const user: UserModel = res.locals.user + + await changeUserBlock(res, user, true) + + return res.status(204).end() +} + function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { return res.json((res.locals.user as UserModel).toFormattedJSON()) } @@ -423,3 +453,21 @@ async function resetUserPassword (req: express.Request, res: express.Response, n function success (req: express.Request, res: express.Response, next: express.NextFunction) { res.end() } + +async function changeUserBlock (res: express.Response, user: UserModel, block: boolean) { + const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) + + user.blocked = block + + await sequelizeTypescript.transaction(async t => { + await OAuthTokenModel.deleteUserToken(user.id, t) + + await user.save({ transaction: t }) + }) + + auditLogger.update( + res.locals.oauth.token.User.Account.Actor.getIdentifier(), + new UserAuditView(user.toFormattedJSON()), + oldUserAuditView + ) +} 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' import * as validator from 'validator' import { UserRole } from '../../../shared' import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers' -import { exists, isFileValid } from './misc' +import { exists, isFileValid, isBooleanValid } from './misc' import { values } from 'lodash' const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS @@ -29,17 +29,17 @@ function isUserDescriptionValid (value: string) { return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION)) } -function isBoolean (value: any) { - return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value)) -} - const nsfwPolicies = values(NSFW_POLICY_TYPES) function isUserNSFWPolicyValid (value: any) { return exists(value) && nsfwPolicies.indexOf(value) !== -1 } function isUserAutoPlayVideoValid (value: any) { - return isBoolean(value) + return isBooleanValid(value) +} + +function isUserBlockedValid (value: any) { + return isBooleanValid(value) } function isUserRoleValid (value: any) { @@ -57,6 +57,7 @@ function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } | // --------------------------------------------------------------------------- export { + isUserBlockedValid, isUserPasswordValid, isUserRoleValid, 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') // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 240 +const LAST_MIGRATION_VERSION = 245 // --------------------------------------------------------------------------- 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 @@ +import * as Sequelize from 'sequelize' +import { createClient } from 'redis' +import { CONFIG } from '../constants' +import { JobQueue } from '../../lib/job-queue' +import { initDatabaseModels } from '../database' + +async function up (utils: { + transaction: Sequelize.Transaction + queryInterface: Sequelize.QueryInterface + sequelize: Sequelize.Sequelize +}): Promise { + { + const data = { + type: Sequelize.BOOLEAN, + allowNull: true, + defaultValue: null + } + await utils.queryInterface.addColumn('user', 'blocked', data) + } + + { + const query = 'UPDATE "user" SET "blocked" = false' + await utils.sequelize.query(query) + } + + { + const data = { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: null + } + await utils.queryInterface.changeColumn('user', 'blocked', data) + } +} + +function down (options) { + throw new Error('Not implemented.') +} + +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 @@ +import { AccessDeniedError} from 'oauth2-server' import { logger } from '../helpers/logger' import { UserModel } from '../models/account/user' import { OAuthClientModel } from '../models/oauth/oauth-client' @@ -34,6 +35,8 @@ async function getUser (usernameOrEmail: string, password: string) { const passwordMatch = await user.isPasswordMatch(password) if (passwordMatch === false) return null + if (user.blocked) throw new AccessDeniedError('User is blocked.') + return user } @@ -67,9 +70,7 @@ async function saveToken (token: TokenInfo, client: OAuthClientModel, user: User } const tokenCreated = await OAuthTokenModel.create(tokenToCreate) - const tokenToReturn = Object.assign(tokenCreated, { client, user }) - - return tokenToReturn + return Object.assign(tokenCreated, { client, user }) } // --------------------------------------------------------------------------- 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 if (err) { return res.status(err.status) .json({ - error: 'Authentication failed.', + error: err.message, code: err.name }) .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 = [ } ] +const usersBlockingValidator = [ + param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking usersRemove parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + if (!await checkUserIdExist(req.params.id, res)) return + + const user = res.locals.user + if (user.username === 'root') { + return res.status(400) + .send({ error: 'Cannot block the root user' }) + .end() + } + + return next() + } +] + const deleteMeValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { const user: UserModel = res.locals.oauth.token.User @@ -230,6 +250,7 @@ export { usersAddValidator, deleteMeValidator, usersRegisterValidator, + usersBlockingValidator, usersRemoveValidator, usersUpdateValidator, 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' import { User, UserRole } from '../../../shared/models/users' import { isUserAutoPlayVideoValid, + isUserBlockedValid, isUserNSFWPolicyValid, isUserPasswordValid, isUserRoleValid, @@ -100,6 +101,12 @@ export class UserModel extends Model { @Column autoPlayVideo: boolean + @AllowNull(false) + @Default(false) + @Is('UserBlocked', value => throwIfNotValid(value, isUserBlockedValid, 'blocked boolean')) + @Column + blocked: boolean + @AllowNull(false) @Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role')) @Column 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' import { AccountModel } from '../account/account' import { UserModel } from '../account/user' import { OAuthClientModel } from './oauth-client' +import { Transaction } from 'sequelize' export type OAuthTokenInfo = { refreshToken: string @@ -125,7 +126,7 @@ export class OAuthTokenModel extends Model { } as OAuthTokenInfo }) .catch(err => { - logger.info('getRefreshToken error.', { err }) + logger.error('getRefreshToken error.', { err }) throw err }) } @@ -163,11 +164,12 @@ export class OAuthTokenModel extends Model { }) } - static deleteUserToken (userId: number) { + static deleteUserToken (userId: number, t?: Transaction) { const query = { where: { userId - } + }, + transaction: t } 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' import { createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest, makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers, - updateUser, uploadVideo, userLogin, deleteMe + updateUser, uploadVideo, userLogin, deleteMe, unblockUser, blockUser } from '../../utils' import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../utils/videos/video-imports' @@ -455,17 +455,29 @@ describe('Test users API validators', function () { }) }) - describe('When removing an user', function () { + describe('When blocking/unblocking/removing user', function () { it('Should fail with an incorrect id', async function () { await removeUser(server.url, 'blabla', server.accessToken, 400) + await blockUser(server.url, 'blabla', server.accessToken, 400) + await unblockUser(server.url, 'blabla', server.accessToken, 400) }) it('Should fail with the root user', async function () { await removeUser(server.url, rootId, server.accessToken, 400) + await blockUser(server.url, rootId, server.accessToken, 400) + await unblockUser(server.url, rootId, server.accessToken, 400) }) it('Should return 404 with a non existing id', async function () { await removeUser(server.url, 4545454, server.accessToken, 404) + await blockUser(server.url, 4545454, server.accessToken, 404) + await unblockUser(server.url, 4545454, server.accessToken, 404) + }) + + it('Should fail with a non admin user', async function () { + await removeUser(server.url, userId, userAccessToken, 403) + await blockUser(server.url, userId, userAccessToken, 403) + await unblockUser(server.url, userId, userAccessToken, 403) }) }) 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 { createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating, getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo, registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin, - deleteMe + deleteMe, blockUser, unblockUser } from '../../utils/index' import { follow } from '../../utils/server/follows' import { setAccessTokensToServers } from '../../utils/users/login' @@ -45,28 +45,28 @@ describe('Test users', function () { const client = { id: 'client', secret: server.client.secret } const res = await login(server.url, client, server.user, 400) - expect(res.body.error).to.equal('Authentication failed.') + expect(res.body.error).to.contain('client is invalid') }) it('Should not login with an invalid client secret', async function () { const client = { id: server.client.id, secret: 'coucou' } const res = await login(server.url, client, server.user, 400) - expect(res.body.error).to.equal('Authentication failed.') + expect(res.body.error).to.contain('client is invalid') }) it('Should not login with an invalid username', async function () { const user = { username: 'captain crochet', password: server.user.password } const res = await login(server.url, server.client, user, 400) - expect(res.body.error).to.equal('Authentication failed.') + expect(res.body.error).to.contain('credentials are invalid') }) it('Should not login with an invalid password', async function () { const user = { username: server.user.username, password: 'mew_three' } const res = await login(server.url, server.client, user, 400) - expect(res.body.error).to.equal('Authentication failed.') + expect(res.body.error).to.contain('credentials are invalid') }) it('Should not be able to upload a video', async function () { @@ -493,6 +493,27 @@ describe('Test users', function () { } }) + it('Should block and unblock a user', async function () { + const user16 = { + username: 'user_16', + password: 'my super password' + } + const resUser = await createUser(server.url, server.accessToken, user16.username, user16.password) + const user16Id = resUser.body.user.id + + accessToken = await userLogin(server, user16) + + await getMyUserInformation(server.url, accessToken, 200) + await blockUser(server.url, user16Id, server.accessToken) + + await getMyUserInformation(server.url, accessToken, 401) + await userLogin(server, user16, 400) + + await unblockUser(server.url, user16Id, server.accessToken) + accessToken = await userLogin(server, user16) + await getMyUserInformation(server.url, accessToken, 200) + }) + after(async function () { killallServers([ server ]) 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, .expect(expectedStatus) } +function blockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) { + const path = '/api/v1/users' + + return request(url) + .post(path + '/' + userId + '/block') + .set('Accept', 'application/json') + .set('Authorization', 'Bearer ' + accessToken) + .expect(expectedStatus) +} + +function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) { + const path = '/api/v1/users' + + return request(url) + .post(path + '/' + userId + '/unblock') + .set('Accept', 'application/json') + .set('Authorization', 'Bearer ' + accessToken) + .expect(expectedStatus) +} + function updateMyUser (options: { url: string accessToken: string, @@ -234,6 +254,8 @@ export { updateUser, updateMyUser, getUserInformation, + blockUser, + unblockUser, askResetPassword, resetPassword, updateMyAvatar diff --git a/yarn.lock b/yarn.lock index 206700a87..c1a3d6e88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -191,6 +191,12 @@ "@types/events" "*" "@types/node" "*" +"@types/oauth2-server@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@types/oauth2-server/-/oauth2-server-3.0.8.tgz#0b7f5083790732ea00bf8c5e0b04b9fa1f22f22c" + dependencies: + "@types/express" "*" + "@types/parse-torrent-file@*": version "4.0.1" 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: nan "^2.9.2" node-pre-gyp "^0.10.0" -fstream@^1.0.0, fstream@^1.0.2: +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" dependencies: @@ -3227,7 +3241,7 @@ hashish@~0.0.4: dependencies: traverse ">=0.2.4" -hawk@~3.1.3: +hawk@3.1.3, hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" dependencies: @@ -4115,13 +4129,13 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libxmljs@0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.1.tgz#bc7a62822c4392363feaab49b116b4786b2d5ada" +libxmljs@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.0.tgz#dd0e635ce752af7701492ceb8c565ab74d494473" dependencies: bindings "~1.3.0" nan "~2.10.0" - node-pre-gyp "~0.10.2" + node-pre-gyp "~0.6.37" lint-staged@^7.1.0: version "7.2.0" @@ -4701,7 +4715,7 @@ minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -4959,7 +4973,7 @@ node-pre-gyp@0.10.2: semver "^5.3.0" tar "^4" -node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2: +node-pre-gyp@^0.10.0: version "0.10.3" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" dependencies: @@ -4974,6 +4988,22 @@ node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2: semver "^5.3.0" tar "^4" +node-pre-gyp@~0.6.37: + version "0.6.39" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" + dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + node-sass@^4.9.0: version "4.9.2" 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: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -oauth2-server@3.0.0: +oauth2-server@3.0.0, oauth2-server@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/oauth2-server/-/oauth2-server-3.0.0.tgz#c46276b74c3d28634d59ee981f76b58a6459cc28" dependencies: @@ -5838,7 +5868,7 @@ raw-body@~1.1.0: bytes "1" string_decoder "0.10" -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: +rc@^1.0.1, rc@^1.1.6, rc@^1.1.7, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" dependencies: @@ -6044,32 +6074,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2.87.0, request@^2.81.0, request@^2.83.0: - version "2.87.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -"request@>=2.9.0 <2.82.0": +request@2.81.0, "request@>=2.9.0 <2.82.0": version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -6096,6 +6101,31 @@ request@2.87.0, request@^2.81.0, request@^2.83.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@2.87.0, request@^2.81.0, request@^2.83.0: + version "2.87.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -7081,6 +7111,19 @@ tar-fs@^1.13.0: pump "^1.0.0" tar-stream "^1.1.2" +tar-pack@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + tar-stream@^1.1.2: version "1.6.1" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395" @@ -7093,7 +7136,7 @@ tar-stream@^1.1.2: to-buffer "^1.1.0" xtend "^4.0.0" -tar@^2.0.0: +tar@^2.0.0, tar@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" dependencies: @@ -7454,6 +7497,10 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + uint64be@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-1.0.1.tgz#1f7154202f2a1b8af353871dda651bf34ce93e95" -- cgit v1.2.3