swarmauri_transport_https_unicast 0.11.0.dev5__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.
- swarmauri_transport_https_unicast/HttpsUnicastTransport.py +301 -0
- swarmauri_transport_https_unicast/__init__.py +15 -0
- swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/METADATA +143 -0
- swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/RECORD +7 -0
- swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/WHEEL +4 -0
- swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/entry_points.txt +3 -0
- swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/licenses/LICENSE +1 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import hashlib
|
|
5
|
+
import hmac
|
|
6
|
+
import ssl
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Literal, Mapping
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from pydantic import Field, PrivateAttr
|
|
12
|
+
from swarmauri_base.ComponentBase import ComponentBase
|
|
13
|
+
from swarmauri_base.transports import TransportBase
|
|
14
|
+
from swarmauri_certs_x509verify import X509VerifyService
|
|
15
|
+
from swarmauri_cipher_suite_tls13 import Tls13CipherSuite
|
|
16
|
+
from swarmauri_core.crypto.types import JWAAlg
|
|
17
|
+
from swarmauri_core.transports import (
|
|
18
|
+
AddressScheme,
|
|
19
|
+
Cast,
|
|
20
|
+
Feature,
|
|
21
|
+
IOModel,
|
|
22
|
+
Protocol,
|
|
23
|
+
SecurityMode,
|
|
24
|
+
TransportCapabilities,
|
|
25
|
+
)
|
|
26
|
+
from swarmauri_middleware_httpsig import HttpSigMiddleware
|
|
27
|
+
from swarmauri_signing_jws import JwsSignerVerifier
|
|
28
|
+
|
|
29
|
+
Bytes = bytes
|
|
30
|
+
Headers = dict[str, str]
|
|
31
|
+
JwsKey = Mapping[str, Any]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class HttpsSecurityPolicy:
|
|
36
|
+
"""Application-layer security material applied around HTTPS requests."""
|
|
37
|
+
|
|
38
|
+
bearer_token: str | None = None
|
|
39
|
+
http_signature_secret: str | bytes | None = None
|
|
40
|
+
http_signature_header: str = "X-Signature"
|
|
41
|
+
request_jws_key: JwsKey | None = None
|
|
42
|
+
request_jws_kid: str | None = None
|
|
43
|
+
request_jws_header: str = "X-Body-JWS"
|
|
44
|
+
response_jws_key: JwsKey | None = None
|
|
45
|
+
response_jws_header: str = "X-Response-JWS"
|
|
46
|
+
jws_alg: JWAAlg = JWAAlg.HS256
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class HttpsTransportEvidence:
|
|
51
|
+
"""Verification evidence captured by a secure HTTPS request."""
|
|
52
|
+
|
|
53
|
+
status_code: int | None = None
|
|
54
|
+
response_jws_verified: bool = False
|
|
55
|
+
certificate_valid: bool | None = None
|
|
56
|
+
certificate_subject: str | None = None
|
|
57
|
+
cipher_suite: str = "tls13"
|
|
58
|
+
cipher_alg: str | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _secret_bytes(secret: str | bytes) -> bytes:
|
|
62
|
+
return secret.encode("utf-8") if isinstance(secret, str) else secret
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def http_signature(secret: str | bytes, body: bytes) -> str:
|
|
66
|
+
"""Return the HMAC-SHA256 signature used by ``HttpSigMiddleware``."""
|
|
67
|
+
|
|
68
|
+
return base64.b64encode(
|
|
69
|
+
hmac.new(_secret_bytes(secret), body, hashlib.sha256).digest()
|
|
70
|
+
).decode("ascii")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@ComponentBase.register_type(TransportBase, "HttpsUnicastTransport")
|
|
74
|
+
class HttpsUnicastTransport(TransportBase):
|
|
75
|
+
"""
|
|
76
|
+
HTTPS unicast transport with Swarmauri trust and request-signing policy.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
type: Literal["HttpsUnicastTransport"] = "HttpsUnicastTransport"
|
|
80
|
+
|
|
81
|
+
base_url: str = ""
|
|
82
|
+
verify: bool | str | ssl.SSLContext = True
|
|
83
|
+
timeout: float = 10.0
|
|
84
|
+
trust_env: bool = False
|
|
85
|
+
security_policy: HttpsSecurityPolicy = Field(
|
|
86
|
+
default_factory=HttpsSecurityPolicy
|
|
87
|
+
)
|
|
88
|
+
require_response_jws: bool = False
|
|
89
|
+
httpx_transport: httpx.AsyncBaseTransport | None = Field(
|
|
90
|
+
default=None, exclude=True
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
_client: httpx.AsyncClient | None = PrivateAttr(default=None)
|
|
94
|
+
_jws: JwsSignerVerifier = PrivateAttr(default_factory=JwsSignerVerifier)
|
|
95
|
+
_cipher_suite: Tls13CipherSuite = PrivateAttr(
|
|
96
|
+
default_factory=Tls13CipherSuite
|
|
97
|
+
)
|
|
98
|
+
_last_evidence: HttpsTransportEvidence = PrivateAttr(
|
|
99
|
+
default_factory=HttpsTransportEvidence
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def supports(self) -> TransportCapabilities:
|
|
103
|
+
return TransportCapabilities(
|
|
104
|
+
protocols=frozenset({Protocol.HTTP1, Protocol.TLS}),
|
|
105
|
+
io=IOModel.STREAM,
|
|
106
|
+
casts=frozenset({Cast.UNICAST}),
|
|
107
|
+
features=frozenset(
|
|
108
|
+
{
|
|
109
|
+
Feature.RELIABLE,
|
|
110
|
+
Feature.ORDERED,
|
|
111
|
+
Feature.ENCRYPTED,
|
|
112
|
+
Feature.AUTHENTICATED,
|
|
113
|
+
}
|
|
114
|
+
),
|
|
115
|
+
security=SecurityMode.TLS,
|
|
116
|
+
schemes=frozenset({AddressScheme.HTTPS}),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def last_evidence(self) -> HttpsTransportEvidence:
|
|
121
|
+
return self._last_evidence
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def cipher_descriptor(self) -> Mapping[str, Any]:
|
|
125
|
+
return self._cipher_suite.normalize(op="encrypt")
|
|
126
|
+
|
|
127
|
+
def allowed_tls_cipher(self, cipher_name: str) -> bool:
|
|
128
|
+
return cipher_name in set(self._cipher_suite.supports()["encrypt"])
|
|
129
|
+
|
|
130
|
+
def http_signature_middleware(self) -> HttpSigMiddleware:
|
|
131
|
+
secret = self.security_policy.http_signature_secret
|
|
132
|
+
if secret is None:
|
|
133
|
+
raise ValueError("http_signature_secret is required")
|
|
134
|
+
if isinstance(secret, bytes):
|
|
135
|
+
secret = secret.decode("utf-8")
|
|
136
|
+
return HttpSigMiddleware(
|
|
137
|
+
secret_key=secret,
|
|
138
|
+
header_name=self.security_policy.http_signature_header,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
async def verify_certificate(
|
|
142
|
+
self,
|
|
143
|
+
cert_pem: bytes | str,
|
|
144
|
+
*,
|
|
145
|
+
trust_roots: list[bytes | str] | None = None,
|
|
146
|
+
) -> dict[str, Any]:
|
|
147
|
+
result = await X509VerifyService().verify_cert(
|
|
148
|
+
cert_pem, trust_roots=trust_roots
|
|
149
|
+
)
|
|
150
|
+
self._last_evidence = HttpsTransportEvidence(
|
|
151
|
+
certificate_valid=bool(result.get("valid")),
|
|
152
|
+
certificate_subject=result.get("subject"),
|
|
153
|
+
cipher_suite=self._cipher_suite.suite_id(),
|
|
154
|
+
cipher_alg=self.cipher_descriptor["alg"],
|
|
155
|
+
)
|
|
156
|
+
return result
|
|
157
|
+
|
|
158
|
+
async def prepare_headers(
|
|
159
|
+
self,
|
|
160
|
+
body: bytes,
|
|
161
|
+
headers: Headers | None = None,
|
|
162
|
+
*,
|
|
163
|
+
bearer_token: str | None = None,
|
|
164
|
+
http_signature_secret: str | bytes | None = None,
|
|
165
|
+
request_jws_key: JwsKey | None = None,
|
|
166
|
+
) -> Headers:
|
|
167
|
+
policy = self.security_policy
|
|
168
|
+
prepared = dict(headers or {})
|
|
169
|
+
|
|
170
|
+
token = (
|
|
171
|
+
bearer_token if bearer_token is not None else policy.bearer_token
|
|
172
|
+
)
|
|
173
|
+
if token:
|
|
174
|
+
prepared.setdefault("Authorization", f"Bearer {token}")
|
|
175
|
+
|
|
176
|
+
secret = (
|
|
177
|
+
http_signature_secret
|
|
178
|
+
if http_signature_secret is not None
|
|
179
|
+
else policy.http_signature_secret
|
|
180
|
+
)
|
|
181
|
+
if secret is not None:
|
|
182
|
+
prepared[policy.http_signature_header] = http_signature(
|
|
183
|
+
secret, body
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
jws_key = (
|
|
187
|
+
request_jws_key
|
|
188
|
+
if request_jws_key is not None
|
|
189
|
+
else policy.request_jws_key
|
|
190
|
+
)
|
|
191
|
+
if jws_key is not None:
|
|
192
|
+
prepared[policy.request_jws_header] = await self._jws.sign_compact(
|
|
193
|
+
payload=body,
|
|
194
|
+
alg=policy.jws_alg,
|
|
195
|
+
key=jws_key,
|
|
196
|
+
kid=policy.request_jws_kid,
|
|
197
|
+
typ="JWS",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return prepared
|
|
201
|
+
|
|
202
|
+
async def verify_body_jws(
|
|
203
|
+
self,
|
|
204
|
+
compact_jws: str,
|
|
205
|
+
body: bytes,
|
|
206
|
+
*,
|
|
207
|
+
hmac_keys: list[JwsKey] | None = None,
|
|
208
|
+
) -> None:
|
|
209
|
+
keys = hmac_keys or [self.security_policy.response_jws_key]
|
|
210
|
+
usable_keys = [key for key in keys if key is not None]
|
|
211
|
+
result = await self._jws.verify_compact(
|
|
212
|
+
compact_jws,
|
|
213
|
+
hmac_keys=usable_keys,
|
|
214
|
+
alg_allowlist=[self.security_policy.jws_alg],
|
|
215
|
+
)
|
|
216
|
+
if result.payload != body:
|
|
217
|
+
raise ValueError("JWS payload does not match body")
|
|
218
|
+
|
|
219
|
+
async def _start_server(self, **bind_kwargs: Any) -> None:
|
|
220
|
+
raise NotImplementedError(
|
|
221
|
+
(
|
|
222
|
+
"HttpsUnicastTransport provides HTTPS client transport "
|
|
223
|
+
"orchestration"
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
async def _stop_server(self) -> None:
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
async def _open_client(self, **connect_kwargs: Any) -> None:
|
|
231
|
+
self._client = httpx.AsyncClient(**self._client_kwargs(connect_kwargs))
|
|
232
|
+
|
|
233
|
+
async def _close_client(self) -> None:
|
|
234
|
+
if self._client is not None:
|
|
235
|
+
await self._client.aclose()
|
|
236
|
+
self._client = None
|
|
237
|
+
|
|
238
|
+
async def request(
|
|
239
|
+
self,
|
|
240
|
+
method: str,
|
|
241
|
+
path: str,
|
|
242
|
+
headers: Headers | None = None,
|
|
243
|
+
body: Bytes = b"",
|
|
244
|
+
**kwargs: Any,
|
|
245
|
+
) -> tuple[int, Headers, Bytes]:
|
|
246
|
+
request_headers = await self.prepare_headers(body, headers)
|
|
247
|
+
response_jws_verified = False
|
|
248
|
+
client = self._client
|
|
249
|
+
|
|
250
|
+
if client is None:
|
|
251
|
+
async with httpx.AsyncClient(
|
|
252
|
+
**self._client_kwargs({})
|
|
253
|
+
) as one_shot:
|
|
254
|
+
response = await one_shot.request(
|
|
255
|
+
method,
|
|
256
|
+
path,
|
|
257
|
+
headers=request_headers,
|
|
258
|
+
content=body,
|
|
259
|
+
**kwargs,
|
|
260
|
+
)
|
|
261
|
+
else:
|
|
262
|
+
response = await client.request(
|
|
263
|
+
method, path, headers=request_headers, content=body, **kwargs
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
response_jws = response.headers.get(
|
|
267
|
+
self.security_policy.response_jws_header
|
|
268
|
+
)
|
|
269
|
+
if response_jws:
|
|
270
|
+
await self.verify_body_jws(response_jws, response.content)
|
|
271
|
+
response_jws_verified = True
|
|
272
|
+
elif self.require_response_jws:
|
|
273
|
+
raise ValueError(
|
|
274
|
+
(
|
|
275
|
+
f"missing {self.security_policy.response_jws_header} "
|
|
276
|
+
f"response header"
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
descriptor = self.cipher_descriptor
|
|
281
|
+
self._last_evidence = HttpsTransportEvidence(
|
|
282
|
+
status_code=response.status_code,
|
|
283
|
+
response_jws_verified=response_jws_verified,
|
|
284
|
+
cipher_suite=self._cipher_suite.suite_id(),
|
|
285
|
+
cipher_alg=descriptor["alg"],
|
|
286
|
+
)
|
|
287
|
+
return response.status_code, dict(response.headers), response.content
|
|
288
|
+
|
|
289
|
+
def _client_kwargs(self, overrides: Mapping[str, Any]) -> dict[str, Any]:
|
|
290
|
+
kwargs: dict[str, Any] = {
|
|
291
|
+
"verify": overrides.get("verify", self.verify),
|
|
292
|
+
"timeout": overrides.get("timeout", self.timeout),
|
|
293
|
+
"trust_env": overrides.get("trust_env", self.trust_env),
|
|
294
|
+
}
|
|
295
|
+
base_url = overrides.get("base_url", self.base_url)
|
|
296
|
+
if base_url:
|
|
297
|
+
kwargs["base_url"] = base_url
|
|
298
|
+
transport = overrides.get("transport", self.httpx_transport)
|
|
299
|
+
if transport is not None:
|
|
300
|
+
kwargs["transport"] = transport
|
|
301
|
+
return kwargs
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Policy-bound HTTPS unicast transport."""
|
|
2
|
+
|
|
3
|
+
from .HttpsUnicastTransport import (
|
|
4
|
+
HttpsSecurityPolicy,
|
|
5
|
+
HttpsTransportEvidence,
|
|
6
|
+
HttpsUnicastTransport,
|
|
7
|
+
http_signature,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"HttpsSecurityPolicy",
|
|
12
|
+
"HttpsTransportEvidence",
|
|
13
|
+
"HttpsUnicastTransport",
|
|
14
|
+
"http_signature",
|
|
15
|
+
]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swarmauri_transport_https_unicast
|
|
3
|
+
Version: 0.11.0.dev5
|
|
4
|
+
Summary: Policy-bound HTTPS unicast transport for Swarmauri.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: swarmauri,sdk,standards,transport,https,httpx,tls,tls13,x509,certificate-validation,http-signature,jws,bearer-token,unicast
|
|
8
|
+
Author: Jacob Stewart
|
|
9
|
+
Author-email: jacob@swarmauri.com
|
|
10
|
+
Requires-Python: >=3.10,<3.15
|
|
11
|
+
Classifier: Development Status :: 1 - Planning
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
16
|
+
Classifier: Topic :: Security :: Cryptography
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Requires-Dist: httpx (>=0.27.0,<1)
|
|
27
|
+
Requires-Dist: swarmauri_base (>=0.11.0.dev5)
|
|
28
|
+
Requires-Dist: swarmauri_certs_x509verify
|
|
29
|
+
Requires-Dist: swarmauri_cipher_suite_tls13
|
|
30
|
+
Requires-Dist: swarmauri_core (>=0.11.0.dev5)
|
|
31
|
+
Requires-Dist: swarmauri_middleware_httpsig
|
|
32
|
+
Requires-Dist: swarmauri_signing_jws
|
|
33
|
+
Project-URL: Documentation, https://github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_transport_https_unicast#readme
|
|
34
|
+
Project-URL: Homepage, https://github.com/swarmauri/swarmauri-sdk
|
|
35
|
+
Project-URL: Source, https://github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_transport_https_unicast
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://pepy.tech/project/swarmauri_transport_https_unicast/">
|
|
42
|
+
<img src="https://static.pepy.tech/badge/swarmauri_transport_https_unicast/month" alt="PyPI - Downloads"/></a>
|
|
43
|
+
<a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_transport_https_unicast/">
|
|
44
|
+
<img alt="Hits" src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_transport_https_unicast.svg"/></a>
|
|
45
|
+
<a href="https://pypi.org/project/swarmauri_transport_https_unicast/">
|
|
46
|
+
<img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue" alt="PyPI - Python Version"/></a>
|
|
47
|
+
<a href="https://pypi.org/project/swarmauri_transport_https_unicast/">
|
|
48
|
+
<img src="https://img.shields.io/pypi/l/swarmauri_transport_https_unicast" alt="PyPI - License"/></a>
|
|
49
|
+
<a href="https://pypi.org/project/swarmauri_transport_https_unicast/">
|
|
50
|
+
<img src="https://img.shields.io/pypi/v/swarmauri_transport_https_unicast?label=swarmauri_transport_https_unicast&color=green" alt="PyPI - swarmauri_transport_https_unicast"/></a>
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
# swarmauri_transport_https_unicast
|
|
54
|
+
|
|
55
|
+
`swarmauri_transport_https_unicast` provides a policy-bound HTTPS client
|
|
56
|
+
transport that composes HTTPX with Swarmauri TLS 1.3 cipher policy, X.509
|
|
57
|
+
certificate verification, HTTP signature headers, bearer-token forwarding, and
|
|
58
|
+
JWS body signing or verification.
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- Advertises HTTPS-over-TLS unicast transport capabilities through
|
|
63
|
+
`TransportBase`.
|
|
64
|
+
- Uses HTTPX for concrete HTTPS request dispatch.
|
|
65
|
+
- Applies bearer tokens, `HttpSigMiddleware`-compatible HMAC headers, and JWS
|
|
66
|
+
request-body signatures.
|
|
67
|
+
- Verifies response-body JWS payloads before returning transport results.
|
|
68
|
+
- Exposes X.509 preflight verification through `X509VerifyService`.
|
|
69
|
+
- Publishes TLS 1.3 cipher-suite policy metadata through `Tls13CipherSuite`.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
With `uv`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
uv add swarmauri_transport_https_unicast
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
With `pip`:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install swarmauri_transport_https_unicast
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import asyncio
|
|
89
|
+
|
|
90
|
+
from swarmauri_transport_https_unicast import (
|
|
91
|
+
HttpsSecurityPolicy,
|
|
92
|
+
HttpsUnicastTransport,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def main() -> None:
|
|
97
|
+
body = b'{"order_id":"ord_1001"}'
|
|
98
|
+
jws_key = {"kind": "raw", "key": b"transport-demo-secret-32-bytes!!", "kid": "body.1"}
|
|
99
|
+
policy = HttpsSecurityPolicy(
|
|
100
|
+
bearer_token="jwt-or-other-bearer-token",
|
|
101
|
+
http_signature_secret="shared-secret",
|
|
102
|
+
request_jws_key=jws_key,
|
|
103
|
+
request_jws_kid="body.1",
|
|
104
|
+
response_jws_key=jws_key,
|
|
105
|
+
)
|
|
106
|
+
transport = HttpsUnicastTransport(
|
|
107
|
+
base_url="https://127.0.0.1:8443",
|
|
108
|
+
verify="/path/to/trust-anchor.pem",
|
|
109
|
+
security_policy=policy,
|
|
110
|
+
require_response_jws=True,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
async with transport.client():
|
|
114
|
+
status, headers, response_body = await transport.request(
|
|
115
|
+
"POST", "/secure/orders", body=body
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
print(status, headers["content-type"], response_body)
|
|
119
|
+
print(transport.last_evidence)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
asyncio.run(main())
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Expected output for a service that returns `application/json` and body
|
|
126
|
+
`b'{"ok":true}'`:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
200 application/json b'{"ok":true}'
|
|
130
|
+
HttpsTransportEvidence(status_code=200, response_jws_verified=True, certificate_valid=None, certificate_subject=None, cipher_suite='tls13', cipher_alg='TLS_AES_256_GCM_SHA384')
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Use `verify_certificate()` when a service has a PEM certificate or chain that
|
|
134
|
+
should be validated against explicit trust roots before making transport calls.
|
|
135
|
+
Use `allowed_tls_cipher()` on server instrumentation to confirm the negotiated
|
|
136
|
+
TLS cipher is within the Swarmauri TLS 1.3 policy.
|
|
137
|
+
|
|
138
|
+
## Project Links
|
|
139
|
+
|
|
140
|
+
- Source:
|
|
141
|
+
<https://github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_transport_https_unicast>
|
|
142
|
+
- Issues: <https://github.com/swarmauri/swarmauri-sdk/issues>
|
|
143
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
swarmauri_transport_https_unicast/HttpsUnicastTransport.py,sha256=SK1u7ObPO43f-B3LC6P-YqH8Ge7PlbSpLhLkpGTVZC4,9871
|
|
2
|
+
swarmauri_transport_https_unicast/__init__.py,sha256=iF6t7xJSBmm0hP_I9fBR_ya8PJ6Zy6LrHmoIOui40EE,307
|
|
3
|
+
swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/METADATA,sha256=yh7rAna2DzAKQISuRySULdyfnYhqOUNNZKmSJeqGP04,5907
|
|
4
|
+
swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
|
|
5
|
+
swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/entry_points.txt,sha256=QgN__bSORmDj7nNEwzyhwbDYKud_mrfER3Sxo2TxjO8,102
|
|
6
|
+
swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/licenses/LICENSE,sha256=MdM7aBX4TuV_14THqIMzp3a9PfFTLWUL_a1zBdnvvzU,11
|
|
7
|
+
swarmauri_transport_https_unicast-0.11.0.dev5.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Apache-2.0
|