rp_python_sdk 0.11.0__tar.gz → 1.1.1__tar.gz
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.
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/PKG-INFO +17 -5
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/README.md +12 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/pyproject.toml +11 -10
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/get_participants.py +25 -9
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/pushed_authorisation_request.py +5 -4
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/retrieve_tokens.py +17 -8
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/user_info.py +1 -1
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/util/fapi.py +7 -8
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/util/mapping.py +1 -1
- rp_python_sdk-1.1.1/rp_python_sdk/endpoints/util/system_info.py +24 -0
- rp_python_sdk-1.1.1/rp_python_sdk/endpoints/util/user_agent.py +8 -0
- rp_python_sdk-1.1.1/rp_python_sdk/endpoints/util/version_info.py +2 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/filters/participant_filters.py +4 -3
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/model.py +4 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/__init__.py +0 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/relying_party_client_sdk.py +0 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/relying_party_client_sdk_exception.py +0 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/sdk_config.py +0 -0
- {rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/setup_logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: rp_python_sdk
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Python SDK for Relying Parties to enable simple integration with the Digital Identity ecosystem
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Erik Pragt
|
|
@@ -10,10 +10,10 @@ Classifier: License :: Other/Proprietary License
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
-
Requires-Dist: authlib (>=1.
|
|
13
|
+
Requires-Dist: authlib (>=1.6.0,<2.0.0)
|
|
14
14
|
Requires-Dist: cffi (>=1.17.1,<2.0.0)
|
|
15
|
-
Requires-Dist: cryptography (>=
|
|
16
|
-
Requires-Dist: joserfc (>=1.
|
|
15
|
+
Requires-Dist: cryptography (>=45.0.0,<46.0.0)
|
|
16
|
+
Requires-Dist: joserfc (>=1.1.0,<2.0.0)
|
|
17
17
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
@@ -85,6 +85,11 @@ eval "$(pyenv virtualenv-init -)"
|
|
|
85
85
|
|
|
86
86
|
## running tests
|
|
87
87
|
|
|
88
|
+
Set RP_SAMPLE_APP_TOKEN environment variable. The value can be obtained from the [ConnectID Docs Portal](https://docs.connectid.com.au/docs/relying-parties/getting-started/).
|
|
89
|
+
```shell
|
|
90
|
+
export RP_SAMPLE_APP_TOKEN=<your token>
|
|
91
|
+
```
|
|
92
|
+
|
|
88
93
|
To run all the test, you can execute the following:
|
|
89
94
|
|
|
90
95
|
```bash
|
|
@@ -426,6 +431,13 @@ poetry publish
|
|
|
426
431
|
|
|
427
432
|
## Release Notes
|
|
428
433
|
|
|
434
|
+
### 1.1.1 (Jul 7, 2025)
|
|
435
|
+
- Bugfix release to support manual providers
|
|
436
|
+
- Updated dependencies
|
|
437
|
+
|
|
438
|
+
### 1.1.0 (Mar 14, 2025)
|
|
439
|
+
- Implement user agent logging.
|
|
440
|
+
|
|
429
441
|
### 0.11.0 (Nov 29, 2024)
|
|
430
442
|
- Update to use Authorisation Server issuer attribute as `aud` value when retrieving tokens.
|
|
431
443
|
|
|
@@ -66,6 +66,11 @@ eval "$(pyenv virtualenv-init -)"
|
|
|
66
66
|
|
|
67
67
|
## running tests
|
|
68
68
|
|
|
69
|
+
Set RP_SAMPLE_APP_TOKEN environment variable. The value can be obtained from the [ConnectID Docs Portal](https://docs.connectid.com.au/docs/relying-parties/getting-started/).
|
|
70
|
+
```shell
|
|
71
|
+
export RP_SAMPLE_APP_TOKEN=<your token>
|
|
72
|
+
```
|
|
73
|
+
|
|
69
74
|
To run all the test, you can execute the following:
|
|
70
75
|
|
|
71
76
|
```bash
|
|
@@ -407,6 +412,13 @@ poetry publish
|
|
|
407
412
|
|
|
408
413
|
## Release Notes
|
|
409
414
|
|
|
415
|
+
### 1.1.1 (Jul 7, 2025)
|
|
416
|
+
- Bugfix release to support manual providers
|
|
417
|
+
- Updated dependencies
|
|
418
|
+
|
|
419
|
+
### 1.1.0 (Mar 14, 2025)
|
|
420
|
+
- Implement user agent logging.
|
|
421
|
+
|
|
410
422
|
### 0.11.0 (Nov 29, 2024)
|
|
411
423
|
- Update to use Authorisation Server issuer attribute as `aud` value when retrieving tokens.
|
|
412
424
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "rp_python_sdk"
|
|
3
|
-
version = "
|
|
3
|
+
version = "1.1.1"
|
|
4
4
|
description = "Python SDK for Relying Parties to enable simple integration with the Digital Identity ecosystem"
|
|
5
5
|
authors = ["Erik Pragt <erik.pragt@connectid.com.au>"]
|
|
6
6
|
license = "Proprietary"
|
|
@@ -9,22 +9,23 @@ readme = "README.md"
|
|
|
9
9
|
[tool.poetry.dependencies]
|
|
10
10
|
python = "^3.12"
|
|
11
11
|
requests = "^2.32.3"
|
|
12
|
-
authlib = "^1.
|
|
13
|
-
cryptography = "^
|
|
14
|
-
joserfc = "^1.
|
|
12
|
+
authlib = "^1.6.0"
|
|
13
|
+
cryptography = "^45.0.0"
|
|
14
|
+
joserfc = "^1.1.0"
|
|
15
15
|
cffi = "^1.17.1"
|
|
16
16
|
|
|
17
17
|
[tool.poetry.group.test.dependencies]
|
|
18
|
-
pytest = "^8.
|
|
19
|
-
httpx = "^0.
|
|
18
|
+
pytest = "^8.4.1"
|
|
19
|
+
httpx = "^0.28.1"
|
|
20
|
+
toml = "^0.10.2"
|
|
20
21
|
|
|
21
22
|
[tool.poetry.group.dev.dependencies]
|
|
22
|
-
black = "^
|
|
23
|
-
pre-commit = "^
|
|
24
|
-
mypy = "^1.
|
|
23
|
+
black = "^25.0.0"
|
|
24
|
+
pre-commit = "^4.2.0"
|
|
25
|
+
mypy = "^1.16.1"
|
|
25
26
|
requests-mock = "^1.12.1"
|
|
26
27
|
freezegun = "^1.4.0"
|
|
27
|
-
flake8 = "^7.
|
|
28
|
+
flake8 = "^7.3.0"
|
|
28
29
|
|
|
29
30
|
[build-system]
|
|
30
31
|
requires = ["poetry-core"]
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from datetime import date
|
|
2
3
|
|
|
3
4
|
import requests
|
|
4
|
-
import json
|
|
5
5
|
|
|
6
6
|
from rp_python_sdk.endpoints.util.mapping import from_dict
|
|
7
|
+
from rp_python_sdk.endpoints.util.user_agent import build_user_agent
|
|
7
8
|
from rp_python_sdk.filters.participant_filters import remove_out_of_date_certifications, \
|
|
8
9
|
remove_unofficial_certifications, filter_for_fallback_identity_service_providers, \
|
|
9
10
|
remove_participants_without_auth_servers, remove_fallback_identity_service_provider, \
|
|
@@ -62,7 +63,10 @@ def _retrieve_full_participants_list(config: SdkConfig) -> list[Participant]:
|
|
|
62
63
|
logger.info(f"Retrieving all identity providers from {config.registry_participants_uri}")
|
|
63
64
|
|
|
64
65
|
response = requests.get(config.registry_participants_uri,
|
|
65
|
-
timeout=config.custom_config.timeout_in_seconds
|
|
66
|
+
timeout=config.custom_config.timeout_in_seconds,
|
|
67
|
+
headers={
|
|
68
|
+
'User-Agent': build_user_agent(config.client_id)
|
|
69
|
+
})
|
|
66
70
|
if response.status_code != 200:
|
|
67
71
|
raise RelyingPartyClientSdkException(
|
|
68
72
|
f"Call to identity provider endpoint failed with code: {response.status_code}")
|
|
@@ -75,17 +79,29 @@ def _retrieve_full_participants_list(config: SdkConfig) -> list[Participant]:
|
|
|
75
79
|
|
|
76
80
|
|
|
77
81
|
def get_auth_server_details(config: SdkConfig, authorisation_server_id: str) -> AuthorisationServer:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
def find_auth_server_by_id(participants: list[Participant], target_id: str) -> AuthorisationServer | None:
|
|
83
|
+
for participant in participants:
|
|
84
|
+
for server in participant.authorisation_servers:
|
|
85
|
+
if str(server.authorisation_server_id) == target_id:
|
|
86
|
+
return server
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
# Search in main participants
|
|
90
|
+
result = find_auth_server_by_id(get_participants(config), authorisation_server_id)
|
|
91
|
+
|
|
92
|
+
# If not found, check fallback provider participants
|
|
93
|
+
if result is None:
|
|
94
|
+
fallback_participants = retrieve_fallback_provider_participants(config)
|
|
95
|
+
result = find_auth_server_by_id(fallback_participants, authorisation_server_id)
|
|
96
|
+
|
|
97
|
+
if result is None:
|
|
98
|
+
raise RelyingPartyClientSdkException(
|
|
99
|
+
f"No authorisation server with id: {authorisation_server_id} was found")
|
|
84
100
|
|
|
85
101
|
return result
|
|
86
102
|
|
|
87
103
|
|
|
88
|
-
def get_issuer_metadata(authorisation_server: AuthorisationServer):
|
|
104
|
+
def get_issuer_metadata(authorisation_server: AuthorisationServer) -> IssuerMetadata:
|
|
89
105
|
auth_server_issuer = authorisation_server.open_i_d_discovery_document
|
|
90
106
|
|
|
91
107
|
try:
|
{rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/endpoints/pushed_authorisation_request.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import time
|
|
3
3
|
import uuid
|
|
4
|
+
from typing import Any
|
|
4
5
|
from urllib.parse import quote
|
|
5
6
|
|
|
6
7
|
import requests
|
|
@@ -125,7 +126,7 @@ def send_pushed_authorisation_request(config: SdkConfig,
|
|
|
125
126
|
code_verifier, state, nonce, x_fapi_interaction_id)
|
|
126
127
|
|
|
127
128
|
|
|
128
|
-
def _check_valid_purpose(purpose) -> None:
|
|
129
|
+
def _check_valid_purpose(purpose: str) -> None:
|
|
129
130
|
if len(purpose) < 3 or len(purpose) > 300:
|
|
130
131
|
raise RelyingPartyClientSdkException("Purpose must be between 3 and 300 characters")
|
|
131
132
|
|
|
@@ -149,7 +150,7 @@ def _ensure_mandatory_claims_present(essential_claims: set[str], voluntary_claim
|
|
|
149
150
|
return result_claims
|
|
150
151
|
|
|
151
152
|
|
|
152
|
-
def _generate_claims_request(essential_claims: set[str], voluntary_claims: set[str]):
|
|
153
|
+
def _generate_claims_request(essential_claims: set[str], voluntary_claims: set[str]) -> dict[str, Any]:
|
|
153
154
|
# Filter out any claims that are in both essential and voluntary sets so essential takes precedence
|
|
154
155
|
deduplicated_voluntary_claims = voluntary_claims - essential_claims
|
|
155
156
|
|
|
@@ -179,7 +180,7 @@ def _generate_claims_request(essential_claims: set[str], voluntary_claims: set[s
|
|
|
179
180
|
return {"id_token": id_token_claims}
|
|
180
181
|
|
|
181
182
|
|
|
182
|
-
def _partition_claims(claims: set[str]):
|
|
183
|
+
def _partition_claims(claims: set[str]) -> dict[bool, set[str]]:
|
|
183
184
|
"""
|
|
184
185
|
Partition claims into basic and extended types based on a criteria (here, whether they are in extended_claims).
|
|
185
186
|
"""
|
|
@@ -189,7 +190,7 @@ def _partition_claims(claims: set[str]):
|
|
|
189
190
|
return partitioned
|
|
190
191
|
|
|
191
192
|
|
|
192
|
-
def _map_claims(claims, claim_map):
|
|
193
|
+
def _map_claims(claims: set[str], claim_map: dict[str, Any]) -> dict[str, Any]:
|
|
193
194
|
"""
|
|
194
195
|
Map each claim to the provided claim_map.
|
|
195
196
|
"""
|
|
@@ -5,12 +5,12 @@ import uuid
|
|
|
5
5
|
from typing import Tuple, Any
|
|
6
6
|
|
|
7
7
|
import requests
|
|
8
|
-
from joserfc import jws
|
|
9
|
-
from joserfc.jwk import RSAKey
|
|
10
|
-
from joserfc.jws import JWSRegistry
|
|
11
8
|
from authlib.jose import JsonWebToken
|
|
12
9
|
from authlib.jose.errors import BadSignatureError, DecodeError
|
|
13
10
|
from authlib.jose.util import extract_header
|
|
11
|
+
from joserfc import jws
|
|
12
|
+
from joserfc.jwk import RSAKey
|
|
13
|
+
from joserfc.jws import JWSRegistry
|
|
14
14
|
|
|
15
15
|
from rp_python_sdk.endpoints.get_participants import get_issuer_metadata, \
|
|
16
16
|
get_auth_server_details
|
|
@@ -72,7 +72,7 @@ def retrieve_tokens(config: SdkConfig, authorisation_server_id: str, callback_bo
|
|
|
72
72
|
|
|
73
73
|
key = RSAKey.import_key(config.signing_key)
|
|
74
74
|
header = {'alg': 'PS256', 'kid': config.signing_kid}
|
|
75
|
-
client_assertion_jwt = jws.serialize_compact(header, json.dumps(client_assertion_payload), key, registry
|
|
75
|
+
client_assertion_jwt = jws.serialize_compact(header, json.dumps(client_assertion_payload), key, registry=registry)
|
|
76
76
|
|
|
77
77
|
# Data payload
|
|
78
78
|
data = {
|
|
@@ -107,7 +107,8 @@ def retrieve_tokens(config: SdkConfig, authorisation_server_id: str, callback_bo
|
|
|
107
107
|
jwks_response_body = _get_jwks_json(issuer_metadata, x_fapi_interaction_id)
|
|
108
108
|
allowed_algs = issuer_metadata.id_token_signing_alg_values_supported
|
|
109
109
|
|
|
110
|
-
header, payload = _validate(token_input, jwks_response_body, allowed_algs, nonce, client_id, callback_body,
|
|
110
|
+
header, payload = _validate(token_input, jwks_response_body, allowed_algs, nonce, client_id, callback_body,
|
|
111
|
+
issuer_metadata.issuer)
|
|
111
112
|
|
|
112
113
|
token_set = TokenSet(token_input, payload, x_fapi_interaction_id)
|
|
113
114
|
|
|
@@ -197,9 +198,11 @@ def _validate_payload(json_payload: dict[str, Any], nonce: str, callback_body: C
|
|
|
197
198
|
if json_payload['aud'] != client_id:
|
|
198
199
|
raise RelyingPartyClientSdkException('aud claim in token response does not match client ID')
|
|
199
200
|
|
|
200
|
-
|
|
201
|
+
|
|
202
|
+
def _get_unverified_header(id_token: str) -> Any:
|
|
201
203
|
return extract_header(id_token.split('.')[0].encode(), DecodeError)
|
|
202
204
|
|
|
205
|
+
|
|
203
206
|
def _decode_base64(base64_str: str) -> str:
|
|
204
207
|
"""
|
|
205
208
|
Decode a Base64 encoded string.
|
|
@@ -223,12 +226,14 @@ def _validate_header(json_header: dict, allowed_algs: list) -> None:
|
|
|
223
226
|
f"alg value in id token header is not one of the supported algorithms listed on the well-known. "
|
|
224
227
|
f"Provided alg {json_header['alg']}")
|
|
225
228
|
|
|
229
|
+
|
|
226
230
|
def _find_key_in_jwks(kid: str, token_alg: str, jwks: dict) -> Any:
|
|
227
231
|
for key_element in jwks.get('keys', []):
|
|
228
232
|
if _matching_kid(kid, key_element) and _matching_alg(token_alg, key_element) and _matching_use(key_element):
|
|
229
233
|
return key_element
|
|
230
234
|
return None
|
|
231
235
|
|
|
236
|
+
|
|
232
237
|
def _validate_signature(issuer: str, audience: str, allowed_alg: list, id_token: str, jwks: dict) -> None:
|
|
233
238
|
try:
|
|
234
239
|
# Pub key extraction
|
|
@@ -257,14 +262,18 @@ def _validate_signature(issuer: str, audience: str, allowed_alg: list, id_token:
|
|
|
257
262
|
logger.debug(f"Invalid JWT token: {id_token}")
|
|
258
263
|
raise RelyingPartyClientSdkException(f"Invalid JWT signature: {e}")
|
|
259
264
|
|
|
265
|
+
|
|
260
266
|
def _matching_use(key_element: dict) -> bool:
|
|
261
267
|
return key_element.get('use') == "sig"
|
|
262
268
|
|
|
269
|
+
|
|
263
270
|
def _matching_kid(kid: str, key_element: dict) -> bool:
|
|
264
271
|
return key_element.get('kid') == kid
|
|
265
272
|
|
|
273
|
+
|
|
266
274
|
def _matching_alg(token_alg: str, key_element: dict) -> bool:
|
|
267
|
-
return
|
|
275
|
+
return key_element.get("alg") is None or key_element.get("alg") == token_alg
|
|
276
|
+
|
|
268
277
|
|
|
269
278
|
def _get_jwks_json(issuer_metadata: IssuerMetadata, x_fapi_interaction_id: str) -> Any:
|
|
270
279
|
headers = {
|
|
@@ -279,4 +288,4 @@ def _get_jwks_json(issuer_metadata: IssuerMetadata, x_fapi_interaction_id: str)
|
|
|
279
288
|
f"Response code for JWKS to {issuer_metadata.jwks_uri} not successful, was: {response.status_code}, "
|
|
280
289
|
f"x-fapi-interaction-id: {x_fapi_interaction_id}, response body: {response.text}")
|
|
281
290
|
|
|
282
|
-
return response.json()
|
|
291
|
+
return response.json()
|
|
@@ -40,5 +40,5 @@ def call_user_info(config: SdkConfig, authorisation_server_id: str, access_token
|
|
|
40
40
|
f"Failed to read body from response while connecting to {user_info_endpoint}") from e
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def _is_success(response: requests.Response):
|
|
43
|
+
def _is_success(response: requests.Response) -> bool:
|
|
44
44
|
return 200 <= response.status_code < 300
|
|
@@ -6,25 +6,24 @@ from rp_python_sdk.model import AuthorisationServer
|
|
|
6
6
|
from rp_python_sdk.setup_logger import logger
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def create_x_fapi_interaction_id():
|
|
9
|
+
def create_x_fapi_interaction_id() -> str:
|
|
10
10
|
return str(uuid.uuid4())
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def log_x_fapi_interaction_id_matches(authorisation_server: AuthorisationServer, request_name: str,
|
|
14
|
-
x_fapi_interaction_id: str, response: Response):
|
|
14
|
+
x_fapi_interaction_id: str, response: Response) -> None:
|
|
15
15
|
fapi_header = response.headers.get('x-fapi-interaction-id')
|
|
16
16
|
if not fapi_header:
|
|
17
17
|
logger.warning(
|
|
18
18
|
f"No x-fapi-interaction-id header returned from auth server {authorisation_server.authorisation_server_id} "
|
|
19
19
|
f"on request {request_name}, expected {x_fapi_interaction_id}")
|
|
20
20
|
return
|
|
21
|
-
|
|
22
|
-
if x_fapi_interaction_id == fapi_header:
|
|
21
|
+
elif x_fapi_interaction_id == fapi_header:
|
|
23
22
|
logger.debug(
|
|
24
23
|
f"x-fapi-interaction-id correctly returned from auth server {authorisation_server.authorisation_server_id} "
|
|
25
24
|
f"on {request_name} matched expected {x_fapi_interaction_id}")
|
|
26
25
|
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
else:
|
|
27
|
+
logger.warning(
|
|
28
|
+
f"x-fapi-interaction-id returned from auth server {authorisation_server.authorisation_server_id} "
|
|
29
|
+
f"on {request_name} did not match. sent {x_fapi_interaction_id}, returned {fapi_header}")
|
|
@@ -11,7 +11,7 @@ def snake_to_pascal(name: str) -> str:
|
|
|
11
11
|
return ''.join(word.capitalize() for word in name.split('_'))
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def from_dict(data_class: Type[T], data: Union[dict, list], pascal_case=False) -> Union[T, List[T]]:
|
|
14
|
+
def from_dict(data_class: Type[T], data: Union[dict, list], pascal_case: bool = False) -> Union[T, List[T]]:
|
|
15
15
|
"""
|
|
16
16
|
Recursively maps a dictionary or a list of dictionaries to a dataclass or a list of dataclasses.
|
|
17
17
|
Handles key transformations from snake_case to PascalCase, and resolves forward references.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import platform
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_system_info() -> str:
|
|
5
|
+
os_name = platform.system() # e.g., "Darwin", "Windows", "Linux"
|
|
6
|
+
os_version = platform.version() # e.g., "19.6.0", "10.0.19043", "5.15.0-84-generic"
|
|
7
|
+
python_version = platform.python_version() # Python version
|
|
8
|
+
|
|
9
|
+
return f"{detect_operating_system(os_name)}; {os_name} {os_version}; Python{python_version}"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def detect_operating_system(raw_input: str) -> str:
|
|
13
|
+
if raw_input is None:
|
|
14
|
+
return "Unknown"
|
|
15
|
+
|
|
16
|
+
os_name = raw_input.lower()
|
|
17
|
+
if "darwin" in os_name:
|
|
18
|
+
return "Macintosh"
|
|
19
|
+
elif "win" in os_name:
|
|
20
|
+
return "Windows"
|
|
21
|
+
elif "linux" in os_name:
|
|
22
|
+
return "Linux"
|
|
23
|
+
else:
|
|
24
|
+
return "Other"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from rp_python_sdk.endpoints.util.system_info import get_system_info
|
|
2
|
+
from rp_python_sdk.endpoints.util.version_info import get_version_info
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def build_user_agent(client_id: str) -> str:
|
|
6
|
+
version = get_version_info()
|
|
7
|
+
platform = get_system_info()
|
|
8
|
+
return f"cid-rp-python-sdk/{version} ({platform}) +{client_id}"
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
from datetime import datetime, date
|
|
1
2
|
from typing import List
|
|
2
3
|
|
|
3
4
|
from rp_python_sdk.model import Participant
|
|
4
5
|
from rp_python_sdk.sdk_config import CertificationFilter
|
|
5
|
-
from datetime import datetime, date
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def remove_out_of_date_certifications(participants: List[Participant], reference_date: date) -> List[Participant]:
|
|
9
9
|
for participant in participants:
|
|
10
10
|
for auth_server in participant.authorisation_servers:
|
|
11
11
|
filtered = [certification for certification in auth_server.authorisation_server_certifications if
|
|
12
|
-
to_date(certification.certification_start_date) < reference_date < to_date(
|
|
12
|
+
to_date(certification.certification_start_date) < reference_date < to_date(
|
|
13
|
+
certification.certification_expiration_date)]
|
|
13
14
|
auth_server.authorisation_server_certifications = filtered
|
|
14
15
|
|
|
15
16
|
return participants
|
|
@@ -93,6 +94,6 @@ def filter_for_fallback_identity_service_providers(participants: List[Participan
|
|
|
93
94
|
return participants
|
|
94
95
|
|
|
95
96
|
|
|
96
|
-
def to_date(date_string: str):
|
|
97
|
+
def to_date(date_string: str) -> date:
|
|
97
98
|
date_format = "%d/%m/%Y"
|
|
98
99
|
return datetime.strptime(date_string, date_format).date()
|
|
@@ -2,6 +2,7 @@ from dataclasses import dataclass
|
|
|
2
2
|
from typing import Any, Dict, Optional
|
|
3
3
|
from typing import List
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
@dataclass
|
|
6
7
|
class Participant:
|
|
7
8
|
organisation_id: str
|
|
@@ -45,11 +46,13 @@ class OrgDomainClaim:
|
|
|
45
46
|
registration_id: str
|
|
46
47
|
status: str
|
|
47
48
|
|
|
49
|
+
|
|
48
50
|
@dataclass
|
|
49
51
|
class ApiDiscoveryEndpoint:
|
|
50
52
|
api_discovery_id: str
|
|
51
53
|
api_endpoint: str
|
|
52
54
|
|
|
55
|
+
|
|
53
56
|
@dataclass
|
|
54
57
|
class ApiResource:
|
|
55
58
|
api_resource_id: str
|
|
@@ -64,6 +67,7 @@ class ApiResource:
|
|
|
64
67
|
certification_expiration_date: str
|
|
65
68
|
api_family_type: Optional[str]
|
|
66
69
|
|
|
70
|
+
|
|
67
71
|
@dataclass
|
|
68
72
|
class AuthorisationServer:
|
|
69
73
|
authorisation_server_id: str
|
|
File without changes
|
|
File without changes
|
{rp_python_sdk-0.11.0 → rp_python_sdk-1.1.1}/rp_python_sdk/relying_party_client_sdk_exception.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|