jaaql-middleware-python 4.33.8__tar.gz → 4.33.10__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.8 → jaaql_middleware_python-4.33.10}/PKG-INFO +1 -1
  2. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/constants.py +1 -1
  3. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/db/db_utils.py +4 -1
  4. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/documentation/documentation_internal.py +89 -0
  5. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/documentation/documentation_public.py +0 -234
  6. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/exceptions/http_status_exception.py +3 -1
  7. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/generated_constants.py +1 -1
  8. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/interpreter/interpret_jaaql.py +0 -2
  9. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/controller.py +30 -27
  10. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/exception_queries.py +5 -55
  11. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/generated_queries.py +71 -146
  12. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/model.py +365 -436
  13. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/01.install_domains.generated.sql +2 -5
  14. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/04.install_jaaql_data_structures.generated.sql +6 -23
  15. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/05.install_static_data.generated.sql +6 -2
  16. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/ZZZZ.generated_functions_views_and_permissions.sql +419 -965
  17. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/ZZZZ.reset_references.sql +101 -120
  18. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql_middleware_python.egg-info/PKG-INFO +1 -1
  19. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/LICENSE.txt +0 -0
  20. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/README.md +0 -0
  21. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/__init__.py +0 -0
  22. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/config/__init__.py +0 -0
  23. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/config/config-docker.ini +0 -0
  24. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/config/config-test.ini +0 -0
  25. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/config/config.ini +0 -0
  26. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/config_constants.py +0 -0
  27. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/db/__init__.py +0 -0
  28. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/db/db_interface.py +0 -0
  29. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/db/db_pg_interface.py +0 -0
  30. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/db/db_utils_no_circ.py +0 -0
  31. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/documentation/__init__.py +0 -0
  32. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/documentation/documentation_shared.py +0 -0
  33. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/email/__init__.py +0 -0
  34. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/email/email_manager.py +0 -0
  35. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/email/email_manager_service.py +0 -0
  36. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/email/patch_ems.py +0 -0
  37. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/exceptions/__init__.py +0 -0
  38. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/exceptions/custom_http_status.py +0 -0
  39. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/exceptions/jaaql_interpretable_handled_errors.py +0 -0
  40. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/exceptions/not_yet_implement_exception.py +0 -0
  41. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/interpreter/__init__.py +0 -0
  42. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/jaaql.py +0 -0
  43. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/migrations/__init__.py +0 -0
  44. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/migrations/migrations.py +0 -0
  45. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/__init__.py +0 -0
  46. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/base_controller.py +0 -0
  47. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/base_model.py +0 -0
  48. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/controller_interface.py +0 -0
  49. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/handmade_queries.py +0 -0
  50. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/model_interface.py +0 -0
  51. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/mvc/response.py +0 -0
  52. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/openapi/__init__.py +0 -0
  53. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/openapi/swagger_documentation.py +0 -0
  54. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/patch.py +0 -0
  55. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/02.install_super_user.exceptions.sql +0 -0
  56. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/03.install_super_user.handwritten.sql +0 -0
  57. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/06.install_jaaql.exceptions.sql +0 -0
  58. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/scripts/swagger_template.html +0 -0
  59. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/services/__init__.py +0 -0
  60. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/services/cached_canned_query_service.py +0 -0
  61. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/services/migrations_manager_service.py +0 -0
  62. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/services/patch_mms.py +0 -0
  63. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/services/patch_shared_var_service.py +0 -0
  64. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/services/shared_var_service.py +0 -0
  65. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/__init__.py +0 -0
  66. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/cron.py +0 -0
  67. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/crypt_utils.py +0 -0
  68. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/options.py +0 -0
  69. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/utils.py +0 -0
  70. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/utils_no_project_imports.py +0 -0
  71. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql/utilities/vault.py +0 -0
  72. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql_middleware_python.egg-info/SOURCES.txt +0 -0
  73. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql_middleware_python.egg-info/dependency_links.txt +0 -0
  74. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql_middleware_python.egg-info/requires.txt +0 -0
  75. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/jaaql_middleware_python.egg-info/top_level.txt +0 -0
  76. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/setup.cfg +0 -0
  77. {jaaql_middleware_python-4.33.8 → jaaql_middleware_python-4.33.10}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaaql-middleware-python
3
- Version: 4.33.8
3
+ Version: 4.33.10
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.8"
211
+ VERSION = "4.33.10"
212
212
 
@@ -116,11 +116,14 @@ def execute_supplied_statement(db_interface, query: str, parameters: dict = None
116
116
 
117
117
  def force_singleton(data, as_objects: bool = False, singleton_code: int = None, singleton_message: str = None):
118
118
  was_no_singleton = False
119
+ original_count = 1
119
120
  if as_objects:
120
121
  if len(data) != 1:
122
+ original_count = len(data)
121
123
  was_no_singleton = True
122
124
  else:
123
125
  if len(data["rows"]) != 1:
126
+ original_count = len(data["rows"])
124
127
  was_no_singleton = True
125
128
  if len(data["rows"]) != 0:
126
129
  data["rows"] = data["rows"][0]
@@ -129,7 +132,7 @@ def force_singleton(data, as_objects: bool = False, singleton_code: int = None,
129
132
  err = ERR__expected_single_row % len(data)
130
133
  if singleton_message is not None:
131
134
  err = singleton_message
132
- raise HttpSingletonStatusException(err, singleton_code)
135
+ raise HttpSingletonStatusException(err, singleton_code, original_count)
133
136
 
134
137
  return data[0] if as_objects else data
135
138
 
@@ -1,3 +1,4 @@
1
+ from jaaql.documentation.documentation_public import ARG_RES__query
1
2
  from jaaql.openapi.swagger_documentation import *
2
3
  from jaaql.constants import *
3
4
  from jaaql.mvc.generated_queries import *
@@ -478,4 +479,92 @@ DOCUMENTATION__procedures = SwaggerDocumentation(
478
479
  )
479
480
  ]
480
481
  )
482
+ )
483
+
484
+ ARG_RES__id_token_hint = SwaggerArgumentResponse(
485
+ name="id_token_hint",
486
+ description="Optional ID Token previously issued to the user; recommended for OIDC RP-initiated logout.",
487
+ arg_type=str,
488
+ required=False,
489
+ condition="Recommended",
490
+ example=["eyJ..."]
491
+ )
492
+
493
+ DOCUMENTATION__oidc_begin_logout = SwaggerDocumentation(
494
+ tags="oidc",
495
+ security=False,
496
+ methods=SwaggerMethod(
497
+ name="Begin OIDC logout",
498
+ 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.",
499
+ method=REST__GET,
500
+ arguments=[
501
+ ARG_RES__tenant,
502
+ ARG_RES__provider,
503
+ ARG_RES__application,
504
+ ARG_RES__schema,
505
+ ARG_RES__redirect_uri,
506
+ ARG_RES__id_token_hint
507
+ ],
508
+ response=SwaggerFlatResponse(
509
+ description="Redirect",
510
+ code=HTTPStatus.FOUND,
511
+ body="You are being redirected to the identity server for logout..."
512
+ )
513
+ )
514
+ )
515
+
516
+ DOCUMENTATION__oidc_post_logout = SwaggerDocumentation(
517
+ tags="oidc",
518
+ security=False,
519
+ methods=SwaggerMethod(
520
+ name="OIDC post-logout redirect",
521
+ description="Receives the OP redirect after logout, validates state, clears the cookie, and redirects to the final app URL.",
522
+ method=REST__GET,
523
+ arguments=SwaggerArgumentResponse(
524
+ name="state",
525
+ description="Opaque state returned by the OP; must match the value set at logout start.",
526
+ arg_type=str,
527
+ example=["y3nvJ..."]
528
+ ),
529
+ response=SwaggerFlatResponse(
530
+ description="Redirect",
531
+ code=HTTPStatus.FOUND,
532
+ body="You are being redirected back to your place in the app..."
533
+ )
534
+ )
535
+ )
536
+
537
+ DOCUMENTATION__security_event = SwaggerDocumentation(
538
+ tags="security",
539
+ methods=SwaggerMethod(
540
+ name="Action security event",
541
+ description="Executes a security event",
542
+ method=REST__POST,
543
+ body=[
544
+ ARG_RES__application,
545
+ SwaggerArgumentResponse(
546
+ name=KG__security_event__name,
547
+ description="The name of the security event",
548
+ arg_type=str,
549
+ example=["add-user"]
550
+ ),
551
+ SwaggerArgumentResponse(
552
+ name=KG__security_event__type,
553
+ description="The name of the security event",
554
+ arg_type=str,
555
+ example=["add-user"]
556
+ ),
557
+ SwaggerArgumentResponse(
558
+ name=KEY__parameters,
559
+ description="Any parameters to pass to the underlying function",
560
+ arg_type=ARG_RESP__allow_all
561
+ ),
562
+ SwaggerArgumentResponse(
563
+ name="explicit_types",
564
+ description="Explicit types for parameters",
565
+ arg_type=ARG_RESP__allow_all
566
+ )
567
+ ],
568
+ response=RES__allow_all
569
+ )
481
570
  )
@@ -76,20 +76,6 @@ DOCUMENTATION__create_account_batch = SwaggerDocumentation(
76
76
  )
77
77
  )
78
78
 
79
- DOCUMENTATION__password = SwaggerDocumentation(
80
- tags="Account",
81
- methods=SwaggerMethod(
82
- method=REST__POST,
83
- name="Change password",
84
- description="Will change the logged in users password to the new one",
85
- body=[
86
- rename_arg(ARG_RES__password, KEY__old_password, "The old password for the user"),
87
- ARG_RES__password
88
- ],
89
- response=RES__oauth_token
90
- )
91
- )
92
-
93
79
  ARG_RES__event_application = SwaggerArgumentResponse(
94
80
  name=KG__security_event__application,
95
81
  description="The application of the dispatcher",
@@ -112,31 +98,6 @@ ARG_RES__query = SwaggerArgumentResponse(
112
98
  required=True
113
99
  )
114
100
 
115
- ARG_RES__event_lock = SwaggerArgumentResponse(
116
- name=KG__security_event__event_lock,
117
- description="A key that is used to reference a security event",
118
- arg_type=str,
119
- example="0aff271e-bf0a-463b-b234-f558abd70edd"
120
- )
121
-
122
- ARG_RES__security_event_unlock_key = SwaggerArgumentResponse(
123
- name=KG__security_event__unlock_key,
124
- description="The unlock key",
125
- required=False,
126
- condition="Are you using a key to unlock or a code",
127
- arg_type=str,
128
- example="c9a941d5-2aea-4b96-967b-32a01e889d03"
129
- )
130
-
131
- ARG_RES__security_event_unlock_code = SwaggerArgumentResponse(
132
- name=KG__security_event__unlock_code,
133
- description="The unlock code",
134
- required=False,
135
- condition="Are you using a key to unlock or a code",
136
- arg_type=str,
137
- example="ABC123"
138
- )
139
-
140
101
  ARG_RES__oauth_token = SwaggerArgumentResponse(
141
102
  name=KEY__oauth_token,
142
103
  description="An auth token",
@@ -144,51 +105,6 @@ ARG_RES__oauth_token = SwaggerArgumentResponse(
144
105
  example=EXAMPLE__jwt
145
106
  )
146
107
 
147
- DOCUMENTATION__reset_password = SwaggerDocumentation(
148
- tags="Reset Password",
149
- security=False,
150
- methods=[
151
- SwaggerMethod(
152
- name="Request an password reset",
153
- description="Requests a password reset, sending an email to the user (or if the email template does not contain "
154
- "the reset code, they will then need to be approved by an admin).",
155
- method=REST__POST,
156
- body=[
157
- ARG_RES__username,
158
- ARG_RES__parameters,
159
- ARG_RES__event_application,
160
- SwaggerArgumentResponse(
161
- name=KEY__reset_password_template,
162
- description="The template to send if the user is already signed up",
163
- required=False,
164
- condition="Should an email be sent or the default be used",
165
- arg_type=str,
166
- example=KG__application__default_r_et
167
- ),
168
- SwaggerArgumentResponse(
169
- name=KEY__unregistered_user_reset_password_template,
170
- description="The template to send if the user hasn't signed up",
171
- required=False,
172
- condition="Should an email be sent or the default be used",
173
- arg_type=str,
174
- example=KG__application__default_u_et
175
- )
176
- ],
177
- response=[
178
- SwaggerResponse(
179
- description="Reset password response",
180
- response=ARG_RES__event_lock
181
- ),
182
- SwaggerFlatResponse(
183
- description=ERR__too_many_reset_requests,
184
- code=HTTPStatus.TOO_MANY_REQUESTS,
185
- body=ERR__too_many_reset_requests
186
- )
187
- ]
188
- )
189
- ]
190
- )
191
-
192
108
  DOCUMENTATION__emails = SwaggerDocumentation(
193
109
  tags="Emails",
194
110
  security=True,
@@ -209,156 +125,6 @@ DOCUMENTATION__emails = SwaggerDocumentation(
209
125
  )
210
126
  )
211
127
 
212
- DOCUMENTATION__sign_up = SwaggerDocumentation(
213
- tags="sign_up",
214
- # Explicitly set sign up to true to enforce design decision. We use the attempted insert of the current user (which can be the public user) as security
215
- # If the insert fails then the user cannot sign up
216
- security=False,
217
- methods=[
218
- SwaggerMethod(
219
- name="Signs up to the Platform",
220
- description="Requests an invitation, sending an email to the user.",
221
- method=REST__POST,
222
- body=[
223
- ARG_RES__username,
224
- set_nullable(ARG_RES__password, "Is the user going through an unconfirmed signup"),
225
- ARG_RES__parameters,
226
- ARG_RES__remember_me,
227
- ARG_RES__query,
228
- ARG_RES__event_application,
229
- SwaggerArgumentResponse(
230
- name=KEY__sign_up_template,
231
- description="The template to send if the user hasn't signed up",
232
- required=False,
233
- condition="Should an email be sent or the default be used",
234
- arg_type=str,
235
- example=KG__application__default_s_et
236
- ),
237
- SwaggerArgumentResponse(
238
- name=KEY__already_signed_up_template,
239
- description="The template to send if the user is already signed up",
240
- required=False,
241
- condition="Should an email be sent or the default be used",
242
- arg_type=str,
243
- example=KG__application__default_a_et
244
- )
245
- ],
246
- response=RES__allow_all
247
- )
248
- ]
249
- )
250
-
251
- DOCUMENTATION__resend_sign_up = SwaggerDocumentation(
252
- tags="sign_up",
253
- security=True,
254
- methods=[
255
- SwaggerMethod(
256
- name="Resend sign up email",
257
- description="Allows an unconfirmed user to resend themselves a signup email when they are logged in.",
258
- method=REST__POST,
259
- body=[
260
- ARG_RES__event_application,
261
- SwaggerArgumentResponse(
262
- name=KEY__sign_up_template,
263
- description="The template to send if the user hasn't signed up",
264
- required=False,
265
- condition="Should an email be sent or the default be used",
266
- arg_type=str,
267
- example=KG__application__default_s_et
268
- ),
269
- SwaggerArgumentResponse(
270
- name=KEY__already_signed_up_template,
271
- description="The template to send if the user is already signed up",
272
- required=False,
273
- condition="Should an email be sent or the default be used",
274
- arg_type=str,
275
- example=KG__application__default_a_et
276
- )
277
- ],
278
- response=RES__allow_all
279
- )
280
- ]
281
- )
282
-
283
- DOCUMENTATION__invite = SwaggerDocumentation(
284
- tags="invite",
285
- # Explicitly set sign up to true to enforce design decision. We use the attempted insert of the current user (which can be the public user) as security
286
- # If the insert fails then the user cannot sign up
287
- security=True,
288
- methods=[
289
- SwaggerMethod(
290
- name="Request an invitation",
291
- description="Requests an invitation, sending an email to the user (or if the email template does not contain "
292
- "the unlock code, they will then need to be approved by an admin).",
293
- method=REST__POST,
294
- body=[
295
- set_nullable(ARG_RES__username, "Potentially if the user is not known"),
296
- ARG_RES__parameters,
297
- ARG_RES__event_application,
298
- SwaggerArgumentResponse(
299
- name=KEY__sign_up_template,
300
- description="The template to send if the user hasn't signed up",
301
- required=False,
302
- condition="Should an email be sent or the default be used",
303
- arg_type=str,
304
- example=KG__application__default_s_et
305
- ),
306
- SwaggerArgumentResponse(
307
- name=KEY__already_signed_up_template,
308
- description="The template to send if the user is already signed up",
309
- required=False,
310
- condition="Should an email be sent or the default be used",
311
- arg_type=str,
312
- example=KG__application__default_a_et
313
- )
314
- ],
315
- response=RES__allow_all
316
- )
317
- ]
318
- )
319
-
320
- from jaaql.mvc.handmade_queries import EMAIL_TYPE__signup
321
-
322
- DOCUMENTATION__security_event = SwaggerDocumentation(
323
- tags="Security Event",
324
- security=False,
325
- methods=[
326
- SwaggerMethod(
327
- name="Check Event and Key",
328
- description="Checks a security event is unused and that the key is valid",
329
- method=REST__POST,
330
- body=[
331
- set_nullable(ARG_RES__event_lock, "Just the unlock key can be provided"),
332
- ARG_RES__security_event_unlock_key,
333
- ARG_RES__security_event_unlock_code
334
- ],
335
- response=SwaggerFlatResponse(
336
- description="Will receive the type of the description if unlock code is correct",
337
- body=EMAIL_TYPE__signup
338
- )
339
- ),
340
- SwaggerMethod(
341
- name="Finish Security Event",
342
- description="Finishes the security event, supplying a password which can either be a new password or your login password, if using the "
343
- "already signed up flow",
344
- method=REST__PUT,
345
- body=[
346
- set_nullable(ARG_RES__event_lock, "Just the unlock key can be provided"),
347
- ARG_RES__security_event_unlock_key,
348
- ARG_RES__security_event_unlock_code,
349
- set_nullable(ARG_RES__password, "only if it's required")
350
- ],
351
- response=SwaggerResponse(
352
- description="Signup parameters",
353
- response=[
354
- ARG_RES__parameters,
355
- ARG_RES__username
356
- ]
357
- )
358
- )
359
- ]
360
- )
361
-
362
128
  ARG_RES__proc = [
363
129
  SwaggerArgumentResponse(
364
130
  name=KG__remote_procedure__application,
@@ -28,9 +28,11 @@ class HttpStatusException(Exception):
28
28
 
29
29
 
30
30
  class HttpSingletonStatusException(HttpStatusException):
31
- def __init__(self, message: str, response_code: int = HTTPStatus.UNPROCESSABLE_ENTITY):
31
+ def __init__(self, message: str, response_code: int = HTTPStatus.UNPROCESSABLE_ENTITY, actual_count: int = 1):
32
32
  super().__init__(message, response_code)
33
33
 
34
+ self.actual_count = actual_count
35
+
34
36
 
35
37
  class JaaqlInterpretableHandledError(Exception):
36
38
  def __init__(self, error_code: int, http_response_code: int,
@@ -268,7 +268,7 @@ class Constraints(Enum):
268
268
  DatabaseUserRegistryProviderTenantDatabaseKey = "database_user_registry_provider_tenant_database_key"
269
269
  AccountIdKey = "account_id_key"
270
270
  ValidatedIpAddressUuidKey = "validated_ip_address_uuid_key"
271
- SecurityEventApplicationEventLockKey = "security_event_application_event_lock_key"
271
+ SecurityEventApplicationNameTypeKey = "security_event_application_name_type_key"
272
272
  HandledErrorCodeKey = "handled_error_code_key"
273
273
  PgBaseExceptionNameKey = "pg_base_exception_name_key"
274
274
  PgErrorClassCodeKey = "pg_error_class_code_key"
@@ -612,8 +612,6 @@ GROUP BY CH.column_name, CH.data_type, CH.udt_name, CH.domain_name;"""
612
612
  return PYFORMAT_str
613
613
  elif isinstance(value, list):
614
614
  return PYFORMAT_str
615
- elif isinstance(value, list):
616
- return PYFORMAT_str
617
615
  elif type(value).__name__ == "date":
618
616
  return PYFORMAT_str
619
617
  elif isinstance(value, Decimal):
@@ -1,3 +1,5 @@
1
+ from jaaql.mvc.exception_queries import SECURITY_EVENT_TYPE__create, SECURITY_EVENT_TYPE__delete, \
2
+ SECURITY_EVENT_TYPE__reset
1
3
  from jaaql.mvc.model import JAAQLModel
2
4
  from jaaql.mvc.base_controller import BaseJAAQLController
3
5
  from jaaql.documentation.documentation_internal import *
@@ -93,10 +95,6 @@ class JAAQLController(BaseJAAQLController):
93
95
  def prepare(http_inputs: dict, account_id: str):
94
96
  return self.model.fetch_domains(http_inputs, account_id)
95
97
 
96
- @self.publish_route('/account/password', DOCUMENTATION__password)
97
- def password(account_id: str, username: str, ip_address: str, is_the_anonymous_user: bool, http_inputs: dict):
98
- return self.model.add_my_account_password(account_id, username, ip_address, is_the_anonymous_user, **http_inputs)
99
-
100
98
  @self.publish_route('/submit', DOCUMENTATION__submit)
101
99
  def submit(http_inputs: dict, account_id: str, verification_hook: queue.Queue, ip_address: str):
102
100
  return self.model.submit(http_inputs, account_id, verification_hook=verification_hook, ip_address=ip_address)
@@ -113,37 +111,29 @@ class JAAQLController(BaseJAAQLController):
113
111
  def clean(connection: DBInterface):
114
112
  self.model.clean(connection)
115
113
 
114
+ @self.publish_route('/security-event', DOCUMENTATION__security_event)
115
+ def security_event(http_inputs: dict, account_id: str):
116
+ security_event = security_event__select(self.model.jaaql_lookup_connection, http_inputs[KEY__application],
117
+ name=http_inputs[KG__security_event__name],
118
+ type=http_inputs[KG__security_event__type])
119
+ http_inputs["email"] = http_inputs[KEY__parameters]["email"]
120
+ if security_event[KG__security_event__type] == SECURITY_EVENT_TYPE__create:
121
+ return self.model.security_event__create_user(http_inputs, account_id, security_event)
122
+ elif security_event[KG__security_event__type] == SECURITY_EVENT_TYPE__delete:
123
+ return self.model.security_event__delete_user(http_inputs, account_id, security_event)
124
+ elif security_event[KG__security_event__type] == SECURITY_EVENT_TYPE__reset:
125
+ return self.model.security_event__reset_user_password(http_inputs, account_id, security_event)
126
+ else:
127
+ raise HttpStatusException("Invalid security event type")
128
+
116
129
  @self.publish_route('/internal/dispatchers', DOCUMENTATION__dispatchers)
117
130
  def dispatchers(connection: DBInterface, http_inputs: dict):
118
131
  self.model.attach_dispatcher_credentials(connection, http_inputs)
119
132
 
120
- @self.publish_route('/invite', DOCUMENTATION__invite)
121
- def invite(http_inputs: dict, account_id: str, is_the_anonymous_user: bool):
122
- return self.model.invite(http_inputs, account_id, is_the_anonymous_user)
123
-
124
- @self.publish_route('/sign-up', DOCUMENTATION__sign_up)
125
- def sign_up(http_inputs: dict, ip_address: str, response: JAAQLResponse):
126
- return self.model.sign_up(http_inputs, ip_address, response)
127
-
128
- @self.publish_route('/sign-up-resend', DOCUMENTATION__resend_sign_up)
129
- def sign_up_resend(http_inputs: dict, account_id: str, username: str):
130
- return self.model.resend_signup_email(http_inputs, account_id, username)
131
-
132
133
  @self.publish_route('/email', DOCUMENTATION__emails)
133
134
  def send_email(is_the_anonymous_user: bool, account_id: str, http_inputs: dict, username: str, auth_token: str):
134
135
  return self.model.send_email(is_the_anonymous_user, account_id, http_inputs, username, auth_token)
135
136
 
136
- @self.publish_route('/account/reset-password', DOCUMENTATION__reset_password)
137
- def reset_password(http_inputs: dict):
138
- return self.model.reset_password(http_inputs)
139
-
140
- @self.publish_route('/security-event', DOCUMENTATION__security_event)
141
- def security_event(http_inputs: dict):
142
- if self.is_post():
143
- return self.model.check_security_event_key_and_security_event_is_unlocked(http_inputs)
144
- else:
145
- return self.model.finish_security_event(http_inputs)
146
-
147
137
  @self.publish_route('/cron', DOCUMENTATION__cron)
148
138
  def cron(ip_address: str):
149
139
  self.model.execute_cron_jobs(ip_address)
@@ -201,3 +191,16 @@ class JAAQLController(BaseJAAQLController):
201
191
  @self.publish_route('/rendered_documents', DOCUMENTATION__rendered_document)
202
192
  def documents(http_inputs: dict):
203
193
  return self.model.fetch_document_stream(http_inputs)
194
+
195
+ @self.publish_route('/oidc/logout', DOCUMENTATION__oidc_begin_logout)
196
+ def begin_oidc_logout(http_inputs: dict, response: JAAQLResponse):
197
+ # inputs: application, tenant, provider, optional schema, redirect_uri, optional id_token_hint
198
+ self.model.begin_oidc_logout(http_inputs, response)
199
+
200
+ @self.publish_route('/oidc/post-logout', DOCUMENTATION__oidc_post_logout)
201
+ def finish_oidc_logout(http_inputs: dict, response: JAAQLResponse):
202
+ # KC returns ?state=... here; validate against cookie and bounce to final app URL
203
+ self.model.finish_oidc_logout(
204
+ {"state": http_inputs["state"], "oidc_cookie": request.cookies.get(COOKIE_OIDC)},
205
+ response
206
+ )
@@ -1,5 +1,5 @@
1
1
  """
2
- This script was generated from jaaql.exceptions.fxli at 2025-05-21, 11:25:44
2
+ This script was generated from jaaql.exceptions.fxli at 2025-11-05, 14:24:08
3
3
  """
4
4
 
5
5
  from jaaql.utilities.crypt_utils import get_repeatable_salt
@@ -190,69 +190,19 @@ QUERY___add_or_update_validated_ip_address = "INSERT INTO validated_ip_address (
190
190
  QUERY__fetch_application_schemas = "SELECT S.name, S.database, (A.default_schema = S.name) as is_default, A.is_live FROM application_schema S INNER JOIN application A ON A.name = S.application WHERE S.application = :application"
191
191
  KEY__is_default = "is_default"
192
192
 
193
- QUERY__count_security_events_of_type_in_24hr_window = """
194
- SELECT
195
- COUNT(*) as count
196
- FROM security_event S
197
- INNER JOIN email_template E ON E.name = S.email_template AND E.application = S.application
198
- WHERE E.type IN (:type_one, :type_two) AND ((:account::postgres_role is not null AND account = :account) OR (:fake_account::encrypted__jaaql_username is not null AND fake_account = :fake_account)) AND (creation_timestamp + interval '24 hour') > current_timestamp
199
- """
200
-
201
-
202
- def count_for_security_event(
203
- connection: DBInterface, encryption_key: bytes, vault_repeatable_salt: str,
204
- type_one: str, type_two: str,
205
- account, fake_account=None
206
- ):
207
- return execute_supplied_statement_singleton(
208
- connection, QUERY__count_security_events_of_type_in_24hr_window, {
209
- "type_one": type_one, "type_two": type_two,
210
- KG__security_event__account: account,
211
- KG__security_event__fake_account: fake_account
212
- }, as_objects=True, encryption_key=encryption_key, encrypt_parameters=[KG__security_event__fake_account],
213
- encryption_salts={KG__security_event__fake_account: get_repeatable_salt(vault_repeatable_salt, fake_account)}
214
- )[KEY__count]
215
-
216
193
 
217
194
  RPC_ACCESS__private = "P"
218
195
  RPC_ACCESS__public = "U"
219
196
  RPC_ACCESS__webhook = "W"
220
197
 
198
+ SECURITY_EVENT_TYPE__create = "C"
199
+ SECURITY_EVENT_TYPE__delete = "D"
200
+ SECURITY_EVENT_TYPE__reset = "R"
201
+
221
202
  KEY__type_one = "type_one"
222
203
  KEY__type_two = "type_two"
223
204
 
224
- EMAIL_TYPE__signup = "S"
225
- EMAIL_TYPE__already_signed_up = "A"
226
- EMAIL_TYPE__reset_password = "R"
227
- EMAIL_TYPE__unregistered_password_reset = "U"
228
- EMAIL_TYPE__general = "G"
229
-
230
- KEY__key_fits = "key_fits"
231
- QUERY__check_security_event_unlock = """
232
- UPDATE
233
- security_event S
234
- SET
235
- wrong_key_attempt_count = S.wrong_key_attempt_count + (case when (S.unlock_code = :unlock_code OR S.unlock_key = :unlock_key OR S.wrong_key_attempt_count >= 3) then 0 else 1 end)
236
- FROM application A
237
- WHERE
238
- S.application = A.name AND
239
- (S.event_lock = :event_lock OR S.unlock_key = :unlock_key) AND S.finish_timestamp is null AND
240
- S.creation_timestamp + (A.unlock_key_validity_period || ' seconds')::interval > current_timestamp
241
- RETURNING S.*, A.unlock_code_validity_period, (S.unlock_code = :unlock_code OR S.unlock_key = :unlock_key) as key_fits
242
- """
243
-
244
205
 
245
- def check_security_event_unlock(
246
- connection: DBInterface, event_lock, unlock_code,
247
- unlock_key, singleton_code: int = None, singleton_message: str = None
248
- ):
249
- return execute_supplied_statement_singleton(
250
- connection, QUERY__check_security_event_unlock, {
251
- KG__security_event__event_lock: event_lock,
252
- KG__security_event__unlock_code: unlock_code,
253
- KG__security_event__unlock_key: unlock_key
254
- }, as_objects=True, singleton_code=singleton_code, singleton_message=singleton_message
255
- )
256
206
 
257
207
 
258
208
  QUERY__fetch_document_templates_for_email_template = """