fix(GUI): provide a friendly message when no polkit agent is available (#1221)

`sudo-prompt`, the module we use to provide elevation for GNU/Linux,
relies on polkit to show the elevation dialog. Polkit implements a
"backend" to control privileges, but relies on different packages to
provide a GUI frontend.

If no GUI frontend can be found on the system, then `sudo-prompt` will
fail with a `No polkit authentication agent found.` In this case, we
shouldn't present a scary stack trace to the user, and we should also
not send this error to TrackJS.

As a solution, we intercept such error in
`lib/child-writer/writer-proxy.js`, and convert it into a human readable
user error using `errors.createUserError()`, which is passed to the
Etcher GUI using the "robot" mechanism.

Fixes: https://github.com/resin-io/etcher/issues/1179
Change-Type: patch
Changelog-Entry: Provide a user friendly error message when no polkit authentication agent is available on the system.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
Juan Cruz Viotti 2017-03-29 11:26:34 -04:00 committed by GitHub
parent e7f0613d82
commit 0e8dee0506
2 changed files with 49 additions and 17 deletions

View File

@ -113,25 +113,43 @@ exports.write = (image, drive, options) => {
emitter.emit('error', error);
};
ipc.server.on('error', emitError);
ipc.server.on('message', (data) => {
let message = null;
/**
* @summary Bridge robot message to the child writer caller
* @function
* @private
*
* @param {String} message - robot message
*
* @example
* bridgeRobotMessage(robot.buildMessage('foo', {
* bar: 'baz'
* }));
*/
const bridgeRobotMessage = (message) => {
try {
message = robot.parseMessage(data);
const parsedMessage = robot.parseMessage(message);
// These are lighweight accessor methods for
// the properties of the parsed message
const messageCommand = robot.getCommand(parsedMessage);
const messageData = robot.getData(parsedMessage);
// The error object is decomposed by the CLI for serialisation
// purposes. We compose it back to an `Error` here in order
// to provide better encapsulation.
if (messageCommand === 'error') {
emitError(robot.recomposeErrorMessage(parsedMessage));
} else {
emitter.emit(messageCommand, messageData);
}
} catch (error) {
return emitError(error);
emitError(error);
}
};
// The error object is decomposed by the CLI for serialisation
// purposes. We compose it back to an `Error` here in order
// to provide better encapsulation.
if (robot.getCommand(message) === 'error') {
return emitError(robot.recomposeErrorMessage(message));
}
return emitter.emit(robot.getCommand(message), robot.getData(message));
});
ipc.server.on('error', emitError);
ipc.server.on('message', bridgeRobotMessage);
ipc.server.on('start', () => {
const child = childProcess.fork(CONSTANTS.WRITER_PROXY_SCRIPT, argv, {
@ -144,7 +162,7 @@ exports.write = (image, drive, options) => {
});
child.stderr.on('data', (data) => {
emitError(new Error(data.toString()));
bridgeRobotMessage(data.toString());
// This function causes the `close` event to be emitted
child.kill();

View File

@ -28,6 +28,7 @@ const sudoPrompt = Bluebird.promisifyAll(require('sudo-prompt'));
const utils = require('./utils');
const EXIT_CODES = require('../shared/exit-codes');
const errors = require('../shared/errors');
const robot = require('../shared/robot');
const packageJSON = require('../../package.json');
// This script is in charge of spawning the writer process and
@ -153,10 +154,23 @@ return isElevated().then((elevated) => {
if (!_.isEmpty(stderr)) {
throw errors.createError(stderr);
}
// We're hardcoding internal error messages declared by `sudo-prompt`.
// There doesn't seem to be a better way to handle these errors, so
// for now, we should make sure we double check if the error messages
// have changed every time we upgrade `sudo-prompt`.
}).catch({
message: 'User did not grant permission.'
}, () => {
process.exit(EXIT_CODES.CANCELLED);
}).catch({
message: 'No polkit authentication agent found.'
}, () => {
throw errors.createUserError(
'No polkit authentication agent found',
'Please install a polkit authentication agent for your desktop environment of choice to continue'
);
});
}
@ -226,6 +240,6 @@ return isElevated().then((elevated) => {
process.exit(exitCode);
});
}).catch((error) => {
console.error(error);
robot.printError(error);
process.exit(EXIT_CODES.GENERAL_ERROR);
});