jaaql-middleware-python 4.33.7__tar.gz → 4.33.9__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.
Files changed (77) hide show
  1. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/PKG-INFO +1 -1
  2. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/constants.py +1 -1
  3. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/documentation/documentation_internal.py +53 -0
  4. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/interpreter/interpret_jaaql.py +4 -1
  5. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/controller.py +13 -0
  6. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/model.py +118 -0
  7. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql_middleware_python.egg-info/PKG-INFO +1 -1
  8. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/LICENSE.txt +0 -0
  9. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/README.md +0 -0
  10. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/__init__.py +0 -0
  11. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/config/__init__.py +0 -0
  12. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/config/config-docker.ini +0 -0
  13. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/config/config-test.ini +0 -0
  14. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/config/config.ini +0 -0
  15. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/config_constants.py +0 -0
  16. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/db/__init__.py +0 -0
  17. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/db/db_interface.py +0 -0
  18. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/db/db_pg_interface.py +0 -0
  19. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/db/db_utils.py +0 -0
  20. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/db/db_utils_no_circ.py +0 -0
  21. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/documentation/__init__.py +0 -0
  22. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/documentation/documentation_public.py +0 -0
  23. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/documentation/documentation_shared.py +0 -0
  24. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/email/__init__.py +0 -0
  25. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/email/email_manager.py +0 -0
  26. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/email/email_manager_service.py +0 -0
  27. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/email/patch_ems.py +0 -0
  28. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/exceptions/__init__.py +0 -0
  29. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/exceptions/custom_http_status.py +0 -0
  30. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/exceptions/http_status_exception.py +0 -0
  31. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/exceptions/jaaql_interpretable_handled_errors.py +0 -0
  32. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/exceptions/not_yet_implement_exception.py +0 -0
  33. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/generated_constants.py +0 -0
  34. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/interpreter/__init__.py +0 -0
  35. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/jaaql.py +0 -0
  36. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/migrations/__init__.py +0 -0
  37. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/migrations/migrations.py +0 -0
  38. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/__init__.py +0 -0
  39. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/base_controller.py +0 -0
  40. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/base_model.py +0 -0
  41. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/controller_interface.py +0 -0
  42. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/exception_queries.py +0 -0
  43. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/generated_queries.py +0 -0
  44. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/handmade_queries.py +0 -0
  45. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/model_interface.py +0 -0
  46. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/mvc/response.py +0 -0
  47. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/openapi/__init__.py +0 -0
  48. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/openapi/swagger_documentation.py +0 -0
  49. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/patch.py +0 -0
  50. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/01.install_domains.generated.sql +0 -0
  51. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/02.install_super_user.exceptions.sql +0 -0
  52. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/03.install_super_user.handwritten.sql +0 -0
  53. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/04.install_jaaql_data_structures.generated.sql +0 -0
  54. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/05.install_static_data.generated.sql +0 -0
  55. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/06.install_jaaql.exceptions.sql +0 -0
  56. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/ZZZZ.generated_functions_views_and_permissions.sql +0 -0
  57. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/ZZZZ.reset_references.sql +0 -0
  58. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/scripts/swagger_template.html +0 -0
  59. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/services/__init__.py +0 -0
  60. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/services/cached_canned_query_service.py +0 -0
  61. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/services/migrations_manager_service.py +0 -0
  62. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/services/patch_mms.py +0 -0
  63. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/services/patch_shared_var_service.py +0 -0
  64. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/services/shared_var_service.py +0 -0
  65. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/__init__.py +0 -0
  66. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/cron.py +0 -0
  67. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/crypt_utils.py +0 -0
  68. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/options.py +0 -0
  69. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/utils.py +0 -0
  70. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/utils_no_project_imports.py +0 -0
  71. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql/utilities/vault.py +0 -0
  72. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql_middleware_python.egg-info/SOURCES.txt +0 -0
  73. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql_middleware_python.egg-info/dependency_links.txt +0 -0
  74. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql_middleware_python.egg-info/requires.txt +0 -0
  75. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/jaaql_middleware_python.egg-info/top_level.txt +0 -0
  76. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/setup.cfg +0 -0
  77. {jaaql_middleware_python-4.33.7 → jaaql_middleware_python-4.33.9}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaaql-middleware-python
3
- Version: 4.33.7
3
+ Version: 4.33.9
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
@@ -208,5 +208,5 @@ ROLE__postgres = "postgres"
208
208
 
209
209
  PROTOCOL__postgres = "postgresql://"
210
210
 
211
- VERSION = "4.33.7"
211
+ VERSION = "4.33.9"
212
212
 
@@ -478,4 +478,57 @@ DOCUMENTATION__procedures = SwaggerDocumentation(
478
478
  )
479
479
  ]
480
480
  )
481
+ )
482
+
483
+ ARG_RES__id_token_hint = SwaggerArgumentResponse(
484
+ name="id_token_hint",
485
+ description="Optional ID Token previously issued to the user; recommended for OIDC RP-initiated logout.",
486
+ arg_type=str,
487
+ required=False,
488
+ condition="Recommended",
489
+ example=["eyJ..."]
490
+ )
491
+
492
+ DOCUMENTATION__oidc_begin_logout = SwaggerDocumentation(
493
+ tags="oidc",
494
+ security=False,
495
+ methods=SwaggerMethod(
496
+ name="Begin OIDC logout",
497
+ description="Clears the app session, sets a logout state cookie, and redirects the browser to the OP end-session endpoint. After logout, the OP redirects to /api/oidc/post-logout.",
498
+ method=REST__GET,
499
+ arguments=[
500
+ ARG_RES__tenant,
501
+ ARG_RES__provider,
502
+ ARG_RES__application,
503
+ ARG_RES__schema,
504
+ ARG_RES__redirect_uri,
505
+ ARG_RES__id_token_hint
506
+ ],
507
+ response=SwaggerFlatResponse(
508
+ description="Redirect",
509
+ code=HTTPStatus.FOUND,
510
+ body="You are being redirected to the identity server for logout..."
511
+ )
512
+ )
513
+ )
514
+
515
+ DOCUMENTATION__oidc_post_logout = SwaggerDocumentation(
516
+ tags="oidc",
517
+ security=False,
518
+ methods=SwaggerMethod(
519
+ name="OIDC post-logout redirect",
520
+ description="Receives the OP redirect after logout, validates state, clears the cookie, and redirects to the final app URL.",
521
+ method=REST__GET,
522
+ arguments=SwaggerArgumentResponse(
523
+ name="state",
524
+ description="Opaque state returned by the OP; must match the value set at logout start.",
525
+ arg_type=str,
526
+ example=["y3nvJ..."]
527
+ ),
528
+ response=SwaggerFlatResponse(
529
+ description="Redirect",
530
+ code=HTTPStatus.FOUND,
531
+ body="You are being redirected back to your place in the app..."
532
+ )
533
+ )
481
534
  )
@@ -2,6 +2,7 @@ import string
2
2
  import traceback
3
3
  import random
4
4
 
5
+ from decimal import Decimal
5
6
  from jaaql.exceptions.http_status_exception import *
6
7
  from datetime import datetime
7
8
  import re
@@ -28,7 +29,7 @@ ERR_missing_query = "Missing query key from input dictionary"
28
29
  ERR_unused_parameter = "Unused parameter! Supplied with '%s' but was never used"
29
30
  ERR_polluted_input = "Input polluted. If passing query, only pass parameters as well"
30
31
  ERR_malformed_query = "Value of 'query' key malformed. Please use a string if you are passing a value to 'query'"
31
- ERR_mistyped_parameter = "Type '%s', for parameter '%s' unexpected. Please provide either a date, string, float, list or integer"
32
+ ERR_mistyped_parameter = "Type '%s', for parameter '%s' unexpected. Please provide either a date, string, float, list, Decimal or integer"
32
33
  ERR_malformed_operation_type = "Operation malformed. Expecting either a list, string or dictionary input"
33
34
  ERR_assert_expecting = "Assert expecting %s row(s) but received %d row(s)!"
34
35
  ERR_malformed_join = "Joins only allowed as list or string input at the moment!"
@@ -613,6 +614,8 @@ GROUP BY CH.column_name, CH.data_type, CH.udt_name, CH.domain_name;"""
613
614
  return PYFORMAT_str
614
615
  elif type(value).__name__ == "date":
615
616
  return PYFORMAT_str
617
+ elif isinstance(value, Decimal):
618
+ return PYFORMAT_str
616
619
  else:
617
620
  raise HttpStatusException(ERR_mistyped_parameter % (type(value).__name__, key), HTTPStatus.BAD_REQUEST)
618
621
 
@@ -201,3 +201,16 @@ class JAAQLController(BaseJAAQLController):
201
201
  @self.publish_route('/rendered_documents', DOCUMENTATION__rendered_document)
202
202
  def documents(http_inputs: dict):
203
203
  return self.model.fetch_document_stream(http_inputs)
204
+
205
+ @self.publish_route('/oidc/logout', DOCUMENTATION__oidc_begin_logout)
206
+ def begin_oidc_logout(http_inputs: dict, response: JAAQLResponse):
207
+ # inputs: application, tenant, provider, optional schema, redirect_uri, optional id_token_hint
208
+ self.model.begin_oidc_logout(http_inputs, response)
209
+
210
+ @self.publish_route('/oidc/post-logout', DOCUMENTATION__oidc_post_logout)
211
+ def finish_oidc_logout(http_inputs: dict, response: JAAQLResponse):
212
+ # KC returns ?state=... here; validate against cookie and bounce to final app URL
213
+ self.model.finish_oidc_logout(
214
+ {"state": http_inputs["state"], "oidc_cookie": request.cookies.get(COOKIE_OIDC)},
215
+ response
216
+ )
@@ -1987,3 +1987,121 @@ WHERE
1987
1987
 
1988
1988
  return submit(self.vault, self.config, self.get_db_crypt_key(), self.jaaql_lookup_connection, inputs, account_id, verification_hook,
1989
1989
  self.cached_canned_query_service, as_objects=as_objects, singleton=singleton)
1990
+
1991
+
1992
+ def begin_oidc_logout(self, inputs: dict, response: JAAQLResponse):
1993
+ """
1994
+ Start RP-initiated logout: clear app cookies, set logout state cookie, and 302 to OP end-session.
1995
+ inputs requires:
1996
+ - KEY__application
1997
+ - KG__user_registry__provider
1998
+ - KG__user_registry__tenant
1999
+ - KEY__redirect_uri (final place in your app after logout, e.g. "logged-out" or "")
2000
+ - optional: "id_token_hint" (if you persisted the ID token)
2001
+ """
2002
+ # Resolve schema & app
2003
+ schema = inputs.get(KEY__schema)
2004
+ application = application__select(self.jaaql_lookup_connection, inputs[KEY__application])
2005
+ if not schema:
2006
+ schema = application[KG__application__default_schema]
2007
+
2008
+ # Resolve registry and discovery
2009
+ database = application_schema__select(self.jaaql_lookup_connection, inputs[KEY__application], schema)
2010
+ user_registry = user_registry__select(self.jaaql_lookup_connection, inputs[KG__user_registry__provider], inputs[KG__user_registry__tenant])
2011
+ db_user_registry = database_user_registry__select(
2012
+ self.jaaql_lookup_connection, self.get_db_crypt_key(),
2013
+ inputs[KG__user_registry__provider], inputs[KG__user_registry__tenant],
2014
+ database[KG__application_schema__database]
2015
+ )
2016
+
2017
+ discovery = self.fetch_discovery_content(
2018
+ database[KG__application_schema__database],
2019
+ inputs[KG__user_registry__provider],
2020
+ inputs[KG__user_registry__tenant],
2021
+ user_registry[KG__user_registry__discovery_url]
2022
+ )
2023
+
2024
+ end_session = discovery.get("end_session_endpoint")
2025
+ if not end_session:
2026
+ # Fallback for older KC if discovery is missing the field
2027
+ issuer = discovery.get("issuer", "").rstrip("/")
2028
+ end_session = issuer + "/protocol/openid-connect/logout"
2029
+
2030
+ # Final hop inside YOUR app after KC returns
2031
+ final_app_redirect = application[KG__application__base_url] + "/" + (inputs.get(KEY__redirect_uri) or "")
2032
+ # The URL that KC will call after it logs the user out (your handler)
2033
+ post_logout = application[KG__application__base_url] + "/api/oidc/post-logout"
2034
+
2035
+ # Store the logout round-trip state (re-using your OIDC cookie machinery)
2036
+ state = secrets.token_urlsafe(32)
2037
+ logout_session = crypt_utils.jwt_encode(
2038
+ self.vault.get_obj(VAULT_KEY__jwt_crypt_key),
2039
+ {
2040
+ "redirect_uri": final_app_redirect,
2041
+ "state": state,
2042
+ "tenant": inputs[KG__user_registry__tenant],
2043
+ "provider": inputs[KG__user_registry__provider],
2044
+ "application": inputs[KEY__application],
2045
+ "schema": schema
2046
+ },
2047
+ JWT_PURPOSE__oidc,
2048
+ expiry_ms=self.oidc_login_expiry_ms
2049
+ )
2050
+
2051
+ response.set_cookie(
2052
+ COOKIE_OIDC,
2053
+ value=logout_session,
2054
+ attributes=get_cookie_attrs(True, False, self.is_container),
2055
+ is_https=self.is_https
2056
+ )
2057
+
2058
+ # Kill your own app session immediately
2059
+ response.delete_cookie(COOKIE_JAAQL_AUTH, self.is_https)
2060
+ # Optional: clear the UI helper marker
2061
+ response.set_cookie(COOKIE_LOGIN_MARKER, value="", is_https=True, attributes=get_sloppy_cookie_attrs())
2062
+
2063
+ # Build front-channel logout URL (Keycloak requires either id_token_hint OR client_id with post_logout_redirect_uri)
2064
+ url = urllib.parse.urlsplit(end_session)
2065
+ q = dict(urllib.parse.parse_qsl(url.query, keep_blank_values=True))
2066
+
2067
+ q["post_logout_redirect_uri"] = post_logout
2068
+ q["state"] = state
2069
+
2070
+ id_token_hint = inputs.get("id_token_hint")
2071
+ if id_token_hint:
2072
+ q["id_token_hint"] = id_token_hint
2073
+ else:
2074
+ q["client_id"] = db_user_registry[KG__database_user_registry__client_id] # KC >= 18 accepts this fallback
2075
+
2076
+ logout_url = urllib.parse.urlunsplit((
2077
+ url.scheme, url.netloc, url.path,
2078
+ urllib.parse.urlencode(q, doseq=False, safe="/:"), url.fragment
2079
+ ))
2080
+
2081
+ response.response_code = HTTPStatus.FOUND
2082
+ response.raw_headers["Location"] = logout_url
2083
+
2084
+ def finish_oidc_logout(self, inputs: dict, response: JAAQLResponse):
2085
+ """
2086
+ Handle the OP redirect after logout. Validates state, clears OIDC cookie, and 302 to your final URL.
2087
+ Expect 'state' in inputs (query param).
2088
+ """
2089
+ # Read & clear the stored logout session
2090
+ oidc_cookie = inputs.get("oidc_cookie") # if your routing passes cookies in inputs; otherwise fetch from request in your framework
2091
+ if not oidc_cookie:
2092
+ # fall back to reading directly if your framework wires cookies differently
2093
+ raise HttpStatusException("Missing logout state", HTTPStatus.BAD_REQUEST)
2094
+
2095
+ logout_state = crypt_utils.jwt_decode(self.vault.get_obj(VAULT_KEY__jwt_crypt_key), oidc_cookie, JWT_PURPOSE__oidc)
2096
+ if not logout_state:
2097
+ raise HttpStatusException("Invalid logout state", HTTPStatus.BAD_REQUEST)
2098
+
2099
+ if inputs.get("state") != logout_state.get("state"):
2100
+ raise HttpStatusException("Logout state mismatch", HTTPStatus.BAD_REQUEST)
2101
+
2102
+ # Remove the OIDC cookie now that we're done
2103
+ response.delete_cookie(COOKIE_OIDC, self.is_https)
2104
+
2105
+ # Redirect to the place the app wanted to land after logout
2106
+ response.response_code = HTTPStatus.FOUND
2107
+ response.raw_headers["Location"] = logout_state["redirect_uri"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaaql-middleware-python
3
- Version: 4.33.7
3
+ Version: 4.33.9
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