chore(xo-server-auth-ldap): use ldapts instead of unmaintained ldapjs (#4732)

This commit is contained in:
Julien Fontanet
2020-01-24 09:27:48 +01:00
committed by GitHub
parent c1b6d1706a
commit 4773f9ebf6
3 changed files with 54 additions and 164 deletions

View File

@@ -36,7 +36,7 @@
"dependencies": {
"exec-promise": "^0.7.0",
"inquirer": "^7.0.0",
"ldapjs": "^1.0.1",
"ldapts": "1.10.0",
"promise-toolbox": "^0.15.0"
},
"devDependencies": {

View File

@@ -1,8 +1,7 @@
/* eslint no-throw-literal: 0 */
import { createClient } from 'ldapjs'
import { escape } from 'ldapjs/lib/filters/escape'
import { fromEvent, promisify } from 'promise-toolbox'
import { Client } from 'ldapts'
import { Filter } from 'ldapts/filters/Filter'
import { readFile } from 'fs'
// ===================================================================
@@ -12,6 +11,8 @@ const DEFAULTS = {
filter: '(uid={{name}})',
}
const { escape } = Filter.prototype
const VAR_RE = /\{\{([^}]+)\}\}/g
const evalFilter = (filter, vars) =>
filter.replace(VAR_RE, (_, name) => {
@@ -194,68 +195,48 @@ class AuthLdap {
return null
}
const client = createClient(this._clientOpts)
const client = new Client(this._clientOpts)
try {
// Promisify some methods.
const bind = promisify(client.bind, client)
const search = promisify(client.search, client)
await fromEvent(client, 'connect')
// Bind if necessary.
{
const { _credentials: credentials } = this
if (credentials) {
logger(`attempting to bind with as ${credentials.dn}...`)
await bind(credentials.dn, credentials.password)
await client.bind(credentials.dn, credentials.password)
logger(`successfully bound as ${credentials.dn}`)
}
}
// Search for the user.
const entries = []
{
logger('searching for entries...')
const response = await search(this._searchBase, {
scope: 'sub',
filter: evalFilter(this._searchFilter, {
name: username,
}),
})
response.on('searchEntry', entry => {
logger('.')
entries.push(entry.json)
})
const { status } = await fromEvent(response, 'end')
if (status) {
throw new Error('unexpected search response status: ' + status)
}
logger(`${entries.length} entries found`)
}
logger('searching for entries...')
const { searchEntries: entries } = await client.search(this._searchBase, {
scope: 'sub',
filter: evalFilter(this._searchFilter, {
name: username,
}),
})
logger(`${entries.length} entries found`)
// Try to find an entry which can be bind with the given password.
for (const entry of entries) {
try {
logger(`attempting to bind as ${entry.objectName}`)
await bind(entry.objectName, password)
logger(`attempting to bind as ${entry.dn}`)
await client.bind(entry.dn, password)
logger(
`successfully bound as ${entry.objectName} => ${username} authenticated`
`successfully bound as ${entry.dn} => ${username} authenticated`
)
logger(JSON.stringify(entry, null, 2))
return { username }
} catch (error) {
logger(`failed to bind as ${entry.objectName}: ${error.message}`)
logger(`failed to bind as ${entry.dn}: ${error.message}`)
}
}
logger(`could not authenticate ${username}`)
return null
} finally {
client.unbind()
await client.unbind()
}
}
}

159
yarn.lock
View File

@@ -1138,6 +1138,13 @@
dependencies:
execa "^2.0.1"
"@types/asn1@~0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@types/asn1/-/asn1-0.2.0.tgz#23b568688b321740bd4eafef49e6b13ffc101b31"
integrity sha512-5TMxIpYbIA9c1J0hYQjQDX3wr+rTgQEAXaW2BI8ECM8FO53wSW4HFZplTalrKSHuZUc76NtXcePRhwuOHqGD5g==
dependencies:
"@types/node" "*"
"@types/babel-types@*", "@types/babel-types@^7.0.0":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.7.tgz#667eb1640e8039436028055737d2b9986ee336e3"
@@ -1232,6 +1239,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.21.tgz#aa44a6363291c7037111c47e4661ad210aded23f"
integrity sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==
"@types/node@~8.10.54":
version "8.10.59"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.59.tgz#9e34261f30183f9777017a13d185dfac6b899e04"
integrity sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
@@ -1829,23 +1841,13 @@ asn1.js@^4.0.0:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
asn1@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=
asn1@~0.2.3:
asn1@~0.2.3, asn1@~0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
assert-plus@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160"
integrity sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
@@ -2868,13 +2870,6 @@ bach@^1.0.0:
async-settle "^1.0.0"
now-and-later "^2.0.0"
backoff@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f"
integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=
dependencies:
precond "0.2"
backoff@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.3.0.tgz#ee7c7e38093f92e472859db635e7652454fc21ea"
@@ -3356,16 +3351,6 @@ bundle-collapser@^1.3.0:
minimist "^1.1.1"
through2 "^2.0.0"
bunyan@^1.8.3:
version "1.8.12"
resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797"
integrity sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=
optionalDependencies:
dtrace-provider "~0.8"
moment "^2.10.6"
mv "~2"
safe-json-stringify "~1"
bytes@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.2.0.tgz#fd35464a403f6f9117c2de3609ecff9cae000588"
@@ -4553,7 +4538,7 @@ dash-ast@^1.0.0:
resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37"
integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==
dashdash@^1.12.0, dashdash@^1.14.0:
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
@@ -4626,7 +4611,7 @@ debug@3.X, debug@^3.0.0, debug@^3.1.0, debug@^3.2.6:
dependencies:
ms "^2.1.1"
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@@ -5035,13 +5020,6 @@ double-ended-queue@^2.1.0-0:
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=
dtrace-provider@~0.8:
version "0.8.8"
resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e"
integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==
dependencies:
nan "^2.14.0"
duplexer2@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db"
@@ -5879,11 +5857,6 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extsprintf@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529"
integrity sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
@@ -6581,17 +6554,6 @@ glob-watcher@^5.0.3:
just-debounce "^1.0.0"
object.defaults "^1.1.0"
glob@^6.0.1:
version "6.0.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=
dependencies:
inflight "^1.0.4"
inherits "2"
minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@@ -8729,29 +8691,16 @@ lcid@^2.0.0:
dependencies:
invert-kv "^2.0.0"
ldap-filter@0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/ldap-filter/-/ldap-filter-0.2.2.tgz#f2b842be0b86da3352798505b31ebcae590d77d0"
integrity sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=
ldapts@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/ldapts/-/ldapts-1.10.0.tgz#8fa4487ba6814f024bf4e92553baaa5eceea0e6c"
integrity sha512-5L06EuLEXGbStDyhrT31Fp+4tNFzOUnd1Unz5GCbNM1IemtHmxlBujVHhsgT6AUq5RXWhMUUvuXZrDt7XMa95Q==
dependencies:
assert-plus "0.1.5"
ldapjs@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/ldapjs/-/ldapjs-1.0.2.tgz#544ff7032b7b83c68f0701328d9297aa694340f9"
integrity sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=
dependencies:
asn1 "0.2.3"
assert-plus "^1.0.0"
backoff "^2.5.0"
bunyan "^1.8.3"
dashdash "^1.14.0"
ldap-filter "0.2.2"
once "^1.4.0"
vasync "^1.6.4"
verror "^1.8.1"
optionalDependencies:
dtrace-provider "~0.8"
"@types/asn1" "~0.2.0"
"@types/node" "~8.10.54"
asn1 "~0.2.4"
debug "~4.1.1"
strict-event-emitter-types "~2.0.0"
lead@^1.0.0:
version "1.0.0"
@@ -9600,7 +9549,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
"minimatch@2 || 3", minimatch@^3.0.4, 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"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -9645,7 +9594,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@0.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
mkdirp@0.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
@@ -9713,7 +9662,7 @@ moment-timezone@^0.5.13, moment-timezone@^0.5.14:
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0", moment@^2.10.6, moment@^2.20.1, moment@^2.22.1:
"moment@>= 2.9.0", moment@^2.20.1, moment@^2.22.1:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
@@ -9783,15 +9732,6 @@ mute-stream@0.0.8:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
mv@~2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2"
integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=
dependencies:
mkdirp "~0.5.1"
ncp "~2.0.0"
rimraf "~2.4.0"
mz@^2.6.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
@@ -9806,7 +9746,7 @@ nan@2.10.0:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
nan@^2.10.0, nan@^2.12.1, nan@^2.13.2, nan@^2.14.0:
nan@^2.10.0, nan@^2.12.1, nan@^2.13.2:
version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
@@ -9843,11 +9783,6 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
ncp@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=
ndjson@^1.4.0, ndjson@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/ndjson/-/ndjson-1.5.0.tgz#ae603b36b134bcec347b452422b0bf98d5832ec8"
@@ -11083,11 +11018,6 @@ postcss@^7.0.17, postcss@^7.0.23:
source-map "^0.6.1"
supports-color "^6.1.0"
precond@0.2:
version "0.2.3"
resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac"
integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -12470,13 +12400,6 @@ rimraf@^3.0.0:
dependencies:
glob "^7.1.3"
rimraf@~2.4.0:
version "2.4.5"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da"
integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=
dependencies:
glob "^6.0.1"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -12537,11 +12460,6 @@ safe-buffer@5.2.0, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0,
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
safe-json-stringify@~1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd"
integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==
safe-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
@@ -13108,6 +13026,11 @@ stream-splicer@^2.0.0:
inherits "^2.0.1"
readable-stream "^2.0.2"
strict-event-emitter-types@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz#05e15549cb4da1694478a53543e4e2f4abcf277f"
integrity sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==
strict-timeout@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strict-timeout/-/strict-timeout-1.0.0.tgz#f77c6dedec379a3b9d8d922bee0398ae5b1415ee"
@@ -14149,14 +14072,7 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
vasync@^1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.4.tgz#dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f"
integrity sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=
dependencies:
verror "1.6.0"
verror@1.10.0, verror@^1.8.1:
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
@@ -14165,13 +14081,6 @@ verror@1.10.0, verror@^1.8.1:
core-util-is "1.0.2"
extsprintf "^1.2.0"
verror@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5"
integrity sha1-fROyex+swuLakEBetepuW90lLqU=
dependencies:
extsprintf "1.2.0"
vinyl-file@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a"