diff --git a/.travis.yml b/.travis.yml index b9a8c60e..61349451 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,8 @@ node_js: branches: only: - master + +addons: + apt: + packages: + - libsecret-1-dev diff --git a/client/src/builtin/E2EE.js b/client/src/builtin/E2EE.js index 7ca77efc..34b184f8 100644 --- a/client/src/builtin/E2EE.js +++ b/client/src/builtin/E2EE.js @@ -12,7 +12,7 @@ import { Settings, Cache, Events } from 'modules'; import BuiltinModule from './BuiltinModule'; import { WebpackModules, ReactComponents, MonkeyPatch, Patcher, DiscordApi, Security } from 'modules'; import { VueInjector, Reflection, Modals, Toasts } from 'ui'; -import { ClientLogger as Logger } from 'common'; +import { ClientLogger as Logger, ClientIPC } from 'common'; import { request } from 'vendor'; import { Utils } from 'common'; import E2EEComponent from './E2EEComponent.vue'; @@ -35,33 +35,59 @@ export default new class E2EE extends BuiltinModule { this.encryptNewMessages = true; this.ecdhDate = START_DATE; this.handlePublicKey = this.handlePublicKey.bind(this); + this.fetchMasterKey = this.fetchMasterKey.bind(this); } async enabled(e) { - try { - const newMaster = await Modals.input('Open Database', 'Master Password', true).promise; - this.setMaster(newMaster); - Events.on('discord:MESSAGE_CREATE', this.handlePublicKey); - this.patchDispatcher(); - this.patchMessageContent(); - const selector = `.${WebpackModules.getClassName('channelTextArea', 'emojiButton')}`; - const cta = await ReactComponents.getComponent('ChannelTextArea', { selector }); - this.patchChannelTextArea(cta); - this.patchChannelTextAreaSubmit(cta); - cta.forceUpdateAll(); - } catch (err) { - Settings.getSetting(...this.settingPath).value = false; - Toasts.error('Invalid master password! E2EE Disabled'); - } + await this.fetchMasterKey(); + Settings.getSetting('security', 'default', 'use-keytar').on('setting-updated', this.fetchMasterKey); + + Events.on('discord:MESSAGE_CREATE', this.handlePublicKey); + this.patchDispatcher(); + this.patchMessageContent(); + const selector = `.${WebpackModules.getClassName('channelTextArea', 'emojiButton')}`; + const cta = await ReactComponents.getComponent('ChannelTextArea', { selector }); + this.patchChannelTextArea(cta); + this.patchChannelTextAreaSubmit(cta); + cta.forceUpdateAll(); } async disabled(e) { + Settings.getSetting('security', 'default', 'use-keytar').off('setting-updated', this.fetchMasterKey); Events.off('discord:MESSAGE_CREATE', this.handlePublicKey); for (const patch of Patcher.getPatchesByCaller('BD:E2EE')) patch.unpatch(); const ctaComponent = await ReactComponents.getComponent('ChannelTextArea'); ctaComponent.forceUpdateAll(); } + async fetchMasterKey() { + try { + if (Settings.get('security', 'default', 'use-keytar')) { + const master = await ClientIPC.getPassword('betterdiscord', 'master'); + if (master) return this.setMaster(master); + + if (Settings.getSetting('security', 'e2eedb', 'e2ekvps').items.length) { + // Ask the user for their current password to save to the system keychain + const currentMaster = await Modals.input('Save to System Keychain', 'Master Password', true).promise; + await ClientIPC.setPassword('betterdiscord', 'master', currentMaster); + return this.setMaster(currentMaster); + } + + // Generate a new master password and save it to the system keychain + const newMaster = Security.randomBytes(); + await ClientIPC.setPassword('betterdiscord', 'master', newMaster); + return this.setMaster(newMaster); + } + + const newMaster = await Modals.input('Open Database', 'Master Password', true).promise; + return this.setMaster(newMaster); + } catch (err) { + Settings.getSetting(...this.settingPath).value = false; + Toasts.error('Invalid master password! E2EE Disabled'); + Logger.err('E2EE', ['Error fetching master password', err]); + } + } + setMaster(key) { seed = Security.randomBytes(); const newMaster = Security.encrypt(seed, key); @@ -300,8 +326,11 @@ export default new class E2EE extends BuiltinModule { component.props.readyState = 'LOADING'; Logger.info('E2EE', `Decrypting image: ${src}`); - request.get(src, { encoding: 'binary' }).then(res => { - (async () => { + + (async () => { + try { + const res = await request.get(src, { encoding: 'binary' }); + const arr = new Uint8Array(new ArrayBuffer(res.length)); for (let i = 0; i < res.length; i++) arr[i] = res.charCodeAt(i); @@ -330,10 +359,10 @@ export default new class E2EE extends BuiltinModule { component.props.decrypting = false; component.forceUpdate(); - })(); - }).catch(err => { - console.log('request error', err); - }); + } catch (err) { + console.log('request error', err); + } + })(); } patchChannelTextArea(cta) { @@ -355,4 +384,5 @@ export default new class E2EE extends BuiltinModule { if (!this.encryptNewMessages || !key) return; component.props.value = Security.encrypt(Security.decrypt(seed, [this.master, key]), component.props.value, '$:'); } + } diff --git a/client/src/data/user.settings.default.json b/client/src/data/user.settings.default.json index fc60504a..546b9d66 100644 --- a/client/src/data/user.settings.default.json +++ b/client/src/data/user.settings.default.json @@ -235,6 +235,13 @@ "text": "E2EE", "hint": "End-to-end encryption", "value": false + }, + { + "id": "use-keytar", + "type": "bool", + "text": "Use system keychain", + "hint": "Store the master password in the system keychain", + "value": true } ] }, diff --git a/client/src/modules/settings.js b/client/src/modules/settings.js index 8c927f2f..d22cb9e7 100644 --- a/client/src/modules/settings.js +++ b/client/src/modules/settings.js @@ -12,6 +12,7 @@ import { Toasts } from 'ui'; import { SettingsSet } from 'structs'; import { FileUtils, ClientLogger as Logger } from 'common'; import path from 'path'; +import process from 'process'; import Globals from './globals'; import CssEditor from './csseditor'; import Events from './events'; @@ -38,6 +39,12 @@ export default new class Settings { return set; }); + + // Set a hint for each platform for the use-keytar setting + const useKeytarSetting = this.getSetting('security', 'default', 'use-keytar'); + if (process.platform === 'win32') useKeytarSetting.hint = 'Store the master password in Credential Manager'; + if (process.platform === 'darwin') useKeytarSetting.hint = 'Store the master password in the default keychain'; + if (process.platform === 'linux') useKeytarSetting.hint = 'Store the master password in libsecret'; } /** diff --git a/common/modules/bdipc.js b/common/modules/bdipc.js index b687f1a0..e18a1b65 100644 --- a/common/modules/bdipc.js +++ b/common/modules/bdipc.js @@ -112,6 +112,22 @@ const ClientIPC = new class ClientIPC { return this.send('dba', command); } + getPassword(service, account) { + return this.send('keytar-get', {service, account}); + } + + setPassword(service, account, password) { + return this.send('keytar-set', {service, account, password}); + } + + deletePassword(service, account) { + return this.send('keytar-delete', {service, account}); + } + + findCredentials(service) { + return this.send('keytar-find-credentials', {service}); + } + } export default ClientIPC; diff --git a/core/src/main.js b/core/src/main.js index ff7e85d3..5327975b 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -13,6 +13,7 @@ import sass from 'node-sass'; import { BrowserWindow, dialog, session } from 'electron'; import deepmerge from 'deepmerge'; import ContentSecurityPolicy from 'csp-parse'; +import keytar from 'keytar'; import { FileUtils, BDIpc, Config, WindowUtils, CSSEditor, Database } from './modules'; @@ -57,9 +58,10 @@ const globals = { const CSP = { 'img-src': ['https://cdn.betterttv.net', 'https://cdn.frankerfacez.com'], 'script-src': [ - '\'sha256-fSHKdpQGCHaIqWP3SpJOuUHrLp49jy4dWHzZ/RBJ/p4=\'', // React Devtools - '\'sha256-VFJcfKY5B3EBkFDgQnv3CozPwBlZcxwssfLVWlPFfZU=\'', // Vue Devtools - '\'sha256-VzDmLZ4PxPkOS/KY7ITzLQsSWhfCnvUrNculcj8UNgE=\' \'sha256-l6K+77Z1cmldR9gIvaVWlboF/zr5MXCQHcsEHfnr5TU=\''] // Vue Detector + `'sha256-fSHKdpQGCHaIqWP3SpJOuUHrLp49jy4dWHzZ/RBJ/p4='`, // React Devtools + `'sha256-VFJcfKY5B3EBkFDgQnv3CozPwBlZcxwssfLVWlPFfZU='`, // Vue Devtools + `'sha256-VzDmLZ4PxPkOS/KY7ITzLQsSWhfCnvUrNculcj8UNgE=' 'sha256-l6K+77Z1cmldR9gIvaVWlboF/zr5MXCQHcsEHfnr5TU='` // Vue Detector + ] }; class PatchedBrowserWindow extends BrowserWindow { @@ -136,6 +138,11 @@ class Comms { }); BDIpc.on('bd-dba', (event, options) => this.bd.dbInstance.exec(options), true); + + BDIpc.on('bd-keytar-get', (event, {service, account}) => keytar.getPassword(service, account), true); + BDIpc.on('bd-keytar-set', (event, {service, account, password}) => keytar.setPassword(service, account, password), true); + BDIpc.on('bd-keytar-delete', (event, {service, account}) => keytar.deletePassword(service, account), true); + BDIpc.on('bd-keytar-find-credentials', (event, {service}) => keytar.findCredentials(service), true); } async send(channel, message) { diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 042c796f..4d6cd488 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -70,18 +70,31 @@ gulp.task('css-editor', function () { ]); }); -gulp.task('dependencies', function () { +gulp.task('node-modules', function () { return copydeps('.', 'release'); }); -gulp.task('node-sass-bindings', function () { +gulp.task('node-sass-bindings', gulp.series(function () { + return del(['release/node_modules/node-sass/vendor']); +}, function () { return pump([ gulp.src('other/node_sass_bindings/**/*'), copy('release/node_modules/node-sass/vendor', { prefix: 2 }) ]); }); -gulp.task('build-release', gulp.parallel('release-package', 'client', 'core', 'sparkplug', 'core-modules', 'index', 'css-editor', gulp.series('dependencies', 'node-sass-bindings'))); +gulp.task('keytar-bindings', gulp.series(function () { + return del(['release/node_modules/keytar/build']); +}, function () { + return pump([ + gulp.src('other/keytar/**/*'), + copy('release/node_modules/keytar/build/Release', {prefix: 2}) + ]); +})); + +gulp.task('dependencies', gulp.series('node-modules', gulp.parallel('node-sass-bindings', 'keytar-bindings'))); + +gulp.task('build-release', gulp.parallel('release-package', 'client', 'core', 'sparkplug', 'core-modules', 'index', 'css-editor', 'dependencies')); gulp.task('release', gulp.series(function () { return del(['release/**/*']); diff --git a/other/keytar/keytar.node/README.md b/other/keytar/keytar.node/README.md new file mode 100644 index 00000000..d883f86c --- /dev/null +++ b/other/keytar/keytar.node/README.md @@ -0,0 +1,3 @@ +### node-keytar bindings + +Copy this directory to `node_modules/keytar/build/Release/keytar.node` to use. diff --git a/other/keytar/keytar.node/index.js b/other/keytar/keytar.node/index.js new file mode 100644 index 00000000..351a3b6d --- /dev/null +++ b/other/keytar/keytar.node/index.js @@ -0,0 +1,4 @@ +const keytar_version = require('keytar/package').version; + +// module.exports = require('./keytar-' + process.platform + '-' + process.versions.modules + '-' + process.arch + '.node'); +module.exports = require(`./keytar-${keytar_version}/${process.platform}-${process.arch}-${process.versions.modules}.node`); diff --git a/other/keytar/keytar.node/keytar-4.2.1/darwin-x64-53.node b/other/keytar/keytar.node/keytar-4.2.1/darwin-x64-53.node new file mode 100755 index 00000000..7063683c Binary files /dev/null and b/other/keytar/keytar.node/keytar-4.2.1/darwin-x64-53.node differ diff --git a/other/keytar/keytar.node/keytar-4.2.1/linux-x64-53.node b/other/keytar/keytar.node/keytar-4.2.1/linux-x64-53.node new file mode 100755 index 00000000..ebb4d635 Binary files /dev/null and b/other/keytar/keytar.node/keytar-4.2.1/linux-x64-53.node differ diff --git a/other/keytar/keytar.node/keytar-4.2.1/win32-ia32-53.node b/other/keytar/keytar.node/keytar-4.2.1/win32-ia32-53.node new file mode 100644 index 00000000..38adc225 Binary files /dev/null and b/other/keytar/keytar.node/keytar-4.2.1/win32-ia32-53.node differ diff --git a/package-lock.json b/package-lock.json index 39eb5eb9..5f627d6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1563,7 +1563,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "dev": true, "requires": { "readable-stream": "2.3.5", "safe-buffer": "5.1.1" @@ -1572,20 +1571,17 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "readable-stream": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", - "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -1600,7 +1596,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -2012,8 +2007,7 @@ "chownr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" }, "cipher-base": { "version": "1.0.4", @@ -2943,11 +2937,18 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "1.0.1" + } + }, "deep-extend": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" }, "deep-is": { "version": "0.1.3", @@ -3083,8 +3084,7 @@ "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "diff-match-patch": { "version": "1.0.1", @@ -3323,6 +3323,28 @@ "sumchecker": "1.3.1" }, "dependencies": { + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0", + "klaw": "1.3.1", + "path-is-absolute": "1.0.1", + "rimraf": "2.6.2" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -3417,7 +3439,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "1.4.0" } @@ -3984,6 +4005,11 @@ } } }, + "expand-template": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", + "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==" + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -4431,16 +4457,13 @@ } }, "fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", - "dev": true, + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", + "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", "requires": { "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "klaw": "1.3.1", - "path-is-absolute": "1.0.1", - "rimraf": "2.6.2" + "jsonfile": "4.0.0", + "universalify": "0.1.2" } }, "fs-mkdirp-stream": { @@ -4811,9 +4834,9 @@ "dev": true, "optional": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" } }, "fs.realpath": { @@ -4916,8 +4939,8 @@ "dev": true, "optional": true, "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" + "ajv": "4.11.8", + "har-schema": "1.0.5" } }, "has-unicode": { @@ -4986,7 +5009,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-typedarray": { @@ -5091,7 +5114,7 @@ "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", "dev": true, "requires": { - "mime-db": "~1.27.0" + "mime-db": "1.27.0" } }, "minimatch": { @@ -5100,7 +5123,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.7" } }, "minimist": { @@ -5219,8 +5242,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -5634,6 +5657,11 @@ "assert-plus": "1.0.0" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -7155,8 +7183,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", - "dev": true + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=" }, "inquirer": { "version": "3.3.0", @@ -7727,10 +7754,9 @@ "dev": true }, "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "requires": { "graceful-fs": "4.1.11" } @@ -7763,6 +7789,15 @@ "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", "dev": true }, + "keytar": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.2.1.tgz", + "integrity": "sha1-igamV3/fY3PgqmsRInfmPex3/RI=", + "requires": { + "nan": "2.8.0", + "prebuild-install": "2.5.3" + } + }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -8445,6 +8480,11 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -8643,9 +8683,7 @@ "nan": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "dev": true, - "optional": true + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" }, "nanomatch": { "version": "1.2.7", @@ -8724,7 +8762,6 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.3.tgz", "integrity": "sha512-b656V5C0628gOOA2kwcpNA/bxdlqYF9FvxJ+qqVX0ctdXNVZpS8J6xEUYir3WAKc7U0BH/NRlSpNbGsy+azjeg==", - "dev": true, "requires": { "semver": "5.5.0" } @@ -9025,6 +9062,11 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -10501,6 +10543,44 @@ "uniqs": "2.0.0" } }, + "prebuild-install": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz", + "integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==", + "requires": { + "detect-libc": "1.0.3", + "expand-template": "1.1.1", + "github-from-package": "0.0.0", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "node-abi": "2.4.3", + "noop-logger": "0.1.1", + "npmlog": "4.1.2", + "os-homedir": "1.0.2", + "pump": "2.0.1", + "rc": "1.2.6", + "simple-get": "2.8.1", + "tar-fs": "1.16.3", + "tunnel-agent": "0.6.0", + "which-pm-runs": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -10754,7 +10834,6 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", - "dev": true, "requires": { "deep-extend": "0.4.2", "ini": "1.3.5", @@ -10765,8 +10844,7 @@ "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, @@ -11877,6 +11955,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "3.3.0", + "once": "1.4.0", + "simple-concat": "1.0.0" + } + }, "single-line-log": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", @@ -12574,8 +12667,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "sumchecker": { "version": "1.3.1", @@ -12721,11 +12813,32 @@ "inherits": "2.0.3" } }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "requires": { + "chownr": "1.0.1", + "mkdirp": "0.5.1", + "pump": "1.0.3", + "tar-stream": "1.5.5" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, "tar-stream": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", - "dev": true, "requires": { "bl": "1.2.2", "end-of-stream": "1.4.1", @@ -12737,7 +12850,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "1.4.0" } @@ -12745,20 +12857,17 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "readable-stream": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", - "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -12773,7 +12882,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -12781,8 +12889,7 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" } } }, @@ -13367,8 +13474,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unset-value": { "version": "1.0.0", @@ -14413,6 +14519,11 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", diff --git a/package.json b/package.json index bac0fca2..6ea13aba 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "dependencies": { "csp-parse": "github:macropodhq/csp-parse", "deepmerge": "^2.1.1", + "fs-extra": "^7.0.0", + "keytar": "4.2.1", "nedb": "^1.8.0", "node-sass": "^4.9.2" }, @@ -69,6 +71,7 @@ "webpack": "^3.12.0" }, "scripts": { + "install": "node scripts/install.js", "build": "npm run build --prefix client && npm run build --prefix core && npm run build --prefix csseditor && npm run build --prefix installer", "build_client": "npm run build --prefix client", "watch_client": "npm run watch --prefix client", diff --git a/scripts/install.js b/scripts/install.js new file mode 100644 index 00000000..35775ac1 --- /dev/null +++ b/scripts/install.js @@ -0,0 +1,41 @@ +const process = require('process'); +const fs = require('fs-extra'); +const path = require('path'); + +// Copy the node-sass bindings to node_modules/node-sass/vendor +const node_sass_path = path.resolve(require.resolve('node-sass'), '..', '..'); +const prebuilt_node_sass_bindings_path = path.resolve(__dirname, '..', 'other', 'node_sass_bindings'); + +for (const node_sass_binding_name of fs.readdirSync(prebuilt_node_sass_bindings_path)) { + const binding_path = path.join(prebuilt_node_sass_bindings_path, node_sass_binding_name); + const installation_path = path.join(node_sass_path, 'vendor', node_sass_binding_name); + + if (fs.existsSync(installation_path)) continue; + + console.log('Copying node-sass binding from', binding_path, 'to', installation_path); + + fs.copySync(binding_path, installation_path); +} + +// Copy the keytar bindings to node_modules/keytar/vendor +const keytar_path = path.resolve(require.resolve('keytar'), '..', '..'); +const prebuilt_keytar_bindings_path = path.resolve(__dirname, '..', 'other', 'keytar', 'keytar.node'); +const keytar_release_path = path.join(keytar_path, 'build', 'Release'); +const keytar_binding_path = path.join(keytar_release_path, 'keytar.node'); + +if (fs.existsSync(path.join(keytar_release_path, 'keytar.node'))) { + const stat = fs.statSync(path.join(keytar_release_path, 'keytar.node')); + + // if (stat.isFile()) { + console.log('Deleting keytar binding'); + fs.removeSync(path.join(keytar_path, 'build')); + // } +} + +if (!fs.existsSync(path.join(keytar_release_path, 'keytar.node'))) { + fs.mkdirpSync(keytar_release_path); + + console.log('Copying keytar bindings from', prebuilt_keytar_bindings_path, 'to', keytar_binding_path); + + fs.copySync(prebuilt_keytar_bindings_path, keytar_binding_path); +}