• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6Finds the team identifier to use for code signing bundle given its
7bundle identifier.
8"""
9
10import argparse
11import fnmatch
12import glob
13import json
14import os
15import plistlib
16import subprocess
17import sys
18
19
20class ProvisioningProfile(object):
21
22  def __init__(self, mobileprovision_path):
23    self._path = mobileprovision_path
24    self._data = plistlib.loads(
25        subprocess.check_output(
26            ['security', 'cms', '-D', '-i', mobileprovision_path]))
27
28  @property
29  def application_identifier_pattern(self):
30    return self._data.get('Entitlements', {}).get('application-identifier', '')
31
32  @property
33  def app_identifier_prefix(self):
34    return self._data.get('ApplicationIdentifierPrefix', [''])[0]
35
36  def ValidToSignBundle(self, bundle_identifier):
37    """Returns whether the provisioning profile can sign |bundle_identifier|."""
38    return fnmatch.fnmatch(
39        self.app_identifier_prefix + '.' + bundle_identifier,
40        self.application_identifier_pattern)
41
42
43def GetProvisioningProfilesDir():
44  """Returns the location of the locally installed provisioning profiles."""
45  return os.path.join(
46      os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
47
48
49def ListProvisioningProfiles():
50  """Returns a list of all installed provisioning profiles."""
51  return glob.glob(
52      os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision'))
53
54
55def LoadProvisioningProfile(mobileprovision_path):
56  """Loads the Apple Property List embedded in |mobileprovision_path|."""
57  return ProvisioningProfile(mobileprovision_path)
58
59
60def ListValidProvisioningProfiles(bundle_identifier):
61  """Returns a list of provisioning profile valid for |bundle_identifier|."""
62  result = []
63  for mobileprovision_path in ListProvisioningProfiles():
64    mobileprovision = LoadProvisioningProfile(mobileprovision_path)
65    if mobileprovision.ValidToSignBundle(bundle_identifier):
66      result.append(mobileprovision)
67  return result
68
69
70def FindProvisioningProfile(bundle_identifier):
71  """Returns the path to the provisioning profile for |bundle_identifier|."""
72  return max(
73      ListValidProvisioningProfiles(bundle_identifier),
74      key=lambda p: len(p.application_identifier_pattern))
75
76
77def GenerateSubsitutions(bundle_identifier, mobileprovision):
78  if mobileprovision:
79    app_identifier_prefix = mobileprovision.app_identifier_prefix + '.'
80  else:
81    app_identifier_prefix = '*.'
82
83  return {
84      'CFBundleIdentifier': bundle_identifier,
85      'AppIdentifierPrefix': app_identifier_prefix
86  }
87
88
89def ParseArgs(argv):
90  """Parses command line arguments."""
91  parser = argparse.ArgumentParser(
92      description=__doc__,
93      formatter_class=argparse.ArgumentDefaultsHelpFormatter)
94
95  parser.add_argument(
96      '-b', '--bundle-identifier', required=True,
97      help='bundle identifier for the application')
98  parser.add_argument(
99      '-o', '--output', default='-',
100      help='path to the result; - means stdout')
101
102  return parser.parse_args(argv)
103
104
105def main(argv):
106  args = ParseArgs(argv)
107
108  mobileprovision = FindProvisioningProfile(args.bundle_identifier)
109  substitutions = GenerateSubsitutions(args.bundle_identifier, mobileprovision)
110
111  if args.output == '-':
112    sys.stdout.write(json.dumps(substitutions))
113  else:
114    with open(args.output, 'w') as output:
115      output.write(json.dumps(substitutions))
116
117
118if __name__ == '__main__':
119  sys.exit(main(sys.argv[1:]))
120