1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import sys
16
17 import cStringIO
18 import json
19 import zipfile
20 import os
21 from M2Crypto import X509
22
23 from spacewalk.satellite_tools.syncLib import log2
24 from spacewalk.server.rhnServer.satellite_cert import SatelliteCert
25
26 import constants
27
28
30 """Class containing relevant data from RHSM manifest."""
31
32 SIGNATURE_NAME = "signature"
33 INNER_ZIP_NAME = "consumer_export.zip"
34 ENTITLEMENTS_PATH = "export/entitlements"
35 CERTIFICATE_PATH = "export/extensions"
36 PRODUCTS_PATH = "export/products"
37 CONSUMER_INFO = "export/consumer.json"
38 META_INFO = "export/meta.json"
39 UPSTREAM_CONSUMER_PATH = "export/upstream_consumer"
40
42 self.all_entitlements = []
43 self.manifest_repos = {}
44 self.sat5_certificate = None
45 self.satellite_version = None
46 self.consumer_credentials = None
47 self.uuid = None
48 self.name = None
49 self.ownerid = None
50 self.api_url = None
51 self.web_url = None
52 self.created = None
53
54 self.signature = None
55 self.data = None
56
57 top_zip = None
58 inner_zip = None
59 inner_file = None
60
61
62 zip_path = os.path.abspath(os.path.expanduser(zip_path))
63 try:
64 top_zip = zipfile.ZipFile(zip_path, 'r')
65
66 try:
67
68 inner_file = top_zip.open(self.INNER_ZIP_NAME)
69 self.data = inner_file.read()
70 inner_file_data = cStringIO.StringIO(self.data)
71 signature_file = top_zip.open(self.SIGNATURE_NAME)
72 self.signature = signature_file.read()
73
74 try:
75 inner_zip = zipfile.ZipFile(inner_file_data)
76 self._extract_consumer_info(inner_zip)
77 self._load_entitlements(inner_zip)
78 self._extract_certificate(inner_zip)
79 self._extract_meta_info(inner_zip)
80 self._extract_consumer_credentials(inner_zip)
81 finally:
82 if inner_zip is not None:
83 inner_zip.close()
84 finally:
85 if inner_file is not None:
86 inner_file.close()
87 finally:
88 if top_zip is not None:
89 top_zip.close()
90
92 files = zip_file.namelist()
93 certificates_names = []
94 for f in files:
95 if f.startswith(self.CERTIFICATE_PATH) and f.endswith(".xml"):
96 certificates_names.append(f)
97 if len(certificates_names) >= 1:
98
99 cert_file = zip_file.open(certificates_names[0])
100 self.sat5_certificate = cert_file.read().strip()
101 cert_file.close()
102
103 sat5_cert = SatelliteCert()
104 sat5_cert.load(self.sat5_certificate)
105 self.satellite_version = getattr(sat5_cert, 'satellite-version')
106 else:
107 raise MissingSatelliteCertificateError("Satellite Certificate was not found in manifest.")
108
110 product_file = zip_file.open(self.PRODUCTS_PATH + '/' + str(product.get_id()) + '.json')
111 product_data = json.load(product_file)
112 product_file.close()
113 try:
114 for content in product_data['productContent']:
115 content = content['content']
116 product.add_repository(content['label'], content['contentUrl'])
117 except KeyError:
118 log2(0, 0, "ERROR: Cannot access required field in product '%s'" % product.get_id(), stream=sys.stderr)
119 raise
120
122 files = zip_file.namelist()
123 entitlements_files = []
124 for f in files:
125 if f.startswith(self.ENTITLEMENTS_PATH) and f.endswith(".json"):
126 entitlements_files.append(f)
127
128 if len(entitlements_files) >= 1:
129 self.all_entitlements = []
130 for entitlement_file in entitlements_files:
131 entitlements = zip_file.open(entitlement_file)
132
133
134 try:
135 try:
136 data = json.load(entitlements)
137
138
139 certs = data['certificates']
140 if len(certs) != 1:
141 raise IncorrectEntitlementsFileFormatError(
142 "Single certificate in entitlements file '%s' is expected, found: %d"
143 % (entitlement_file, len(certs)))
144 cert = certs[0]
145 credentials = Credentials(data['id'], cert['cert'], cert['key'])
146
147
148 products = []
149 provided_products = data['pool']['providedProducts'] or []
150 derived_provided_products = data['pool']['derivedProvidedProducts'] or []
151 product_ids = [provided_product['productId'] for provided_product
152 in provided_products + derived_provided_products]
153 for product_id in set(product_ids):
154 product = Product(product_id)
155 self._fill_product_repositories(zip_file, product)
156 products.append(product)
157
158
159 if products:
160 entitlement = Entitlement(products, credentials)
161 self.all_entitlements.append(entitlement)
162 except KeyError:
163 log2(0, 0, "ERROR: Cannot access required field in file '%s'" % entitlement_file,
164 stream=sys.stderr)
165 raise
166 finally:
167 entitlements.close()
168 else:
169 refer_url = "%s%s" % (self.web_url, self.uuid)
170 if not refer_url.startswith("http"):
171 refer_url = "https://" + refer_url
172 raise IncorrectEntitlementsFileFormatError(
173 "No subscriptions were found in manifest.\n\nPlease refer to %s for setting up subscriptions."
174 % refer_url)
175
177 files = zip_file.namelist()
178 found = False
179 for f in files:
180 if f == self.CONSUMER_INFO:
181 found = True
182 break
183 if found:
184 consumer_info = zip_file.open(self.CONSUMER_INFO)
185 try:
186 try:
187 data = json.load(consumer_info)
188 self.uuid = data['uuid']
189 self.name = data['name']
190 self.ownerid = data['owner']['key']
191 self.api_url = data['urlApi']
192 self.web_url = data['urlWeb']
193 except KeyError:
194 log2(0, 0, "ERROR: Cannot access required field in file '%s'" % self.CONSUMER_INFO,
195 stream=sys.stderr)
196 raise
197 finally:
198 consumer_info.close()
199 else:
200 raise MissingConsumerInfoError()
201
222
224 files = zip_file.namelist()
225 consumer_credentials = []
226 for f in files:
227 if f.startswith(self.UPSTREAM_CONSUMER_PATH) and f.endswith(".json"):
228 consumer_credentials.append(f)
229
230 if len(consumer_credentials) == 1:
231 upstream_consumer = zip_file.open(consumer_credentials[0])
232 try:
233 try:
234 data = json.load(upstream_consumer)
235 self.consumer_credentials = Credentials(data['id'], data['cert'], data['key'])
236 except KeyError:
237 log2(0, 0, "ERROR: Cannot access required field in file '%s'" % consumer_credentials[0],
238 stream=sys.stderr)
239 raise
240 finally:
241 upstream_consumer.close()
242 else:
243 raise IncorrectCredentialsError(
244 "ERROR: Single upstream consumer certificate expected, found %d." % len(consumer_credentials))
245
247 return self.all_entitlements
248
250 return self.sat5_certificate
251
253 return self.satellite_version
254
256 return self.consumer_credentials
257
260
263
266
269
272
274 if self.signature and self.data:
275 certs = os.listdir(constants.CANDLEPIN_CA_CERT_DIR)
276
277 for cert_name in certs:
278 cert_file = None
279 try:
280 try:
281 cert_file = open(constants.CANDLEPIN_CA_CERT_DIR + '/' + cert_name, 'r')
282 cert = X509.load_cert_string(cert_file.read())
283 except (IOError, X509.X509Error):
284 continue
285 finally:
286 if cert_file is not None:
287 cert_file.close()
288 pubkey = cert.get_pubkey()
289 pubkey.reset_context(md='sha256')
290 pubkey.verify_init()
291
292 pubkey.verify_update(self.data)
293 if pubkey.verify_final(self.signature):
294 return True
295 return False
296
297
299 - def __init__(self, products, credentials):
300 if products and credentials:
301 self.products = products
302 self.credentials = credentials
303 else:
304 raise IncorrectEntitlementError()
305
308
310 return self.credentials
311
312
314 - def __init__(self, identifier, cert, key):
315 if identifier:
316 self.id = identifier
317 else:
318 raise IncorrectCredentialsError(
319 "ERROR: ID of credentials has to be defined"
320 )
321
322 if cert and key:
323 self.cert = cert
324 self.key = key
325 else:
326 raise IncorrectCredentialsError(
327 "ERROR: Trying to create object with cert = %s and key = %s"
328 % (cert, key)
329 )
330
333
336
339
340
343 try:
344 self.id = int(identifier)
345 except ValueError:
346 raise IncorrectProductError(
347 "ERROR: Invalid product id: %s" % identifier
348 )
349 self.repositories = {}
350
353
355 return self.repositories
356
358 self.repositories[label] = url
359
360
363
364
367
368
371
372
375
376
379
380
383
384
387
388
391