]> git.immae.eu Git - perso/Immae/Config/Nix/NUR.git/blame - pkgs/webapps/peertube/ldap.patch
Use fetchgit rather than builtins
[perso/Immae/Config/Nix/NUR.git] / pkgs / webapps / peertube / ldap.patch
CommitLineData
5b1c5dbd 1commit ffb4a59047a014d6bb050b67a2fc7bc116be4682
24fd1fe6
IB
2Author: Ismaël Bouya <ismael.bouya@normalesup.org>
3Date: Tue Feb 12 18:47:53 2019 +0100
4
5 Add LDAP authentication
6
7diff --git a/config/default.yaml b/config/default.yaml
5b1c5dbd 8index 3260c62fc..dcce721b9 100644
24fd1fe6
IB
9--- a/config/default.yaml
10+++ b/config/default.yaml
5b1c5dbd 11@@ -51,6 +51,19 @@ redis:
24fd1fe6
IB
12 auth: null
13 db: 0
14
5b1c5dbd
IB
15+auth:
16+ local:
17+ enabled: true
18+ ldap:
19+ enabled: true
20+ url: ldap://localhost:389/dc=example,dc=com
21+ insecure_tls: false
22+ bind_dn: cn=admin,dc=example,dc=com
23+ bind_password: adminPass
24+ base: dc=example,dc=com
25+ mail_entry: "mail"
26+ user_filter: "(|(email=%username%)(uid=%username%))"
24fd1fe6
IB
27+
28 smtp:
29 hostname: null
30 port: 465
31diff --git a/config/production.yaml.example b/config/production.yaml.example
5b1c5dbd 32index 30cd2ffe0..c56691bf4 100644
24fd1fe6
IB
33--- a/config/production.yaml.example
34+++ b/config/production.yaml.example
5b1c5dbd 35@@ -51,6 +51,19 @@ redis:
24fd1fe6
IB
36 auth: null
37 db: 0
38
5b1c5dbd
IB
39+auth:
40+ local:
41+ enabled: true
42+ ldap:
43+ enabled: true
44+ url: ldap://localhost:389/dc=example,dc=com
45+ insecure_tls: false
46+ bind_dn: cn=admin,dc=example,dc=com
47+ bind_password: adminPass
48+ base: dc=example,dc=com
49+ mail_entry: "mail"
50+ user_filter: "(|(email=%username%)(uid=%username%))"
24fd1fe6
IB
51+
52 # SMTP server to send emails
53 smtp:
54 hostname: null
55diff --git a/package.json b/package.json
5b1c5dbd 56index 49d9faf97..31eccf797 100644
24fd1fe6
IB
57--- a/package.json
58+++ b/package.json
5b1c5dbd
IB
59@@ -112,6 +112,7 @@
60 "iso-639-3": "^1.0.1",
24fd1fe6 61 "js-yaml": "^3.5.4",
5b1c5dbd 62 "jsonld": "~2.0.1",
24fd1fe6
IB
63+ "ldapjs": "^1.0.2",
64 "lodash": "^4.17.10",
5b1c5dbd 65 "lru-cache": "^5.1.1",
24fd1fe6 66 "magnet-uri": "^5.1.4",
5b1c5dbd
IB
67diff --git a/server/initializers/config.ts b/server/initializers/config.ts
68index 7fd77f3e8..45a667826 100644
69--- a/server/initializers/config.ts
70+++ b/server/initializers/config.ts
71@@ -34,6 +34,21 @@ const CONFIG = {
24fd1fe6
IB
72 AUTH: config.has('redis.auth') ? config.get<string>('redis.auth') : null,
73 DB: config.has('redis.db') ? config.get<number>('redis.db') : null
74 },
5b1c5dbd
IB
75+ AUTH: {
76+ LOCAL: {
77+ ENABLED: config.has('auth.local.enabled') ? config.get<boolean>('auth.local.enabled') : true,
78+ },
79+ LDAP: {
80+ ENABLED: config.has('auth.ldap.enabled') ? config.get<boolean>('auth.ldap.enabled') : false,
81+ URL: config.has('auth.ldap.url') ? config.get<string>('auth.ldap.url') : null,
82+ INSECURE_TLS: config.has('auth.ldap.insecure_tls') ? config.get<boolean>('auth.ldap.insecure_tls') : false,
83+ BIND_DN: config.has('auth.ldap.bind_dn') ? config.get<string>('auth.ldap.bind_dn') : null,
84+ BIND_PASSWORD: config.has('auth.ldap.bind_password') ? config.get<string>('auth.ldap.bind_password') : null,
85+ BASE: config.has('auth.ldap.base') ? config.get<string>('auth.ldap.base') : null,
86+ MAIL_ENTRY: config.has('auth.ldap.mail_entry') ? config.get<string>('auth.ldap.mail_entry') : 'mail',
87+ USER_FILTER: config.has('auth.ldap.user_filter') ? config.get<string>('auth.ldap.user_filter') : null
88+ },
24fd1fe6
IB
89+ },
90 SMTP: {
91 HOSTNAME: config.get<string>('smtp.hostname'),
92 PORT: config.get<number>('smtp.port'),
5b1c5dbd 93diff --git a/server/initializers/migrations/0375-user-ldap-dn.ts b/server/initializers/migrations/0375-user-ldap-dn.ts
24fd1fe6 94new file mode 100644
5b1c5dbd 95index 000000000..a9d68124b
24fd1fe6 96--- /dev/null
5b1c5dbd 97+++ b/server/initializers/migrations/0375-user-ldap-dn.ts
24fd1fe6
IB
98@@ -0,0 +1,26 @@
99+import * as Sequelize from 'sequelize'
100+
101+async function up (utils: {
102+ transaction: Sequelize.Transaction,
103+ queryInterface: Sequelize.QueryInterface,
104+ sequelize: Sequelize.Sequelize
105+}): Promise<void> {
106+
107+ {
108+ const data = {
109+ type: Sequelize.STRING,
110+ allowNull: true,
111+ defaultValue: null
112+ }
113+ await utils.queryInterface.addColumn('user', 'ldapDn', data)
114+ }
115+}
116+
117+function down (options) {
118+ throw new Error('Not implemented.')
119+}
120+
121+export {
122+ up,
123+ down
124+}
5b1c5dbd
IB
125diff --git a/server/lib/ldap.ts b/server/lib/ldap.ts
126new file mode 100644
127index 000000000..e6601e5cb
128--- /dev/null
129+++ b/server/lib/ldap.ts
130@@ -0,0 +1,89 @@
131+import * as express from 'express'
132+import { createClient, Client, parseFilter } from 'ldapjs'
133+import { logger } from '../helpers/logger'
134+import { CONFIG } from '../initializers/config'
135+
136+class Ldap {
137+
138+ private static instance: Ldap
139+ private initialized = false
140+ private client: Client
141+ private prefix: string
142+
143+ private constructor () {}
144+
145+ init () {
146+ // Already initialized
147+ if (this.initialized === true) return
148+ this.initialized = true
149+
150+ this.client = createClient(Ldap.getLdapClientOptions())
151+ }
152+
153+ static getLdapClientOptions () {
154+ return Object.assign({}, {
155+ url: CONFIG.AUTH.LDAP.URL,
156+ reconnect: true,
157+ tlsOptions: { rejectUnauthorized: !CONFIG.AUTH.LDAP.INSECURE_TLS }
158+ })
159+ }
160+
161+ getClient () {
162+ this.init()
163+ return this.client
164+ }
165+
166+ findUser (username: string) {
167+ const filter = parseFilter(CONFIG.AUTH.LDAP.USER_FILTER)
168+ filter.forEach(function (element) {
169+ if (element.value === '%username%') element.value = username
170+ })
171+ const opts = {
172+ filter,
173+ scope: 'sub',
174+ attributes: [ CONFIG.AUTH.LDAP.MAIL_ENTRY, 'dn' ]
175+ }
176+
177+ const client = this.getClient()
178+
179+ return new Promise(function (resolve, reject) {
180+ client.bind(CONFIG.AUTH.LDAP.BIND_DN, CONFIG.AUTH.LDAP.BIND_PASSWORD, function (err) {
181+ if (err) reject(err)
182+ let entries = []
183+ client.search(CONFIG.AUTH.LDAP.BASE, opts, function (err, search) {
184+ if (err) reject(err)
185+ search.on('searchEntry', function (entry) {
186+ entries.push(entry.object)
187+ })
188+ search.on('end', function (result) {
189+ if (entries.length === 1) {
190+ resolve(entries[0])
191+ } else {
192+ reject("No user found corresponding to this username")
193+ }
194+ })
195+ })
196+ })
197+ })
198+ }
199+
200+ checkUser (dn: string, password: string) {
201+ const client = this.getClient()
202+ return new Promise(function (resolve, reject) {
203+ client.bind(dn, password, function (err) {
204+ resolve(!err)
205+ })
206+ })
207+ }
208+
209+
210+ static get Instance () {
211+ return this.instance || (this.instance = new this())
212+ }
213+}
214+
215+// ---------------------------------------------------------------------------
216+
217+export {
218+ Ldap
219+}
24fd1fe6 220diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts
5b1c5dbd 221index 086856f41..ab10effd0 100644
24fd1fe6
IB
222--- a/server/lib/oauth-model.ts
223+++ b/server/lib/oauth-model.ts
5b1c5dbd
IB
224@@ -9,6 +9,7 @@ import { Transaction } from 'sequelize'
225 import { CONFIG } from '../initializers/config'
226 import * as LRUCache from 'lru-cache'
227 import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
228+import { MUserDefault } from '@server/typings/models'
229
230 type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
231
232@@ -74,7 +75,13 @@ function getRefreshToken (refreshToken: string) {
24fd1fe6
IB
233 async function getUser (usernameOrEmail: string, password: string) {
234 logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).')
235
236- const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail)
5b1c5dbd
IB
237+ let user : MUserDefault
238+ if (CONFIG.AUTH.LDAP.ENABLED) {
24fd1fe6
IB
239+ user = await UserModel.findOrCreateLDAPUser(usernameOrEmail)
240+ }
5b1c5dbd 241+ if (!user && CONFIG.AUTH.LOCAL.ENABLED) {
24fd1fe6
IB
242+ user = await UserModel.loadByUsernameOrEmail(usernameOrEmail)
243+ }
244 if (!user) return null
245
246 const passwordMatch = await user.isPasswordMatch(password)
247diff --git a/server/models/account/user.ts b/server/models/account/user.ts
5b1c5dbd 248index 4c2c5e278..0b38f7cb2 100644
24fd1fe6
IB
249--- a/server/models/account/user.ts
250+++ b/server/models/account/user.ts
5b1c5dbd
IB
251@@ -1,4 +1,5 @@
252 import { FindOptions, literal, Op, QueryTypes, where, fn, col } from 'sequelize'
253+import { Ldap } from '../../lib/ldap'
24fd1fe6
IB
254 import {
255 AfterDestroy,
5b1c5dbd
IB
256 AfterUpdate,
257@@ -50,7 +51,9 @@ import { AccountModel } from './account'
24fd1fe6
IB
258 import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
259 import { values } from 'lodash'
5b1c5dbd
IB
260 import { DEFAULT_THEME_NAME, DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants'
261+import { CONFIG } from '../../initializers/config'
24fd1fe6 262 import { clearCacheByUserId } from '../../lib/oauth-model'
5b1c5dbd 263+import { createUserAccountAndChannelAndPlaylist } from '../../lib/user'
24fd1fe6
IB
264 import { UserNotificationSettingModel } from './user-notification-setting'
265 import { VideoModel } from '../video/video'
266 import { ActorModel } from '../activitypub/actor'
5b1c5dbd 267@@ -149,6 +152,11 @@ export class UserModel extends Model<UserModel> {
24fd1fe6 268 @Column(DataType.STRING(400))
5b1c5dbd 269 pendingEmail: string
24fd1fe6
IB
270
271+ @AllowNull(true)
272+ @Default(null)
273+ @Column
274+ ldapDn: string
275+
276 @AllowNull(true)
277 @Default(null)
5b1c5dbd
IB
278 @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean', true))
279@@ -440,6 +448,48 @@ export class UserModel extends Model<UserModel> {
24fd1fe6
IB
280 return UserModel.findOne(query)
281 }
282
283+ static loadByLdapDn (ldapDn: string) {
284+ const query = {
285+ where: {
286+ ldapDn
287+ }
288+ }
289+
290+ return UserModel.findOne(query)
291+ }
292+
5b1c5dbd
IB
293+ static async findOrCreateLDAPUser (username: string) {
294+ try {
295+ const userInfos = await Ldap.Instance.findUser(username)
296+ const user = await UserModel.loadByLdapDn(userInfos['dn'])
297+ if (user) {
298+ return user
299+ } else {
300+ return await UserModel.createLDAPUser(username, userInfos)
301+ }
302+ } catch (e) {
303+ return null
24fd1fe6 304+ }
24fd1fe6
IB
305+ }
306+
5b1c5dbd
IB
307+ static async createLDAPUser (username: string, userInfos: {}) {
308+ const userToCreate = new UserModel({
309+ username,
310+ password: 'SomeInvalidPassword',
311+ email: userInfos[CONFIG.AUTH.LDAP.MAIL_ENTRY],
312+ ldapDn: userInfos['dn'],
313+ nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
314+ autoPlayVideo: true,
315+ role: UserRole.USER,
316+ videoQuota: CONFIG.USER.VIDEO_QUOTA,
317+ videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
318+ emailVerified: true,
319+ adminFlags: UserAdminFlag.NONE
320+ })
321+ const { user } = await createUserAccountAndChannelAndPlaylist({ userToCreate })
322+ return user
24fd1fe6
IB
323+ }
324+
5b1c5dbd 325 static loadForMeAPI (username: string): Bluebird<MUserNotifSettingChannelDefault> {
24fd1fe6
IB
326 const query = {
327 where: {
5b1c5dbd 328@@ -627,7 +677,11 @@ export class UserModel extends Model<UserModel> {
24fd1fe6
IB
329 }
330
24fd1fe6
IB
331 isPasswordMatch (password: string) {
332- return comparePassword(password, this.password)
333+ if (this.ldapDn === null) {
334+ return comparePassword(password, this.password)
335+ } else {
5b1c5dbd 336+ return Ldap.Instance.checkUser(this.ldapDn, password)
24fd1fe6
IB
337+ }
338 }
339
5b1c5dbd 340 toFormattedJSON (this: MUserFormattable, parameters: { withAdminFlags?: boolean } = {}): User {
24fd1fe6 341diff --git a/yarn.lock b/yarn.lock
5b1c5dbd 342index 76ce7ed27..f087746df 100644
24fd1fe6
IB
343--- a/yarn.lock
344+++ b/yarn.lock
5b1c5dbd
IB
345@@ -616,6 +616,11 @@ arraybuffer.slice@~0.0.7:
346 resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
347 integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==
24fd1fe6
IB
348
349+asn1@0.2.3:
350+ version "0.2.3"
351+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
352+ integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=
353+
354 asn1@~0.2.3:
355 version "0.2.4"
356 resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
5b1c5dbd 357@@ -623,6 +628,11 @@ asn1@~0.2.3:
24fd1fe6
IB
358 dependencies:
359 safer-buffer "~2.1.0"
360
361+assert-plus@0.1.5:
362+ version "0.1.5"
363+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160"
364+ integrity sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=
365+
366 assert-plus@1.0.0, assert-plus@^1.0.0:
367 version "1.0.0"
368 resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
5b1c5dbd 369@@ -692,6 +702,13 @@ backo2@1.0.2:
24fd1fe6
IB
370 resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
371 integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
372
373+backoff@^2.5.0:
374+ version "2.5.0"
375+ resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f"
376+ integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=
377+ dependencies:
378+ precond "0.2"
379+
380 balanced-match@^1.0.0:
381 version "1.0.0"
382 resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
5b1c5dbd
IB
383@@ -1001,6 +1018,16 @@ bull@^3.4.2:
384 util.promisify "^1.0.0"
385 uuid "^3.3.3"
24fd1fe6
IB
386
387+bunyan@^1.8.3:
388+ version "1.8.12"
389+ resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797"
390+ integrity sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=
391+ optionalDependencies:
392+ dtrace-provider "~0.8"
393+ moment "^2.10.6"
394+ mv "~2"
395+ safe-json-stringify "~1"
396+
397 busboy@^0.2.11:
398 version "0.2.14"
399 resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453"
5b1c5dbd
IB
400@@ -1619,7 +1646,7 @@ d@1, d@^1.0.1:
401 es5-ext "^0.10.50"
402 type "^1.0.1"
24fd1fe6
IB
403
404-dashdash@^1.12.0:
405+dashdash@^1.12.0, dashdash@^1.14.0:
406 version "1.14.1"
407 resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
408 integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
5b1c5dbd 409@@ -1845,6 +1872,13 @@ double-ended-queue@^2.1.0-0:
24fd1fe6
IB
410 resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
411 integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=
412
413+dtrace-provider@~0.8:
5b1c5dbd
IB
414+ version "0.8.8"
415+ resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e"
416+ integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==
24fd1fe6 417+ dependencies:
5b1c5dbd 418+ nan "^2.14.0"
24fd1fe6
IB
419+
420 duplexer3@^0.1.4:
421 version "0.1.4"
422 resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
5b1c5dbd
IB
423@@ -2228,6 +2262,11 @@ extend@^3.0.0, extend@~3.0.0, extend@~3.0.2:
424 resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
425 integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
24fd1fe6
IB
426
427+extsprintf@1.2.0:
428+ version "1.2.0"
429+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529"
430+ integrity sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=
431+
432 extsprintf@1.3.0:
433 version "1.3.0"
434 resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
5b1c5dbd 435@@ -2567,6 +2606,17 @@ glob@7.1.3:
24fd1fe6
IB
436 once "^1.3.0"
437 path-is-absolute "^1.0.0"
438
439+glob@^6.0.1:
440+ version "6.0.4"
441+ resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
442+ integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=
443+ dependencies:
444+ inflight "^1.0.4"
445+ inherits "2"
446+ minimatch "2 || 3"
447+ once "^1.3.0"
448+ path-is-absolute "^1.0.0"
449+
5b1c5dbd
IB
450 glob@^7.0.3, glob@^7.1.1, glob@^7.1.3:
451 version "7.1.6"
452 resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
453@@ -3356,6 +3406,30 @@ latest-version@^3.0.0:
24fd1fe6 454 dependencies:
5b1c5dbd 455 package-json "^4.0.0"
24fd1fe6
IB
456
457+ldap-filter@0.2.2:
458+ version "0.2.2"
459+ resolved "https://registry.yarnpkg.com/ldap-filter/-/ldap-filter-0.2.2.tgz#f2b842be0b86da3352798505b31ebcae590d77d0"
460+ integrity sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=
461+ dependencies:
462+ assert-plus "0.1.5"
463+
464+ldapjs@^1.0.2:
465+ version "1.0.2"
466+ resolved "https://registry.yarnpkg.com/ldapjs/-/ldapjs-1.0.2.tgz#544ff7032b7b83c68f0701328d9297aa694340f9"
467+ integrity sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=
468+ dependencies:
469+ asn1 "0.2.3"
470+ assert-plus "^1.0.0"
471+ backoff "^2.5.0"
472+ bunyan "^1.8.3"
473+ dashdash "^1.14.0"
474+ ldap-filter "0.2.2"
475+ once "^1.4.0"
476+ vasync "^1.6.4"
477+ verror "^1.8.1"
478+ optionalDependencies:
479+ dtrace-provider "~0.8"
480+
5b1c5dbd
IB
481 libxmljs@0.19.7:
482 version "0.19.7"
483 resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.7.tgz#96c2151b0b73f33dd29917edec82902587004e5a"
484@@ -3724,7 +3798,7 @@ mimic-response@^2.0.0:
485 resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46"
486 integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==
24fd1fe6 487
5b1c5dbd
IB
488-minimatch@3.0.4, minimatch@^3.0.4:
489+"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4:
24fd1fe6
IB
490 version "3.0.4"
491 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
492 integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
5b1c5dbd
IB
493@@ -3825,7 +3899,7 @@ moment-timezone@^0.5.21, moment-timezone@^0.5.25:
494 dependencies:
495 moment ">= 2.9.0"
24fd1fe6 496
5b1c5dbd
IB
497-"moment@>= 2.9.0", moment@^2.24.0:
498+"moment@>= 2.9.0", moment@^2.10.6, moment@^2.24.0:
499 version "2.24.0"
500 resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
501 integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
502@@ -3898,6 +3972,15 @@ mute-stream@~0.0.4:
503 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
504 integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
24fd1fe6
IB
505
506+mv@~2:
507+ version "2.1.1"
508+ resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2"
509+ integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=
510+ dependencies:
511+ mkdirp "~0.5.1"
512+ ncp "~2.0.0"
513+ rimraf "~2.4.0"
514+
5b1c5dbd
IB
515 nan@2.14.0, nan@^2.14.0, nan@~2.14.0:
516 version "2.14.0"
517 resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
518@@ -3913,6 +3996,11 @@ ncp@1.0.x:
24fd1fe6
IB
519 resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246"
520 integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=
521
522+ncp@~2.0.0:
523+ version "2.0.0"
524+ resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
525+ integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=
526+
527 needle@^2.2.1:
5b1c5dbd
IB
528 version "2.4.0"
529 resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
530@@ -4597,6 +4685,11 @@ prebuild-install@^5.3.3:
24fd1fe6
IB
531 tunnel-agent "^0.6.0"
532 which-pm-runs "^1.0.0"
533
534+precond@0.2:
535+ version "0.2.3"
536+ resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac"
537+ integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=
538+
5b1c5dbd
IB
539 prepend-http@^1.0.1:
540 version "1.0.4"
541 resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
542@@ -5032,6 +5125,13 @@ rimraf@^3.0.0:
24fd1fe6 543 dependencies:
5b1c5dbd 544 glob "^7.1.3"
24fd1fe6
IB
545
546+rimraf@~2.4.0:
547+ version "2.4.5"
548+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da"
549+ integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=
550+ dependencies:
551+ glob "^6.0.1"
552+
5b1c5dbd
IB
553 run-parallel-limit@^1.0.3:
554 version "1.0.5"
555 resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.0.5.tgz#c29a4fd17b4df358cb52a8a697811a63c984f1b7"
556@@ -5069,6 +5169,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2,
557 resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
558 integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
24fd1fe6
IB
559
560+safe-json-stringify@~1:
561+ version "1.2.0"
562+ resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd"
563+ integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==
564+
5b1c5dbd
IB
565 "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
566 version "2.1.2"
567 resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
568@@ -6337,7 +6442,14 @@ vary@^1, vary@~1.1.2:
569 resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
570 integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
24fd1fe6
IB
571
572-verror@1.10.0:
573+vasync@^1.6.4:
574+ version "1.6.4"
575+ resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.4.tgz#dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f"
576+ integrity sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=
577+ dependencies:
578+ verror "1.6.0"
579+
580+verror@1.10.0, verror@^1.8.1:
581 version "1.10.0"
582 resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
583 integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
5b1c5dbd 584@@ -6346,6 +6458,13 @@ verror@1.10.0:
24fd1fe6
IB
585 core-util-is "1.0.2"
586 extsprintf "^1.2.0"
587
588+verror@1.6.0:
589+ version "1.6.0"
590+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5"
591+ integrity sha1-fROyex+swuLakEBetepuW90lLqU=
592+ dependencies:
593+ extsprintf "1.2.0"
594+
5b1c5dbd
IB
595 videostream@^3.2.0:
596 version "3.2.1"
597 resolved "https://registry.yarnpkg.com/videostream/-/videostream-3.2.1.tgz#643688ad4bfbf37570d421e3196b7e0ad38eeebc"