otdf-python 0.1.10__py3-none-any.whl → 0.3.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.
Files changed (144) hide show
  1. otdf_python/__init__.py +25 -0
  2. otdf_python/__main__.py +12 -0
  3. otdf_python/address_normalizer.py +84 -0
  4. otdf_python/aesgcm.py +55 -0
  5. otdf_python/assertion_config.py +84 -0
  6. otdf_python/asym_crypto.py +85 -0
  7. otdf_python/asym_decryption.py +53 -0
  8. otdf_python/asym_encryption.py +75 -0
  9. otdf_python/auth_headers.py +21 -0
  10. otdf_python/autoconfigure_utils.py +113 -0
  11. otdf_python/cli.py +570 -0
  12. otdf_python/collection_store.py +41 -0
  13. otdf_python/collection_store_impl.py +22 -0
  14. otdf_python/config.py +69 -0
  15. otdf_python/connect_client.py +0 -0
  16. otdf_python/constants.py +1 -0
  17. otdf_python/crypto_utils.py +78 -0
  18. otdf_python/dpop.py +81 -0
  19. otdf_python/ecc_mode.py +32 -0
  20. otdf_python/eckeypair.py +75 -0
  21. otdf_python/header.py +143 -0
  22. otdf_python/invalid_zip_exception.py +8 -0
  23. otdf_python/kas_client.py +603 -0
  24. otdf_python/kas_connect_rpc_client.py +207 -0
  25. otdf_python/kas_info.py +25 -0
  26. otdf_python/kas_key_cache.py +52 -0
  27. otdf_python/key_type.py +31 -0
  28. otdf_python/key_type_constants.py +43 -0
  29. otdf_python/manifest.py +215 -0
  30. otdf_python/nanotdf.py +553 -0
  31. otdf_python/nanotdf_ecdsa_struct.py +132 -0
  32. otdf_python/nanotdf_type.py +43 -0
  33. otdf_python/policy_binding_serializer.py +39 -0
  34. otdf_python/policy_info.py +78 -0
  35. otdf_python/policy_object.py +22 -0
  36. otdf_python/policy_stub.py +2 -0
  37. otdf_python/resource_locator.py +44 -0
  38. otdf_python/sdk.py +528 -0
  39. otdf_python/sdk_builder.py +448 -0
  40. otdf_python/sdk_exceptions.py +16 -0
  41. otdf_python/symmetric_and_payload_config.py +30 -0
  42. otdf_python/tdf.py +479 -0
  43. otdf_python/tdf_reader.py +153 -0
  44. otdf_python/tdf_writer.py +23 -0
  45. otdf_python/token_source.py +34 -0
  46. otdf_python/version.py +57 -0
  47. otdf_python/zip_reader.py +47 -0
  48. otdf_python/zip_writer.py +70 -0
  49. otdf_python-0.3.0.dist-info/METADATA +231 -0
  50. otdf_python-0.3.0.dist-info/RECORD +137 -0
  51. {otdf_python-0.1.10.dist-info → otdf_python-0.3.0.dist-info}/WHEEL +1 -2
  52. {otdf_python-0.1.10.dist-info → otdf_python-0.3.0.dist-info/licenses}/LICENSE +1 -1
  53. otdf_python_proto/__init__.py +37 -0
  54. otdf_python_proto/authorization/__init__.py +1 -0
  55. otdf_python_proto/authorization/authorization_pb2.py +80 -0
  56. otdf_python_proto/authorization/authorization_pb2.pyi +161 -0
  57. otdf_python_proto/authorization/authorization_pb2_connect.py +191 -0
  58. otdf_python_proto/authorization/v2/authorization_pb2.py +105 -0
  59. otdf_python_proto/authorization/v2/authorization_pb2.pyi +134 -0
  60. otdf_python_proto/authorization/v2/authorization_pb2_connect.py +233 -0
  61. otdf_python_proto/common/__init__.py +1 -0
  62. otdf_python_proto/common/common_pb2.py +52 -0
  63. otdf_python_proto/common/common_pb2.pyi +61 -0
  64. otdf_python_proto/entity/__init__.py +1 -0
  65. otdf_python_proto/entity/entity_pb2.py +47 -0
  66. otdf_python_proto/entity/entity_pb2.pyi +50 -0
  67. otdf_python_proto/entityresolution/__init__.py +1 -0
  68. otdf_python_proto/entityresolution/entity_resolution_pb2.py +57 -0
  69. otdf_python_proto/entityresolution/entity_resolution_pb2.pyi +55 -0
  70. otdf_python_proto/entityresolution/entity_resolution_pb2_connect.py +149 -0
  71. otdf_python_proto/entityresolution/v2/entity_resolution_pb2.py +55 -0
  72. otdf_python_proto/entityresolution/v2/entity_resolution_pb2.pyi +55 -0
  73. otdf_python_proto/entityresolution/v2/entity_resolution_pb2_connect.py +149 -0
  74. otdf_python_proto/kas/__init__.py +9 -0
  75. otdf_python_proto/kas/kas_pb2.py +103 -0
  76. otdf_python_proto/kas/kas_pb2.pyi +170 -0
  77. otdf_python_proto/kas/kas_pb2_connect.py +192 -0
  78. otdf_python_proto/legacy_grpc/__init__.py +1 -0
  79. otdf_python_proto/legacy_grpc/authorization/authorization_pb2_grpc.py +163 -0
  80. otdf_python_proto/legacy_grpc/authorization/v2/authorization_pb2_grpc.py +206 -0
  81. otdf_python_proto/legacy_grpc/common/common_pb2_grpc.py +4 -0
  82. otdf_python_proto/legacy_grpc/entity/entity_pb2_grpc.py +4 -0
  83. otdf_python_proto/legacy_grpc/entityresolution/entity_resolution_pb2_grpc.py +122 -0
  84. otdf_python_proto/legacy_grpc/entityresolution/v2/entity_resolution_pb2_grpc.py +120 -0
  85. otdf_python_proto/legacy_grpc/kas/kas_pb2_grpc.py +172 -0
  86. otdf_python_proto/legacy_grpc/logger/audit/test_pb2_grpc.py +4 -0
  87. otdf_python_proto/legacy_grpc/policy/actions/actions_pb2_grpc.py +249 -0
  88. otdf_python_proto/legacy_grpc/policy/attributes/attributes_pb2_grpc.py +873 -0
  89. otdf_python_proto/legacy_grpc/policy/kasregistry/key_access_server_registry_pb2_grpc.py +602 -0
  90. otdf_python_proto/legacy_grpc/policy/keymanagement/key_management_pb2_grpc.py +251 -0
  91. otdf_python_proto/legacy_grpc/policy/namespaces/namespaces_pb2_grpc.py +427 -0
  92. otdf_python_proto/legacy_grpc/policy/objects_pb2_grpc.py +4 -0
  93. otdf_python_proto/legacy_grpc/policy/registeredresources/registered_resources_pb2_grpc.py +524 -0
  94. otdf_python_proto/legacy_grpc/policy/resourcemapping/resource_mapping_pb2_grpc.py +516 -0
  95. otdf_python_proto/legacy_grpc/policy/selectors_pb2_grpc.py +4 -0
  96. otdf_python_proto/legacy_grpc/policy/subjectmapping/subject_mapping_pb2_grpc.py +551 -0
  97. otdf_python_proto/legacy_grpc/policy/unsafe/unsafe_pb2_grpc.py +485 -0
  98. otdf_python_proto/legacy_grpc/wellknownconfiguration/wellknown_configuration_pb2_grpc.py +77 -0
  99. otdf_python_proto/logger/__init__.py +1 -0
  100. otdf_python_proto/logger/audit/test_pb2.py +43 -0
  101. otdf_python_proto/logger/audit/test_pb2.pyi +45 -0
  102. otdf_python_proto/policy/__init__.py +1 -0
  103. otdf_python_proto/policy/actions/actions_pb2.py +75 -0
  104. otdf_python_proto/policy/actions/actions_pb2.pyi +87 -0
  105. otdf_python_proto/policy/actions/actions_pb2_connect.py +275 -0
  106. otdf_python_proto/policy/attributes/attributes_pb2.py +234 -0
  107. otdf_python_proto/policy/attributes/attributes_pb2.pyi +328 -0
  108. otdf_python_proto/policy/attributes/attributes_pb2_connect.py +863 -0
  109. otdf_python_proto/policy/kasregistry/key_access_server_registry_pb2.py +266 -0
  110. otdf_python_proto/policy/kasregistry/key_access_server_registry_pb2.pyi +450 -0
  111. otdf_python_proto/policy/kasregistry/key_access_server_registry_pb2_connect.py +611 -0
  112. otdf_python_proto/policy/keymanagement/key_management_pb2.py +79 -0
  113. otdf_python_proto/policy/keymanagement/key_management_pb2.pyi +87 -0
  114. otdf_python_proto/policy/keymanagement/key_management_pb2_connect.py +275 -0
  115. otdf_python_proto/policy/namespaces/namespaces_pb2.py +117 -0
  116. otdf_python_proto/policy/namespaces/namespaces_pb2.pyi +147 -0
  117. otdf_python_proto/policy/namespaces/namespaces_pb2_connect.py +443 -0
  118. otdf_python_proto/policy/objects_pb2.py +150 -0
  119. otdf_python_proto/policy/objects_pb2.pyi +464 -0
  120. otdf_python_proto/policy/registeredresources/registered_resources_pb2.py +139 -0
  121. otdf_python_proto/policy/registeredresources/registered_resources_pb2.pyi +196 -0
  122. otdf_python_proto/policy/registeredresources/registered_resources_pb2_connect.py +527 -0
  123. otdf_python_proto/policy/resourcemapping/resource_mapping_pb2.py +139 -0
  124. otdf_python_proto/policy/resourcemapping/resource_mapping_pb2.pyi +194 -0
  125. otdf_python_proto/policy/resourcemapping/resource_mapping_pb2_connect.py +527 -0
  126. otdf_python_proto/policy/selectors_pb2.py +57 -0
  127. otdf_python_proto/policy/selectors_pb2.pyi +90 -0
  128. otdf_python_proto/policy/subjectmapping/subject_mapping_pb2.py +127 -0
  129. otdf_python_proto/policy/subjectmapping/subject_mapping_pb2.pyi +189 -0
  130. otdf_python_proto/policy/subjectmapping/subject_mapping_pb2_connect.py +569 -0
  131. otdf_python_proto/policy/unsafe/unsafe_pb2.py +113 -0
  132. otdf_python_proto/policy/unsafe/unsafe_pb2.pyi +145 -0
  133. otdf_python_proto/policy/unsafe/unsafe_pb2_connect.py +485 -0
  134. otdf_python_proto/wellknownconfiguration/__init__.py +1 -0
  135. otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2.py +51 -0
  136. otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2.pyi +32 -0
  137. otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2_connect.py +107 -0
  138. otdf_python/_gotdf_python.cpython-312-darwin.so +0 -0
  139. otdf_python/build.py +0 -190
  140. otdf_python/go.py +0 -1478
  141. otdf_python/gotdf_python.py +0 -383
  142. otdf_python-0.1.10.dist-info/METADATA +0 -149
  143. otdf_python-0.1.10.dist-info/RECORD +0 -10
  144. otdf_python-0.1.10.dist-info/top_level.txt +0 -1
@@ -0,0 +1,448 @@
1
+ """
2
+ Python port of the SDKBuilder class for OpenTDF platform interaction.
3
+ Provides methods to configure and build SDK instances.
4
+ """
5
+
6
+ import logging
7
+ import os
8
+ import ssl
9
+ from dataclasses import dataclass
10
+ from typing import Any
11
+
12
+ import httpx
13
+
14
+ from otdf_python.sdk import KAS, SDK
15
+ from otdf_python.sdk_exceptions import AutoConfigureException
16
+
17
+ # Configure logging
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ @dataclass
22
+ class OAuthConfig:
23
+ client_id: str
24
+ client_secret: str
25
+ grant_type: str = "client_credentials"
26
+ scope: str = "openid profile email"
27
+ token_endpoint: str | None = None
28
+ access_token: str | None = None
29
+
30
+
31
+ class SDKBuilder:
32
+ """
33
+ A builder class for creating instances of the SDK class.
34
+ """
35
+
36
+ PLATFORM_ISSUER = "platform_issuer"
37
+
38
+ # Class variable to store the latest platform URL
39
+ _platform_url = None
40
+
41
+ def __init__(self):
42
+ self.platform_endpoint: str | None = None
43
+ self.issuer_endpoint: str | None = None
44
+ self.oauth_config: OAuthConfig | None = None
45
+ self.use_plaintext: bool = False
46
+ self.insecure_skip_verify: bool = False
47
+ self.ssl_context: ssl.SSLContext | None = None
48
+ self.auth_token: str | None = None
49
+ self.cert_paths: list[str] = []
50
+
51
+ @staticmethod
52
+ def new_builder() -> "SDKBuilder":
53
+ """
54
+ Creates a new SDKBuilder instance.
55
+ Returns:
56
+ SDKBuilder: A new builder instance
57
+ """
58
+ return SDKBuilder()
59
+
60
+ @staticmethod
61
+ def get_platform_url() -> str | None:
62
+ """
63
+ Gets the last set platform URL.
64
+ Returns:
65
+ str | None: The platform URL or None if not set
66
+ """
67
+ return SDKBuilder._platform_url
68
+
69
+ def ssl_context_from_directory(self, certs_dir_path: str) -> "SDKBuilder":
70
+ """
71
+ Add SSL Context with trusted certs from certDirPath
72
+ Args:
73
+ certs_dir_path: Path to a directory containing .pem or .crt trusted certs
74
+ Returns:
75
+ self: The builder instance for chaining
76
+ """
77
+ self.cert_paths = []
78
+
79
+ # Find all .pem and .crt files in the directory
80
+ for filename in os.listdir(certs_dir_path):
81
+ if filename.endswith(".pem") or filename.endswith(".crt"):
82
+ self.cert_paths.append(os.path.join(certs_dir_path, filename))
83
+
84
+ # Create SSL context with these certificates
85
+ if self.cert_paths:
86
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
87
+ for cert_path in self.cert_paths:
88
+ context.load_verify_locations(cert_path)
89
+ self.ssl_context = context
90
+
91
+ return self
92
+
93
+ def client_secret(self, client_id: str, client_secret: str) -> "SDKBuilder":
94
+ """
95
+ Sets client credentials for OAuth 2.0 client_credentials grant.
96
+ Args:
97
+ client_id: The OAuth client ID
98
+ client_secret: The OAuth client secret
99
+ Returns:
100
+ self: The builder instance for chaining
101
+ """
102
+ self.oauth_config = OAuthConfig(
103
+ client_id=client_id, client_secret=client_secret
104
+ )
105
+ return self
106
+
107
+ def set_platform_endpoint(self, endpoint: str) -> "SDKBuilder":
108
+ """
109
+ Sets the OpenTDF platform endpoint URL.
110
+ Args:
111
+ endpoint: The platform endpoint URL
112
+ Returns:
113
+ self: The builder instance for chaining
114
+ """
115
+ # Normalize the endpoint URL
116
+ if endpoint and not (
117
+ endpoint.startswith("http://") or endpoint.startswith("https://")
118
+ ):
119
+ if self.use_plaintext:
120
+ endpoint = f"http://{endpoint}"
121
+ else:
122
+ endpoint = f"https://{endpoint}"
123
+
124
+ self.platform_endpoint = endpoint
125
+ # Store in class variable for access from other components
126
+ SDKBuilder._platform_url = endpoint
127
+ return self
128
+
129
+ def set_issuer_endpoint(self, issuer: str) -> "SDKBuilder":
130
+ """
131
+ Sets the OpenID Connect issuer endpoint URL.
132
+ Args:
133
+ issuer: The issuer endpoint URL
134
+ Returns:
135
+ self: The builder instance for chaining
136
+ """
137
+ # Normalize the issuer URL
138
+ if issuer and not (
139
+ issuer.startswith("http://") or issuer.startswith("https://")
140
+ ):
141
+ issuer = f"https://{issuer}"
142
+
143
+ self.issuer_endpoint = issuer
144
+ return self
145
+
146
+ def use_insecure_plaintext_connection(
147
+ self, use_plaintext: bool = True
148
+ ) -> "SDKBuilder":
149
+ """
150
+ Configures whether to use plain text (HTTP) connection instead of HTTPS.
151
+ Args:
152
+ use_plaintext: Whether to use plain text connection
153
+ Returns:
154
+ self: The builder instance for chaining
155
+ """
156
+ self.use_plaintext = use_plaintext
157
+
158
+ # Update platform endpoint protocol if necessary
159
+ if self.platform_endpoint:
160
+ if use_plaintext and self.platform_endpoint.startswith("https://"):
161
+ self.platform_endpoint = f"http://{self.platform_endpoint[8:]}"
162
+ elif not use_plaintext and self.platform_endpoint.startswith("http://"):
163
+ self.platform_endpoint = f"https://{self.platform_endpoint[7:]}"
164
+
165
+ # Update the class variable as well since kas() method uses it
166
+ SDKBuilder._platform_url = self.platform_endpoint
167
+
168
+ return self
169
+
170
+ def use_insecure_skip_verify(self, skip_verify: bool = True) -> "SDKBuilder":
171
+ """
172
+ Configures whether to skip SSL verification.
173
+ Args:
174
+ skip_verify: Whether to skip SSL verification
175
+ Returns:
176
+ self: The builder instance for chaining
177
+ """
178
+ self.insecure_skip_verify = skip_verify
179
+
180
+ # If skipping verification, create a default SSL context that does not verify
181
+ if skip_verify:
182
+ self.ssl_context = ssl._create_unverified_context()
183
+
184
+ return self
185
+
186
+ def bearer_token(self, token: str) -> "SDKBuilder":
187
+ """
188
+ Sets a bearer token to use for authorization.
189
+ Args:
190
+ token: The bearer token
191
+ Returns:
192
+ self: The builder instance for chaining
193
+ """
194
+ self.auth_token = token
195
+ return self
196
+
197
+ def _discover_token_endpoint_from_platform(self) -> None:
198
+ """
199
+ Discover token endpoint using OpenTDF platform configuration.
200
+ Raises:
201
+ AutoConfigureException: If discovery fails
202
+ """
203
+ if not self.platform_endpoint or not self.oauth_config:
204
+ return
205
+
206
+ # Try to get OpenTDF configuration first
207
+ well_known_url = f"{self.platform_endpoint}/.well-known/opentdf-configuration"
208
+ response = httpx.get(well_known_url, verify=not self.insecure_skip_verify)
209
+
210
+ if response.status_code != 200:
211
+ raise AutoConfigureException(
212
+ f"Failed to retrieve OpenTDF configuration from {well_known_url} (status: {response.status_code}). "
213
+ "Please provide an explicit issuer endpoint or check platform URL."
214
+ )
215
+
216
+ config_doc = response.json()
217
+ configuration = config_doc.get("configuration", {})
218
+
219
+ # Try to get token endpoint from IDP configuration
220
+ idp_config = configuration.get("idp", {})
221
+ if idp_config.get("token_endpoint"):
222
+ self.oauth_config.token_endpoint = idp_config["token_endpoint"]
223
+ return
224
+
225
+ # Fall back to using platform_issuer for OIDC discovery
226
+ platform_issuer = configuration.get("platform_issuer")
227
+ if not platform_issuer:
228
+ raise AutoConfigureException(
229
+ "No platform_issuer found in OpenTDF configuration"
230
+ )
231
+
232
+ self._discover_token_endpoint_from_issuer(platform_issuer)
233
+
234
+ def _discover_token_endpoint_from_issuer(self, issuer_url: str) -> None:
235
+ """
236
+ Discover token endpoint using OIDC discovery from issuer.
237
+ Args:
238
+ issuer_url: The issuer URL to use for discovery
239
+ Raises:
240
+ AutoConfigureException: If discovery fails
241
+ """
242
+ if not self.oauth_config:
243
+ return
244
+
245
+ oidc_discovery_url = f"{issuer_url}/.well-known/openid-configuration"
246
+ oidc_response = httpx.get(
247
+ oidc_discovery_url, verify=not self.insecure_skip_verify
248
+ )
249
+
250
+ if oidc_response.status_code != 200:
251
+ raise AutoConfigureException(
252
+ f"Failed to retrieve OIDC configuration from {oidc_discovery_url}: {oidc_response.status_code}"
253
+ )
254
+
255
+ oidc_doc = oidc_response.json()
256
+ self.oauth_config.token_endpoint = oidc_doc.get("token_endpoint")
257
+ if not self.oauth_config.token_endpoint:
258
+ raise AutoConfigureException(
259
+ "Token endpoint not found in OIDC discovery document"
260
+ )
261
+
262
+ def _discover_token_endpoint(self) -> None:
263
+ """
264
+ Discover the token endpoint using available configuration.
265
+ Raises:
266
+ AutoConfigureException: If discovery fails
267
+ """
268
+ # Try platform endpoint first
269
+ if self.platform_endpoint:
270
+ try:
271
+ self._discover_token_endpoint_from_platform()
272
+ return
273
+ except Exception as e:
274
+ # If platform fails and we have an explicit issuer, try that
275
+ if self.issuer_endpoint:
276
+ try:
277
+ realm_name = "opentdf" # Default realm name
278
+ issuer_url = f"{self.issuer_endpoint}/realms/{realm_name}"
279
+ self._discover_token_endpoint_from_issuer(issuer_url)
280
+ return
281
+ except Exception:
282
+ # Re-raise the original platform error
283
+ pass
284
+ raise AutoConfigureException(
285
+ f"Error during token endpoint discovery: {e!s}"
286
+ )
287
+
288
+ # Fall back to explicit issuer endpoint
289
+ if self.issuer_endpoint:
290
+ realm_name = "opentdf" # Default realm name
291
+ issuer_url = f"{self.issuer_endpoint}/realms/{realm_name}"
292
+ self._discover_token_endpoint_from_issuer(issuer_url)
293
+ return
294
+
295
+ raise AutoConfigureException(
296
+ "Platform endpoint or issuer endpoint must be configured for OIDC token discovery"
297
+ )
298
+
299
+ def _get_token_from_client_credentials(self) -> str:
300
+ """
301
+ Obtains an OAuth token using client credentials.
302
+ Returns:
303
+ str: The access token
304
+ Raises:
305
+ AutoConfigureException: If token acquisition fails
306
+ """
307
+ if not self.oauth_config:
308
+ raise AutoConfigureException("OAuth configuration is not set")
309
+
310
+ if not self.oauth_config.token_endpoint:
311
+ self._discover_token_endpoint()
312
+
313
+ # Ensure we have a token endpoint before proceeding
314
+ if not self.oauth_config.token_endpoint:
315
+ raise AutoConfigureException("Token endpoint discovery failed")
316
+
317
+ # Request the token
318
+ try:
319
+ token_data = {
320
+ "grant_type": self.oauth_config.grant_type,
321
+ "client_id": self.oauth_config.client_id,
322
+ "client_secret": self.oauth_config.client_secret,
323
+ "scope": self.oauth_config.scope,
324
+ }
325
+
326
+ response = httpx.post(
327
+ self.oauth_config.token_endpoint,
328
+ data=token_data,
329
+ verify=not self.insecure_skip_verify,
330
+ )
331
+
332
+ if response.status_code == 200:
333
+ token_response = response.json()
334
+ access_token = token_response.get("access_token")
335
+ if not access_token:
336
+ raise AutoConfigureException("No access_token in token response")
337
+ return access_token
338
+ else:
339
+ raise AutoConfigureException(
340
+ f"Token request failed: {response.status_code} - {response.text}"
341
+ )
342
+
343
+ except Exception as e:
344
+ raise AutoConfigureException(f"Error during token acquisition: {e!s}")
345
+
346
+ def _create_auth_interceptor(self) -> Any:
347
+ """
348
+ Creates an authentication interceptor for API requests (httpx).
349
+ Returns:
350
+ Any: An auth interceptor object
351
+ Raises:
352
+ AutoConfigureException: If auth configuration fails
353
+ """
354
+ # For now, this is just a placeholder returning a dict with auth headers
355
+ # In a real implementation, this would create a proper interceptor object
356
+ # that injects auth headers into httpx requests
357
+
358
+ token = None
359
+
360
+ if self.auth_token:
361
+ # Use provided token
362
+ token = self.auth_token
363
+ elif self.oauth_config:
364
+ # Get token from OAuth
365
+ token = self._get_token_from_client_credentials()
366
+
367
+ if token:
368
+ return {"Authorization": f"Bearer {token}"}
369
+
370
+ return None
371
+
372
+ def _create_services(self) -> SDK.Services:
373
+ """
374
+ Creates service client instances.
375
+ Returns:
376
+ SDK.Services: The service client instances
377
+ Raises:
378
+ AutoConfigureException: If service creation fails
379
+ """
380
+ # For now, return a simple implementation of Services
381
+ # In a real implementation, this would create actual service clients
382
+ # connecting to the platform endpoints
383
+
384
+ ssl_verify = not self.insecure_skip_verify
385
+ auth_interceptor = self._create_auth_interceptor()
386
+
387
+ class ServicesImpl(SDK.Services):
388
+ def __init__(self, builder_instance):
389
+ self.closed = False
390
+ self._ssl_verify = ssl_verify
391
+ self._auth_headers = auth_interceptor if auth_interceptor else {}
392
+ self._builder = builder_instance
393
+
394
+ def kas(self) -> KAS:
395
+ """
396
+ Returns the KAS interface with SSL verification settings.
397
+ """
398
+ platform_url = SDKBuilder.get_platform_url()
399
+
400
+ # Create a token source function that can refresh tokens
401
+ def token_source():
402
+ if self._builder.auth_token:
403
+ return self._builder.auth_token
404
+ elif self._builder.oauth_config:
405
+ return self._builder._get_token_from_client_credentials()
406
+ return None
407
+
408
+ kas_impl = KAS(
409
+ platform_url=platform_url,
410
+ token_source=token_source,
411
+ sdk_ssl_verify=self._ssl_verify,
412
+ use_plaintext=self._builder.use_plaintext,
413
+ )
414
+ return kas_impl
415
+
416
+ def close(self):
417
+ self.closed = True
418
+
419
+ def __exit__(self, exc_type, exc_val, exc_tb):
420
+ self.close()
421
+
422
+ return ServicesImpl(self)
423
+
424
+ def build(self) -> SDK:
425
+ """
426
+ Builds and returns an SDK instance with the configured properties.
427
+ Returns:
428
+ SDK: The configured SDK instance
429
+ Raises:
430
+ AutoConfigureException: If the build fails
431
+ """
432
+ if not self.platform_endpoint:
433
+ raise AutoConfigureException("Platform endpoint is not set")
434
+
435
+ # Create the auth interceptor
436
+ auth_interceptor = self._create_auth_interceptor()
437
+
438
+ # Create services
439
+ services = self._create_services()
440
+
441
+ # Return the SDK instance, platform_url is set for new_tdf_config
442
+ return SDK(
443
+ services=services,
444
+ auth_interceptor=auth_interceptor,
445
+ platform_url=self.platform_endpoint,
446
+ ssl_verify=not self.insecure_skip_verify,
447
+ use_plaintext=getattr(self, "use_plaintext", False),
448
+ )
@@ -0,0 +1,16 @@
1
+ class SDKException(Exception):
2
+ def __init__(self, message, reason=None):
3
+ super().__init__(message)
4
+ self.reason = reason
5
+
6
+
7
+ class AutoConfigureException(SDKException):
8
+ def __init__(self, message, cause=None):
9
+ super().__init__(message, cause)
10
+
11
+
12
+ class KASBadRequestException(SDKException):
13
+ """Thrown when the KAS returns a bad request response or other client request errors."""
14
+
15
+ def __init__(self, message):
16
+ super().__init__(message)
@@ -0,0 +1,30 @@
1
+ class SymmetricAndPayloadConfig:
2
+ def __init__(
3
+ self,
4
+ cipher_type: int = 0,
5
+ signature_ecc_mode: int = 0,
6
+ has_signature: bool = True,
7
+ ):
8
+ self.cipher_type = cipher_type
9
+ self.signature_ecc_mode = signature_ecc_mode
10
+ self.has_signature = has_signature
11
+
12
+ def set_has_signature(self, flag: bool):
13
+ self.has_signature = flag
14
+
15
+ def set_signature_ecc_mode(self, mode: int):
16
+ self.signature_ecc_mode = mode
17
+
18
+ def set_symmetric_cipher_type(self, cipher_type: int):
19
+ self.cipher_type = cipher_type
20
+
21
+ def get_cipher_type(self) -> int:
22
+ return self.cipher_type
23
+
24
+ def get_symmetric_and_payload_config_as_byte(self) -> int:
25
+ # Most significant bit: has_signature, next 3 bits: signature_ecc_mode, lower 4 bits: cipher_type
26
+ return (
27
+ ((1 if self.has_signature else 0) << 7)
28
+ | ((self.signature_ecc_mode & 0x07) << 4)
29
+ | (self.cipher_type & 0x0F)
30
+ )