pulpcore 3.84.0__py3-none-any.whl → 3.85.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pulpcore might be problematic. Click here for more details.

Files changed (163) hide show
  1. pulp_certguard/app/__init__.py +1 -1
  2. pulp_certguard/app/models.py +7 -26
  3. pulp_certguard/app/serializers.py +0 -2
  4. pulp_certguard/rhsm/__init__.py +4 -0
  5. pulp_certguard/rhsm/rhsm_check_path.py +198 -0
  6. pulp_certguard/tests/unit/certdata.py +249 -0
  7. pulp_certguard/tests/unit/test_rhsm_check_path.py +213 -0
  8. pulp_file/app/__init__.py +1 -1
  9. pulp_file/app/migrations/0001_initial_squashed_0016_add_domain.py +0 -20
  10. pulp_file/app/migrations/0017_alter_filealternatecontentsource_alternatecontentsource_ptr_and_more.py +1 -1
  11. pulpcore/app/apps.py +2 -12
  12. pulpcore/app/entrypoint.py +22 -22
  13. pulpcore/app/migrations/0001_squashed_0090_char_to_text_field.py +0 -95
  14. pulpcore/app/migrations/0091_systemid.py +1 -1
  15. pulp_file/app/migrations/0006_delete_filefilesystemexporter.py → pulpcore/app/migrations/0136_delete_basedistribution.py +3 -3
  16. pulpcore/app/migrations/0137_appstatus.py +33 -0
  17. pulpcore/app/migrations/0138_vulnerabilityreport.py +33 -0
  18. pulpcore/app/models/__init__.py +4 -1
  19. pulpcore/app/models/publication.py +0 -41
  20. pulpcore/app/models/status.py +145 -0
  21. pulpcore/app/models/task.py +1 -0
  22. pulpcore/app/models/vulnerability_report.py +34 -0
  23. pulpcore/app/serializers/__init__.py +1 -0
  24. pulpcore/app/serializers/content.py +13 -1
  25. pulpcore/app/serializers/repository.py +8 -1
  26. pulpcore/app/serializers/vulnerability_report.py +27 -0
  27. pulpcore/app/settings.py +14 -47
  28. pulpcore/app/tasks/__init__.py +2 -0
  29. pulpcore/app/tasks/vulnerability_report.py +159 -0
  30. pulpcore/app/viewsets/__init__.py +1 -0
  31. pulpcore/app/viewsets/vulnerability_report.py +20 -0
  32. pulpcore/constants.py +4 -0
  33. pulpcore/content/__init__.py +23 -22
  34. pulpcore/content/handler.py +5 -2
  35. pulpcore/migrations.py +38 -11
  36. pulpcore/openapi/__init__.py +8 -0
  37. pulpcore/plugin/models/__init__.py +2 -0
  38. pulpcore/plugin/serializers/__init__.py +2 -0
  39. pulpcore/plugin/tasking.py +2 -0
  40. pulpcore/plugin/viewsets/__init__.py +2 -0
  41. pulpcore/pytest_plugin.py +21 -21
  42. pulpcore/tasking/worker.py +38 -35
  43. pulpcore/tests/functional/api/test_auth.py +18 -3
  44. pulpcore/tests/functional/api/test_openapi_schema.py +32 -15
  45. pulpcore/tests/functional/api/using_plugin/test_checkpoint.py +23 -1
  46. pulpcore/tests/functional/api/using_plugin/test_proxy.py +1 -1
  47. pulpcore/tests/unit/content/test_heartbeat.py +11 -8
  48. pulpcore/tests/unit/test_vulnerability_report.py +74 -0
  49. {pulpcore-3.84.0.dist-info → pulpcore-3.85.0.dist-info}/METADATA +12 -17
  50. {pulpcore-3.84.0.dist-info → pulpcore-3.85.0.dist-info}/RECORD +54 -152
  51. pulp_certguard/app/utils.py +0 -28
  52. pulp_certguard/tests/unit/test_models.py +0 -9
  53. pulp_file/app/migrations/0001_initial.py +0 -59
  54. pulp_file/app/migrations/0002_file_related_names.py +0 -55
  55. pulp_file/app/migrations/0003_auto_20191014_1721.py +0 -18
  56. pulp_file/app/migrations/0004_filefilesystemexporter.py +0 -21
  57. pulp_file/app/migrations/0005_filerepository.py +0 -24
  58. pulp_file/app/migrations/0007_filefilesystemexporter.py +0 -25
  59. pulp_file/app/migrations/0008_add_manifest_field.py +0 -19
  60. pulp_file/app/migrations/0009_move_data_to_new_master_distribution_model.py +0 -77
  61. pulp_file/app/migrations/0010_auto_publish.py +0 -23
  62. pulp_file/app/migrations/0011_fix_auto_publish.py +0 -36
  63. pulp_file/app/migrations/0012_delete_filefilesystemexporter.py +0 -28
  64. pulp_file/app/migrations/0013_file_acs.py +0 -24
  65. pulp_file/app/migrations/0014_new_rbac_permissions.py +0 -33
  66. pulp_file/app/migrations/0015_allow_null_manifest.py +0 -23
  67. pulp_file/app/migrations/0016_add_domain.py +0 -25
  68. pulpcore/app/migrations/0001_initial.py +0 -451
  69. pulpcore/app/migrations/0002_increase_artifact_size_field.py +0 -18
  70. pulpcore/app/migrations/0003_remove_upload_completed.py +0 -17
  71. pulpcore/app/migrations/0004_add_duplicated_reserved_resources.py +0 -45
  72. pulpcore/app/migrations/0005_progressreport_code.py +0 -19
  73. pulpcore/app/migrations/0006_repository_plugin_managed.py +0 -18
  74. pulpcore/app/migrations/0007_delete_progress_proxies.py +0 -19
  75. pulpcore/app/migrations/0008_published_metadata_as_content.py +0 -44
  76. pulpcore/app/migrations/0009_remove_task_non_fatal_errors.py +0 -17
  77. pulpcore/app/migrations/0010_pulp_fields.py +0 -570
  78. pulpcore/app/migrations/0011_relative_path.py +0 -28
  79. pulpcore/app/migrations/0012_auto_20191104_2000.py +0 -31
  80. pulpcore/app/migrations/0013_repository_pulp_type.py +0 -18
  81. pulpcore/app/migrations/0014_remove_repository_plugin_managed.py +0 -17
  82. pulpcore/app/migrations/0015_auto_20191112_1426.py +0 -33
  83. pulpcore/app/migrations/0016_charfield_to_textfield.py +0 -68
  84. pulpcore/app/migrations/0017_remove_task_parent.py +0 -17
  85. pulpcore/app/migrations/0018_auto_20191127_2350.py +0 -20
  86. pulpcore/app/migrations/0019_add_signing_service_model.py +0 -27
  87. pulpcore/app/migrations/0020_change_publishedartifact_constraints.py +0 -17
  88. pulpcore/app/migrations/0021_add_signing_service_foreign_key.py +0 -24
  89. pulpcore/app/migrations/0022_rename_last_version.py +0 -27
  90. pulpcore/app/migrations/0023_change_exporter_models.py +0 -82
  91. pulpcore/app/migrations/0024_use_local_storage_for_uploads.py +0 -19
  92. pulpcore/app/migrations/0025_task_parent_task.py +0 -19
  93. pulpcore/app/migrations/0026_task_group.py +0 -32
  94. pulpcore/app/migrations/0027_export_backend.py +0 -31
  95. pulpcore/app/migrations/0028_import_importer_pulpimporter_pulpimporterrepository.py +0 -85
  96. pulpcore/app/migrations/0029_export_delete.py +0 -19
  97. pulpcore/app/migrations/0030_taskgroup_all_tasks_dispatched.py +0 -24
  98. pulpcore/app/migrations/0031_import_export_validate_params.py +0 -19
  99. pulpcore/app/migrations/0032_export_to_chunks.py +0 -27
  100. pulpcore/app/migrations/0033_increase_remote_artifact_size_field.py +0 -18
  101. pulpcore/app/migrations/0034_groupprogressreport.py +0 -32
  102. pulpcore/app/migrations/0035_content_upstream_id.py +0 -18
  103. pulpcore/app/migrations/0036_unprotect_last_export.py +0 -19
  104. pulpcore/app/migrations/0037_pulptemporaryfile.py +0 -28
  105. pulpcore/app/migrations/0038_repository_remote.py +0 -19
  106. pulpcore/app/migrations/0039_change_download_concurrency.py +0 -25
  107. pulpcore/app/migrations/0040_set_admin_is_staff.py +0 -28
  108. pulpcore/app/migrations/0041_accesspolicy.py +0 -29
  109. pulpcore/app/migrations/0042_rbac_for_tasks.py +0 -56
  110. pulpcore/app/migrations/0043_toc_attribute.py +0 -19
  111. pulpcore/app/migrations/0044_temp_file_artifact_field.py +0 -20
  112. pulpcore/app/migrations/0045_accesspolicy_permissions_allow_null.py +0 -19
  113. pulpcore/app/migrations/0046_task__resource_job_id.py +0 -35
  114. pulpcore/app/migrations/0047_improve_orphan_cleanup.py +0 -59
  115. pulpcore/app/migrations/0048_fips_checksums.py +0 -38
  116. pulpcore/app/migrations/0049_add_file_field_to_uploadchunk.py +0 -24
  117. pulpcore/app/migrations/0050_namespace_access_policies.py +0 -28
  118. pulpcore/app/migrations/0051_timeoutfields.py +0 -34
  119. pulpcore/app/migrations/0052_tasking_logging_cid.py +0 -18
  120. pulpcore/app/migrations/0053_remote_headers.py +0 -19
  121. pulpcore/app/migrations/0054_add_public_key.py +0 -104
  122. pulpcore/app/migrations/0055_label.py +0 -31
  123. pulpcore/app/migrations/0056_remote_rate_limit.py +0 -18
  124. pulpcore/app/migrations/0057_add_label_indexes.py +0 -23
  125. pulpcore/app/migrations/0058_accesspolicy_customized.py +0 -18
  126. pulpcore/app/migrations/0059_proxy_creds.py +0 -23
  127. pulpcore/app/migrations/0060_data_migration_proxy_creds.py +0 -45
  128. pulpcore/app/migrations/0061_call_handle_artifact_checksums_command.py +0 -87
  129. pulpcore/app/migrations/0062_add_new_distribution_mastermodel.py +0 -36
  130. pulpcore/app/migrations/0063_repository_retained_versions.py +0 -18
  131. pulpcore/app/migrations/0064_add_new_style_task_columns.py +0 -109
  132. pulpcore/app/migrations/0064_repository_user_hidden.py +0 -18
  133. pulpcore/app/migrations/0065_merge_20210615_1211.py +0 -14
  134. pulpcore/app/migrations/0066_download_concurrency_and_retry_changes.py +0 -24
  135. pulpcore/app/migrations/0067_add_protect_to_task_reservation.py +0 -19
  136. pulpcore/app/migrations/0068_add_timestamp_of_interest.py +0 -23
  137. pulpcore/app/migrations/0069_update_json_fields.py +0 -63
  138. pulpcore/app/migrations/0070_rename_retained_versions.py +0 -18
  139. pulpcore/app/migrations/0071_filesystemexport_filesystemexporter.py +0 -35
  140. pulpcore/app/migrations/0072_add_method_to_filesystem_exporter.py +0 -18
  141. pulpcore/app/migrations/0073_encrypt_remote_fields.py +0 -139
  142. pulpcore/app/migrations/0074_acs.py +0 -47
  143. pulpcore/app/migrations/0075_rbaccontentguard.py +0 -25
  144. pulpcore/app/migrations/0076_remove_reserved_resource.py +0 -39
  145. pulpcore/app/migrations/0077_move_remote_url_credentials.py +0 -41
  146. pulpcore/app/migrations/0078_grouprole_role_userrole.py +0 -70
  147. pulpcore/app/migrations/0079_rename_permissions_assignment_accesspolicy_creation_hooks.py +0 -18
  148. pulpcore/app/migrations/0080_proxy_group_model.py +0 -37
  149. pulpcore/app/migrations/0081_reapplabel_group_permissions.py +0 -59
  150. pulpcore/app/migrations/0082_add_manage_roles_permissions.py +0 -17
  151. pulpcore/app/migrations/0083_alter_group_options.py +0 -17
  152. pulpcore/app/migrations/0084_alter_rbaccontentguard_options.py +0 -17
  153. pulpcore/app/migrations/0085_contentredirectcontentguard.py +0 -26
  154. pulpcore/app/migrations/0086_task_json_fields.py +0 -77
  155. pulpcore/app/migrations/0087_taskschedule.py +0 -34
  156. pulpcore/app/migrations/0088_accesspolicy_queryset_scoping.py +0 -18
  157. pulpcore/app/migrations/0089_alter_contentredirectcontentguard_options.py +0 -17
  158. pulpcore/app/migrations/0090_char_to_text_field.py +0 -79
  159. pulpcore/tests/unit/migration/test_0077_move_remote_url_credentials.py +0 -35
  160. {pulpcore-3.84.0.dist-info → pulpcore-3.85.0.dist-info}/WHEEL +0 -0
  161. {pulpcore-3.84.0.dist-info → pulpcore-3.85.0.dist-info}/entry_points.txt +0 -0
  162. {pulpcore-3.84.0.dist-info → pulpcore-3.85.0.dist-info}/licenses/LICENSE +0 -0
  163. {pulpcore-3.84.0.dist-info → pulpcore-3.85.0.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,6 @@ class PulpCertGuardPluginAppConfig(PulpPluginAppConfig):
6
6
 
7
7
  name = "pulp_certguard.app"
8
8
  label = "certguard"
9
- version = "3.84.0"
9
+ version = "3.85.0"
10
10
  python_package_name = "pulpcore"
11
11
  domain_compatible = True
@@ -6,16 +6,12 @@ from urllib.parse import unquote
6
6
  from django.conf import settings
7
7
  from django.db import models
8
8
 
9
+ from cryptography import x509
9
10
  from OpenSSL import crypto as openssl
10
11
 
11
12
  from pulpcore.plugin.models import ContentGuard
12
13
 
13
- from pulp_certguard.app.utils import get_rhsm
14
-
15
- try:
16
- from rhsm import certificate
17
- except ImportError:
18
- pass
14
+ from pulp_certguard import rhsm
19
15
 
20
16
 
21
17
  logger = getLogger(__name__)
@@ -139,11 +135,6 @@ class RHSMCertGuard(BaseCertGuard):
139
135
 
140
136
  TYPE = "rhsm"
141
137
 
142
- def __init__(self, *args, **kwargs):
143
- """Initialize a RHSMCertGuard and ensure this system has python-rhsm on it."""
144
- get_rhsm() # Validate that rhsm is installed
145
- super().__init__(*args, **kwargs)
146
-
147
138
  class Meta:
148
139
  default_related_name = "%(app_label)s_%(model_name)s"
149
140
 
@@ -159,29 +150,19 @@ class RHSMCertGuard(BaseCertGuard):
159
150
  certificate, or if the client certificate is not trusted from the CA certificated
160
151
  stored as `ca_certificate`.
161
152
  """
162
- get_rhsm()
153
+ # TODO use python cryptography and only parse the certificate once.
163
154
  unquoted_certificate = self._get_client_cert_header(request)
164
155
  self._ensure_client_cert_is_trusted(unquoted_certificate)
165
- rhsm_cert = self._create_rhsm_cert_from_pem(unquoted_certificate)
156
+ cert = x509.load_pem_x509_certificate(unquoted_certificate.encode())
166
157
  content_path_prefix_without_trail_slash = settings.CONTENT_PATH_PREFIX.rstrip("/")
167
158
  len_prefix_to_remove = len(content_path_prefix_without_trail_slash)
168
159
  path_without_content_path_prefix = request.path[len_prefix_to_remove:]
169
- self._check_paths(rhsm_cert, path_without_content_path_prefix)
170
-
171
- @staticmethod
172
- def _create_rhsm_cert_from_pem(unquoted_certificate):
173
- try:
174
- rhsm_cert = certificate.create_from_pem(unquoted_certificate)
175
- except certificate.CertificateException:
176
- msg = _("An error occurred while loading the client certificate data into python-rhsm.")
177
- logger.warning(msg)
178
- raise PermissionError(msg)
179
- return rhsm_cert
160
+ self._check_paths(cert, path_without_content_path_prefix)
180
161
 
181
162
  @staticmethod
182
- def _check_paths(rhsm_cert, path):
163
+ def _check_paths(cert, path):
183
164
  logger.debug(f"Checking that path {path} is allowed in client cert")
184
- if rhsm_cert.check_path(path) is False:
165
+ if rhsm.check_path(cert, path) is False:
185
166
  logger.warning(f"Path {path} is *not* allowed in client cert")
186
167
  msg = _("Requested path is not a subpath of a path in the client certificate.")
187
168
  raise PermissionError(msg)
@@ -6,7 +6,6 @@ from pulpcore.plugin.serializers import ContentGuardSerializer
6
6
 
7
7
  from rest_framework import serializers
8
8
 
9
- from pulp_certguard.app.utils import get_rhsm
10
9
  from .models import RHSMCertGuard, X509CertGuard
11
10
 
12
11
 
@@ -45,7 +44,6 @@ class RHSMCertGuardSerializer(BaseCertGuardSerializer):
45
44
  @staticmethod
46
45
  def validate_ca_certificate(ca_certificate):
47
46
  """Validates the given certificate."""
48
- get_rhsm() # Validate that rhsm is installed
49
47
  return BaseCertGuardSerializer.validate_ca_certificate(ca_certificate)
50
48
 
51
49
 
@@ -0,0 +1,4 @@
1
+ from .rhsm_check_path import check_path
2
+
3
+
4
+ __all__ = ["check_path"]
@@ -0,0 +1,198 @@
1
+ import typing as t
2
+ from cryptography import x509
3
+ from heapq import heappush, heappop
4
+ import itertools
5
+ import re
6
+ import zlib
7
+
8
+
9
+ RH_OID = "1.3.6.1.4.1.2312.9"
10
+ RH_ORDER_NAME_OID = RH_OID + ".4.1"
11
+ RH_VERSION_OID = RH_OID + ".6"
12
+ RH_ENTITLEMENT_OID = RH_OID + ".7"
13
+ V1_PATH_OID_REGEX = re.compile(rf"^{re.escape(RH_OID)}\.2\.\d+\.1\.6$")
14
+
15
+
16
+ T = t.TypeVar("T")
17
+ RecursiveDict = t.Dict[str, "RecursiveDict"]
18
+
19
+
20
+ # Proper anntoation of generic classes was introduced in Python 3.12
21
+ # class HuffmannNode[T]
22
+ class HuffmannNode:
23
+ def __init__(
24
+ self,
25
+ left: "t.Self | T",
26
+ right: "t.Self | T",
27
+ ) -> None:
28
+ self.left: "t.Self | T" = left
29
+ self.right: "t.Self | T" = right
30
+
31
+ @classmethod
32
+ def build_tree(cls, items: t.Iterable[T]) -> "t.Self":
33
+ # This builds a special type of tree, where the weights are _always_ like 1,2,3,...
34
+ serial = itertools.count(1) # Used to keep items of equal weight in order.
35
+ heap: list[tuple[int, int, "t.Self | T"]] = []
36
+ for value in items:
37
+ weight = next(serial)
38
+ # First item is the actual weight, second is the tiebreaker.
39
+ # Values are never compared.
40
+ heappush(heap, (weight, weight, value))
41
+ while True:
42
+ lw, _, left = heappop(heap)
43
+ try:
44
+ rw, _, right = heappop(heap)
45
+ node = cls(left=left, right=right)
46
+ heappush(heap, (lw + rw, next(serial), node))
47
+ except IndexError:
48
+ assert isinstance(left, cls)
49
+ return left
50
+
51
+ def decode(self, bitstream: t.Iterator[bool]) -> T:
52
+ node = self.right if next(bitstream) else self.left
53
+ if isinstance(node, self.__class__):
54
+ return node.decode(bitstream)
55
+ else:
56
+ return t.cast(T, node)
57
+
58
+
59
+ def split_count(v: bytes) -> tuple[int, bytes]:
60
+ if v[0] & 128:
61
+ len_count = v[0] - 128
62
+ length = 0
63
+ for i in range(len_count):
64
+ length <<= 8
65
+ length += v[i + 1]
66
+ else:
67
+ len_count = 0
68
+ length = int(v[0])
69
+ return length, v[len_count + 1 :]
70
+
71
+
72
+ def asn1_string(v: bytes) -> str:
73
+ assert v[0] == 12
74
+ length, rest = split_count(v[1:])
75
+ result = rest.decode()
76
+ assert len(result) == length
77
+ return result
78
+
79
+
80
+ def asn1_bytes(v: bytes) -> bytes:
81
+ assert v[0] == 4
82
+ length, result = split_count(v[1:])
83
+ assert len(result) == length
84
+ return result
85
+
86
+
87
+ def cert_version(cert: x509.Certificate) -> str:
88
+ return next(
89
+ (
90
+ asn1_string(ext.value.value)
91
+ for ext in cert.extensions
92
+ if ext.oid.dotted_string == RH_VERSION_OID
93
+ ),
94
+ "1.0",
95
+ )
96
+
97
+
98
+ def is_v1_entitlement(cert: x509.Certificate) -> bool:
99
+ return any((ext for ext in cert.extensions if ext.oid.dotted_string == RH_ORDER_NAME_OID))
100
+
101
+
102
+ def v1_paths(cert: x509.Certificate) -> t.Iterator[str]:
103
+ for ext in cert.extensions:
104
+ if V1_PATH_OID_REGEX.match(ext.oid.dotted_string):
105
+ yield asn1_string(ext.value.value)
106
+
107
+
108
+ def check_path_v1(cert: x509.Certificate, normalized_path: str) -> bool:
109
+ assert is_v1_entitlement(cert)
110
+ for cert_path in v1_paths(cert):
111
+ # We build a regex here, nicely escaped, from the cert_path,
112
+ # that translates the '$variables' to regex wildcards.
113
+ path_regex = (
114
+ r"^"
115
+ + r"[^/]+".join(map(re.escape, re.split(r"\$[^/]*", cert_path.strip("/"))))
116
+ + r"($|/)"
117
+ )
118
+ if re.match(path_regex, normalized_path):
119
+ return True
120
+ return False
121
+
122
+
123
+ def bitstream(data: bytes) -> t.Iterator[bool]:
124
+ for b in data:
125
+ for i in range(8):
126
+ yield (b << i) & 128 != 0
127
+
128
+
129
+ class Entitlement:
130
+ def __init__(self, cert: x509.Certificate):
131
+ payload = self._read_payload(cert)
132
+ lex_data, node_count, path_data = self._split_payload(payload)
133
+ word_tree = self._build_word_tree(lex_data)
134
+ self._path_tree = self._build_path_tree(word_tree, node_count, path_data)
135
+
136
+ @staticmethod
137
+ def _read_payload(cert: x509.Certificate) -> bytes:
138
+ return asn1_bytes(
139
+ next(
140
+ ext.value.value
141
+ for ext in cert.extensions
142
+ if ext.oid.dotted_string == RH_ENTITLEMENT_OID
143
+ )
144
+ )
145
+
146
+ @staticmethod
147
+ def _split_payload(payload: bytes) -> tuple[bytes, int, bytes]:
148
+ decobj = zlib.decompressobj()
149
+ lex_data = decobj.decompress(payload)
150
+ node_count, path_data = split_count(decobj.unused_data)
151
+ return lex_data, node_count, path_data
152
+
153
+ @staticmethod
154
+ def _build_word_tree(lex_data: bytes) -> "HuffmannNode[str]":
155
+ words = [item.decode() for item in lex_data.split(b"\x00")]
156
+ assert len(words) > 1
157
+ return HuffmannNode.build_tree(words)
158
+
159
+ @staticmethod
160
+ def _build_path_tree(
161
+ word_tree: "HuffmannNode[str]", node_count: int, path_data: bytes
162
+ ) -> RecursiveDict:
163
+ path_nodes: list[RecursiveDict] = [{} for _ in range(node_count)]
164
+ address_tree: "HuffmannNode[RecursiveDict]" = HuffmannNode.build_tree(path_nodes[1:])
165
+ bitsource = bitstream(path_data)
166
+ for node in path_nodes:
167
+ while True:
168
+ word = word_tree.decode(bitsource)
169
+ if word == "":
170
+ break
171
+ assert word not in node
172
+ node[word] = address_tree.decode(bitsource)
173
+ return path_nodes[0]
174
+
175
+ @classmethod
176
+ def _check_path_tree(cls, node: RecursiveDict, segments: t.List[str]) -> bool:
177
+ if len(node) == 0:
178
+ return True
179
+ if len(segments) == 0:
180
+ return False
181
+ segment = segments[0]
182
+ segments = segments[1:]
183
+ for key, child in node.items():
184
+ if (segment == key or key[0] == "$") and cls._check_path_tree(child, segments):
185
+ return True
186
+ return False
187
+
188
+ def check_path(self, normalized_path: str) -> bool:
189
+ segments = normalized_path.split("/")
190
+ return self._check_path_tree(self._path_tree, segments)
191
+
192
+
193
+ def check_path(cert: x509.Certificate, path: str) -> bool:
194
+ normalized_path = re.sub(r"/+", "/", path.strip("/"))
195
+ if int(cert_version(cert).split(".")[0]) >= 3:
196
+ return Entitlement(cert).check_path(normalized_path)
197
+ else:
198
+ return check_path_v1(cert, normalized_path)
@@ -0,0 +1,249 @@
1
+ #
2
+ # Copyright (c) 2012 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public License,
5
+ # version 2 (GPLv2). There is NO WARRANTY for this software, express or
6
+ # implied, including the implied warranties of MERCHANTABILITY or FITNESS
7
+ # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
8
+ # along with this software; if not, see
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
10
+ #
11
+ # Red Hat trademarks are not licensed under GPLv2. No permission is
12
+ # granted to use or replicate Red Hat trademarks that are incorporated
13
+ # in this software or its documentation.
14
+ #
15
+
16
+ # A product cert from Candlepin's test data (product ID 100000000000002):
17
+ PRODUCT_CERT_V1_0 = """
18
+ -----BEGIN CERTIFICATE-----
19
+ MIIDeDCCAuGgAwIBAgIECgf/jTANBgkqhkiG9w0BAQUFADAzMRIwEAYDVQQDDAls
20
+ b2NhbGhvc3QxCzAJBgNVBAYTAlVTMRAwDgYDVQQHDAdSYWxlaWdoMB4XDTExMDgy
21
+ MzE3NDEzOVoXDTIxMDgyMzE3NDEzOVowGjEYMBYGA1UEAxMPMTAwMDAwMDAwMDAw
22
+ MDAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkbvsjJXu+vRzbBa+
23
+ G9S77gbQTtSbZXWncykJkinwqQkUl2jEnGN+BBYa30dfLFatkA6UP4XICIu+xVMw
24
+ qXc2O82BBubYvBTcqJy62U1YQnJFz/upSF0b9mKhcwJv2sAqMfkviGTYpjuCTfw6
25
+ VVSFUvIj+K16vRzZWgn+qTIUIt8yXipuz7E/4t+R2BG/9GCqjQq7LQb4y0FmWdGT
26
+ OehTjEY+G4+evsjyom5hXLgXlMhd3vkb7gHOyc3Yuk9h9eqGKg0oCiaF88KafMcu
27
+ hCp0mIC7W97jE2tHTWzfERw99j+uPjccBUljqVpNfW6S/iKDx0tkzv0pOnSxYYmd
28
+ HF5rbQIDAQABo4IBLDCCASgwEQYJYIZIAYb4QgEBBAQDAgWgMAsGA1UdDwQEAwIE
29
+ sDBjBgNVHSMEXDBagBQckL9Tc4HS6Df/ppG4uN7zSUsnD6E3pDUwMzESMBAGA1UE
30
+ AwwJbG9jYWxob3N0MQswCQYDVQQGEwJVUzEQMA4GA1UEBwwHUmFsZWlnaIIJAPrR
31
+ 0C/V93lLMB0GA1UdDgQWBBQPunbOh02rAFia+TmNDHCa05vI/DATBgNVHSUEDDAK
32
+ BggrBgEFBQcDAjAxBhErBgEEAZIICQGW3rGD6YACAQQcDBpBd2Vzb21lIE9TIGZv
33
+ ciB4ODZfNjQgQml0czAdBhErBgEEAZIICQGW3rGD6YACAwQIDAZ4ODZfNjQwGwYR
34
+ KwYBBAGSCAkBlt6xg+mAAgIEBgwEMy4xMTANBgkqhkiG9w0BAQUFAAOBgQARCW1E
35
+ d/w1WYaQElsYAN9/EyDXyXq4GJfTFES8eg09nUKYrMACds2wdh8m8vV7NtHl8E0y
36
+ wE//vrTlHGSD5m4/mcXsgpkHvbj/kOTP7aag7RPa51M8ocjtOplugUyIF0PsXO4B
37
+ SOxSnd1U0dX6pzEwMaJD9lCW8xZ2jsmdLUtLzQ==
38
+ -----END CERTIFICATE-----
39
+ """
40
+
41
+ # Test entitlement to the product cert above:
42
+ ENTITLEMENT_CERT_V1_0 = """
43
+ -----BEGIN CERTIFICATE-----
44
+ MIIJSzCCCLSgAwIBAgIICT4q81yqebkwDQYJKoZIhvcNAQEFBQAwNjEVMBMGA1UE
45
+ AwwMMTkyLjE2OC4xLjI1MQswCQYDVQQGEwJVUzEQMA4GA1UEBwwHUmFsZWlnaDAe
46
+ Fw0xMjA3MDUwMDAwMDBaFw0xMzA3MDUwMDAwMDBaMCsxKTAnBgNVBAMTIGZmODA4
47
+ MDgxMzg1NzRiZDIwMTM4NTc0ZDg1YTUwYjJmMIIBIjANBgkqhkiG9w0BAQEFAAOC
48
+ AQ8AMIIBCgKCAQEAlUV2otyTyQbhSV77Od1qoZMwr2Xz1gvgQUN5/0b7HREutMCT
49
+ 4okqvDkYkt50pu76M0CatAK89A716plMjzcFyyWvUuR9adxzD6VXTSxORabL1hto
50
+ 30a6hERNdwt7ma43ezvouY0tvjQ8MDFMQb2MC5yhKAq3H1mdZEtDdjlU7MQ1ujX3
51
+ wK2We0HW4sMkZXM/ZtiqXU5vB9h5hVFeZo4E7d+HayopsQoB5cKMu9MAGCg75rdD
52
+ i3pO/9za1q2fQsUjnzImMPBCVfpN2K7CuI7098MxDyjvNrU/Vql6rYhDbdGOJTWD
53
+ GZf40j/j/T+CReEMZsKHNRgLtMK+dcg4GuD+DQIDAQABo4IG5zCCBuMwEQYJYIZI
54
+ AYb4QgEBBAQDAgWgMAsGA1UdDwQEAwIEsDBmBgNVHSMEXzBdgBREQHYTNskB3AHY
55
+ /8EcmROo5jrOoqE6pDgwNjEVMBMGA1UEAwwMMTkyLjE2OC4xLjI1MQswCQYDVQQG
56
+ EwJVUzEQMA4GA1UEBwwHUmFsZWlnaIIJAPMeoqmMwB3XMB0GA1UdDgQWBBSsreit
57
+ gWXwXzIMZ3JIMdUOEkylIDATBgNVHSUEDDAKBggrBgEFBQcDAjAxBhErBgEEAZII
58
+ CQGW3rGD6YACAQQcDBpBd2Vzb21lIE9TIGZvciB4ODZfNjQgQml0czAdBhErBgEE
59
+ AZIICQGW3rGD6YACAwQIDAZ4ODZfNjQwGwYRKwYBBAGSCAkBlt6xg+mAAgIEBgwE
60
+ My4xMTAUBgsrBgEEAZIICQIBAQQFDAN5dW0wKAYMKwYBBAGSCAkCAQEBBBgMFmFs
61
+ d2F5cy1lbmFibGVkLWNvbnRlbnQwKAYMKwYBBAGSCAkCAQECBBgMFmFsd2F5cy1l
62
+ bmFibGVkLWNvbnRlbnQwHQYMKwYBBAGSCAkCAQEFBA0MC3Rlc3QtdmVuZG9yMC4G
63
+ DCsGAQQBkggJAgEBBgQeDBwvZm9vL3BhdGgvYWx3YXlzLyRyZWxlYXNldmVyMCYG
64
+ DCsGAQQBkggJAgEBBwQWDBQvZm9vL3BhdGgvYWx3YXlzL2dwZzATBgwrBgEEAZII
65
+ CQIBAQgEAwwBMTAVBgwrBgEEAZIICQIBAQkEBQwDMjAwMBUGDCsGAQQBkggJAtZ0
66
+ AQQFDAN5dW0wIwYNKwYBBAGSCAkC1nQBAQQSDBBhd2Vzb21lb3MteDg2XzY0MCMG
67
+ DSsGAQQBkggJAtZ0AQIEEgwQYXdlc29tZW9zLXg4Nl82NDAaBg0rBgEEAZIICQLW
68
+ dAEFBAkMB1JlZCBIYXQwLAYNKwYBBAGSCAkC1nQBBgQbDBkvcGF0aC90by9hd2Vz
69
+ b21lb3MveDg2XzY0MCoGDSsGAQQBkggJAtZ0AQcEGQwXL3BhdGgvdG8vYXdlc29t
70
+ ZW9zL2dwZy8wFAYNKwYBBAGSCAkC1nQBCAQDDAEwMBcGDSsGAQQBkggJAtZ0AQkE
71
+ BgwEMzYwMDAUBgsrBgEEAZIICQIAAQQFDAN5dW0wJwYMKwYBBAGSCAkCAAEBBBcM
72
+ FW5ldmVyLWVuYWJsZWQtY29udGVudDAnBgwrBgEEAZIICQIAAQIEFwwVbmV2ZXIt
73
+ ZW5hYmxlZC1jb250ZW50MB0GDCsGAQQBkggJAgABBQQNDAt0ZXN0LXZlbmRvcjAh
74
+ BgwrBgEEAZIICQIAAQYEEQwPL2Zvby9wYXRoL25ldmVyMCUGDCsGAQQBkggJAgAB
75
+ BwQVDBMvZm9vL3BhdGgvbmV2ZXIvZ3BnMBMGDCsGAQQBkggJAgABCAQDDAEwMBUG
76
+ DCsGAQQBkggJAgABCQQFDAM2MDAwFQYMKwYBBAGSCAkC1mkBBAUMA3l1bTAcBg0r
77
+ BgEEAZIICQLWaQEBBAsMCWF3ZXNvbWVvczAcBg0rBgEEAZIICQLWaQECBAsMCWF3
78
+ ZXNvbWVvczAaBg0rBgEEAZIICQLWaQEFBAkMB1JlZCBIYXQwOwYNKwYBBAGSCAkC
79
+ 1mkBBgQqDCgvcGF0aC90by8kYmFzZWFyY2gvJHJlbGVhc2V2ZXIvYXdlc29tZW9z
80
+ MCoGDSsGAQQBkggJAtZpAQcEGQwXL3BhdGgvdG8vYXdlc29tZW9zL2dwZy8wFAYN
81
+ KwYBBAGSCAkC1mkBCAQDDAExMBcGDSsGAQQBkggJAtZpAQkEBgwEMzYwMDAlBgor
82
+ BgEEAZIICQQBBBcMFUF3ZXNvbWUgT1MgZm9yIHg4Nl82NDAwBgorBgEEAZIICQQC
83
+ BCIMIGZmODA4MDgxMzg1NzRiZDIwMTM4NTc0YzdmMWYwMjNjMCAGCisGAQQBkggJ
84
+ BAMEEgwQYXdlc29tZW9zLXg4Nl82NDARBgorBgEEAZIICQQFBAMMATUwEQYKKwYB
85
+ BAGSCAkECQQDDAExMCQGCisGAQQBkggJBAYEFgwUMjAxMi0wNy0wNVQwMDowMDow
86
+ MFowJAYKKwYBBAGSCAkEBwQWDBQyMDEzLTA3LTA1VDAwOjAwOjAwWjASBgorBgEE
87
+ AZIICQQMBAQMAjMwMBIGCisGAQQBkggJBAoEBAwCNjYwGwYKKwYBBAGSCAkEDQQN
88
+ DAsxMjMzMTEzMTIzMTARBgorBgEEAZIICQQOBAMMATAwEQYKKwYBBAGSCAkEEQQD
89
+ DAExMBEGCisGAQQBkggJBAsEAwwBMjA0BgorBgEEAZIICQUBBCYMJDQwOTJlYzcz
90
+ LTE5MGUtNGY0Ny1iMmEzLWUxZTg4OWVkOGE5ODANBgkqhkiG9w0BAQUFAAOBgQA4
91
+ BLbyoRumbzf3/UXZfzkQbUPeN1CYBP64soVdIe8Wp0Dp3H6ZF8CFR6+w1R3Q7asL
92
+ Ej4AxyURBnHhQCz5UleHnMBdLU9BK2Z0BTHCrkke8tDFxsIQ2oz2Ny/+gqegyJS/
93
+ AOY8AMtVsQ9LuANGqNf42hcpFsAWy5kboT+gIXFxmw==
94
+ -----END CERTIFICATE-----
95
+ """
96
+
97
+ ENTITLEMENT_CERT_V3_0 = """
98
+ -----BEGIN CERTIFICATE-----
99
+ MIIDjzCCAvigAwIBAgIIEiB+vHfSYuwwDQYJKoZIhvcNAQEFBQAwMDEPMA0GA1UE
100
+ AwwGcGlwYm95MQswCQYDVQQGEwJVUzEQMA4GA1UEBwwHUmFsZWlnaDAeFw0xMjA5
101
+ MTgwMDAwMDBaFw0xMzA5MTgwMDAwMDBaMCsxKTAnBgNVBAMTIGZmODA4MDgxMzlk
102
+ OWUyNmMwMTM5ZGEyMzQ4OWEwMDY2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
103
+ CgKCAQEAgDfkG4OZAfxAGKPjNrhrqBJbGbB0r7CmFRnFPcnwdQQVxx6gxyH5ia5/
104
+ VoNmQRsFlWJJsl2W64IJq8xe2PeF8nhqOh3bg8Q5lPkSqHHb/9G370quRj0Ig+FX
105
+ up3eDUfiF+BpKkTHlelpPOhG6aeZzvwuLcF9+5SSqJTtuyMv759Kneybijm/Gv7x
106
+ t8EZkaUydoZhjgkN7fQAhAEW9L6y0+fudL4nBXnm7CW7k3/JSA7V73Ae1u6L/3+k
107
+ W+FDGGqKa0NR+sYOroCarMsqVd29uRabSO4rxzmYZ5W0RE0f1cUfYFM0aDwaU1+l
108
+ bVnNelesXnhjJ219BWcVdZhS+X84VwIDAQABo4IBMTCCAS0wEQYJYIZIAYb4QgEB
109
+ BAQDAgWgMAsGA1UdDwQEAwIEsDBgBgNVHSMEWTBXgBThiP6NmxGeYgJ5XWPC2dy0
110
+ EoDcVKE0pDIwMDEPMA0GA1UEAwwGcGlwYm95MQswCQYDVQQGEwJVUzEQMA4GA1UE
111
+ BwwHUmFsZWlnaIIJAN6RU5TAoOZ5MB0GA1UdDgQWBBTqqQFQLeZGIrYGXQSYxfw8
112
+ zNaXqzATBgNVHSUEDDAKBggrBgEFBQcDAjASBgkrBgEEAZIICQYEBQwDMy4wMGEG
113
+ CSsGAQQBkggJBwRUBFJ42hXKwQmAMBAEwC0mJUiwG1llJY/ohbtgtHv1O8w95yVP
114
+ 6IZTlxy7GVgHn0BaGaJvBY29ILmqPvkXh8IOWeAFpiEVtwrrTkxmoYKDgtHpgfcA
115
+ MA0GCSqGSIb3DQEBBQUAA4GBAF0jikJbd1C0PqhpRkmRGaNnAPO4kCSH+nTee1UF
116
+ xSYERpmDHnimDWYjcs35gb1wWwSOLVsTn4X+3TQqMQ1rdnEX5mn7iutqCjj5Rjzk
117
+ icfe5PF7mmnMv6FeKlFEg3WGkIkatI1tF13spHowM8GFZvAcMGVzFkvL6QtzyNKa
118
+ t6+/
119
+ -----END CERTIFICATE-----
120
+ -----BEGIN ENTITLEMENT DATA-----
121
+ eJydUl1vmzAU/SvI6iMMG1NCeNue9jZp29OmKvIXKQrYzDZNo6j/fdeQ0kT5aFU+
122
+ hM25595z7vUeCaPd0CmLKsSZpJLldUJzLpOcLklS3tc8KReiXt4XC8o5RTH6NzDt
123
+ G79DVRYjN3AnbNP7xmhU7ZHbDJCJbZUznTIueS6LVZEDS7NOAfJ1QqIfv6La2GiG
124
+ t8zqRq9RRTEkNWKjvEMVgbVnYgPIqpFAJ+glRsbKoHeP9NDxUXldlxhuQpdymfMc
125
+ 42klMCnhHQu8qSZ4zGo9EDNMsgQvE1L+xrganz8QrLScQHoBhI55y0SgFwvYMyHM
126
+ oMOWZJQSQuEz6uytkYMIPv7u0SQfH1/ZO22JvjVAjtGTsm5sL6JfCAkVrXhsvBJ+
127
+ sCpkR4c2PkziVBAzl4R4v+tDkd3QvVVk7ZbtXKI0462SySsvRi3jqr0V8ATdMaHr
128
+ XjmfHHbglvlH+JnWxqRhnU4J0jurWsWcAhcQte7Xq8G2lwIBgoBOeSaZZyv13DcW
129
+ lGYYv8SvZgjJ8muGzs/cbOUcmk38VDL6zvyRgVGTN+nMSmfWoRmoqlnr1ImbcxaA
130
+ 6SVDtDhyhK+40aFfN6ZzDf/ocPRhHjccncZeG09xOh5C3xvPpbl8bCB3HI5ROPvH
131
+ Zyo9TvL5eTzA/R9AE5nG
132
+ -----END ENTITLEMENT DATA-----
133
+ -----BEGIN RSA SIGNATURE-----
134
+ FWYaGqnaDRpuaKLEXu9RtxNLbp3q7BM651s1P1jwlE/Ff1GMZrzsreBfRa4FE6ST
135
+ jdWIkOEVpZPEHtdnHlpQaphYDOQBfdSGrOb2ksKqfp0qKrqT4dvzau5IzqtVmkTJ
136
+ SgW0kgf/C5sFxdgD6s9NKmP/u6OgI/qqE+KfnCex/ko=
137
+ -----END RSA SIGNATURE-----
138
+ """
139
+
140
+ ENTITLEMENT_CERT_V3_2 = """
141
+ -----BEGIN CERTIFICATE-----
142
+ MIIDtTCCAx6gAwIBAgIIV9sJOhe2u10wDQYJKoZIhvcNAQEFBQAwQzEiMCAGA1UE
143
+ AwwZYXJraGFtLnVzZXJzeXMucmVkaGF0LmNvbTELMAkGA1UEBhMCVVMxEDAOBgNV
144
+ BAcMB1JhbGVpZ2gwHhcNMTMwMjE0MDAwMDAwWhcNMTQwMjE0MDAwMDAwWjArMSkw
145
+ JwYDVQQDEyA4YThkMDFmNTNjZWVkM2FmMDEzY2VlZDQ4ZDIzMDAzYzCCASIwDQYJ
146
+ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAIzSGrfBi8FZ6G4mRRtXGB3QJFXrg/Ni
147
+ Q7+7RxCJ9iMlH7XNceUyIAkJ1R3dNXLxjGrMW1Oe5uZwFrwsOnVrCC0FGXqGVRL1
148
+ afXWqcKfRwfCIYjlKVuufD5Yx4uQL+qCyqwZLvW+uSfxrFR6h6EGly8FEuAj4HFV
149
+ bosBZkD3U763olTGRThq1TNXDtP5azExjet83SHG77xfkYqKQ72d1/2zVs8HrTHN
150
+ QrDAnmaDkMu1j0DBLMEoRuHHTnWosMa3h/pDZl0U9J2fD2EGsqM1d0S7cgn4qkG3
151
+ PyIfaIMTyv3hxqGvZNP6N0yc4DMQytuqonocE3vW1jA4eJLNUakXVSkCAwEAAaOC
152
+ AUQwggFAMBEGCWCGSAGG+EIBAQQEAwIFoDALBgNVHQ8EBAMCBLAwcwYDVR0jBGww
153
+ aoAUNqBl9Y1c0laNj+4nS/ds3IYTlnOhR6RFMEMxIjAgBgNVBAMMGWFya2hhbS51
154
+ c2Vyc3lzLnJlZGhhdC5jb20xCzAJBgNVBAYTAlVTMRAwDgYDVQQHDAdSYWxlaWdo
155
+ ggkAmKoF4QTK9QYwHQYDVR0OBBYEFEQE1wfCnyRQkK87AIDtELjHI22RMBMGA1Ud
156
+ JQQMMAoGCCsGAQUFBwMCMBIGCSsGAQQBkggJBgQFDAMzLjIwYQYJKwYBBAGSCAkH
157
+ BFQEUnjaFcrBCYAwEATALSYlSLAbWWUlj+iFu2C0e/U7zD3nJU/ohlOXHLsZWAef
158
+ QFoZom8Fjb0guao++ReHwg5Z4AWmIRW3CuqQvHBGK2ufmB6KGAAwDQYJKoZIhvcN
159
+ AQEFBQADgYEAsSQHCtJeEqXGEMoug4T95mOPNqqs30gRG/159E1fklNs3Ao1G+WH
160
+ /W/WrOAR8tyPDboWhhCJ3h8q7vEZgmq9Y6bO2HYu+PXrDqzgWYhOgytZnlvTsp3O
161
+ cCwL0kVRZWaSVAPcaM9HUqFcPthidsVPP0gw3iws7XhFhYVaILIPSZ0=
162
+ -----END CERTIFICATE-----
163
+ -----BEGIN ENTITLEMENT DATA-----
164
+ eJydVMuOmzAU/ZXImiUUGxOSsGtX3VVqu2o1iox9mUEBm9pmMlGUf+81ZGiiPGZU
165
+ hATm3Mc599jsiTTa9S1YUhCp0pzmGY8X+TKLM6hEXDJF41ykq/mCV2KxyklE/vRC
166
+ +9rvSMEi4vrSSVt3vjaaFHviNj1WEltwpgXj4tdlvs4zzNKiBUQ+j8js249ZZexs
167
+ grfC6lo/kYJTLGrkBrwbG3ghN4isa4XpjBwiYqwKfPdE9205MB++xMt8Med0dU6R
168
+ DiWsx6iUMh7TNGbZT0qL4f6FwaDVCGZXQByPt0KG9HyBayGl6XVYspRzxjg+BlKd
169
+ NaqXgfTvPRm50tMrfWcGsy81JkfkBawbZkn4J8ZCRyufaw/S9xZCdXKc2eNIDgKZ
170
+ qSXG+10Xmuz69l9H0WzFzsWgRdmAit/yItKIEpp7AS84HRNG7MH5+LhCtcI/48ek
171
+ MiYJ78lYIHmw0IBwgCow6ql7Wve2uRaIEAa04IUSXqzhtastMk0pPURvYhhLs1uC
172
+ LjfYJOUSmkR8BzX7KvyJgIGTN8mUlUxZx2GQohKNgzM1l1kIJtcE8fxEEb2hRod5
173
+ 3XHnFv5Rc/TRjzuKzmNv2ZOf28P4e/Zc8+VjhjyUuI3C3j/dU8lpkf/34/GAh6cz
174
+ pgk/kUHMUiwVZdWcSyVWSlH8U4Q3UHM8xdliTg6Hvy+doq0=
175
+ -----END ENTITLEMENT DATA-----
176
+ -----BEGIN RSA SIGNATURE-----
177
+ sO2ZOqXX5Vz0v41cWwd24emxZkmo+kHjYPvZ7W3UGW1bIUrPPOfNNlv+k9eybPWh
178
+ 9kULCZHdj9ULg9FXAKnQCG412vc2jWld0/vcOTuPdr8sUpEsGF5jeWNzqTiF3Yxb
179
+ Ik5dd1fP0nqF9cgWitFmsdG9olZ17jU1jg3uMj+ujyg=
180
+ -----END RSA SIGNATURE-----
181
+ """
182
+
183
+ ENTITLEMENT_CERT_V3_2_WITH_CONTENT_ARCH = """
184
+ -----BEGIN CERTIFICATE-----
185
+ MIIDpDCCAw2gAwIBAgIIGDqZvvF1e2kwDQYJKoZIhvcNAQEFBQAwQzEiMCAGA1UE
186
+ AwwZZGhjcDIzMS0yOC5yZHUucmVkaGF0LmNvbTELMAkGA1UEBhMCVVMxEDAOBgNV
187
+ BAcMB1JhbGVpZ2gwHhcNMTMwNTA4MDAwMDAwWhcNMTQwNTA4MDAwMDAwWjArMSkw
188
+ JwYDVQQDEyA4YThiNjc5YzNlODRkODQ5MDEzZTg0ZGViYjUyMTUwMjCCASIwDQYJ
189
+ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4Fg6UL7AIch/3riQTxvSFh/6AUZmFR
190
+ OQ+Pz270AF8JC9Vrc/nRA/EQB1CUoRn2hgJjJulkMgZ1rVOTBnq9KQomPhOOwbMJ
191
+ fWwrdkfvZnUFVT/hXzYhu+7NVIzX6MAovvJc6iYrfctPFVJj7TmWVR2Ay/Dyio0f
192
+ B4ZTang7GDePbNhuHD6PSUZ/3fINJUEkYOW9DyEDFPTZ+LiMbJGV/ZoR6Ww6myAG
193
+ BnIE5M2oiQZlD44/XhbD3VDXOv3HFvxdr366EVBm+LFpOBWPHXW0wSYPcEopWlqB
194
+ sH3QJqSWYVsLP+MC2rreVpTKB80i9/pvbyYX6nRRKnZPCONv+0JemL8CAwEAAaOC
195
+ ATMwggEvMBEGCWCGSAGG+EIBAQQEAwIFoDALBgNVHQ8EBAMCBLAwcwYDVR0jBGww
196
+ aoAUSlRGfcR6m6MjZo741DCPph6ow5ehR6RFMEMxIjAgBgNVBAMMGWRoY3AyMzEt
197
+ MjgucmR1LnJlZGhhdC5jb20xCzAJBgNVBAYTAlVTMRAwDgYDVQQHDAdSYWxlaWdo
198
+ ggkA0xVWC8yXifgwHQYDVR0OBBYEFP8CFn4AqepLndT/X68rOESBHQTtMBMGA1Ud
199
+ JQQMMAoGCCsGAQUFBwMCMBIGCSsGAQQBkggJBgQFDAMzLjIwUAYJKwYBBAGSCAkH
200
+ BEMEQXjaK8lnyEstSy1iSMvPZ1ApSs1JTSwG8xNzyhMrixkSy1OL83NT84GsnByG
201
+ gsSSDAYA1JYR5Aim7nCyJxRoevAAMA0GCSqGSIb3DQEBBQUAA4GBAI1jfsg9ODpZ
202
+ exsNA1d0OZbUn/1sK4LUtP/eFD0YQl3dv9A0/wRnoDXXFlJLsGt5EzY8qK7iOzti
203
+ K4bLngHfsWDJTOm95FvRRucUu5pAJnuzC/c9fGro+VsqlnNDpU+40bxlY4ODZj5g
204
+ FZ6RLuYKmu5IXLONxIrAVBMAckIA4vrS
205
+ -----END CERTIFICATE-----
206
+ -----BEGIN ENTITLEMENT DATA-----
207
+ eJyNk02PmzAQhv8KsnpoJVw+TPi6paceVqrU9tRqFRkzZFGMTY3Z3SjKf++YkOxm
208
+ m2wjEYE978y8zzjeEaHVMHZgSElYLKI8boA2abigSVQsaJHFGa2aeJEIkRQs48Qn
209
+ f0aubGu3pIx8MozVIEzb21YrUu7IsBmxEn+CQXegB8qlpM95SrGNxVzFO8D48hD3
210
+ vv3wGm28pZTe0ogH7yNKnRKU/YTqJ25Uq9akZCF20mIDdjh0tVxsMLJqa6wWkb1P
211
+ tKkdxI6osasmnGmH5mm2YGFx5nsxVTAWRXEYMYq0Yf4zDMvp+YVaUPUhmFwIOoeG
212
+ C5depLjmQuhRuWUUMxZFDF+Tp97oehTO8+8dmazGEUsw6n7ZbePwjvPwvrRYySeP
213
+ YIZp2IR9jiLXHqWtBWFHA64VWd7dkfuDTXC2js2d2G5713E7di/tuXzi24GC4pWE
214
+ mh7zfCJ5BfI9wSPOSbtZWxgsnVfIze0DbgaN1oH7Dg4Fgg8GJPABEGH2fWZ43a9X
215
+ o5GXMjGEGR1YXnPLV/Dctwatx2G492e68Aqdcu3egbsWv5VNzThzBVI2XA5wI96U
216
+ fI0ufUXH8jRNrp3f6bbh/2WVJrRl8417c4z/0Z2Iv0PtfeX2Fe1k1urgVAIPRp6d
217
+ oauFG4fKb3D/zcZgcAmZOeb7Pab3Wkt3nSf6nOdVmhWCQZ7UeVLgpXVfRSESEWYh
218
+ kP3+L1Ysgb8=
219
+ -----END ENTITLEMENT DATA-----
220
+ -----BEGIN RSA SIGNATURE-----
221
+ Ra24dHnR8vRvtRg4j0DXbftz3DX1c+Cf1yhgKBoLeS6aJ9g2HUJXoDLbLR+6ttHB
222
+ idE9jFhxTj/sNUvewMCKJIE1+El6tcfHmQRAZTiI4Dgqq50buHVmXmTNhotmaMsp
223
+ vVOVtdoqHYcPIZ3makIL9FO9/jLs+G5NH4dCJVrpj8o=
224
+ -----END RSA SIGNATURE-----
225
+ """
226
+
227
+ IDENTITY_CERT = """
228
+ -----BEGIN CERTIFICATE-----
229
+ MIIDdzCCAuCgAwIBAgIIBdnhr/WCKpMwDQYJKoZIhvcNAQEFBQAwTDErMCkGA1UE
230
+ Awwid3BvdGVhdC1kZXNrdG9wLnVzZXJzeXMucmVkaGF0LmNvbTELMAkGA1UEBhMC
231
+ VVMxEDAOBgNVBAcMB1JhbGVpZ2gwHhcNMTQwNTA4MTMwNDA0WhcNMzAwNTA4MTMw
232
+ NDA0WjAvMS0wKwYDVQQDEyQwZjVkNDYxNy1kOTEzLTRhMGYtYmU2MS1kOGE5Yzg4
233
+ ZTE0NzYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCHsIAMwqDBji/k
234
+ SMy0BgkrWscYYwy/3vhaU8oEmhFBRMFwy8gzByKcKTTMB642cDhFa+anBbNm4+QS
235
+ OKpfKf8JDj8GKnBspQF3qWxxi1vJGo6iTD/znPQRAlB//furFNmEDaDbhMUtcHEK
236
+ UvjALioR5V3flfMU3hPCYdaAcVvYu8ikI6N52abPlFWCkzbp6EGehISyVZxrlqac
237
+ QOCIPmnUnMVZ1pxYjOTrEhqjvjHMlUuEAmeKlLXKtirgs+X2yfulMJFYXUDeeO20
238
+ AB9CRp/wg3zoGcSzwtriO8GuRSuAmXO0O4HzcpXU7oXJGYBjrTy/B8rukuZbep+t
239
+ Y3+FQmr1AgMBAAGjgfowgfcwEQYJYIZIAYb4QgEBBAQDAgWgMAsGA1UdDwQEAwIE
240
+ sDB8BgNVHSMEdTBzgBTPYX+LWg9Ed3S0lIZtc4PuYrbv5qFQpE4wTDErMCkGA1UE
241
+ Awwid3BvdGVhdC1kZXNrdG9wLnVzZXJzeXMucmVkaGF0LmNvbTELMAkGA1UEBhMC
242
+ VVMxEDAOBgNVBAcMB1JhbGVpZ2iCCQCQJzUqJvMgMjAdBgNVHQ4EFgQU4yyaMLWq
243
+ Mc3ch8CwTPRHZrhHFCQwEwYDVR0lBAwwCgYIKwYBBQUHAwIwIwYDVR0RBBwwGoYY
244
+ Q049cmVkaGF0LmxvY2FsLnJtLXJmLmNhMA0GCSqGSIb3DQEBBQUAA4GBAEso9d/F
245
+ xZsoPA4OgfEcTad/5CorTswNTqpmwlkQ6Yp61h8L7BDWJ6ywovqXJQqo6lYK3149
246
+ 9+EC6nhTA9uhtODtQQgITkU6nwlYRkB0vrbofR7yO7eewlJ+ybhcVw1FllXfc4E9
247
+ 7SS6c7YbmlfQhcoGyzfYXuJYGCyDmDHvcQiU
248
+ -----END CERTIFICATE-----
249
+ """