jaaql-middleware-python 4.26.2__tar.gz → 4.27.0__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.
- {jaaql-middleware-python-4.26.2/jaaql_middleware_python.egg-info → jaaql-middleware-python-4.27.0}/PKG-INFO +7 -6
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/constants.py +2 -1
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/documentation/documentation_internal.py +59 -17
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/base_controller.py +6 -1
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/base_model.py +30 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/controller.py +5 -1
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/model.py +144 -19
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/utils_no_project_imports.py +12 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0/jaaql_middleware_python.egg-info}/PKG-INFO +7 -6
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql_middleware_python.egg-info/requires.txt +6 -5
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/LICENSE.txt +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/README.md +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config/config-docker.ini +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config/config-test.ini +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config/config.ini +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config_constants.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/db_interface.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/db_pg_interface.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/db_utils.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/db_utils_no_circ.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/documentation/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/documentation/documentation_public.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/documentation/documentation_shared.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/email/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/email/email_manager.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/email/email_manager_service.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/email/patch_ems.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/exceptions/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/exceptions/custom_http_status.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/exceptions/http_status_exception.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/exceptions/jaaql_interpretable_handled_errors.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/exceptions/not_yet_implement_exception.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/generated_constants.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/interpreter/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/interpreter/interpret_jaaql.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/jaaql.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/migrations/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/migrations/migrations.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/controller_interface.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/exception_queries.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/generated_queries.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/handmade_queries.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/model_interface.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/response.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/openapi/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/openapi/swagger_documentation.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/patch.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/01.install_domains.generated.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/02.install_super_user.exceptions.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/03.install_super_user.handwritten.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/04.install_jaaql_data_structures.generated.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/05.install_static_data.generated.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/06.install_jaaql.exceptions.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/ZZZZ.generated_functions_views_and_permissions.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/ZZZZ.reset_references.sql +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/scripts/swagger_template.html +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/cached_canned_query_service.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/migrations_manager_service.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/patch_mms.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/patch_shared_var_service.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/shared_var_service.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/__init__.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/crypt_utils.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/options.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/utils.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/vault.py +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql_middleware_python.egg-info/SOURCES.txt +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql_middleware_python.egg-info/dependency_links.txt +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql_middleware_python.egg-info/top_level.txt +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/setup.cfg +0 -0
- {jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jaaql-middleware-python
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.27.0
|
|
4
4
|
Summary: The jaaql package, allowing for rapid development and deployment of RESTful HTTP applications
|
|
5
5
|
Home-page: https://github.com/JAAQL/JAAQL-middleware-python
|
|
6
6
|
Author: Software Quality Measurement and Improvement bv
|
|
@@ -8,7 +8,7 @@ Author-email: aaron.tasker@sqmi.nl
|
|
|
8
8
|
License: Mozilla Public License Version 2.0 with Commons Clause
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE.txt
|
|
11
|
-
Requires-Dist: jaaql-monitor~=1.
|
|
11
|
+
Requires-Dist: jaaql-monitor~=1.6.3
|
|
12
12
|
Requires-Dist: psycopg[binary]~=3.1.18
|
|
13
13
|
Requires-Dist: Pillow~=10.3.0
|
|
14
14
|
Requires-Dist: cryptography~=42.0.5
|
|
@@ -21,14 +21,15 @@ Requires-Dist: werkzeug~=3.0.1
|
|
|
21
21
|
Requires-Dist: argon2-cffi~=23.1.0
|
|
22
22
|
Requires-Dist: pyotp~=2.9.0
|
|
23
23
|
Requires-Dist: qrcode~=7.4.2
|
|
24
|
-
Requires-Dist: gunicorn~=
|
|
24
|
+
Requires-Dist: gunicorn~=23.0.0
|
|
25
25
|
Requires-Dist: gevent~=24.2.1
|
|
26
|
-
Requires-Dist: requests~=2.
|
|
26
|
+
Requires-Dist: requests~=2.32.3
|
|
27
27
|
Requires-Dist: pyzbar~=0.1.9
|
|
28
|
-
Requires-Dist: parsys-requests-unixsocket~=0.3.
|
|
28
|
+
Requires-Dist: parsys-requests-unixsocket~=0.3.2
|
|
29
29
|
Requires-Dist: selenium~=4.18.1
|
|
30
30
|
Requires-Dist: twine~=5.0.0
|
|
31
|
-
Requires-Dist: urllib3~=2.
|
|
31
|
+
Requires-Dist: urllib3~=2.3.0
|
|
32
|
+
Requires-Dist: jwcrypto~=1.5.6
|
|
32
33
|
|
|
33
34
|
# JAAQL-middleware-python
|
|
34
35
|
Please navigate to docker/docker.md to see setup instructions
|
|
@@ -164,6 +164,7 @@ ENDPOINT__report_sentinel_error = "/sentinel/reporting/error"
|
|
|
164
164
|
ENDPOINT__install = "/internal/install"
|
|
165
165
|
ENDPOINT__set_shared_var = "/set-shared-var"
|
|
166
166
|
ENDPOINT__get_shared_var = "/get-shared-var"
|
|
167
|
+
ENDPOINT__oidc_get_token = "/exchange-auth-code"
|
|
167
168
|
|
|
168
169
|
CONFIG__default = "Default config"
|
|
169
170
|
CONFIG__default_desc = "Default config description"
|
|
@@ -182,5 +183,5 @@ ROLE__postgres = "postgres"
|
|
|
182
183
|
|
|
183
184
|
PROTOCOL__postgres = "postgresql://"
|
|
184
185
|
|
|
185
|
-
VERSION = "4.
|
|
186
|
+
VERSION = "4.27.0"
|
|
186
187
|
|
|
@@ -403,24 +403,66 @@ DOCUMENTATION__oidc_exchange_code = SwaggerDocumentation(
|
|
|
403
403
|
methods=SwaggerMethod(
|
|
404
404
|
name="Fetch OIDC code",
|
|
405
405
|
description="Exchanges OIDC auth code for auth token, returns the token",
|
|
406
|
-
method=
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
),
|
|
414
|
-
SwaggerArgumentResponse(
|
|
415
|
-
name=KEY__state,
|
|
416
|
-
description="The state",
|
|
417
|
-
arg_type=str,
|
|
418
|
-
example=["SplxlOBeZQQYbYS6WxSbIA"]
|
|
419
|
-
)
|
|
420
|
-
],
|
|
406
|
+
method=REST__GET,
|
|
407
|
+
arguments=SwaggerArgumentResponse(
|
|
408
|
+
name="response",
|
|
409
|
+
description="The OIDC response JWT object",
|
|
410
|
+
arg_type=str,
|
|
411
|
+
example=["eyJ..."]
|
|
412
|
+
),
|
|
421
413
|
response=SwaggerFlatResponse(
|
|
422
|
-
description="
|
|
423
|
-
|
|
414
|
+
description="URL",
|
|
415
|
+
code=HTTPStatus.FOUND,
|
|
416
|
+
body="You are being redirected back to your place in the app..."
|
|
424
417
|
)
|
|
425
418
|
)
|
|
426
419
|
)
|
|
420
|
+
|
|
421
|
+
DOCUMENTATION__jwks = SwaggerDocumentation(
|
|
422
|
+
tags="jwks",
|
|
423
|
+
security=False,
|
|
424
|
+
methods=SwaggerMethod(
|
|
425
|
+
name="Fetch JWKS",
|
|
426
|
+
description="Fetches the JWKS so that mTLS can be used with JAAQL",
|
|
427
|
+
method=REST__GET,
|
|
428
|
+
response=SwaggerResponse(
|
|
429
|
+
description="The Keys",
|
|
430
|
+
response=SwaggerArgumentResponse(
|
|
431
|
+
name="keys",
|
|
432
|
+
description="A list of keys",
|
|
433
|
+
arg_type=SwaggerList(
|
|
434
|
+
SwaggerArgumentResponse(
|
|
435
|
+
name="e",
|
|
436
|
+
description="JWKS e",
|
|
437
|
+
arg_type=str,
|
|
438
|
+
example=["AQAB"]
|
|
439
|
+
),
|
|
440
|
+
SwaggerArgumentResponse(
|
|
441
|
+
name="kty",
|
|
442
|
+
description="JWKS kty",
|
|
443
|
+
arg_type=str,
|
|
444
|
+
example=["RSA"]
|
|
445
|
+
),
|
|
446
|
+
SwaggerArgumentResponse(
|
|
447
|
+
name="n",
|
|
448
|
+
description="The public key",
|
|
449
|
+
arg_type=str,
|
|
450
|
+
example=["tKiq..."]
|
|
451
|
+
),
|
|
452
|
+
SwaggerArgumentResponse(
|
|
453
|
+
name="kid",
|
|
454
|
+
description="The unique id",
|
|
455
|
+
arg_type=str,
|
|
456
|
+
example=["tKiq..."]
|
|
457
|
+
),
|
|
458
|
+
SwaggerArgumentResponse(
|
|
459
|
+
name="alg",
|
|
460
|
+
description="The algorithm",
|
|
461
|
+
arg_type=str,
|
|
462
|
+
example=["RS256"]
|
|
463
|
+
)
|
|
464
|
+
)
|
|
465
|
+
)
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
)
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/base_controller.py
RENAMED
|
@@ -15,7 +15,7 @@ import sys
|
|
|
15
15
|
import dataclasses
|
|
16
16
|
import decimal
|
|
17
17
|
from queue import Queue
|
|
18
|
-
from jaaql.utilities.utils_no_project_imports import get_cookie_attrs, COOKIE_JAAQL_AUTH
|
|
18
|
+
from jaaql.utilities.utils_no_project_imports import get_cookie_attrs, COOKIE_JAAQL_AUTH, COOKIE_LOGIN_MARKER, COOKIE_ATTR_PATH
|
|
19
19
|
from jaaql.utilities.utils import time_delta_ms, Profiler
|
|
20
20
|
from flask import Response, Flask, request, jsonify, current_app
|
|
21
21
|
from flask.json.provider import DefaultJSONProvider
|
|
@@ -676,6 +676,11 @@ class BaseJAAQLController:
|
|
|
676
676
|
get_cookie_attrs(self.model.vigilant_sessions, remember_me, self.model.is_container),
|
|
677
677
|
self.model.is_https))
|
|
678
678
|
|
|
679
|
+
if request.cookies.get(COOKIE_LOGIN_MARKER) is not None:
|
|
680
|
+
resp.headers.add("Set-Cookie", format_cookie(COOKIE_LOGIN_MARKER, "",
|
|
681
|
+
{COOKIE_ATTR_EXPIRES: format_date_time(0), COOKIE_ATTR_PATH: "/"},
|
|
682
|
+
self.model.is_https))
|
|
683
|
+
|
|
679
684
|
for _, cookie in jaaql_resp.cookies.items():
|
|
680
685
|
resp.headers.add("Set-Cookie", cookie)
|
|
681
686
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
|
|
3
|
+
import json
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from cryptography import x509
|
|
3
7
|
from jaaql.utilities.vault import Vault, DIR__vault
|
|
4
8
|
from jaaql.db.db_interface import DBInterface
|
|
5
9
|
import jaaql.utilities.crypt_utils as crypt_utils
|
|
@@ -202,6 +206,25 @@ class BaseJAAQLModel:
|
|
|
202
206
|
self.force_mfa = config[CONFIG_KEY__security][CONFIG_KEY_SECURITY__force_mfa]
|
|
203
207
|
self.do_audit = config[CONFIG_KEY__security][CONFIG_KEY_SECURITY__do_audit]
|
|
204
208
|
|
|
209
|
+
self.jwks = None
|
|
210
|
+
if self.is_container:
|
|
211
|
+
with open('/tmp/jwks.json', 'r') as f:
|
|
212
|
+
self.jwks = json.load(f)
|
|
213
|
+
|
|
214
|
+
self.application_url = os.environ.get("SERVER_ADDRESS", "")
|
|
215
|
+
self.use_fapi_advanced = os.environ.get("USE_FAPI_ADVANCED", "").lower() == "true"
|
|
216
|
+
|
|
217
|
+
self.fapi_pem = None
|
|
218
|
+
self.fapi_cert = None
|
|
219
|
+
if self.is_container:
|
|
220
|
+
with open('/tmp/client_key.pem', "rb") as f:
|
|
221
|
+
self.fapi_pem = f.read()
|
|
222
|
+
|
|
223
|
+
if self.use_fapi_advanced:
|
|
224
|
+
with open(f"/etc/letsencrypt/live/{self.application_url}/fullchain.pem", "rb") as f:
|
|
225
|
+
self.fapi_cert = f.read()
|
|
226
|
+
self.fapi_cert = x509.load_pem_x509_certificate(self.fapi_cert)
|
|
227
|
+
|
|
205
228
|
self.vault = Vault(vault_key, DIR__vault)
|
|
206
229
|
self.jaaql_lookup_connection = None
|
|
207
230
|
self.email_manager = EmailManager(self.is_container)
|
|
@@ -260,9 +283,16 @@ class BaseJAAQLModel:
|
|
|
260
283
|
install_key_file.write(self.install_key)
|
|
261
284
|
print("INSTALL KEY: " + self.install_key, file=sys.stderr) # Print to stderr as unbuffered
|
|
262
285
|
|
|
286
|
+
self.idp_session = requests.Session()
|
|
287
|
+
|
|
263
288
|
def get_db_crypt_key(self):
|
|
264
289
|
return self.vault.get_obj(VAULT_KEY__db_crypt_key).encode(crypt_utils.ENCODING__ascii)
|
|
265
290
|
|
|
291
|
+
def reload_fapi_cert(self):
|
|
292
|
+
with open(f"/etc/letsencrypt/live/{self.application_url}/fullchain.pem", "rb") as f:
|
|
293
|
+
self.fapi_cert = f.read()
|
|
294
|
+
self.fapi_cert = x509.load_pem_x509_certificate(self.fapi_cert)
|
|
295
|
+
|
|
266
296
|
def get_vault_repeatable_salt(self):
|
|
267
297
|
return self.vault.get_obj(VAULT_KEY__db_repeatable_salt)
|
|
268
298
|
|
|
@@ -163,6 +163,10 @@ class JAAQLController(BaseJAAQLController):
|
|
|
163
163
|
def fetch_redirect_url(http_inputs: dict, response: JAAQLResponse):
|
|
164
164
|
self.model.fetch_redirect_uri(http_inputs, response)
|
|
165
165
|
|
|
166
|
-
@self.publish_route(
|
|
166
|
+
@self.publish_route(ENDPOINT__oidc_get_token, DOCUMENTATION__oidc_exchange_code)
|
|
167
167
|
def exchange_auth_code(http_inputs: dict, ip_address: str, response: JAAQLResponse):
|
|
168
168
|
self.model.exchange_auth_code(http_inputs, request.cookies.get(COOKIE_OIDC), ip_address, response)
|
|
169
|
+
|
|
170
|
+
@self.publish_route('/.well-known/jwks', DOCUMENTATION__jwks)
|
|
171
|
+
def fetch_jwks():
|
|
172
|
+
return self.model.fetch_jwks()
|
|
@@ -6,6 +6,7 @@ import traceback
|
|
|
6
6
|
import urllib.parse
|
|
7
7
|
import uuid
|
|
8
8
|
import secrets
|
|
9
|
+
from cryptography.hazmat.primitives import hashes
|
|
9
10
|
|
|
10
11
|
import re
|
|
11
12
|
|
|
@@ -26,7 +27,8 @@ from jaaql.utilities.utils import get_jaaql_root, get_base_url
|
|
|
26
27
|
from jaaql.db.db_utils import create_interface, jaaql__encrypt, create_interface_for_db, jaaql__decrypt
|
|
27
28
|
from jaaql.db.db_utils_no_circ import submit, get_required_db, objectify
|
|
28
29
|
from jaaql.utilities import crypt_utils
|
|
29
|
-
from jaaql.utilities.utils_no_project_imports import get_cookie_attrs, COOKIE_JAAQL_AUTH,
|
|
30
|
+
from jaaql.utilities.utils_no_project_imports import get_cookie_attrs, COOKIE_JAAQL_AUTH, COOKIE_OIDC, COOKIE_LOGIN_MARKER, \
|
|
31
|
+
get_sloppy_cookie_attrs
|
|
30
32
|
from jaaql.mvc.response import *
|
|
31
33
|
import threading
|
|
32
34
|
from datetime import datetime, timedelta
|
|
@@ -509,13 +511,19 @@ WHERE
|
|
|
509
511
|
challenge = base64.urlsafe_b64encode(digest).decode('ascii').rstrip('=')
|
|
510
512
|
return challenge
|
|
511
513
|
|
|
514
|
+
def fetch_jwks(self):
|
|
515
|
+
return self.jwks
|
|
516
|
+
|
|
517
|
+
def get_cert_thumbprint(self) -> str:
|
|
518
|
+
fingerprint = self.fapi_cert.fingerprint(hashes.SHA256())
|
|
519
|
+
return base64.urlsafe_b64encode(fingerprint).rstrip(b'=').decode('utf-8')
|
|
520
|
+
|
|
512
521
|
def exchange_auth_code(self, inputs: dict, oidc_cookie: str, ip_address: str, response: JAAQLResponse):
|
|
513
522
|
response.delete_cookie(COOKIE_OIDC, self.is_https)
|
|
514
523
|
oidc_state = crypt_utils.jwt_decode(self.vault.get_obj(VAULT_KEY__jwt_crypt_key), oidc_cookie, JWT_PURPOSE__oidc)
|
|
515
|
-
if inputs[KEY__state] != oidc_state.get('state'):
|
|
516
|
-
raise UserUnauthorized()
|
|
517
524
|
|
|
518
525
|
application = oidc_state[KEY__application]
|
|
526
|
+
application_tuple = application__select(self.jaaql_lookup_connection, application)
|
|
519
527
|
provider = oidc_state[KG__user_registry__provider]
|
|
520
528
|
tenant = oidc_state[KG__user_registry__tenant]
|
|
521
529
|
database = jaaql__decrypt(oidc_state["database"], self.get_db_crypt_key())
|
|
@@ -538,27 +546,57 @@ WHERE
|
|
|
538
546
|
if not allowed_algs:
|
|
539
547
|
raise Exception(f"No allowed algs for {provider}, {tenant}")
|
|
540
548
|
|
|
549
|
+
jarms_response = inputs["response"]
|
|
550
|
+
signing_key = jwk_client.get_signing_key_from_jwt(jarms_response)
|
|
551
|
+
try:
|
|
552
|
+
jarms_payload = jwt.decode(
|
|
553
|
+
jarms_response,
|
|
554
|
+
signing_key.key,
|
|
555
|
+
algorithms=allowed_algs,
|
|
556
|
+
audience=database_user_registry[KG__database_user_registry__client_id],
|
|
557
|
+
issuer=expected_issuer
|
|
558
|
+
)
|
|
559
|
+
except:
|
|
560
|
+
raise UserUnauthorized()
|
|
561
|
+
|
|
562
|
+
if jarms_payload[KEY__state] != oidc_state.get('state'):
|
|
563
|
+
raise UserUnauthorized()
|
|
564
|
+
|
|
541
565
|
token_request_payload = {
|
|
542
566
|
'grant_type': 'authorization_code',
|
|
543
|
-
'code':
|
|
544
|
-
'redirect_uri':
|
|
567
|
+
'code': jarms_payload[KEY__code],
|
|
568
|
+
'redirect_uri': application_tuple[KG__application__base_url] + "/api" + ENDPOINT__oidc_get_token,
|
|
545
569
|
'client_id': database_user_registry[KG__database_user_registry__client_id],
|
|
546
|
-
'code_verifier': code_verifier
|
|
570
|
+
'code_verifier': code_verifier
|
|
547
571
|
}
|
|
548
|
-
if database_user_registry[KG__database_user_registry__client_secret]:
|
|
549
|
-
token_request_payload["client_secret"] = database_user_registry[KG__database_user_registry__client_secret]
|
|
550
572
|
|
|
551
|
-
|
|
573
|
+
kwargs = {}
|
|
574
|
+
if self.use_fapi_advanced:
|
|
575
|
+
kwargs["verify"] = True
|
|
576
|
+
kwargs["cert"] = (f"/etc/letsencrypt/live/{self.application_url}/fullchain.pem", f"/etc/letsencrypt/live/{self.application_url}/privkey.pem")
|
|
577
|
+
else:
|
|
578
|
+
payload = {
|
|
579
|
+
"iss": database_user_registry[KG__database_user_registry__client_id],
|
|
580
|
+
"sub": database_user_registry[KG__database_user_registry__client_id],
|
|
581
|
+
"aud": token_endpoint,
|
|
582
|
+
"jti": str(uuid.uuid4()),
|
|
583
|
+
"iat": int(time.time()),
|
|
584
|
+
"exp": int(time.time()) + 300 # Token valid for 5 minutes
|
|
585
|
+
}
|
|
586
|
+
token_request_payload["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
|
587
|
+
token_request_payload["client_assertion"] = jwt.encode(payload, self.fapi_pem, algorithm="PS256", headers={
|
|
588
|
+
"kid": self.jwks["keys"][0]["kid"]
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
token_response = self.idp_session.post(
|
|
552
592
|
token_endpoint.replace("localhost", "host.docker.internal"),
|
|
553
593
|
data=token_request_payload,
|
|
594
|
+
**kwargs
|
|
554
595
|
)
|
|
555
596
|
|
|
556
|
-
if os.environ.get("JAAQL_DEBUGGING") == "TRUE":
|
|
557
|
-
print(token_response.status_code)
|
|
558
|
-
print(token_response.text)
|
|
559
|
-
|
|
560
597
|
token_data = token_response.json()
|
|
561
598
|
id_token = token_data.get('id_token')
|
|
599
|
+
access_token = token_data.get('access_token')
|
|
562
600
|
|
|
563
601
|
signing_key = jwk_client.get_signing_key_from_jwt(id_token)
|
|
564
602
|
try:
|
|
@@ -575,6 +613,38 @@ WHERE
|
|
|
575
613
|
if id_payload.get("nonce") != oidc_state["nonce"]:
|
|
576
614
|
raise UserUnauthorized()
|
|
577
615
|
|
|
616
|
+
if self.use_fapi_advanced:
|
|
617
|
+
access_signing_key = jwk_client.get_signing_key_from_jwt(access_token)
|
|
618
|
+
try:
|
|
619
|
+
access_payload = jwt.decode(
|
|
620
|
+
access_token,
|
|
621
|
+
access_signing_key.key,
|
|
622
|
+
algorithms=allowed_algs,
|
|
623
|
+
audience=database_user_registry[KG__database_user_registry__client_id],
|
|
624
|
+
issuer=expected_issuer
|
|
625
|
+
)
|
|
626
|
+
except:
|
|
627
|
+
raise UserUnauthorized()
|
|
628
|
+
|
|
629
|
+
# 2. Extract and check the cnf claim.
|
|
630
|
+
cnf_claim = access_payload.get("cnf")
|
|
631
|
+
if not cnf_claim:
|
|
632
|
+
raise UserUnauthorized() # Access token is missing the 'cnf' claim.
|
|
633
|
+
|
|
634
|
+
expected_thumbprint = cnf_claim.get("x5t#S256")
|
|
635
|
+
if not expected_thumbprint:
|
|
636
|
+
raise UserUnauthorized() # The 'cnf' claim does not contain the 'x5t#S256' field.
|
|
637
|
+
|
|
638
|
+
# 3. Compute the thumbprint of the certificate used for mTLS.
|
|
639
|
+
client_cert_thumbprint = self.get_cert_thumbprint()
|
|
640
|
+
|
|
641
|
+
# 4. Compare the thumbprints.
|
|
642
|
+
if client_cert_thumbprint != expected_thumbprint:
|
|
643
|
+
self.reload_fapi_cert()
|
|
644
|
+
client_cert_thumbprint = self.get_cert_thumbprint()
|
|
645
|
+
if client_cert_thumbprint != expected_thumbprint:
|
|
646
|
+
raise UserUnauthorized() # Access token 'cnf' claim does not match the client's certificate thumbprint.
|
|
647
|
+
|
|
578
648
|
sub = id_payload.get("sub")
|
|
579
649
|
|
|
580
650
|
account = None
|
|
@@ -659,6 +729,11 @@ WHERE
|
|
|
659
729
|
attributes=get_cookie_attrs(self.vigilant_sessions, False, self.is_container),
|
|
660
730
|
is_https=self.is_https)
|
|
661
731
|
|
|
732
|
+
response.set_cookie(COOKIE_LOGIN_MARKER, value="true", is_https=True, attributes=get_sloppy_cookie_attrs())
|
|
733
|
+
|
|
734
|
+
response.response_code = HTTPStatus.FOUND
|
|
735
|
+
response.raw_headers["Location"] = oidc_state[KEY__redirect_uri]
|
|
736
|
+
|
|
662
737
|
def fetch_redirect_uri(self, inputs: dict, response: JAAQLResponse):
|
|
663
738
|
schema = inputs.get(KEY__schema, None)
|
|
664
739
|
application = application__select(self.jaaql_lookup_connection, inputs[KEY__application])
|
|
@@ -686,10 +761,11 @@ WHERE
|
|
|
686
761
|
state = secrets.token_urlsafe(32)
|
|
687
762
|
code_verifier = secrets.token_urlsafe(64)
|
|
688
763
|
code_challenge = self.generate_code_challenge(code_verifier)
|
|
689
|
-
|
|
764
|
+
real_redirect_uri = application[KG__application__base_url] + "/" + inputs[KEY__redirect_uri]
|
|
765
|
+
oidc_redirect_uri = application[KG__application__base_url] + "/api" + ENDPOINT__oidc_get_token
|
|
690
766
|
|
|
691
767
|
oidc_session = crypt_utils.jwt_encode(self.vault.get_obj(VAULT_KEY__jwt_crypt_key), {
|
|
692
|
-
"redirect_uri":
|
|
768
|
+
"redirect_uri": real_redirect_uri,
|
|
693
769
|
"code_verifier": jaaql__encrypt(code_verifier, self.get_db_crypt_key()),
|
|
694
770
|
"nonce": nonce,
|
|
695
771
|
"state": state,
|
|
@@ -709,12 +785,61 @@ WHERE
|
|
|
709
785
|
if scope not in default_scopes:
|
|
710
786
|
default_scopes.append(scope)
|
|
711
787
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
788
|
+
par_endpoint = discovery.get("pushed_authorization_request_endpoint")
|
|
789
|
+
if not par_endpoint:
|
|
790
|
+
raise Exception("Pushed Authorization Request endpoint not found in discovery document.")
|
|
791
|
+
par_endpoint = par_endpoint.replace("localhost", "host.docker.internal")
|
|
792
|
+
|
|
793
|
+
par_payload = {
|
|
794
|
+
"client_id": client_id,
|
|
795
|
+
"response_type": "code",
|
|
796
|
+
"code_challenge_method": "S256",
|
|
797
|
+
"scope": " ".join(["openid"]), # should later be default scopes, may cause issues now
|
|
798
|
+
"nonce": nonce,
|
|
799
|
+
"state": state,
|
|
800
|
+
"code_challenge": code_challenge,
|
|
801
|
+
"redirect_uri": oidc_redirect_uri,
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
kwargs = {}
|
|
805
|
+
if self.use_fapi_advanced:
|
|
806
|
+
kwargs["verify"] = True
|
|
807
|
+
kwargs["cert"] = ("/tmp/client_cert.pem", "/tmp/client_key.pem")
|
|
808
|
+
else:
|
|
809
|
+
payload = {
|
|
810
|
+
"iss": database_user_registry[KG__database_user_registry__client_id],
|
|
811
|
+
"sub": database_user_registry[KG__database_user_registry__client_id],
|
|
812
|
+
"aud": discovery.get("pushed_authorization_request_endpoint"),
|
|
813
|
+
"jti": str(uuid.uuid4()),
|
|
814
|
+
"iat": int(time.time()),
|
|
815
|
+
"exp": int(time.time()) + 300 # Token valid for 5 minutes
|
|
816
|
+
}
|
|
817
|
+
par_payload["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
|
818
|
+
par_payload["client_assertion"] = jwt.encode(payload, self.fapi_pem, algorithm="PS256", headers={
|
|
819
|
+
"kid": self.jwks["keys"][0]["kid"]
|
|
820
|
+
})
|
|
821
|
+
|
|
822
|
+
par_response = self.idp_session.post(
|
|
823
|
+
par_endpoint,
|
|
824
|
+
data=par_payload,
|
|
825
|
+
**kwargs
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
if par_response.status_code not in (200, 201):
|
|
829
|
+
print(par_response.status_code)
|
|
830
|
+
print(par_response.text)
|
|
831
|
+
raise Exception(f"PAR request failed with status {par_response.status_code}: {par_response.text}")
|
|
832
|
+
|
|
833
|
+
par_data = par_response.json()
|
|
834
|
+
request_uri = par_data.get("request_uri")
|
|
835
|
+
if not request_uri:
|
|
836
|
+
raise Exception("No request_uri returned from the PAR endpoint.")
|
|
837
|
+
|
|
838
|
+
redirect_url = auth_endpoint + "?request_uri=" + urllib.parse.quote(request_uri) + "&response_mode=query.jwt&client_id=" + client_id
|
|
839
|
+
print(request_uri)
|
|
715
840
|
|
|
716
841
|
response.response_code = HTTPStatus.FOUND
|
|
717
|
-
response.raw_headers["Location"] =
|
|
842
|
+
response.raw_headers["Location"] = redirect_url
|
|
718
843
|
|
|
719
844
|
def set_web_config(self, connection: DBInterface):
|
|
720
845
|
self.is_super_admin(connection)
|
|
@@ -71,6 +71,7 @@ def pull_from_dict(self, inputs: dict, keys: Union[list, str, dict]):
|
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
COOKIE_JAAQL_AUTH = "jaaql_auth"
|
|
74
|
+
COOKIE_LOGIN_MARKER = "jaaql_successful_auth"
|
|
74
75
|
COOKIE_OIDC = "oidc"
|
|
75
76
|
COOKIE_FLAG_HTTP_ONLY = "HttpOnly"
|
|
76
77
|
COOKIE_FLAG_SECURE = "Secure"
|
|
@@ -99,8 +100,19 @@ def get_cookie_attrs(vigilant_sessions: bool, remember_me: bool, is_gunicorn: bo
|
|
|
99
100
|
return cookie_attrs
|
|
100
101
|
|
|
101
102
|
|
|
103
|
+
def get_sloppy_cookie_attrs():
|
|
104
|
+
cookie_attrs = {}
|
|
105
|
+
cookie_attrs[COOKIE_ATTR_PATH] = "/"
|
|
106
|
+
|
|
107
|
+
cookie_attrs[COOKIE_ATTR_EXPIRES] = format_date_time(mktime((datetime.now() + timedelta(minutes=15)).timetuple()))
|
|
108
|
+
|
|
109
|
+
return cookie_attrs
|
|
110
|
+
|
|
111
|
+
|
|
102
112
|
def format_cookie(name, value, attributes, is_https: bool):
|
|
103
113
|
cookie_flags = [COOKIE_FLAG_HTTP_ONLY]
|
|
114
|
+
if name == COOKIE_LOGIN_MARKER:
|
|
115
|
+
cookie_flags = []
|
|
104
116
|
if is_https:
|
|
105
117
|
cookie_flags.append(COOKIE_FLAG_SECURE)
|
|
106
118
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jaaql-middleware-python
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.27.0
|
|
4
4
|
Summary: The jaaql package, allowing for rapid development and deployment of RESTful HTTP applications
|
|
5
5
|
Home-page: https://github.com/JAAQL/JAAQL-middleware-python
|
|
6
6
|
Author: Software Quality Measurement and Improvement bv
|
|
@@ -8,7 +8,7 @@ Author-email: aaron.tasker@sqmi.nl
|
|
|
8
8
|
License: Mozilla Public License Version 2.0 with Commons Clause
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE.txt
|
|
11
|
-
Requires-Dist: jaaql-monitor~=1.
|
|
11
|
+
Requires-Dist: jaaql-monitor~=1.6.3
|
|
12
12
|
Requires-Dist: psycopg[binary]~=3.1.18
|
|
13
13
|
Requires-Dist: Pillow~=10.3.0
|
|
14
14
|
Requires-Dist: cryptography~=42.0.5
|
|
@@ -21,14 +21,15 @@ Requires-Dist: werkzeug~=3.0.1
|
|
|
21
21
|
Requires-Dist: argon2-cffi~=23.1.0
|
|
22
22
|
Requires-Dist: pyotp~=2.9.0
|
|
23
23
|
Requires-Dist: qrcode~=7.4.2
|
|
24
|
-
Requires-Dist: gunicorn~=
|
|
24
|
+
Requires-Dist: gunicorn~=23.0.0
|
|
25
25
|
Requires-Dist: gevent~=24.2.1
|
|
26
|
-
Requires-Dist: requests~=2.
|
|
26
|
+
Requires-Dist: requests~=2.32.3
|
|
27
27
|
Requires-Dist: pyzbar~=0.1.9
|
|
28
|
-
Requires-Dist: parsys-requests-unixsocket~=0.3.
|
|
28
|
+
Requires-Dist: parsys-requests-unixsocket~=0.3.2
|
|
29
29
|
Requires-Dist: selenium~=4.18.1
|
|
30
30
|
Requires-Dist: twine~=5.0.0
|
|
31
|
-
Requires-Dist: urllib3~=2.
|
|
31
|
+
Requires-Dist: urllib3~=2.3.0
|
|
32
|
+
Requires-Dist: jwcrypto~=1.5.6
|
|
32
33
|
|
|
33
34
|
# JAAQL-middleware-python
|
|
34
35
|
Please navigate to docker/docker.md to see setup instructions
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
jaaql-monitor~=1.
|
|
1
|
+
jaaql-monitor~=1.6.3
|
|
2
2
|
psycopg[binary]~=3.1.18
|
|
3
3
|
Pillow~=10.3.0
|
|
4
4
|
cryptography~=42.0.5
|
|
@@ -11,11 +11,12 @@ werkzeug~=3.0.1
|
|
|
11
11
|
argon2-cffi~=23.1.0
|
|
12
12
|
pyotp~=2.9.0
|
|
13
13
|
qrcode~=7.4.2
|
|
14
|
-
gunicorn~=
|
|
14
|
+
gunicorn~=23.0.0
|
|
15
15
|
gevent~=24.2.1
|
|
16
|
-
requests~=2.
|
|
16
|
+
requests~=2.32.3
|
|
17
17
|
pyzbar~=0.1.9
|
|
18
|
-
parsys-requests-unixsocket~=0.3.
|
|
18
|
+
parsys-requests-unixsocket~=0.3.2
|
|
19
19
|
selenium~=4.18.1
|
|
20
20
|
twine~=5.0.0
|
|
21
|
-
urllib3~=2.
|
|
21
|
+
urllib3~=2.3.0
|
|
22
|
+
jwcrypto~=1.5.6
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config/config-docker.ini
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/config/config-test.ini
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/db_pg_interface.py
RENAMED
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/db/db_utils_no_circ.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/documentation/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/email/email_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/exceptions/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/generated_constants.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/interpreter/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/migrations/__init__.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/migrations/migrations.py
RENAMED
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/controller_interface.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/exception_queries.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/generated_queries.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/handmade_queries.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/mvc/model_interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/services/patch_mms.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/__init__.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/crypt_utils.py
RENAMED
|
File without changes
|
{jaaql-middleware-python-4.26.2 → jaaql-middleware-python-4.27.0}/jaaql/utilities/options.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|