Commit 2b095ee0 authored by Mark Mentovai's avatar Mark Mentovai Committed by Commit Bot

Mac signing: support --asc-provider for notarization

For Apple Developer accounts associated with multiple development teams
(as mine is), altool requires --asc-provider to select a team any time
--username is used. Arguments are added to the sign_chrome.py and
notarize_thing.py notarization drivers whose values are propagated to
altool as needed.

For most Apple Developer accounts associated only with a single
development team, --asc-provider is not necessary.

This also removes --no-dir-entries from the zip command used to build an
archive for notarization, because if the code was signed with empty
directories, they need to be present for notarization too.

Bug: 980334
Change-Id: I646f3ee5729030df01ea200b9e72138626208931
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1682685
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678370}
parent 97134d9c
......@@ -15,7 +15,7 @@ from signing.config import CodeSignConfig
def main():
parser = argparse.ArgumentParser(
'Notarize and staple an application binary or archive.')
description='Notarize and staple an application binary or archive.')
parser.add_argument(
'--user',
'-u',
......@@ -27,6 +27,13 @@ def main():
required=True,
help='The password or password reference (e.g. @keychain, see '
'`xcrun altool -h`) to access the Apple notary service.')
parser.add_argument(
'--asc-provider',
help='The ASC provider string to be used as the `--asc-provider` '
'argument to `xcrun altool`, to be used when --user is associated with '
'with multiple Apple developer teams. See `xcrun altool -h`. Run '
'`iTMSTransporter -m provider -account_type itunes_connect -v off -u '
'USERNAME -p PASSWORD` to list valid providers.')
parser.add_argument(
'--no-staple',
action='store_true',
......@@ -56,7 +63,8 @@ def main():
config_class = OverrideBundleIDConfig
config = config_class('notused', None, args.user, args.password)
config = config_class('notused', None, args.user, args.password,
args.asc_provider)
uuids = []
for path in args.file:
......
......@@ -72,6 +72,13 @@ def main():
'--notary-password',
help='The password or password reference (e.g. @keychain, see '
'`xcrun altool -h`) used to authenticate to the Apple notary service.')
parser.add_argument(
'--notary-asc-provider',
help='The ASC provider string to be used as the `--asc-provider` '
'argument to `xcrun altool`, to be used when --notary-user is '
'associated with multiple Apple developer teams. See `xcrun altool -h. '
'Run `iTMSTransporter -m provider -account_type itunes_connect -v off '
'-u USERNAME -p PASSWORD` to list valid providers.')
parser.add_argument(
'--development',
action='store_true',
......@@ -114,9 +121,9 @@ def main():
parser.error('The --notary-user and --notary-password arguments '
'are required with --notarize.')
config = create_config(
(args.identity, args.keychain, args.notary_user, args.notary_password),
args.development)
config = create_config((args.identity, args.keychain, args.notary_user,
args.notary_password, args.notary_asc_provider),
args.development)
paths = model.Paths(args.input, args.output, None)
if not os.path.exists(paths.output):
......
......@@ -22,7 +22,8 @@ class CodeSignConfig(object):
identity,
keychain=None,
notary_user=None,
notary_password=None):
notary_password=None,
notary_asc_provider=None):
"""Creates a CodeSignConfig that will sign the product using the static
properties on the class, using the code signing identity passed to the
constructor, which is found in the specified keychain.
......@@ -38,12 +39,16 @@ class CodeSignConfig(object):
notary_password: Optional string password or password reference
(e.g. @keychain, see `xcrun altool -h`) that will be used to
authenticate to Apple's notary service if notarizing.
notary_asc_provider: Optitonal string that will be used as the
`--asc-provider` argument to `xcrun altool`, to be used when
notary_user is associatetd with multiple Apple developer teams.
"""
assert identity
self._identity = identity
self._keychain = keychain
self._notary_user = notary_user
self._notary_password = notary_password
self._notary_asc_provider = notary_asc_provider
@property
def identity(self):
......@@ -71,6 +76,13 @@ class CodeSignConfig(object):
"""
return self._notary_password
@property
def notary_asc_provider(self):
"""Returns the ASC provider for authenticating to Apple's notary service
when notary_user is associatetd with multiple Apple developer teams.
"""
return self._notary_asc_provider
@property
def app_product(self):
"""Returns the product name that is used for the outer .app bundle.
......
......@@ -257,7 +257,7 @@ class Distribution(object):
return DistributionCodeSignConfig(
base_config.identity, base_config.keychain, base_config.notary_user,
base_config.notary_password)
base_config.notary_password, base_config.notary_asc_provider)
class Paths(object):
......
......@@ -33,12 +33,15 @@ def submit(path, config):
Returns:
A UUID from the notary service that represents the request.
"""
output = commands.run_command_output([
command = [
'xcrun', 'altool', '--notarize-app', '--file', path,
'--primary-bundle-id', config.base_bundle_id, '--username',
config.notary_user, '--password', config.notary_password,
'--output-format', 'xml'
])
]
if config.notary_asc_provider is not None:
command.extend(['--asc-provider', config.notary_asc_provider])
output = commands.run_command_output(command)
plist = plistlib.loads(output)
uuid = plist['notarization-upload']['RequestUUID']
print('Submitted {} for notarization, request UUID: {}.'.format(path, uuid))
......@@ -71,11 +74,15 @@ def wait_for_results(uuids, config):
while len(wait_set) > 0:
for uuid in list(wait_set):
try:
output = commands.run_command_output([
command = [
'xcrun', 'altool', '--notarization-info', uuid,
'--username', config.notary_user, '--password',
config.notary_password, '--output-format', 'xml'
])
]
if config.notary_asc_provider is not None:
command.extend(
['--asc-provider', config.notary_asc_provider])
output = commands.run_command_output(command)
except subprocess.CalledProcessError as e:
# A notarization request might report as "not found" immediately
# after submission, which causes altool to exit non-zero. Check
......
......@@ -40,6 +40,25 @@ class TestSubmit(unittest.TestCase):
'--output-format', 'xml'
])
@mock.patch('signing.commands.run_command_output')
def test_valid_upload_with_asc_provider(self, run_command_output):
run_command_output.return_value = _make_plist({
'notarization-upload': {
'RequestUUID': '746f1537-0613-4e49-a9a0-869f2c9dc8e5'
},
})
config = test_config.TestConfig(
notary_asc_provider='[NOTARY-ASC-PROVIDER]')
uuid = notarize.submit('/tmp/file.dmg', config)
self.assertEqual('746f1537-0613-4e49-a9a0-869f2c9dc8e5', uuid)
run_command_output.assert_called_once_with([
'xcrun', 'altool', '--notarize-app', '--file', '/tmp/file.dmg',
'--primary-bundle-id', 'test.signing.bundle_id', '--username',
'[NOTARY-USER]', '--password', '[NOTARY-PASSWORD]',
'--output-format', 'xml', '--asc-provider', '[NOTARY-ASC-PROVIDER]'
])
class TestWaitForResults(unittest.TestCase):
......@@ -65,6 +84,32 @@ class TestWaitForResults(unittest.TestCase):
'--output-format', 'xml'
])
@mock.patch('signing.commands.run_command_output')
def test_success_with_asc_provider(self, run_command_output):
run_command_output.return_value = _make_plist({
'notarization-info': {
'Date': '2019-07-08T20:11:24Z',
'LogFileURL': 'https://example.com/log.json',
'RequestUUID': '0a88b2d8-4098-4d3a-8461-5b543b479d15',
'Status': 'success',
'Status Code': 0
}
})
uuid = '0a88b2d8-4098-4d3a-8461-5b543b479d15'
uuids = [uuid]
self.assertEqual(
uuids,
list(
notarize.wait_for_results(
uuids,
test_config.TestConfig(
notary_asc_provider='[NOTARY-ASC-PROVIDER]'))))
run_command_output.assert_called_once_with([
'xcrun', 'altool', '--notarization-info', uuid, '--username',
'[NOTARY-USER]', '--password', '[NOTARY-PASSWORD]',
'--output-format', 'xml', '--asc-provider', '[NOTARY-ASC-PROVIDER]'
])
@mock.patch('signing.commands.run_command_output')
def test_failure(self, run_command_output):
run_command_output.return_value = _make_plist({
......
......@@ -289,7 +289,7 @@ def sign_all(orig_paths, config, package_dmg=True, do_notarization=True):
dist_config.dmg_basename + '.zip')
commands.run_command([
'zip', '--recurse-paths', '--symlinks', '--quiet',
'--no-dir-entries', zip_file, dist_config.app_dir
zip_file, dist_config.app_dir
],
cwd=dest_dir)
uuid = notarize.submit(zip_file, dist_config)
......
......@@ -483,8 +483,7 @@ class TestSignAll(unittest.TestCase):
# Prepare the app for notarization.
mock.call.run_command([
'zip', '--recurse-paths', '--symlinks', '--quiet',
'--no-dir-entries', '$W_1/AppProduct-99.0.9999.99.zip',
'App Product.app'
'$W_1/AppProduct-99.0.9999.99.zip', 'App Product.app'
],
cwd='$W_1/AppProduct-99.0.9999.99'),
mock.call.submit('$W_1/AppProduct-99.0.9999.99.zip', mock.ANY),
......@@ -531,8 +530,7 @@ class TestSignAll(unittest.TestCase):
# Prepare the app for notarization.
mock.call.run_command([
'zip', '--recurse-paths', '--symlinks', '--quiet',
'--no-dir-entries', '$W_1/AppProduct-99.0.9999.99.zip',
'App Product.app'
'$W_1/AppProduct-99.0.9999.99.zip', 'App Product.app'
],
cwd='$W_1/AppProduct-99.0.9999.99'),
mock.call.submit('$W_1/AppProduct-99.0.9999.99.zip', mock.ANY),
......
......@@ -10,15 +10,17 @@ THIS_DIR = os.path.abspath(os.path.dirname(__file__))
config = imp.load_source('signing.config', os.path.join(THIS_DIR,
'config.py.in'))
class TestConfig(config.CodeSignConfig):
def __init__(self,
identity='[IDENTITY]',
keychain='[KEYCHAIN]',
notary_user='[NOTARY-USER]',
notary_password='[NOTARY-PASSWORD]'):
notary_password='[NOTARY-PASSWORD]',
notary_asc_provider=None):
super(TestConfig, self).__init__(identity, keychain, notary_user,
notary_password)
notary_password, notary_asc_provider)
@property
def app_product(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment