From 433b2734bb162c85bdf2f955b65ae13592194b2d Mon Sep 17 00:00:00 2001 From: Benedict Aas Date: Wed, 25 Oct 2017 16:27:40 +0100 Subject: [PATCH] feat: add sudo-prompt to start on linux (#1708) We prompt the user with a sudo-prompt upon launch on Linux-based systems to ensure the program has enough permissions for features needed throughout the program's runtime. Changelog-Entry: Add a sudo-prompt upon launch on Linux-based systems. Signed-off-by: Juan Cruz Viotti --- electron-builder.yml | 2 + lib/child-writer/renderer-utils.js | 3 -- lib/shared/messages.js | 4 ++ lib/start.js | 84 +++++++++++++++++++++++++++++- npm-shrinkwrap.json | 9 +++- package.json | 1 + 6 files changed, 97 insertions(+), 6 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index 1587f075..8a65cf8d 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -83,10 +83,12 @@ deb: - libxss1 - libxtst6 - polkit-1-auth-agent | policykit-1-gnome | polkit-kde-1 + - gksu | kdesudo rpm: icon: assets/icon.png depends: - lsb - libXScrnSaver + - beesu appImage: icon: assets/icon.png diff --git a/lib/child-writer/renderer-utils.js b/lib/child-writer/renderer-utils.js index 79e311b5..9dba0d07 100644 --- a/lib/child-writer/renderer-utils.js +++ b/lib/child-writer/renderer-utils.js @@ -43,8 +43,5 @@ exports.getApplicationEntryPoint = () => { const ENTRY_POINT_ARGV_INDEX = 1 const relativeEntryPoint = electron.remote.process.argv[ENTRY_POINT_ARGV_INDEX] - // On GNU/Linux, `pkexec` resolves relative paths - // from `/root`, therefore we pass an absolute path, - // in order to be on the safe side. return path.join(CONSTANTS.PROJECT_ROOT, relativeEntryPoint) } diff --git a/lib/shared/messages.js b/lib/shared/messages.js index 45a50907..66f13d7a 100644 --- a/lib/shared/messages.js +++ b/lib/shared/messages.js @@ -35,6 +35,10 @@ module.exports = { flashComplete: _.template([ '<%= imageBasename %> was successfully written to', '<%= drive.description %> (<%= drive.displayName %>)' + ].join(' ')), + + providePassword: _.template([ + '<%= displayName %> needs special permissions to interact with raw devices. Please provide your password.' ].join(' ')) }, diff --git a/lib/start.js b/lib/start.js index 75e9d4d8..e535cdf3 100644 --- a/lib/start.js +++ b/lib/start.js @@ -31,5 +31,87 @@ process.env.DEBUG = `sdk:usbboot,${process.env.DEBUG}` if (process.env.ELECTRON_RUN_AS_NODE || process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE) { require('./cli/etcher') } else { - require('./gui/etcher') + const electron = require('electron') + const os = require('os') + const _ = require('lodash') + const Bluebird = require('bluebird') + const commandExists = Bluebird.promisify(require('command-exists')) + const childProcess = require('child_process') + const permissions = require('./shared/permissions') + const errors = require('./shared/errors') + const EXIT_CODES = require('./shared/exit-codes') + const packageJSON = require('../package.json') + const messages = require('./shared/messages') + + const MESSAGE = messages.info.providePassword({ + displayName: packageJSON.displayName + }) + + const ELEVATOR_COMMANDS = [ + { + name: 'gksudo', + options: [ '--preserve-env', '--message', MESSAGE ] + }, + { + name: 'kdesudo', + options: [ '--comment', MESSAGE, '--' ] + }, + { + name: 'beesu', + options: [ '--preserve-environment' ] + } + ] + + permissions.isElevated().then((isElevated) => { + if (_.includes([ 'win32', 'darwin' ], os.platform()) || isElevated) { + require('./gui/etcher') + return Bluebird.resolve() + } + + return Bluebird.any(_.map(ELEVATOR_COMMANDS, (command) => { + return commandExists(command.name).then((exists) => { + if (!exists) { + throw new Error(`Command does not exist: ${command.name}`) + } + + return command + }) + })).then((command) => { + return new Bluebird((resolve, reject) => { + const argv = process.env.APPIMAGE ? [ process.env.APPIMAGE ] : process.argv + const options = command.options.concat([ 'env', 'SKIP=1' ]).concat(argv) + + console.log(`Running ${command.name}`) + + const child = childProcess.spawn(command.name, options, { + detached: true, + env: process.env + }) + + child.stdout.on('data', (data) => { + console.log(data.toString()) + }) + + child.stderr.on('data', (data) => { + console.error(data.toString()) + }) + + child.on('exit', () => { + electron.app.quit() + }) + + child.on('error', reject) + }) + }).catch(Bluebird.AggregateError, () => { + const commands = _.map(ELEVATOR_COMMANDS, 'name') + const formattedCommands = `${_.initial(commands).join(', ')}, or ${_.last(commands)}` + throw errors.createUserError({ + title: 'Can\'t elevate the application', + description: `Please ensure you have either ${formattedCommands} available in your system` + }) + }) + }).catch((error) => { + electron.dialog.showErrorBox(errors.getTitle(error), errors.getDescription(error)) + electron.app.exit(EXIT_CODES.GENERAL_ERROR) + }) } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 282ab36d..96b0b41c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -38,9 +38,9 @@ "resolved": "https://registry.npmjs.org/@types/lodash.some/-/lodash.some-4.6.2.tgz" }, "@types/node": { - "version": "7.0.44", + "version": "7.0.46", "from": "@types/node@>=7.0.18 <8.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.44.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.46.tgz", "dev": true }, "@types/react": { @@ -855,6 +855,11 @@ "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dev": true }, + "command-exists": { + "version": "1.2.2", + "from": "command-exists@latest", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.2.tgz" + }, "command-join": { "version": "2.0.0", "from": "command-join@2.0.0", diff --git a/package.json b/package.json index 8def4615..967456bd 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "bluebird": "3.4.1", "bootstrap-sass": "3.3.6", "chalk": "1.1.3", + "command-exists": "1.2.2", "command-join": "2.0.0", "debug": "2.6.0", "drivelist": "5.2.4",