Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1022)

Side by Side Diff: build/config/ios/codesign.py

Issue 2446283002: Ignore expired mobile provisioning profiles. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import argparse 5 import argparse
6 import datetime
6 import fnmatch 7 import fnmatch
7 import glob 8 import glob
8 import os 9 import os
9 import plistlib 10 import plistlib
10 import shutil 11 import shutil
11 import subprocess 12 import subprocess
12 import sys 13 import sys
13 import tempfile 14 import tempfile
14 15
15 16
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 return os.path.join(self._path, self._data['CFBundleExecutable']) 59 return os.path.join(self._path, self._data['CFBundleExecutable'])
59 60
60 61
61 class ProvisioningProfile(object): 62 class ProvisioningProfile(object):
62 """Wraps a mobile provisioning profile file.""" 63 """Wraps a mobile provisioning profile file."""
63 64
64 def __init__(self, provisioning_profile_path): 65 def __init__(self, provisioning_profile_path):
65 """Initializes the ProvisioningProfile with data from profile file.""" 66 """Initializes the ProvisioningProfile with data from profile file."""
66 self._path = provisioning_profile_path 67 self._path = provisioning_profile_path
67 self._data = plistlib.readPlistFromString(subprocess.check_output([ 68 self._data = plistlib.readPlistFromString(subprocess.check_output([
68 'xcrun', 'security', 'cms', '-D', '-i', provisioning_profile_path])) 69 'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA',
70 '-i', provisioning_profile_path]))
69 71
70 @property 72 @property
71 def path(self): 73 def path(self):
72 return self._path 74 return self._path
73 75
74 @property 76 @property
75 def application_identifier_pattern(self): 77 def application_identifier_pattern(self):
76 return self._data.get('Entitlements', {}).get('application-identifier', '') 78 return self._data.get('Entitlements', {}).get('application-identifier', '')
77 79
78 @property 80 @property
79 def team_identifier(self): 81 def team_identifier(self):
80 return self._data.get('TeamIdentifier', [''])[0] 82 return self._data.get('TeamIdentifier', [''])[0]
81 83
82 @property 84 @property
83 def entitlements(self): 85 def entitlements(self):
84 return self._data.get('Entitlements', {}) 86 return self._data.get('Entitlements', {})
85 87
88 @property
89 def expiration_date(self):
90 return self._data.get('ExpirationDate', datetime.datetime.now())
91
86 def ValidToSignBundle(self, bundle_identifier): 92 def ValidToSignBundle(self, bundle_identifier):
87 """Checks whether the provisioning profile can sign bundle_identifier. 93 """Checks whether the provisioning profile can sign bundle_identifier.
88 94
89 Args: 95 Args:
90 bundle_identifier: the identifier of the bundle that needs to be signed. 96 bundle_identifier: the identifier of the bundle that needs to be signed.
91 97
92 Returns: 98 Returns:
93 True if the mobile provisioning profile can be used to sign a bundle 99 True if the mobile provisioning profile can be used to sign a bundle
94 with the corresponding bundle_identifier, False otherwise. 100 with the corresponding bundle_identifier, False otherwise.
95 """ 101 """
96 return fnmatch.fnmatch( 102 return fnmatch.fnmatch(
97 '%s.%s' % (self.team_identifier, bundle_identifier), 103 '%s.%s' % (self.team_identifier, bundle_identifier),
98 self.application_identifier_pattern) 104 self.application_identifier_pattern)
justincohen 2016/10/25 17:41:51 can application identifier pattern include a star,
sdefresne 2016/10/25 17:54:57 Yes and yes (this is why I'm using fnmatch.fnmatch
99 105
100 def Install(self, installation_path): 106 def Install(self, installation_path):
101 """Copies mobile provisioning profile info to |installation_path|.""" 107 """Copies mobile provisioning profile info to |installation_path|."""
102 shutil.copy2(self.path, installation_path) 108 shutil.copy2(self.path, installation_path)
103 109
104 110
105 class Entitlements(object): 111 class Entitlements(object):
106 """Wraps an Entitlement plist file.""" 112 """Wraps an Entitlement plist file."""
107 113
108 def __init__(self, entitlements_path): 114 def __init__(self, entitlements_path):
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 bundle_identifier: the identifier of the bundle to sign. 156 bundle_identifier: the identifier of the bundle to sign.
151 157
152 Returns: 158 Returns:
153 The ProvisioningProfile object that can be used to sign the Bundle 159 The ProvisioningProfile object that can be used to sign the Bundle
154 object or None if no matching provisioning profile was found. 160 object or None if no matching provisioning profile was found.
155 """ 161 """
156 provisioning_profile_paths = glob.glob( 162 provisioning_profile_paths = glob.glob(
157 os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision')) 163 os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision'))
158 164
159 # Iterate over all installed mobile provisioning profiles and filter those 165 # Iterate over all installed mobile provisioning profiles and filter those
160 # that can be used to sign the bundle. 166 # that can be used to sign the bundle, ignoring expired ones.
167 now = datetime.datetime.now()
161 valid_provisioning_profiles = [] 168 valid_provisioning_profiles = []
169 one_hour = datetime.timedelta(0, 3600)
162 for provisioning_profile_path in provisioning_profile_paths: 170 for provisioning_profile_path in provisioning_profile_paths:
163 provisioning_profile = ProvisioningProfile(provisioning_profile_path) 171 provisioning_profile = ProvisioningProfile(provisioning_profile_path)
172 if provisioning_profile.expiration_date - now < one_hour:
173 sys.stderr.write(
174 'Warning: ignoring expired provisioning profile: %s.\n' %
175 provisioning_profile_path)
176 continue
164 if provisioning_profile.ValidToSignBundle(bundle_identifier): 177 if provisioning_profile.ValidToSignBundle(bundle_identifier):
165 valid_provisioning_profiles.append(provisioning_profile) 178 valid_provisioning_profiles.append(provisioning_profile)
166 179
167 if not valid_provisioning_profiles: 180 if not valid_provisioning_profiles:
168 if required: 181 if required:
169 sys.stderr.write( 182 sys.stderr.write(
170 'No mobile provisioning profile found for "%s".\n' % 183 'Error: no mobile provisioning profile found for "%s".\n' %
171 bundle_identifier) 184 bundle_identifier)
172 sys.exit(1) 185 sys.exit(1)
173 return None 186 return None
174 187
175 # Select the most specific mobile provisioning profile, i.e. the one with 188 # Select the most specific mobile provisioning profile, i.e. the one with
176 # the longest application identifier pattern. 189 # the longest application identifier pattern (prefer the one with the latest
177 return max( 190 # expiration date as a secondary criteria).
191 selected_provisioning_profile = max(
178 valid_provisioning_profiles, 192 valid_provisioning_profiles,
179 key=lambda p: len(p.application_identifier_pattern)) 193 key=lambda p: (len(p.application_identifier_pattern), p.expiration_date))
194
195 one_week = datetime.timedelta(7)
196 if selected_provisioning_profile.expiration_date - now < 2 * one_week:
197 sys.stderr.write(
198 'Warning: selected provisioning profile will expire soon: %s' %
justincohen 2016/10/25 17:41:51 awesome!
sdefresne 2016/10/25 17:54:57 Acknowledged.
199 selected_provisioning_profile.path)
200 return selected_provisioning_profile
180 201
181 202
182 def CodeSignBundle(bundle_path, identity, extra_args): 203 def CodeSignBundle(bundle_path, identity, extra_args):
183 process = subprocess.Popen(['xcrun', 'codesign', '--force', '--sign', 204 process = subprocess.Popen(['xcrun', 'codesign', '--force', '--sign',
184 identity, '--timestamp=none'] + list(extra_args) + [bundle_path], 205 identity, '--timestamp=none'] + list(extra_args) + [bundle_path],
185 stderr=subprocess.PIPE) 206 stderr=subprocess.PIPE)
186 _, stderr = process.communicate() 207 _, stderr = process.communicate()
187 if process.returncode: 208 if process.returncode:
188 sys.stderr.write(stderr) 209 sys.stderr.write(stderr)
189 sys.exit(process.returncode) 210 sys.exit(process.returncode)
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
403 424
404 for action in actions: 425 for action in actions:
405 action.Register(subparsers) 426 action.Register(subparsers)
406 427
407 args = parser.parse_args() 428 args = parser.parse_args()
408 args.func(args) 429 args.func(args)
409 430
410 431
411 if __name__ == '__main__': 432 if __name__ == '__main__':
412 sys.exit(Main()) 433 sys.exit(Main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698