jaaql-middleware-python 4.34.3__py3-none-any.whl → 5.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jaaql/constants.py +4 -1
- jaaql/db/db_pg_interface.py +35 -1
- jaaql/documentation/documentation_internal.py +67 -7
- jaaql/exceptions/jaaql_interpretable_handled_errors.py +15 -1
- jaaql/mvc/base_controller.py +14 -1
- jaaql/mvc/base_model.py +35 -10
- jaaql/mvc/controller.py +48 -5
- jaaql/mvc/generated_queries.py +1 -0
- jaaql/mvc/model.py +711 -155
- jaaql/scripts/01.install_domains.generated.sql +2 -0
- jaaql/scripts/02.install_super_user.exceptions.sql +3 -5
- jaaql/scripts/03.install_super_user.handwritten.sql +50 -0
- jaaql/scripts/04.install_jaaql_data_structures.generated.sql +1 -0
- jaaql/services/migrations_manager_service.py +550 -7
- jaaql/utilities/bootstrap_secrets.py +33 -0
- jaaql/utilities/utils_no_project_imports.py +1 -0
- {jaaql_middleware_python-4.34.3.dist-info → jaaql_middleware_python-5.2.7.dist-info}/METADATA +1 -1
- {jaaql_middleware_python-4.34.3.dist-info → jaaql_middleware_python-5.2.7.dist-info}/RECORD +21 -20
- {jaaql_middleware_python-4.34.3.dist-info → jaaql_middleware_python-5.2.7.dist-info}/WHEEL +0 -0
- {jaaql_middleware_python-4.34.3.dist-info → jaaql_middleware_python-5.2.7.dist-info}/licenses/LICENSE.txt +0 -0
- {jaaql_middleware_python-4.34.3.dist-info → jaaql_middleware_python-5.2.7.dist-info}/top_level.txt +0 -0
jaaql/constants.py
CHANGED
|
@@ -91,6 +91,8 @@ VAULT_KEY__super_db_credentials = "super_db_credentials"
|
|
|
91
91
|
VAULT_KEY__jaaql_lookup_connection = "jaaql_lookup_connection"
|
|
92
92
|
VAULT_KEY__allow_jaaql_uninstall = "Allow jaaql uninstall"
|
|
93
93
|
VAULT_KEY__jaaql_db_password = "Jaaql DB Password"
|
|
94
|
+
VAULT_KEY__postgres_bootstrap_password = "postgres_bootstrap_password"
|
|
95
|
+
VAULT_KEY__keycloak_realm_admin_secret = "keycloak_realm_admin_secret"
|
|
94
96
|
|
|
95
97
|
# Used for re-installation
|
|
96
98
|
VAULT_KEY__db_connection_string = "db_connection_string"
|
|
@@ -205,8 +207,9 @@ USERNAME__anonymous = "anonymous"
|
|
|
205
207
|
PASSWORD__anonymous = "jaaql_public_password"
|
|
206
208
|
ROLE__jaaql = "jaaql"
|
|
207
209
|
ROLE__postgres = "postgres"
|
|
210
|
+
ROLE__dba = "dba"
|
|
208
211
|
|
|
209
212
|
PROTOCOL__postgres = "postgresql://"
|
|
210
213
|
|
|
211
|
-
VERSION = "
|
|
214
|
+
VERSION = "5.2.7"
|
|
212
215
|
|
jaaql/db/db_pg_interface.py
CHANGED
|
@@ -28,6 +28,40 @@ QUERY__dba_query = "SELECT pg_has_role(datdba::regrole, 'MEMBER') FROM pg_databa
|
|
|
28
28
|
QUERY__dba_query_external = "SELECT pg_has_role(datdba::regrole, 'MEMBER') FROM pg_database WHERE datname = current_database();"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
def _escape_unescaped_percent(query: str) -> str:
|
|
32
|
+
# psycopg3 scans every '%' in the SQL when parameters are supplied and
|
|
33
|
+
# rejects anything that isn't a placeholder or '%%'. JAAQL uses named
|
|
34
|
+
# parameters only (:parameter -> %(name)s), so positional %s/%b/%t must
|
|
35
|
+
# never survive: any '%s' in author SQL is a literal inside a string
|
|
36
|
+
# (LIKE '%saas%', LIKE '%twynstra%', etc.), not a placeholder. Escape
|
|
37
|
+
# everything except '%%' and '%(name)X'.
|
|
38
|
+
out = []
|
|
39
|
+
i = 0
|
|
40
|
+
n = len(query)
|
|
41
|
+
while i < n:
|
|
42
|
+
if query[i] != '%':
|
|
43
|
+
out.append(query[i])
|
|
44
|
+
i += 1
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
nxt = query[i + 1] if i + 1 < n else ''
|
|
48
|
+
if nxt == '%':
|
|
49
|
+
out.append('%%')
|
|
50
|
+
i += 2
|
|
51
|
+
elif nxt == '(':
|
|
52
|
+
close = query.find(')', i + 2)
|
|
53
|
+
if close == -1 or close + 1 >= n:
|
|
54
|
+
out.append('%%')
|
|
55
|
+
i += 1
|
|
56
|
+
else:
|
|
57
|
+
out.append(query[i:close + 2])
|
|
58
|
+
i = close + 2
|
|
59
|
+
else:
|
|
60
|
+
out.append('%%')
|
|
61
|
+
i += 1
|
|
62
|
+
return ''.join(out)
|
|
63
|
+
|
|
64
|
+
|
|
31
65
|
class DBPGInterface(DBInterface):
|
|
32
66
|
|
|
33
67
|
HOST_POOLS = {}
|
|
@@ -210,7 +244,7 @@ class DBPGInterface(DBInterface):
|
|
|
210
244
|
if parameters is None or len(parameters.keys()) == 0:
|
|
211
245
|
cursor.execute(query, prepare=do_prepare)
|
|
212
246
|
else:
|
|
213
|
-
cursor.execute(query, parameters, prepare=do_prepare)
|
|
247
|
+
cursor.execute(_escape_unescaped_percent(query), parameters, prepare=do_prepare)
|
|
214
248
|
if cursor.description is None:
|
|
215
249
|
return [], [], []
|
|
216
250
|
else:
|
|
@@ -367,12 +367,40 @@ DOCUMENTATION__oidc_exchange_code = SwaggerDocumentation(
|
|
|
367
367
|
name="Fetch OIDC code",
|
|
368
368
|
description="Exchanges OIDC auth code for auth token, returns the token",
|
|
369
369
|
method=REST__GET,
|
|
370
|
-
arguments=
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
370
|
+
arguments=[
|
|
371
|
+
SwaggerArgumentResponse(
|
|
372
|
+
name="response",
|
|
373
|
+
description="The OIDC response JWT object (FAPI/JARM mode)",
|
|
374
|
+
arg_type=str,
|
|
375
|
+
required=False,
|
|
376
|
+
condition="When USE_FAPI_ADVANCED or standard JARM is enabled",
|
|
377
|
+
example=["eyJ..."]
|
|
378
|
+
),
|
|
379
|
+
SwaggerArgumentResponse(
|
|
380
|
+
name="code",
|
|
381
|
+
description="The authorization code (basic OIDC mode)",
|
|
382
|
+
arg_type=str,
|
|
383
|
+
required=False,
|
|
384
|
+
condition="When USE_OIDC_BASIC is enabled",
|
|
385
|
+
example=["abc123"]
|
|
386
|
+
),
|
|
387
|
+
SwaggerArgumentResponse(
|
|
388
|
+
name="state",
|
|
389
|
+
description="The state parameter (basic OIDC mode)",
|
|
390
|
+
arg_type=str,
|
|
391
|
+
required=False,
|
|
392
|
+
condition="When USE_OIDC_BASIC is enabled",
|
|
393
|
+
example=["xyz789"]
|
|
394
|
+
),
|
|
395
|
+
SwaggerArgumentResponse(
|
|
396
|
+
name="session_state",
|
|
397
|
+
description="The session state parameter (Azure AD / OpenID Connect session management)",
|
|
398
|
+
arg_type=str,
|
|
399
|
+
required=False,
|
|
400
|
+
condition="When the identity provider includes session management",
|
|
401
|
+
example=["0022d22a-3a6c-953c-be05-80a155fda337"]
|
|
402
|
+
)
|
|
403
|
+
],
|
|
376
404
|
response=SwaggerFlatResponse(
|
|
377
405
|
description="URL",
|
|
378
406
|
code=HTTPStatus.FOUND,
|
|
@@ -381,6 +409,16 @@ DOCUMENTATION__oidc_exchange_code = SwaggerDocumentation(
|
|
|
381
409
|
)
|
|
382
410
|
)
|
|
383
411
|
|
|
412
|
+
DOCUMENTATION__resend_verify_email = SwaggerDocumentation(
|
|
413
|
+
tags="oidc",
|
|
414
|
+
security=False,
|
|
415
|
+
methods=SwaggerMethod(
|
|
416
|
+
name="Resend verification email",
|
|
417
|
+
description="Triggers Keycloak to resend a verification email for a given email address",
|
|
418
|
+
method=REST__POST,
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
|
|
384
422
|
DOCUMENTATION__jwks = SwaggerDocumentation(
|
|
385
423
|
tags="jwks",
|
|
386
424
|
security=False,
|
|
@@ -567,4 +605,26 @@ DOCUMENTATION__security_event = SwaggerDocumentation(
|
|
|
567
605
|
],
|
|
568
606
|
response=RES__allow_all
|
|
569
607
|
)
|
|
570
|
-
)
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
DOCUMENTATION__report_sentinel_error = SwaggerDocumentation(
|
|
611
|
+
tags="Reporting",
|
|
612
|
+
security=False, # PUBLIC: also receives self-posts from the in-core sentinel reporter (SENTINEL_URL=_)
|
|
613
|
+
methods=SwaggerMethod(
|
|
614
|
+
name="Report error",
|
|
615
|
+
description="Logs an application error (user_agent + ip_address encrypted at rest) and triggers Sentinel "
|
|
616
|
+
"alert processing. Never returns 500 (would otherwise make JAAQL report itself recursively).",
|
|
617
|
+
method=REST__POST,
|
|
618
|
+
body=[
|
|
619
|
+
SwaggerArgumentResponse(name="location", description="URL/location where the error occurred", arg_type=str, example=["https://app/index.html"]),
|
|
620
|
+
SwaggerArgumentResponse(name="source_file", description="Source file the error occurred in", arg_type=str, example=["common.js"]),
|
|
621
|
+
SwaggerArgumentResponse(name="error_condensed", description="Condensed error (truncated to 200 chars for grouping)", arg_type=str, example=["Uncaught TypeError"]),
|
|
622
|
+
SwaggerArgumentResponse(name="stacktrace", description="Full stacktrace", arg_type=str, example=["at <anonymous>:1:16"]),
|
|
623
|
+
SwaggerArgumentResponse(name="version", description="Product version", arg_type=str, example=["1.2.3"]),
|
|
624
|
+
SwaggerArgumentResponse(name="source_system", description="Originating system/application", arg_type=str, example=["JAAQL"]),
|
|
625
|
+
SwaggerArgumentResponse(name="file_line_number", description="Line number", arg_type=int, required=False, condition="If available", example=[123]),
|
|
626
|
+
SwaggerArgumentResponse(name="file_col_number", description="Column number", arg_type=int, required=False, condition="If available", example=[21]),
|
|
627
|
+
SwaggerArgumentResponse(name="user_agent", description="Browser user agent (encrypted at rest). Absent on internal self-posts.", arg_type=str, required=False, condition="If present", example=["Mozilla/5.0"]),
|
|
628
|
+
]
|
|
629
|
+
)
|
|
630
|
+
)
|
|
@@ -127,10 +127,24 @@ class UserUnauthorized(JaaqlInterpretableHandledError):
|
|
|
127
127
|
)
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
class
|
|
130
|
+
class EmailConfirmationRequired(JaaqlInterpretableHandledError):
|
|
131
131
|
def __init__(self, descriptor=None):
|
|
132
132
|
super().__init__(
|
|
133
133
|
error_code=1010,
|
|
134
|
+
http_response_code=403,
|
|
135
|
+
table_name=None,
|
|
136
|
+
message="Your email confirmation grace period has expired. Please confirm your email to continue.",
|
|
137
|
+
column_name=None,
|
|
138
|
+
_set=None,
|
|
139
|
+
index=None,
|
|
140
|
+
descriptor=descriptor
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class UnhandledJaaqlServerError(JaaqlInterpretableHandledError):
|
|
145
|
+
def __init__(self, descriptor=None):
|
|
146
|
+
super().__init__(
|
|
147
|
+
error_code=1099,
|
|
134
148
|
http_response_code=500,
|
|
135
149
|
table_name=None,
|
|
136
150
|
message="An unhandled exception has occurred with JAAQL.",
|
jaaql/mvc/base_controller.py
CHANGED
|
@@ -35,6 +35,7 @@ from jaaql.exceptions.http_status_exception import *
|
|
|
35
35
|
ARG__http_inputs = "http_inputs"
|
|
36
36
|
ARG__account_id = "account_id"
|
|
37
37
|
ARG__ip_address = "ip_address"
|
|
38
|
+
ARG__request_headers = "request_headers"
|
|
38
39
|
ARG__response = "response"
|
|
39
40
|
ARG__username = "username"
|
|
40
41
|
ARG__profiler = "profiler"
|
|
@@ -496,7 +497,16 @@ class BaseJAAQLController:
|
|
|
496
497
|
if auth_cookie is not None:
|
|
497
498
|
security_key = auth_cookie
|
|
498
499
|
|
|
499
|
-
|
|
500
|
+
easyauth_resolved = False
|
|
501
|
+
if self.model.use_easyauth and security_key is None:
|
|
502
|
+
easyauth_principal_id = request.headers.get("X-MS-CLIENT-PRINCIPAL-ID")
|
|
503
|
+
if easyauth_principal_id:
|
|
504
|
+
account_id, username, ip_id, is_public, remember_me = \
|
|
505
|
+
self.model.authenticate_via_easyauth(request.headers, ip_addr, jaaql_resp)
|
|
506
|
+
easyauth_resolved = True
|
|
507
|
+
self.perform_profile(request_id, "EasyAuth")
|
|
508
|
+
|
|
509
|
+
if swagger_documentation.security and not easyauth_resolved:
|
|
500
510
|
bypass_super = request.headers.get(HEADER__security_bypass)
|
|
501
511
|
bypass_jaaql = request.headers.get(HEADER__security_bypass_jaaql)
|
|
502
512
|
bypass_user = request.headers.get(HEADER__security_specify_user)
|
|
@@ -566,6 +576,9 @@ class BaseJAAQLController:
|
|
|
566
576
|
if ARG__ip_address in inspect.getfullargspec(view_func_local).args:
|
|
567
577
|
supply_dict[ARG__ip_address] = ip_addr
|
|
568
578
|
|
|
579
|
+
if ARG__request_headers in inspect.getfullargspec(view_func_local).args:
|
|
580
|
+
supply_dict[ARG__request_headers] = request.headers
|
|
581
|
+
|
|
569
582
|
if ARG__response in inspect.getfullargspec(view_func_local).args:
|
|
570
583
|
supply_dict[ARG__response] = jaaql_resp
|
|
571
584
|
|
jaaql/mvc/base_model.py
CHANGED
|
@@ -18,6 +18,7 @@ from jaaql.config_constants import *
|
|
|
18
18
|
from jaaql.email.email_manager import EmailManager
|
|
19
19
|
from os.path import dirname
|
|
20
20
|
from jaaql.services.cached_canned_query_service import CachedCannedQueryService
|
|
21
|
+
from jaaql.utilities.bootstrap_secrets import get_or_seed_vault_secret
|
|
21
22
|
import threading
|
|
22
23
|
|
|
23
24
|
import uuid
|
|
@@ -236,6 +237,19 @@ class BaseJAAQLModel:
|
|
|
236
237
|
|
|
237
238
|
self.application_url = os.environ.get("SERVER_ADDRESS", "")
|
|
238
239
|
self.use_fapi_advanced = os.environ.get("USE_FAPI_ADVANCED", "").lower() == "true"
|
|
240
|
+
self.use_oidc_basic = os.environ.get("USE_OIDC_BASIC", "").lower() == "true"
|
|
241
|
+
if self.use_oidc_basic:
|
|
242
|
+
# Basic OIDC mode intentionally disables the advanced FAPI/JARM/PAR flow.
|
|
243
|
+
self.use_fapi_advanced = False
|
|
244
|
+
|
|
245
|
+
self.use_easyauth = os.environ.get("USE_EASYAUTH", "").lower() == "true"
|
|
246
|
+
if self.use_easyauth:
|
|
247
|
+
if self.use_oidc_basic or self.use_fapi_advanced:
|
|
248
|
+
raise Exception("USE_EASYAUTH cannot be combined with USE_OIDC_BASIC or USE_FAPI_ADVANCED")
|
|
249
|
+
print("EasyAuth mode enabled. Container MUST be behind Azure EasyAuth (App Service or Container Apps).")
|
|
250
|
+
self.easyauth_provider = os.environ.get("EASYAUTH_PROVIDER", "azure")
|
|
251
|
+
self.easyauth_tenant = os.environ.get("EASYAUTH_TENANT", "easyauth")
|
|
252
|
+
self.easyauth_provisioned = False
|
|
239
253
|
|
|
240
254
|
self.fapi_pem = None
|
|
241
255
|
self.fapi_cert = None
|
|
@@ -293,16 +307,27 @@ class BaseJAAQLModel:
|
|
|
293
307
|
|
|
294
308
|
self.reload_lock = threading.Lock()
|
|
295
309
|
|
|
296
|
-
|
|
297
|
-
self.vault
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
310
|
+
jaaql_bypass_key = get_or_seed_vault_secret(
|
|
311
|
+
self.vault,
|
|
312
|
+
VAULT_KEY__jaaql_local_access_key,
|
|
313
|
+
ENVIRON__JAAQL__JAAQL_BYPASS_KEY,
|
|
314
|
+
generate_if_missing=True
|
|
315
|
+
)
|
|
316
|
+
super_bypass_key = get_or_seed_vault_secret(
|
|
317
|
+
self.vault,
|
|
318
|
+
VAULT_KEY__super_local_access_key,
|
|
319
|
+
ENVIRON__JAAQL__SUPER_BYPASS_KEY,
|
|
320
|
+
generate_if_missing=True
|
|
321
|
+
)
|
|
322
|
+
get_or_seed_vault_secret(
|
|
323
|
+
self.vault,
|
|
324
|
+
VAULT_KEY__keycloak_realm_admin_secret,
|
|
325
|
+
"KEYCLOAK_REALM_ADMIN_SECRET",
|
|
326
|
+
generate_if_missing=False
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
self.local_jaaql_access_key = jaaql_bypass_key if self.is_container else "00000-00000"
|
|
330
|
+
self.local_super_access_key = super_bypass_key if self.is_container else "00000-00000"
|
|
306
331
|
|
|
307
332
|
if self.vault.has_obj(VAULT_KEY__jaaql_lookup_connection):
|
|
308
333
|
if self.is_container:
|
jaaql/mvc/controller.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
|
|
1
3
|
from jaaql.mvc.exception_queries import SECURITY_EVENT_TYPE__create, SECURITY_EVENT_TYPE__delete, \
|
|
2
4
|
SECURITY_EVENT_TYPE__reset
|
|
3
5
|
from jaaql.mvc.model import JAAQLModel
|
|
@@ -10,7 +12,8 @@ from jaaql.db.db_interface import DBInterface
|
|
|
10
12
|
from flask import request
|
|
11
13
|
import queue
|
|
12
14
|
|
|
13
|
-
from jaaql.utilities.utils_no_project_imports import COOKIE_OIDC
|
|
15
|
+
from jaaql.utilities.utils_no_project_imports import COOKIE_OIDC, COOKIE_OIDC_RETURN
|
|
16
|
+
from jaaql.interpreter.interpret_jaaql import KEY_query, KEY_parameters
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class JAAQLController(BaseJAAQLController):
|
|
@@ -171,16 +174,26 @@ class JAAQLController(BaseJAAQLController):
|
|
|
171
174
|
return self.model.fetch_user_registries_for_tenant(http_inputs)
|
|
172
175
|
|
|
173
176
|
@self.publish_route('/oidc-redirect-url', DOCUMENTATION__oidc_redirect_url)
|
|
174
|
-
def fetch_redirect_url(http_inputs: dict, response: JAAQLResponse):
|
|
175
|
-
self.model.fetch_redirect_uri(http_inputs, response)
|
|
177
|
+
def fetch_redirect_url(http_inputs: dict, response: JAAQLResponse, request_headers=None, ip_address=None):
|
|
178
|
+
self.model.fetch_redirect_uri(http_inputs, response, request_headers=request_headers, ip_address=ip_address)
|
|
176
179
|
|
|
177
180
|
@self.publish_route(ENDPOINT__oidc_get_token, DOCUMENTATION__oidc_exchange_code)
|
|
178
181
|
def exchange_auth_code(http_inputs: dict, ip_address: str, response: JAAQLResponse):
|
|
179
182
|
try:
|
|
180
183
|
self.model.exchange_auth_code(http_inputs, request.cookies.get(COOKIE_OIDC), ip_address, response)
|
|
181
|
-
except
|
|
184
|
+
except Exception as e:
|
|
185
|
+
import traceback
|
|
186
|
+
traceback.print_exc()
|
|
187
|
+
print(f"OIDC code exchange failed: {e}")
|
|
188
|
+
# Redirect to the saved return URL if available (survives OIDC cookie consumption),
|
|
189
|
+
# otherwise fall back to the app root
|
|
190
|
+
return_url = request.cookies.get(COOKIE_OIDC_RETURN)
|
|
182
191
|
response.response_code = HTTPStatus.FOUND
|
|
183
|
-
response.raw_headers["Location"] = self.model.url.replace("_", "localhost")
|
|
192
|
+
response.raw_headers["Location"] = return_url if return_url else self.model.url.replace("_", "localhost")
|
|
193
|
+
|
|
194
|
+
@self.publish_route('/resend-verify-email', DOCUMENTATION__resend_verify_email)
|
|
195
|
+
def resend_verify_email(http_inputs: dict):
|
|
196
|
+
self.model.resend_verification_email(http_inputs.get("email", ""))
|
|
184
197
|
|
|
185
198
|
@self.publish_route('/.well-known/jwks', DOCUMENTATION__jwks)
|
|
186
199
|
def fetch_jwks():
|
|
@@ -209,3 +222,33 @@ class JAAQLController(BaseJAAQLController):
|
|
|
209
222
|
{"state": http_inputs["state"], "oidc_cookie": request.cookies.get(COOKIE_OIDC)},
|
|
210
223
|
response
|
|
211
224
|
)
|
|
225
|
+
|
|
226
|
+
@self.publish_route(ENDPOINT__report_sentinel_error, DOCUMENTATION__report_sentinel_error, True)
|
|
227
|
+
def report_sentinel_error(http_inputs: dict, ip_address: str):
|
|
228
|
+
# Public error-reporting endpoint, moved here from the deprecated jaaql-sentinel-middleware.
|
|
229
|
+
# MUST NEVER return a 500: a 500 makes JAAQL self-report (SENTINEL_URL=_) and recurse.
|
|
230
|
+
ins_error = (
|
|
231
|
+
"insert into error (location, source_file, user_agent, ip_address, error_condensed, stacktrace, "
|
|
232
|
+
"file_line_number, file_col_number, version, source_system) "
|
|
233
|
+
"VALUES (:location, :source_file, #user_agent, #ip_address, left(:error_condensed, 200), :stacktrace, "
|
|
234
|
+
":file_line_number, :file_col_number, :version, :source_system) "
|
|
235
|
+
"RETURNING id::text as error_id"
|
|
236
|
+
)
|
|
237
|
+
try:
|
|
238
|
+
http_inputs["ip_address"] = ip_address # plaintext -> #ip_address (field-level encrypted)
|
|
239
|
+
inserted = self.model.submit(
|
|
240
|
+
{KEY_query: ins_error, KEY_parameters: http_inputs, KEY__application: "sentinel"},
|
|
241
|
+
ROLE__dba, as_objects=True, singleton=True
|
|
242
|
+
)
|
|
243
|
+
self.model.submit(
|
|
244
|
+
{
|
|
245
|
+
KEY_query: 'SELECT "error.process_alert"(:id, :raw_ip_address)',
|
|
246
|
+
KEY_parameters: {"id": inserted["error_id"], "raw_ip_address": ip_address},
|
|
247
|
+
KEY__application: "sentinel"
|
|
248
|
+
},
|
|
249
|
+
ROLE__dba
|
|
250
|
+
)
|
|
251
|
+
except Exception as ex:
|
|
252
|
+
import traceback
|
|
253
|
+
traceback.print_exc()
|
|
254
|
+
raise HttpStatusException(str(ex))
|
jaaql/mvc/generated_queries.py
CHANGED
|
@@ -1309,6 +1309,7 @@ KG__account__sub = "sub"
|
|
|
1309
1309
|
KG__account__username = "username"
|
|
1310
1310
|
KG__account__email = "email"
|
|
1311
1311
|
KG__account__email_verified = "email_verified"
|
|
1312
|
+
KG__account__created_timestamp = "created_timestamp"
|
|
1312
1313
|
KG__account__deletion_timestamp = "deletion_timestamp"
|
|
1313
1314
|
KG__account__provider = "provider"
|
|
1314
1315
|
KG__account__tenant = "tenant"
|