Skip to content

Commit

Permalink
Merge pull request #44 from sethlu/automate-entitlements-by-info-plist
Browse files Browse the repository at this point in the history
Introduce entitlements automation
  • Loading branch information
sethlu committed Jun 1, 2016
2 parents 3a14582 + 7bfda8c commit 0d04b23
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 38 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Code-signing for packaged Electron OS X apps.

[`electron-osx-sign`][electron-osx-sign] minimizes the extra work needed to eventually prepare your apps for shipping, providing the most basic tools and assets. Note that the bare necessities here are sufficient for enabling app sandbox, yet other configurations for like network access require additional work.

It is worth noting as well that starting from [Electron] v1.1.1, a new mechanism was introduced to satisfy IPC communications (see [electron#5601](https://github.com/electron/electron/pull/5601)); wishing to have full support of various Electron versions, please utilize `opts.version`, which brings less hassle with making default settings among releases.

We are trying to keep updated to the Electron specifications; please [file us an issue](https://github.com/electron-userland/electron-osx-sign/issues/new) if having any suggestions or experiencing difficulties code signing your products.

Please visit our [Wiki](https://github.com/electron-userland/electron-osx-sign/wiki) hosted here on GitHub for walk-throughs and notes from past projects shipped with [`electron-packager`][electron-packager] and [`electron-osx-sign`][electron-osx-sign].
Expand Down Expand Up @@ -50,6 +52,12 @@ Example:
electron-osx-sign path/to/my.app
```

The script above being sufficient, it is, however, recommended to make use of `opts.version` while signing for example:

```sh
electron-osx-sign path/to/my.app --version=1.2.0
```

For details on the optional flags, run `electron-osx-sign --help` or see [electron-osx-sign-usage.txt](https://github.com/electron-userland/electron-osx-sign/blob/master/bin/electron-osx-sign-usage.txt).

#### From the API
Expand Down Expand Up @@ -127,7 +135,7 @@ See [default.mas.inherit.entitlements](https://github.com/electron-userland/elec
`identity` - *String*

Name of certificate to use when signing.
Default to retrieve from `opts.keychain` (see below) or system default keychain.
Default to retrieve from `keychain` (see below) or system default keychain.

Signing platform `mas` will look for `3rd Party Mac Developer Application: * (*)`, and platform `darwin` will look for `Developer ID Application: * (*)` by default.

Expand All @@ -147,6 +155,20 @@ Build platform of Electron.
Allowed values: `darwin`, `mas`.
Default to auto detect by presence of `Squirrel.framework` within the application bundle.

`pre-auto-entitlements` - *Boolean*

Flag to enable automation of `com.apple.security.application-groups` in entitlements file and update `Info.plist` with `ElectronTeamID`.
Allowed values: `true`, `false`.
Default to `true`.

`version` - *String*

Build version of Electron.
Values may be like: `1.1.1`, `1.2.0`.
Default to latest Electron version.

It is recommended to utilize this option for best support of specific Electron versions. This may trigger pre/post operations for signing: For example, automation of setting `com.apple.security.application-groups` in entitlements file and of updating `Info.plist` with `ElectronTeamID` is enabled for all versions starting from `1.1.1`; set `pre-auto-entitlements` option to `false` to disable this feature.

###### cb - Callback

`err` - *Error*
Expand Down Expand Up @@ -227,7 +249,7 @@ Needs file extension `.app`.
`identity` - *String*

Name of certificate to use when flattening.
Default to retrieve from `opts.keychain`(see below) or system default keychain.
Default to retrieve from `keychain`(see below) or system default keychain.

Flattening platform `mas` will look for `3rd Party Mac Developer Installer: * (*)`, and platform `darwin` will look for `Developer ID Installer: * (*)` by default.

Expand Down Expand Up @@ -373,6 +395,7 @@ ok 48 app flattened
- [electron-builder] - Complete solution to build ready for distribution and "auto update" installers of your app for OS X, Windows and Linux.

[Bluebird]: https://github.com/petkaantonov/bluebird
[Electron]: https://github.com/electron/electron
[electron-builder]: https://github.com/electron-userland/electron-builder
[electron-packager]: https://github.com/electron-userland/electron-packager
[electron-osx-sign]: https://github.com/electron-userland/electron-osx-sign
Expand Down
13 changes: 7 additions & 6 deletions bin/electron-osx-flat-usage.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@

Usage: electron-osx-flat <app> [--options...]

Optional options
Options:

identity Name of certificate to use when flattening. Default to retrieve from keychain specified, see below.
install Path to install for the bundle. Default `/Applications`.
keychain The keychain name. Default to system default keychain.
platform Build platform of Electron. Allowed values: darwin, mas. Default to auto detect from application package.
pkg Path to the output package.
identity Name of certificate to use when flattening. Default to retrieve from keychain specified, see below.
install Path to install for the bundle. Default `/Applications`.
keychain The keychain name. Default to system default keychain.
platform Build platform of Electron. Allowed values: darwin, mas. Default to auto detect from application package.
pkg Path to the output package.
6 changes: 5 additions & 1 deletion bin/electron-osx-flat.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#!/usr/bin/env node
var fs = require('fs')
var path = require('path')
var args = require('minimist')(process.argv.slice(2), {'boolean': ['help']})
var args = require('minimist')(process.argv.slice(2), {
'boolean': [
'help'
]
})
var usage = fs.readFileSync(path.join(__dirname, 'electron-osx-flat-usage.txt')).toString()
var flat = require('../').flat

Expand Down
19 changes: 11 additions & 8 deletions bin/electron-osx-sign-usage.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@

Usage: electron-osx-sign <app> [additional-binaries...] [--options...]

Optional options
Options:

binaries Path to additional binaries that will be signed along with built-ins of Electron, spaced.
entitlements Path to entitlements file for signing Mac App Store application.
entitlements-inherit Path to child entitlements file for signing frameworks and bundles of Mac App Store application.
identity Name of certificate to use when signing. Default to retrieve from keychain specified, see below.
keychain The keychain name. Default to system default keychain.
ignore Regex that signals ignoring a file before signing. Default to undefined.
platform Build platform of Electron. Allowed values: `darwin`, `mas`. Default to auto detect from application package.
binaries Path to additional binaries that will be signed along with built-ins of Electron, spaced.
entitlements Path to entitlements file for signing Mac App Store application.
entitlements-inherit Path to child entitlements file for signing frameworks and bundles of Mac App Store application.
identity Name of certificate to use when signing. Default to retrieve from keychain specified, see below.
keychain The keychain name. Default to system default keychain.
ignore Regex that signals ignoring a file before signing. Default to undefined.
no-pre-auto-entitlements Flag to disable automation of `com.apple.security.application-groups` in entitlements file and update `Info.plist` with `ElectronTeamID`.
platform Build platform of Electron. Allowed values: `darwin`, `mas`. Default to auto detect from application package.
version Build version of Electron. Values may be: `1.2.0`.
10 changes: 9 additions & 1 deletion bin/electron-osx-sign.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
#!/usr/bin/env node
var fs = require('fs')
var path = require('path')
var args = require('minimist')(process.argv.slice(2), {'boolean': ['help']})
var args = require('minimist')(process.argv.slice(2), {
'boolean': [
'help',
'pre-auto-entitlements'
],
'default': {
'pre-auto-entitlements': true
}
})
var usage = fs.readFileSync(path.join(__dirname, 'electron-osx-sign-usage.txt')).toString()
var sign = require('../')

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
118 changes: 100 additions & 18 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ var isBinaryFileAsync = Promise.promisify(require('isbinaryfile'))
var execFileAsync = Promise.promisify(child.execFile)
var lstatAsync = Promise.promisify(fs.lstat)

var compareVersion = require('compare-version')
var plist = require('plist')
var tempfile = require('tempfile')

var debug = require('debug')
var debuglog = debug('electron-osx-sign')
debuglog.log = console.log.bind(console)
Expand Down Expand Up @@ -62,7 +66,7 @@ function findIdentityAsync (opts, identity) {
for (var i = 0, l = lines.length; i < l; i++) {
var line = lines[i]
location = line.indexOf(identity)
if (location >= 0) {
if (location > -1) {
opts.identity = line.substring(line.indexOf('"') + 1, line.lastIndexOf('"'))
break
}
Expand Down Expand Up @@ -130,6 +134,63 @@ function getFilePathIfBinaryAsync (filePath) {
})
}

/**
* This function returns a promise completing the entitlements automation: The process includes checking in `Info.plist` for `ElectronTeamID` or setting parsed value from identity, and checking in entitlements file for `com.apple.security.application-groups` or inserting new into array. A temporary entitlements file may be created to replace the input for any changes introduced.
* @param {Object} opts - Options.
* @returns {Promise} Promise.
*/
function preAutoEntitlementAppGroupAsync (opts) {
// If entitlements file not provided, default will be used. Fixes #41
var readFileAsync = Promise.promisify(fs.readFile)
var writeFileAsync = Promise.promisify(fs.writeFile)

var appInfoPath = path.join(getAppContentsPath(opts), 'Info.plist')
var appInfo
var entitlementsPath = tempfile('.plist')
var entitlements

return readFileAsync(opts.entitlements, 'utf8')
.then(function (result) {
entitlements = plist.parse(result)
if (!entitlements['com.apple.security.app-sandbox']) {
// Only automate when app sandbox enabled
return Promise.resolve()
}

return readFileAsync(appInfoPath, 'utf8')
.then(function (result) {
appInfo = plist.parse(result)

// Use ElectronTeamID in Info.plist if already specified
if (!appInfo.ElectronTeamID) {
appInfo.ElectronTeamID = opts.identity.substring(opts.identity.indexOf('(') + 1, opts.identity.lastIndexOf(')'))
debugwarn('`ElectronTeamID` not found in `Info.plist`, use parsed from signing identity: ' + appInfo.ElectronTeamID)
return writeFileAsync(appInfoPath, plist.build(appInfo), 'utf8')
} else {
debuglog('`ElectronTeamID` found in `Info.plist`: ' + appInfo.ElectronTeamID)
return Promise.resolve()
}
})
.then(function () {
// Init entitlements app group key to array
if (!entitlements['com.apple.security.application-groups']) {
entitlements['com.apple.security.application-groups'] = []
}
// Insert app group if not exists
var appGroup = appInfo.ElectronTeamID + '.' + appInfo.CFBundleIdentifier
if (entitlements['com.apple.security.application-groups'].indexOf(appGroup) === -1) {
entitlements['com.apple.security.application-groups'].push(appGroup)
debugwarn('`com.apple.security.application-groups` not found in entitlements file, new inserted: ' + appGroup)
return writeFileAsync(entitlementsPath, plist.build(entitlements), 'utf8')
} else {
debuglog('`com.apple.security.application-groups` found in entitlements file: ' + appGroup)
return Promise.resolve()
}
})
.thenReturn(null)
})
}

/**
* This function returns a promise validating opts.app, the application to be signed or flattened
* @param {Object} opts - Options.
Expand Down Expand Up @@ -328,7 +389,7 @@ function signApplicationAsync (opts) {
})
.then(function () {
debuglog('Signing... ' + opts.app)
execFileAsync('codesign', args.concat('--entitlements', opts.entitlements, opts.app))
return execFileAsync('codesign', args.concat('--entitlements', opts.entitlements, opts.app))
})
} else {
// Otherwise normally
Expand All @@ -346,10 +407,7 @@ function signApplicationAsync (opts) {
.then(function () {
// Verify code sign
debuglog('Verifying code sign...')
var promise = Promise.delay(1000)
.then(function () {
return verifySignApplicationAsync(opts)
})
var promise = verifySignApplicationAsync(opts)
.then(function (result) {
debuglog('Verification displayed below:\n' + result)
})
Expand All @@ -367,6 +425,7 @@ function signApplicationAsync (opts) {
})
.then(function (result) {
debuglog('Entitlements:\n' + result)
return null
})
}
return promise
Expand Down Expand Up @@ -396,11 +455,11 @@ function signAsync (opts) {
// Further reading: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
if (!opts.entitlements) {
debugwarn('No `entitlements` passed in arguments, will fallback to default settings.')
opts.entitlements = path.join(__dirname, 'default.mas.entitlements')
opts.entitlements = path.join(__dirname, 'default.mas.plist')
}
if (!opts['entitlements-inherit']) {
debugwarn('No `entitlements-inherit` passed in arguments, will fallback to default settings.')
opts['entitlements-inherit'] = path.join(__dirname, 'default.mas.inherit.entitlements')
opts['entitlements-inherit'] = path.join(__dirname, 'default.mas.inherit.plist')
}
} else if (opts.platform === 'darwin') {
// Not necessary to have entitlements for non Mac App Store distribution
Expand All @@ -410,11 +469,11 @@ function signAsync (opts) {
// If entitlements is provided as a flag, fallback to default
if (opts.entitlements === true) {
debugwarn('`entitlements` not specified in arguments, will fallback to default settings.')
opts.entitlements = path.join(__dirname, 'default.darwin.entitlements')
opts.entitlements = path.join(__dirname, 'default.darwin.plist')
}
if (!opts['entitlements-inherit']) {
debugwarn('No `entitlements-inherit` passed in arguments, will fallback to default settings.')
opts['entitlements-inherit'] = path.join(__dirname, 'default.darwin.inherit.entitlements')
opts['entitlements-inherit'] = path.join(__dirname, 'default.darwin.inherit.plist')
}
}
} else {
Expand All @@ -437,6 +496,23 @@ function signAsync (opts) {
return Promise.reject(new Error('Unexpected Electron platform.'))
}
})
.then(function () {
// Pre-sign operations
var promise = Promise.resolve()
if (opts.version ? compareVersion(opts.version, '1.1.1') >= 0 : true) {
// Enable Mac App Store sandboxing without using temporary-exception, introduced in Electron v1.1.1. Relates to electron#5601
if (opts['pre-auto-entitlements'] === false) {
debugwarn('Pre-sign operation disabled for entitlements automation.')
} else {
debuglog('Pre-sign operation enabled for entitlements automation with versions >= `1.1.1`; disable by setting `pre-auto-entitlements` to `false`.')
promise = promise.then(function () {
debuglog('Automating entitlement app group...')
return preAutoEntitlementAppGroupAsync(opts)
})
}
}
return promise
})
.then(function () {
debuglog('Signing application...')
debuglog('> application ' + opts.app)
Expand All @@ -446,10 +522,11 @@ function signAsync (opts) {
debuglog('> additional-binaries ' + opts.binaries)
debuglog('> identity ' + opts.identity)
return signApplicationAsync(opts)
.then(function () {
debuglog('Application signed.')
return null
})
})
.then(function () {
// Post-sign operations
debuglog('Application signed.')
return null
})
}

Expand Down Expand Up @@ -486,17 +563,22 @@ function flatAsync (opts) {
return Promise.reject(new Error('Unexpected Electron platform.'))
}
})
.then(function () {
// Pre-flat operations
return null
})
.then(function () {
debuglog('Flattening application...')
debuglog('> application ' + opts.app)
debuglog('> package-output ' + opts.pkg)
debuglog('> install-path ' + opts.install)
debuglog('> identity ' + opts.identity)
return flatApplicationAsync(opts)
.then(function () {
debuglog('Application flattened.')
return null
})
})
.then(function () {
// Post-flat operations
debuglog('Application flattened.')
return null
})
}

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
"homepage": "https://github.com/electron-userland/electron-osx-sign",
"dependencies": {
"bluebird": "^3.3.5",
"compare-version": "^0.1.2",
"debug": "^2.2.0",
"isbinaryfile": "^3.0.0",
"minimist": "^1.2.0"
"minimist": "^1.2.0",
"plist": "^1.2.0",
"tempfile": "^1.1.1"
},
"devDependencies": {
"compare-version": "^0.1.2",
"electron-download": "^2.1.2",
"extract-zip": "^1.5.0",
"mkdirp": "^0.5.1",
Expand Down

0 comments on commit 0d04b23

Please sign in to comment.