]>
Commit | Line | Data |
---|---|---|
0eaac6ba IB |
1 | commit 45a9e4a7b1badbea15d74bd8b3990303a424dfa6 |
2 | Author: Ismaël Bouya <ismael.bouya@normalesup.org> | |
3 | Date: Tue Feb 12 18:47:53 2019 +0100 | |
4 | ||
5 | Add LDAP authentication | |
6 | ||
7 | diff --git a/config/default.yaml b/config/default.yaml | |
8 | index e16b8c35..eac0dd3f 100644 | |
9 | --- a/config/default.yaml | |
10 | +++ b/config/default.yaml | |
11 | @@ -33,6 +33,9 @@ redis: | |
12 | auth: null | |
13 | db: 0 | |
14 | ||
15 | +ldap: | |
16 | + enable: false | |
17 | + | |
18 | smtp: | |
19 | hostname: null | |
20 | port: 465 | |
21 | diff --git a/config/production.yaml.example b/config/production.yaml.example | |
22 | index 661eac0d..bb5ac251 100644 | |
23 | --- a/config/production.yaml.example | |
24 | +++ b/config/production.yaml.example | |
25 | @@ -33,6 +33,17 @@ redis: | |
26 | auth: null | |
27 | db: 0 | |
28 | ||
29 | +ldap: | |
30 | + enable: true | |
31 | + # Disallow non-ldap users (it also disables root!) | |
32 | + ldap_only: true | |
33 | + url: ldap://localhost:389/dc=example,dc=com | |
34 | + bind_dn: cn=admin,dc=example,dc=com | |
35 | + bind_password: adminPass | |
36 | + base: dc=example,dc=com | |
37 | + mail_entry: "mail" | |
38 | + user_filter: "(|(email=%username%)(uid=%username%))" | |
39 | + | |
40 | # SMTP server to send emails | |
41 | smtp: | |
42 | hostname: null | |
43 | diff --git a/package.json b/package.json | |
44 | index 0cf39c7e..fd1ce3ea 100644 | |
45 | --- a/package.json | |
46 | +++ b/package.json | |
47 | @@ -125,6 +125,7 @@ | |
48 | "js-yaml": "^3.5.4", | |
49 | "jsonld": "^1.0.1", | |
50 | "jsonld-signatures": "https://github.com/Chocobozzz/jsonld-signatures#rsa2017", | |
51 | + "ldapjs": "^1.0.2", | |
52 | "lodash": "^4.17.10", | |
53 | "magnet-uri": "^5.1.4", | |
54 | "memoizee": "^0.4.14", | |
55 | diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts | |
56 | index 6f3ebb9a..fb21df37 100644 | |
57 | --- a/server/initializers/constants.ts | |
58 | +++ b/server/initializers/constants.ts | |
59 | @@ -16,7 +16,7 @@ let config: IConfig = require('config') | |
60 | ||
61 | // --------------------------------------------------------------------------- | |
62 | ||
63 | -const LAST_MIGRATION_VERSION = 325 | |
64 | +const LAST_MIGRATION_VERSION = 326 | |
65 | ||
66 | // --------------------------------------------------------------------------- | |
67 | ||
68 | @@ -177,6 +177,16 @@ const CONFIG = { | |
69 | AUTH: config.has('redis.auth') ? config.get<string>('redis.auth') : null, | |
70 | DB: config.has('redis.db') ? config.get<number>('redis.db') : null | |
71 | }, | |
72 | + LDAP: { | |
73 | + ENABLE: config.has('ldap.enable') ? config.get<boolean>('ldap.enable') : false, | |
74 | + LDAP_ONLY: config.has('ldap.ldap_only') ? config.get<boolean>('ldap.ldap_only') : false, | |
75 | + URL: config.has('ldap.url') ? config.get<string>('ldap.url') : null, | |
76 | + BIND_DN: config.has('ldap.bind_dn') ? config.get<string>('ldap.bind_dn') : null, | |
77 | + BIND_PASSWORD: config.has('ldap.bind_password') ? config.get<string>('ldap.bind_password') : null, | |
78 | + BASE: config.has('ldap.base') ? config.get<string>('ldap.base') : null, | |
79 | + MAIL_ENTRY: config.has('ldap.mail_entry') ? config.get<string>('ldap.mail_entry') : 'mail', | |
80 | + USER_FILTER: config.has('ldap.user_filter') ? config.get<string>('ldap.user_filter') : '(|(email=%username%)(uid=%username%))' | |
81 | + }, | |
82 | SMTP: { | |
83 | HOSTNAME: config.get<string>('smtp.hostname'), | |
84 | PORT: config.get<number>('smtp.port'), | |
85 | diff --git a/server/initializers/migrations/0326-user-ldap-dn.ts b/server/initializers/migrations/0326-user-ldap-dn.ts | |
86 | new file mode 100644 | |
87 | index 00000000..a9d68124 | |
88 | --- /dev/null | |
89 | +++ b/server/initializers/migrations/0326-user-ldap-dn.ts | |
90 | @@ -0,0 +1,26 @@ | |
91 | +import * as Sequelize from 'sequelize' | |
92 | + | |
93 | +async function up (utils: { | |
94 | + transaction: Sequelize.Transaction, | |
95 | + queryInterface: Sequelize.QueryInterface, | |
96 | + sequelize: Sequelize.Sequelize | |
97 | +}): Promise<void> { | |
98 | + | |
99 | + { | |
100 | + const data = { | |
101 | + type: Sequelize.STRING, | |
102 | + allowNull: true, | |
103 | + defaultValue: null | |
104 | + } | |
105 | + await utils.queryInterface.addColumn('user', 'ldapDn', data) | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +function down (options) { | |
110 | + throw new Error('Not implemented.') | |
111 | +} | |
112 | + | |
113 | +export { | |
114 | + up, | |
115 | + down | |
116 | +} | |
117 | diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts | |
118 | index 2cd2ae97..3f14b216 100644 | |
119 | --- a/server/lib/oauth-model.ts | |
120 | +++ b/server/lib/oauth-model.ts | |
121 | @@ -66,7 +66,13 @@ function getRefreshToken (refreshToken: string) { | |
122 | async function getUser (usernameOrEmail: string, password: string) { | |
123 | logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).') | |
124 | ||
125 | - const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail) | |
126 | + let user | |
127 | + if (CONFIG.LDAP.ENABLE) { | |
128 | + user = await UserModel.findOrCreateLDAPUser(usernameOrEmail) | |
129 | + } | |
130 | + if (!user && (!CONFIG.LDAP.ENABLE || !CONFIG.LDAP.LDAP_ONLY)) { | |
131 | + user = await UserModel.loadByUsernameOrEmail(usernameOrEmail) | |
132 | + } | |
133 | if (!user) return null | |
134 | ||
135 | const passwordMatch = await user.isPasswordMatch(password) | |
136 | diff --git a/server/models/account/user.ts b/server/models/account/user.ts | |
137 | index 017a9665..a4d0145c 100644 | |
138 | --- a/server/models/account/user.ts | |
139 | +++ b/server/models/account/user.ts | |
140 | @@ -1,3 +1,4 @@ | |
141 | +import * as ldap from 'ldapjs' | |
142 | import * as Sequelize from 'sequelize' | |
143 | import { | |
144 | AfterDestroy, | |
145 | @@ -42,8 +43,9 @@ import { VideoChannelModel } from '../video/video-channel' | |
146 | import { AccountModel } from './account' | |
147 | import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' | |
148 | import { values } from 'lodash' | |
149 | -import { NSFW_POLICY_TYPES } from '../../initializers' | |
150 | +import { CONFIG, NSFW_POLICY_TYPES } from '../../initializers' | |
151 | import { clearCacheByUserId } from '../../lib/oauth-model' | |
152 | +import { createUserAccountAndChannel } from '../../lib/user' | |
153 | import { UserNotificationSettingModel } from './user-notification-setting' | |
154 | import { VideoModel } from '../video/video' | |
155 | import { ActorModel } from '../activitypub/actor' | |
156 | @@ -111,6 +113,11 @@ export class UserModel extends Model<UserModel> { | |
157 | @Column(DataType.STRING(400)) | |
158 | email: string | |
159 | ||
160 | + @AllowNull(true) | |
161 | + @Default(null) | |
162 | + @Column | |
163 | + ldapDn: string | |
164 | + | |
165 | @AllowNull(true) | |
166 | @Default(null) | |
167 | @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean')) | |
168 | @@ -354,6 +361,90 @@ export class UserModel extends Model<UserModel> { | |
169 | return UserModel.findOne(query) | |
170 | } | |
171 | ||
172 | + static loadByLdapDn (ldapDn: string) { | |
173 | + const query = { | |
174 | + where: { | |
175 | + ldapDn | |
176 | + } | |
177 | + } | |
178 | + | |
179 | + return UserModel.findOne(query) | |
180 | + } | |
181 | + | |
182 | + static findOrCreateLDAPUser (username: string) { | |
183 | + let userInfos | |
184 | + | |
185 | + return Promise.resolve(UserModel.findLDAPUser(username)) | |
186 | + .then((_userInfos) => { | |
187 | + userInfos = _userInfos | |
188 | + return UserModel.loadByLdapDn(userInfos['dn']) | |
189 | + }) | |
190 | + .then((user) => { | |
191 | + if (user) { | |
192 | + return user | |
193 | + } else { | |
194 | + return UserModel.createLDAPUser(username, userInfos) | |
195 | + } | |
196 | + }) | |
197 | + .catch(() => { return null }) | |
198 | + } | |
199 | + | |
200 | + static findLDAPUser (username: string) { | |
201 | + const client = ldap.createClient({ | |
202 | + url: CONFIG.LDAP.URL | |
203 | + }) | |
204 | + const filter = ldap.parseFilter(CONFIG.LDAP.USER_FILTER) | |
205 | + filter.forEach(function (element) { | |
206 | + if (element.value === '%username%') element.value = username | |
207 | + }) | |
208 | + const opts = { | |
209 | + filter, | |
210 | + scope: 'sub', | |
211 | + attributes: [ CONFIG.LDAP.MAIL_ENTRY, 'dn' ] | |
212 | + } | |
213 | + | |
214 | + return new Promise(function (resolve, reject) { | |
215 | + client.bind(CONFIG.LDAP.BIND_DN, CONFIG.LDAP.BIND_PASSWORD, function (err) { | |
216 | + if (err) reject() | |
217 | + let entries = [] | |
218 | + client.search(CONFIG.LDAP.BASE, opts, function (err, search) { | |
219 | + if (err) reject() | |
220 | + search.on('searchEntry', function (entry) { | |
221 | + entries.push(entry.object) | |
222 | + }) | |
223 | + search.on('end', function (result) { | |
224 | + if (entries.length === 1) { | |
225 | + resolve(entries[0]) | |
226 | + } else { | |
227 | + reject() | |
228 | + } | |
229 | + }) | |
230 | + }) | |
231 | + }) | |
232 | + }) | |
233 | + } | |
234 | + | |
235 | + static createLDAPUser (username: string, userInfos: {}) { | |
236 | + return Promise.resolve(userInfos) | |
237 | + .then((userInfos) => { | |
238 | + const userToCreate = new UserModel({ | |
239 | + username: username, | |
240 | + password: 'SomeInvalidPassword', | |
241 | + email: userInfos[CONFIG.LDAP.MAIL_ENTRY], | |
242 | + ldapDn: userInfos['dn'], | |
243 | + nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, | |
244 | + autoPlayVideo: true, | |
245 | + role: UserRole.USER, | |
246 | + videoQuota: CONFIG.USER.VIDEO_QUOTA, | |
247 | + videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY, | |
248 | + emailVerified: true | |
249 | + }) | |
250 | + | |
251 | + return createUserAccountAndChannel(userToCreate) | |
252 | + }) | |
253 | + .then(({ user }) => { return user }) | |
254 | + } | |
255 | + | |
256 | static loadByUsernameAndPopulateChannels (username: string) { | |
257 | const query = { | |
258 | where: { | |
259 | @@ -516,8 +607,22 @@ export class UserModel extends Model<UserModel> { | |
260 | return hasUserRight(this.role, right) | |
261 | } | |
262 | ||
263 | + static checkLDAPUser (dn: string, password: string) { | |
264 | + const client = ldap.createClient({ url: CONFIG.LDAP.URL }) | |
265 | + | |
266 | + return new Promise(function (resolve, reject) { | |
267 | + client.bind(dn, password, function (err) { | |
268 | + resolve(!err) | |
269 | + }) | |
270 | + }) | |
271 | + } | |
272 | + | |
273 | isPasswordMatch (password: string) { | |
274 | - return comparePassword(password, this.password) | |
275 | + if (this.ldapDn === null) { | |
276 | + return comparePassword(password, this.password) | |
277 | + } else { | |
278 | + return UserModel.checkLDAPUser(this.ldapDn, password) | |
279 | + } | |
280 | } | |
281 | ||
282 | toFormattedJSON (): User { | |
283 | diff --git a/yarn.lock b/yarn.lock | |
284 | index 1e759af1..1eb61a9f 100644 | |
285 | --- a/yarn.lock | |
286 | +++ b/yarn.lock | |
287 | @@ -674,6 +674,11 @@ asap@^2.0.0: | |
288 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" | |
289 | integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= | |
290 | ||
291 | +asn1@0.2.3: | |
292 | + version "0.2.3" | |
293 | + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" | |
294 | + integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y= | |
295 | + | |
296 | asn1@~0.2.3: | |
297 | version "0.2.4" | |
298 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" | |
299 | @@ -681,6 +686,11 @@ asn1@~0.2.3: | |
300 | dependencies: | |
301 | safer-buffer "~2.1.0" | |
302 | ||
303 | +assert-plus@0.1.5: | |
304 | + version "0.1.5" | |
305 | + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" | |
306 | + integrity sha1-7nQAlBMALYTOxyGcasgRgS5yMWA= | |
307 | + | |
308 | assert-plus@1.0.0, assert-plus@^1.0.0: | |
309 | version "1.0.0" | |
310 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" | |
311 | @@ -779,6 +789,13 @@ backo2@1.0.2: | |
312 | resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" | |
313 | integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= | |
314 | ||
315 | +backoff@^2.5.0: | |
316 | + version "2.5.0" | |
317 | + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" | |
318 | + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= | |
319 | + dependencies: | |
320 | + precond "0.2" | |
321 | + | |
322 | balanced-match@^1.0.0: | |
323 | version "1.0.0" | |
324 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" | |
325 | @@ -1186,6 +1203,16 @@ bull@^3.4.2: | |
326 | semver "^5.6.0" | |
327 | uuid "^3.2.1" | |
328 | ||
329 | +bunyan@^1.8.3: | |
330 | + version "1.8.12" | |
331 | + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797" | |
332 | + integrity sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c= | |
333 | + optionalDependencies: | |
334 | + dtrace-provider "~0.8" | |
335 | + moment "^2.10.6" | |
336 | + mv "~2" | |
337 | + safe-json-stringify "~1" | |
338 | + | |
339 | busboy@^0.2.11: | |
340 | version "0.2.14" | |
341 | resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" | |
342 | @@ -1966,7 +1993,7 @@ d@1: | |
343 | dependencies: | |
344 | es5-ext "^0.10.9" | |
345 | ||
346 | -dashdash@^1.12.0: | |
347 | +dashdash@^1.12.0, dashdash@^1.14.0: | |
348 | version "1.14.1" | |
349 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" | |
350 | integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= | |
351 | @@ -2292,6 +2319,13 @@ double-ended-queue@^2.1.0-0: | |
352 | resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" | |
353 | integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= | |
354 | ||
355 | +dtrace-provider@~0.8: | |
356 | + version "0.8.7" | |
357 | + resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.7.tgz#dc939b4d3e0620cfe0c1cd803d0d2d7ed04ffd04" | |
358 | + integrity sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ= | |
359 | + dependencies: | |
360 | + nan "^2.10.0" | |
361 | + | |
362 | duplexer3@^0.1.4: | |
363 | version "0.1.4" | |
364 | resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" | |
365 | @@ -2864,6 +2898,11 @@ extglob@^2.0.4: | |
366 | snapdragon "^0.8.1" | |
367 | to-regex "^3.0.1" | |
368 | ||
369 | +extsprintf@1.2.0: | |
370 | + version "1.2.0" | |
371 | + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529" | |
372 | + integrity sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk= | |
373 | + | |
374 | extsprintf@1.3.0: | |
375 | version "1.3.0" | |
376 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" | |
377 | @@ -3350,6 +3389,17 @@ glob@7.1.2: | |
378 | once "^1.3.0" | |
379 | path-is-absolute "^1.0.0" | |
380 | ||
381 | +glob@^6.0.1: | |
382 | + version "6.0.4" | |
383 | + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" | |
384 | + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= | |
385 | + dependencies: | |
386 | + inflight "^1.0.4" | |
387 | + inherits "2" | |
388 | + minimatch "2 || 3" | |
389 | + once "^1.3.0" | |
390 | + path-is-absolute "^1.0.0" | |
391 | + | |
392 | glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1, glob@~7.1.2: | |
393 | version "7.1.3" | |
394 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" | |
395 | @@ -4522,6 +4572,30 @@ lcid@^2.0.0: | |
396 | dependencies: | |
397 | invert-kv "^2.0.0" | |
398 | ||
399 | +ldap-filter@0.2.2: | |
400 | + version "0.2.2" | |
401 | + resolved "https://registry.yarnpkg.com/ldap-filter/-/ldap-filter-0.2.2.tgz#f2b842be0b86da3352798505b31ebcae590d77d0" | |
402 | + integrity sha1-8rhCvguG2jNSeYUFsx68rlkNd9A= | |
403 | + dependencies: | |
404 | + assert-plus "0.1.5" | |
405 | + | |
406 | +ldapjs@^1.0.2: | |
407 | + version "1.0.2" | |
408 | + resolved "https://registry.yarnpkg.com/ldapjs/-/ldapjs-1.0.2.tgz#544ff7032b7b83c68f0701328d9297aa694340f9" | |
409 | + integrity sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk= | |
410 | + dependencies: | |
411 | + asn1 "0.2.3" | |
412 | + assert-plus "^1.0.0" | |
413 | + backoff "^2.5.0" | |
414 | + bunyan "^1.8.3" | |
415 | + dashdash "^1.14.0" | |
416 | + ldap-filter "0.2.2" | |
417 | + once "^1.4.0" | |
418 | + vasync "^1.6.4" | |
419 | + verror "^1.8.1" | |
420 | + optionalDependencies: | |
421 | + dtrace-provider "~0.8" | |
422 | + | |
423 | leven@^2.1.0: | |
424 | version "2.1.0" | |
425 | resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" | |
426 | @@ -5186,7 +5260,7 @@ minimalistic-assert@^1.0.1: | |
427 | resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" | |
428 | integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== | |
429 | ||
430 | -minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2: | |
431 | +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2: | |
432 | version "3.0.4" | |
433 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" | |
434 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== | |
435 | @@ -5299,6 +5373,11 @@ moment-timezone@^0.5.14, moment-timezone@^0.5.23: | |
436 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" | |
437 | integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y= | |
438 | ||
439 | +moment@^2.10.6: | |
440 | + version "2.24.0" | |
441 | + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" | |
442 | + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== | |
443 | + | |
444 | morgan@^1.5.3: | |
445 | version "1.9.1" | |
446 | resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" | |
447 | @@ -5394,6 +5473,15 @@ mute-stream@~0.0.4: | |
448 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" | |
449 | integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= | |
450 | ||
451 | +mv@~2: | |
452 | + version "2.1.1" | |
453 | + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" | |
454 | + integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= | |
455 | + dependencies: | |
456 | + mkdirp "~0.5.1" | |
457 | + ncp "~2.0.0" | |
458 | + rimraf "~2.4.0" | |
459 | + | |
460 | nan@2.11.1, nan@^2.10.0, nan@^2.11.1, nan@^2.9.2: | |
461 | version "2.11.1" | |
462 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766" | |
463 | @@ -5431,6 +5519,11 @@ ncp@1.0.x: | |
464 | resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246" | |
465 | integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY= | |
466 | ||
467 | +ncp@~2.0.0: | |
468 | + version "2.0.0" | |
469 | + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" | |
470 | + integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= | |
471 | + | |
472 | needle@^2.2.1: | |
473 | version "2.2.4" | |
474 | resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" | |
475 | @@ -6520,6 +6613,11 @@ prebuild-install@^5.2.0: | |
476 | tunnel-agent "^0.6.0" | |
477 | which-pm-runs "^1.0.0" | |
478 | ||
479 | +precond@0.2: | |
480 | + version "0.2.3" | |
481 | + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" | |
482 | + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= | |
483 | + | |
484 | prelude-ls@~1.1.2: | |
485 | version "1.1.2" | |
486 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" | |
487 | @@ -7141,6 +7239,13 @@ rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.4.2, rimraf@^2.5.2, rimraf@^2.5 | |
488 | dependencies: | |
489 | glob "^7.0.5" | |
490 | ||
491 | +rimraf@~2.4.0: | |
492 | + version "2.4.5" | |
493 | + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" | |
494 | + integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= | |
495 | + dependencies: | |
496 | + glob "^6.0.1" | |
497 | + | |
498 | run-async@^0.1.0: | |
499 | version "0.1.0" | |
500 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" | |
501 | @@ -7197,6 +7302,11 @@ safe-buffer@5.1.2, safe-buffer@^5.0.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, s | |
502 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" | |
503 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== | |
504 | ||
505 | +safe-json-stringify@~1: | |
506 | + version "1.2.0" | |
507 | + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" | |
508 | + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== | |
509 | + | |
510 | safe-regex@^1.1.0: | |
511 | version "1.1.0" | |
512 | resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" | |
513 | @@ -8857,7 +8967,14 @@ vary@~1.0.1: | |
514 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.0.1.tgz#99e4981566a286118dfb2b817357df7993376d10" | |
515 | integrity sha1-meSYFWaihhGN+yuBc1ffeZM3bRA= | |
516 | ||
517 | -verror@1.10.0: | |
518 | +vasync@^1.6.4: | |
519 | + version "1.6.4" | |
520 | + resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.4.tgz#dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f" | |
521 | + integrity sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8= | |
522 | + dependencies: | |
523 | + verror "1.6.0" | |
524 | + | |
525 | +verror@1.10.0, verror@^1.8.1: | |
526 | version "1.10.0" | |
527 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" | |
528 | integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= | |
529 | @@ -8866,6 +8983,13 @@ verror@1.10.0: | |
530 | core-util-is "1.0.2" | |
531 | extsprintf "^1.2.0" | |
532 | ||
533 | +verror@1.6.0: | |
534 | + version "1.6.0" | |
535 | + resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5" | |
536 | + integrity sha1-fROyex+swuLakEBetepuW90lLqU= | |
537 | + dependencies: | |
538 | + extsprintf "1.2.0" | |
539 | + | |
540 | videostream@^2.5.1: | |
541 | version "2.6.0" | |
542 | resolved "https://registry.yarnpkg.com/videostream/-/videostream-2.6.0.tgz#7f0b2b84bc457c12cfe599aa2345f5cc06241ab6" |