From 9b82891abb86fc27c3df531fa6e1086192de4f03 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Fri, 1 Nov 2019 16:16:26 +0100 Subject: [PATCH] Use sudo instead of sudo-prompt on macOS >= Catalina Change-type: patch --- .../catalina-sudo/sudo-askpass.osascript.js | 23 ++++++++++ lib/shared/catalina-sudo/sudo.js | 42 +++++++++++++++++++ lib/shared/permissions.js | 16 +++++++ 3 files changed, 81 insertions(+) create mode 100755 lib/shared/catalina-sudo/sudo-askpass.osascript.js create mode 100644 lib/shared/catalina-sudo/sudo.js diff --git a/lib/shared/catalina-sudo/sudo-askpass.osascript.js b/lib/shared/catalina-sudo/sudo-askpass.osascript.js new file mode 100755 index 00000000..541297dd --- /dev/null +++ b/lib/shared/catalina-sudo/sudo-askpass.osascript.js @@ -0,0 +1,23 @@ +#!/usr/bin/env osascript -l JavaScript + +/* eslint-disable */ + +ObjC.import('stdlib') + +const app = Application.currentApplication() +app.includeStandardAdditions = true + +const result = app.displayDialog('balenaEtcher wants to make changes. Type your password to allow this.', { + defaultAnswer: '', + withIcon: 'stop', + buttons: ['Cancel', 'Ok'], + defaultButton: 'Ok', + hiddenAnswer: true, +}) + +if (result.buttonReturned === 'Ok') { + result.textReturned +} else { + $.exit(255) +} + diff --git a/lib/shared/catalina-sudo/sudo.js b/lib/shared/catalina-sudo/sudo.js new file mode 100644 index 00000000..fc4d97bd --- /dev/null +++ b/lib/shared/catalina-sudo/sudo.js @@ -0,0 +1,42 @@ +'use strict' + +const { execFile } = require('child_process') +const { env } = require('process') +const { join } = require('path') +const { promisify } = require('util') + +const execFileAsync = promisify(execFile) + +const SUCCESSFUL_AUTH_MARKER = 'AUTHENTICATION SUCCEEDED' +const EXPECTED_SUCCESSFUL_AUTH_MARKER = `${SUCCESSFUL_AUTH_MARKER}\n` + +exports.sudo = async (command) => { + try { + const { stdout, stderr } = await execFileAsync( + 'sudo', + [ '--askpass', 'sh', '-c', `echo ${SUCCESSFUL_AUTH_MARKER} && ${command}` ], + { + encoding: 'utf8', + env: { + PATH: env.PATH, + SUDO_ASKPASS: join(__dirname, 'sudo-askpass.osascript.js') + } + } + ) + return { + cancelled: false, + stdout: stdout.slice(EXPECTED_SUCCESSFUL_AUTH_MARKER.length), + stderr + } + } catch (error) { + /* eslint-disable-next-line no-magic-numbers */ + if (error.code === 1) { + /* eslint-disable-next-line lodash/prefer-lodash-method */ + if (!error.stdout.startsWith(EXPECTED_SUCCESSFUL_AUTH_MARKER)) { + return { cancelled: true } + } + error.stdout = error.stdout.slice(EXPECTED_SUCCESSFUL_AUTH_MARKER.length) + } + throw error + } +} diff --git a/lib/shared/permissions.js b/lib/shared/permissions.js index b051cc7f..6c23a23d 100755 --- a/lib/shared/permissions.js +++ b/lib/shared/permissions.js @@ -23,12 +23,14 @@ const childProcess = Bluebird.promisifyAll(require('child_process')) const fs = require('fs') const _ = require('lodash') const os = require('os') +const semver = require('semver') const sudoPrompt = Bluebird.promisifyAll(require('sudo-prompt')) const { promisify } = require('util') const errors = require('./errors') const { tmpFileDisposer } = require('./utils') +const { sudo: catalinaSudo } = require('./catalina-sudo/sudo') const writeFileAsync = promisify(fs.writeFile) @@ -154,6 +156,16 @@ const elevateScriptUnix = async (path, name) => { return { cancelled: false } } +const elevateScriptCatalina = async (path) => { + const cmd = [ 'sh', escapeSh(path) ].join(' ') + try { + const { cancelled } = await catalinaSudo(cmd) + return { cancelled } + } catch (error) { + return errors.createError({ title: error.stderr }) + } +} + /** * @summary Elevate a command * @function @@ -190,6 +202,10 @@ exports.elevateCommand = async (command, options) => { if (isWindows) { return elevateScriptWindows(path) } + if (os.platform() === 'darwin' && semver.compare(os.release(), '19.0.0') >= 0) { + // >= macOS Catalina + return elevateScriptCatalina(path) + } try { return await elevateScriptUnix(path, options.applicationName) } catch (error) {