arpakitlib 1.8.19__py3-none-any.whl → 1.8.21__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.
- arpakitlib/_arpakit_project_template_v_5/example.env +1 -3
- arpakitlib/_arpakit_project_template_v_5/project/additional_model/common.py +0 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/auth.py +170 -206
- arpakitlib/_arpakit_project_template_v_5/project/api/exception_handler.py +2 -7
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_arpakitlib_project_template_info.py +11 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_auth_data.py +10 -8
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_story_log.py +46 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/main_router.py +28 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/raise_fake_error.py +31 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/reinit_sqlalchemy_db.py +35 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user.py +33 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user_token.py +33 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_errors_info.py +6 -8
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/main_router.py +17 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/general/main_router.py +5 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/router/{client → general}/now_utc_datetime.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/common.py +7 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/operation.py +2 -2
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/story_log.py +2 -2
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/common.py +7 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/{admin → client}/user.py +6 -6
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/user_token.py +14 -0
- arpakitlib/_arpakit_project_template_v_5/project/core/settings.py +3 -19
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/api_key.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py +13 -3
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py +19 -4
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py +2 -2
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user_token.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_api_keys.py +35 -0
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_1.py +4 -2
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_2.py +4 -2
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_3.py +4 -2
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_4.py +4 -2
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_5.py +4 -2
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/blank/client.py +14 -6
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/const.py +1 -0
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/about.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/general/author.py +31 -0
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/support.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/main_router.py +4 -3
- arpakitlib/_arpakit_project_template_v_5/project/tg_bot/util/set_tg_bot_commands.py +8 -0
- {arpakitlib-1.8.19.dist-info → arpakitlib-1.8.21.dist-info}/METADATA +1 -1
- {arpakitlib-1.8.19.dist-info → arpakitlib-1.8.21.dist-info}/RECORD +55 -49
- arpakitlib/_arpakit_project_template_v_5/project/api/auth2.py +0 -218
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/user_token.py +0 -14
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/__init__.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/error_handler.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/healthcheck.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/hello_world.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/raw_callback_query.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/raw_inline_query.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/raw_message.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/remove_message.py +0 -0
- /arpakitlib/_arpakit_project_template_v_5/project/tg_bot/router/{client → general}/start.py +0 -0
- {arpakitlib-1.8.19.dist-info → arpakitlib-1.8.21.dist-info}/LICENSE +0 -0
- {arpakitlib-1.8.19.dist-info → arpakitlib-1.8.21.dist-info}/WHEEL +0 -0
- {arpakitlib-1.8.19.dist-info → arpakitlib-1.8.21.dist-info}/entry_points.txt +0 -0
@@ -11,12 +11,10 @@
|
|
11
11
|
# api_port=
|
12
12
|
# api_init_sqlalchemy_db=
|
13
13
|
# api_init_json_db=
|
14
|
-
#
|
15
|
-
# api_user_tokens=
|
14
|
+
# api_correct_api_keys=
|
16
15
|
# api_enable_sqladmin=
|
17
16
|
# api_start_operation_executor_worker=
|
18
17
|
# api_start_scheduled_operation_creator_worker=
|
19
|
-
# api_create_story_log_func_before_in_exception_handler=
|
20
18
|
# sqladmin_secret_key=
|
21
19
|
# sqladmin_auth_keys=
|
22
20
|
# sqladmin_port=
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Callable
|
1
|
+
from typing import Callable, Any
|
2
2
|
|
3
3
|
import fastapi
|
4
4
|
import fastapi.exceptions
|
@@ -8,10 +8,10 @@ import sqlalchemy
|
|
8
8
|
from fastapi import Security
|
9
9
|
from fastapi.security import APIKeyHeader
|
10
10
|
from pydantic import BaseModel, ConfigDict
|
11
|
+
from sqlalchemy.orm import joinedload
|
11
12
|
|
12
13
|
from arpakitlib.ar_func_util import is_async_func, is_sync_func
|
13
14
|
from arpakitlib.ar_json_util import transfer_data_to_json_str_to_data
|
14
|
-
from arpakitlib.ar_type_util import raise_for_type
|
15
15
|
from project.api.const import APIErrorCodes
|
16
16
|
from project.api.exception import APIException
|
17
17
|
from project.core.settings import get_cached_settings
|
@@ -22,70 +22,27 @@ from project.sqlalchemy_db_.sqlalchemy_model import ApiKeyDBM, UserTokenDBM
|
|
22
22
|
class APIAuthData(BaseModel):
|
23
23
|
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, from_attributes=True)
|
24
24
|
|
25
|
-
require_api_key_string: bool = False
|
26
|
-
require_user_token_string: bool = False
|
27
|
-
|
28
|
-
require_correct_api_key: bool = False
|
29
|
-
require_correct_user_token: bool = False
|
30
|
-
|
31
|
-
require_mode_type: str | None = None
|
32
|
-
require_not_mode_type: str | None = None
|
33
|
-
|
34
|
-
current_mode_type: str | None = None
|
35
|
-
|
36
25
|
api_key_string: str | None = None
|
37
26
|
user_token_string: str | None = None
|
38
27
|
|
39
28
|
is_api_key_correct: bool | None = None
|
40
|
-
is_user_token_correct: bool | None = None
|
41
29
|
|
42
30
|
api_key_dbm: ApiKeyDBM | None = None
|
43
31
|
user_token_dbm: UserTokenDBM | None = None
|
44
32
|
|
33
|
+
prod_mode: bool = False
|
45
34
|
|
46
|
-
|
47
|
-
*,
|
48
|
-
require_api_key_string: bool = False,
|
49
|
-
require_user_token_string: bool = False,
|
50
|
-
|
51
|
-
require_correct_api_key: bool = False,
|
52
|
-
require_correct_user_token: bool = False,
|
35
|
+
extra_data: dict[str, Any] = {}
|
53
36
|
|
54
|
-
require_mode_type: str | None = None,
|
55
|
-
require_not_mode_type: str | None = None,
|
56
37
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
correct_user_tokens: str | list[str] | None = None,
|
38
|
+
def api_auth(
|
39
|
+
*,
|
40
|
+
middlewares: list[Callable] | None = None
|
61
41
|
) -> Callable:
|
62
|
-
if
|
63
|
-
|
64
|
-
if correct_api_keys is not None:
|
65
|
-
raise_for_type(correct_api_keys, list)
|
66
|
-
|
67
|
-
if is_api_key_correct_func is None and correct_api_keys is not None:
|
68
|
-
is_api_key_correct_func = (
|
69
|
-
lambda *args, **kwargs_: kwargs_["api_auth_data"].api_key_string in correct_api_keys
|
70
|
-
)
|
71
|
-
|
72
|
-
if isinstance(correct_user_tokens, str):
|
73
|
-
correct_user_tokens = [correct_user_tokens]
|
74
|
-
if correct_user_tokens is not None:
|
75
|
-
raise_for_type(correct_user_tokens, list)
|
76
|
-
|
77
|
-
if is_user_token_correct_func is None and correct_user_tokens is not None:
|
78
|
-
is_user_token_correct_func = (
|
79
|
-
lambda *args, **kwargs_: kwargs_["api_auth_data"].user_token_string in correct_user_tokens
|
80
|
-
)
|
42
|
+
if middlewares is None:
|
43
|
+
middlewares = []
|
81
44
|
|
82
|
-
|
83
|
-
require_api_key_string = True
|
84
|
-
|
85
|
-
if require_correct_user_token is True:
|
86
|
-
require_user_token_string = True
|
87
|
-
|
88
|
-
async def func(
|
45
|
+
async def async_func(
|
89
46
|
*,
|
90
47
|
ac: fastapi.security.HTTPAuthorizationCredentials | None = fastapi.Security(
|
91
48
|
fastapi.security.HTTPBearer(auto_error=False)
|
@@ -97,16 +54,10 @@ def api_auth(
|
|
97
54
|
) -> APIAuthData:
|
98
55
|
|
99
56
|
api_auth_data = APIAuthData(
|
100
|
-
|
101
|
-
require_user_token_string=require_user_token_string,
|
102
|
-
require_correct_api_key=require_correct_api_key,
|
103
|
-
require_correct_user_token=require_correct_user_token,
|
104
|
-
require_mode_type=require_mode_type,
|
105
|
-
require_not_mode_type=require_not_mode_type,
|
106
|
-
current_mode_type=get_cached_settings().mode_type
|
57
|
+
prod_mode=get_cached_settings().prod_mode
|
107
58
|
)
|
108
59
|
|
109
|
-
# parse
|
60
|
+
# parse api_key_string
|
110
61
|
|
111
62
|
api_auth_data.api_key_string = api_key_string
|
112
63
|
|
@@ -116,6 +67,8 @@ def api_auth(
|
|
116
67
|
api_auth_data.api_key_string = request.headers["api-key"]
|
117
68
|
if not api_auth_data.api_key_string and "apikey" in request.headers.keys():
|
118
69
|
api_auth_data.api_key_string = request.headers["apikey"]
|
70
|
+
if not api_auth_data.api_key_string and "api_key_string" in request.headers.keys():
|
71
|
+
api_auth_data.api_key_string = request.headers["api_key_string"]
|
119
72
|
|
120
73
|
if not api_auth_data.api_key_string and "api_key" in request.query_params.keys():
|
121
74
|
api_auth_data.api_key_string = request.query_params["api_key"]
|
@@ -123,13 +76,15 @@ def api_auth(
|
|
123
76
|
api_auth_data.api_key_string = request.query_params["api-key"]
|
124
77
|
if not api_auth_data.api_key_string and "apikey" in request.query_params.keys():
|
125
78
|
api_auth_data.api_key_string = request.query_params["apikey"]
|
79
|
+
if not api_auth_data.api_key_string and "api_key_string" in request.query_params.keys():
|
80
|
+
api_auth_data.api_key_string = request.query_params["api_key_string"]
|
126
81
|
|
127
82
|
if api_auth_data.api_key_string:
|
128
83
|
api_auth_data.api_key_string = api_auth_data.api_key_string.strip()
|
129
84
|
if not api_auth_data.api_key_string:
|
130
85
|
api_auth_data.api_key_string = None
|
131
86
|
|
132
|
-
# parse
|
87
|
+
# parse user_token_string
|
133
88
|
|
134
89
|
api_auth_data.user_token_string = ac.credentials if ac and ac.credentials and ac.credentials.strip() else None
|
135
90
|
|
@@ -158,196 +113,205 @@ def api_auth(
|
|
158
113
|
if not api_auth_data.user_token_string:
|
159
114
|
api_auth_data.user_token_string = None
|
160
115
|
|
161
|
-
#
|
116
|
+
# is_api_key_correct
|
162
117
|
|
163
|
-
if
|
164
|
-
if get_cached_settings().
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
118
|
+
if api_auth_data.api_key_string is not None:
|
119
|
+
if get_cached_settings().api_correct_api_keys is None:
|
120
|
+
api_auth_data.is_api_key_correct = None
|
121
|
+
else:
|
122
|
+
api_auth_data.is_api_key_correct = (
|
123
|
+
api_auth_data.api_key_string in get_cached_settings().api_correct_api_keys
|
169
124
|
)
|
170
125
|
|
171
|
-
#
|
172
|
-
|
173
|
-
if require_not_mode_type is not None:
|
174
|
-
if get_cached_settings().mode_type == require_not_mode_type:
|
175
|
-
raise APIException(
|
176
|
-
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
177
|
-
error_code=APIErrorCodes.cannot_authorize,
|
178
|
-
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
179
|
-
)
|
126
|
+
# api_key_dbm
|
180
127
|
|
181
|
-
|
128
|
+
if api_auth_data.api_key_string is not None:
|
129
|
+
if get_cached_sqlalchemy_db() is not None:
|
130
|
+
async with get_cached_sqlalchemy_db().new_async_session() as async_session:
|
131
|
+
api_auth_data.api_key_dbm = await async_session.scalar(
|
132
|
+
sqlalchemy.select(ApiKeyDBM).where(ApiKeyDBM.value == api_auth_data.api_key_string)
|
133
|
+
)
|
182
134
|
|
183
|
-
|
184
|
-
raise APIException(
|
185
|
-
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
186
|
-
error_code=APIErrorCodes.cannot_authorize,
|
187
|
-
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
188
|
-
)
|
135
|
+
# user_token_dbm
|
189
136
|
|
190
|
-
|
137
|
+
if api_auth_data.user_token_string is not None:
|
138
|
+
if get_cached_sqlalchemy_db() is not None:
|
139
|
+
async with get_cached_sqlalchemy_db().new_async_session() as async_session:
|
140
|
+
api_auth_data.user_token_dbm = await async_session.scalar(
|
141
|
+
sqlalchemy.select(UserTokenDBM)
|
142
|
+
.options(joinedload(UserTokenDBM.user))
|
143
|
+
.where(UserTokenDBM.value == api_auth_data.user_token_string)
|
144
|
+
)
|
191
145
|
|
192
|
-
|
193
|
-
raise APIException(
|
194
|
-
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
195
|
-
error_code=APIErrorCodes.cannot_authorize,
|
196
|
-
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
197
|
-
)
|
146
|
+
# middlewares
|
198
147
|
|
199
|
-
|
148
|
+
for middleware in middlewares:
|
149
|
+
api_auth_data.extra_data[middleware.__name__] = True
|
200
150
|
|
201
|
-
|
202
|
-
if is_async_func(
|
203
|
-
await
|
151
|
+
for middleware in middlewares:
|
152
|
+
if is_async_func(middleware):
|
153
|
+
await middleware(
|
204
154
|
api_auth_data=api_auth_data,
|
205
155
|
request=request
|
206
156
|
)
|
207
|
-
elif is_sync_func(
|
208
|
-
|
157
|
+
elif is_sync_func(middleware):
|
158
|
+
middleware(
|
209
159
|
api_auth_data=api_auth_data,
|
210
160
|
request=request
|
211
161
|
)
|
212
162
|
else:
|
213
|
-
raise TypeError("unknown
|
163
|
+
raise TypeError(f"unknown middleware type, {middleware.__name__}")
|
214
164
|
|
215
|
-
|
165
|
+
return api_auth_data
|
216
166
|
|
217
|
-
|
218
|
-
if is_async_func(is_user_token_correct_func):
|
219
|
-
await is_user_token_correct_func(
|
220
|
-
api_auth_data=api_auth_data,
|
221
|
-
request=request
|
222
|
-
)
|
223
|
-
elif is_sync_func(is_user_token_correct_func):
|
224
|
-
is_user_token_correct_func(
|
225
|
-
api_auth_data=api_auth_data,
|
226
|
-
request=request
|
227
|
-
)
|
228
|
-
else:
|
229
|
-
raise TypeError("unknown validate_token_func type")
|
167
|
+
return async_func
|
230
168
|
|
231
|
-
# require_correct_api_key
|
232
169
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
)
|
170
|
+
def require_prod_mode_api_middleware():
|
171
|
+
def func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
172
|
+
if not get_cached_settings().prod_mode:
|
173
|
+
raise APIException(
|
174
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
175
|
+
error_code=APIErrorCodes.cannot_authorize,
|
176
|
+
error_description=f"prod_mode is required, {get_cached_settings().prod_mode=}",
|
177
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
178
|
+
)
|
241
179
|
|
242
|
-
|
180
|
+
func.__name__ = require_prod_mode_api_middleware.__name__
|
243
181
|
|
244
|
-
|
245
|
-
if not api_auth_data.is_user_token_correct:
|
246
|
-
raise APIException(
|
247
|
-
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
248
|
-
error_code=APIErrorCodes.cannot_authorize,
|
249
|
-
error_description="not api_auth_data.is_user_token_correct",
|
250
|
-
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
251
|
-
)
|
182
|
+
return func
|
252
183
|
|
253
|
-
|
184
|
+
|
185
|
+
def require_not_prod_mode_api_middleware():
|
186
|
+
def func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
187
|
+
if get_cached_settings().prod_mode:
|
188
|
+
raise APIException(
|
189
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
190
|
+
error_code=APIErrorCodes.cannot_authorize,
|
191
|
+
error_description=f"not prod_mode is required, {get_cached_settings().prod_mode=}",
|
192
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
193
|
+
)
|
194
|
+
|
195
|
+
func.__name__ = require_not_prod_mode_api_middleware.__name__
|
254
196
|
|
255
197
|
return func
|
256
198
|
|
257
199
|
|
258
|
-
def
|
259
|
-
|
260
|
-
*,
|
261
|
-
api_auth_data: APIAuthData,
|
262
|
-
request: fastapi.requests.Request,
|
263
|
-
):
|
264
|
-
if get_cached_settings().api_correct_api_keys is None:
|
265
|
-
api_auth_data.is_api_key_correct = False
|
266
|
-
return
|
200
|
+
def require_api_key_string_api_middleware():
|
201
|
+
def func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
267
202
|
if api_auth_data.api_key_string is None:
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
return
|
203
|
+
raise APIException(
|
204
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
205
|
+
error_code=APIErrorCodes.cannot_authorize,
|
206
|
+
error_description="api_key_string is required",
|
207
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
208
|
+
)
|
275
209
|
|
276
|
-
|
210
|
+
func.__name__ = require_api_key_string_api_middleware.__name__
|
277
211
|
|
212
|
+
return func
|
278
213
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
api_auth_data: APIAuthData,
|
283
|
-
request: fastapi.requests.Request,
|
284
|
-
):
|
285
|
-
if get_cached_settings().api_correct_tokens is None:
|
286
|
-
api_auth_data.is_api_key_correct = False
|
287
|
-
return
|
214
|
+
|
215
|
+
def require_user_token_string_api_middleware():
|
216
|
+
def func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
288
217
|
if api_auth_data.user_token_string is None:
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
return
|
218
|
+
raise APIException(
|
219
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
220
|
+
error_code=APIErrorCodes.cannot_authorize,
|
221
|
+
error_description="user_token_string is required",
|
222
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
223
|
+
)
|
296
224
|
|
297
|
-
|
225
|
+
func.__name__ = require_user_token_string_api_middleware.__name__
|
298
226
|
|
227
|
+
return func
|
299
228
|
|
300
|
-
def correct_api_key_from_sqlalchemy_db__is_api_key_correct_func() -> Callable:
|
301
|
-
async def async_func(
|
302
|
-
*,
|
303
|
-
api_auth_data: APIAuthData,
|
304
|
-
request: fastapi.requests.Request,
|
305
|
-
):
|
306
|
-
if api_auth_data.api_key_string is None:
|
307
|
-
api_auth_data.is_api_key_correct = False
|
308
|
-
return
|
309
229
|
|
310
|
-
|
311
|
-
|
312
|
-
|
230
|
+
def require_correct_api_key_api_middleware():
|
231
|
+
def func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
232
|
+
if not api_auth_data.is_api_key_correct:
|
233
|
+
raise APIException(
|
234
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
235
|
+
error_code=APIErrorCodes.cannot_authorize,
|
236
|
+
error_description="correct api_key_string is required",
|
237
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
313
238
|
)
|
314
239
|
|
315
|
-
|
316
|
-
|
317
|
-
|
240
|
+
func.__name__ = require_correct_api_key_api_middleware.__name__
|
241
|
+
|
242
|
+
return func
|
318
243
|
|
319
|
-
|
320
|
-
|
244
|
+
|
245
|
+
def require_api_key_dbm_api_middleware(*, require_active: bool = True):
|
246
|
+
async def async_func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
247
|
+
if api_auth_data.api_key_dbm is None:
|
248
|
+
raise APIException(
|
249
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
250
|
+
error_code=APIErrorCodes.cannot_authorize,
|
251
|
+
error_description=f"api_key_dbm is required, {require_active=}",
|
252
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
253
|
+
)
|
254
|
+
if require_active and not api_auth_data.api_key_dbm.is_active:
|
255
|
+
raise APIException(
|
256
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
257
|
+
error_code=APIErrorCodes.cannot_authorize,
|
258
|
+
error_description=f"api_key_dbm is required, {require_active=}",
|
259
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
260
|
+
)
|
261
|
+
|
262
|
+
async_func.__name__ = require_api_key_dbm_api_middleware.__name__
|
321
263
|
|
322
264
|
return async_func
|
323
265
|
|
324
266
|
|
325
|
-
def
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
267
|
+
def require_correct_api_key_or_api_key_dbm_api_middleware(*, require_active_api_key_dbm: bool = True):
|
268
|
+
async def async_func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
269
|
+
if not api_auth_data.is_api_key_correct and (
|
270
|
+
api_auth_data.api_key_dbm is None
|
271
|
+
or (require_active_api_key_dbm and not api_auth_data.api_key_dbm.is_active)
|
272
|
+
):
|
273
|
+
raise APIException(
|
274
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
275
|
+
error_code=APIErrorCodes.cannot_authorize,
|
276
|
+
error_description=(
|
277
|
+
f"correct api_key is required or api_key_dbm is required, {require_active_api_key_dbm=}"
|
278
|
+
),
|
279
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
280
|
+
)
|
281
|
+
|
282
|
+
async_func.__name__ = require_correct_api_key_or_api_key_dbm_api_middleware.__name__
|
283
|
+
|
284
|
+
return async_func
|
336
285
|
|
337
|
-
with get_cached_sqlalchemy_db().new_session() as session:
|
338
|
-
api_auth_data.user_token_dbm = session.query(
|
339
|
-
UserTokenDBM
|
340
|
-
).filter(
|
341
|
-
UserTokenDBM.value == api_auth_data.user_token_string
|
342
|
-
).one_or_none()
|
343
286
|
|
287
|
+
def require_user_token_dbm_api_middleware(
|
288
|
+
*, require_active_user_token: bool = True,
|
289
|
+
require_user_roles: list[str] | None = None
|
290
|
+
):
|
291
|
+
async def async_func(*, api_auth_data: APIAuthData, request: fastapi.requests.Request):
|
344
292
|
if api_auth_data.user_token_dbm is None:
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
293
|
+
raise APIException(
|
294
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
295
|
+
error_code=APIErrorCodes.cannot_authorize,
|
296
|
+
error_description=f"user_key_dbm is required, {require_active_user_token=}, {require_user_roles=}",
|
297
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
298
|
+
)
|
299
|
+
if require_active_user_token and not api_auth_data.user_token_dbm.is_active:
|
300
|
+
raise APIException(
|
301
|
+
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
302
|
+
error_code=APIErrorCodes.cannot_authorize,
|
303
|
+
error_description=f"user_key_dbm is required, {require_active_user_token=}, {require_user_roles=}",
|
304
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
305
|
+
)
|
306
|
+
if require_user_roles is not None:
|
307
|
+
if not api_auth_data.user_token_dbm.user.compare_roles(require_user_roles):
|
308
|
+
raise APIException(
|
309
|
+
status_code=fastapi.status.HTTP_403_FORBIDDEN,
|
310
|
+
error_code=APIErrorCodes.cannot_authorize,
|
311
|
+
error_description=f"user_key_dbm is required, {require_active_user_token=}, {require_user_roles=}",
|
312
|
+
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
313
|
+
)
|
349
314
|
|
350
|
-
|
351
|
-
return
|
315
|
+
async_func.__name__ = require_user_token_dbm_api_middleware.__name__
|
352
316
|
|
353
317
|
return async_func
|
@@ -11,12 +11,10 @@ from arpakitlib.ar_datetime_util import now_utc_dt
|
|
11
11
|
from arpakitlib.ar_dict_util import combine_dicts
|
12
12
|
from arpakitlib.ar_exception_util import exception_to_traceback_str
|
13
13
|
from arpakitlib.ar_func_util import raise_if_not_async_func, is_async_func, is_sync_func
|
14
|
-
from arpakitlib.ar_json_util import transfer_data_to_json_str
|
15
14
|
from project.api.const import APIErrorCodes
|
16
15
|
from project.api.exception import APIException
|
17
16
|
from project.api.response import APIJSONResponse
|
18
17
|
from project.api.schema.out.common.error import ErrorCommonSO
|
19
|
-
from project.core.settings import get_cached_settings
|
20
18
|
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
21
19
|
from project.sqlalchemy_db_.sqlalchemy_model import StoryLogDBM
|
22
20
|
|
@@ -175,7 +173,7 @@ def logging_func_before_in_api_exception_handler(
|
|
175
173
|
):
|
176
174
|
return
|
177
175
|
|
178
|
-
_logger.
|
176
|
+
_logger.exception(exception)
|
179
177
|
|
180
178
|
return func
|
181
179
|
|
@@ -237,10 +235,7 @@ def get_exception_handler() -> Callable:
|
|
237
235
|
funcs_before = []
|
238
236
|
async_funcs_after = []
|
239
237
|
|
240
|
-
if (
|
241
|
-
get_cached_settings().api_create_story_log_func_before_in_api_exception_handler
|
242
|
-
and get_cached_sqlalchemy_db() is not None
|
243
|
-
):
|
238
|
+
if get_cached_sqlalchemy_db() is not None:
|
244
239
|
funcs_before.append(
|
245
240
|
create_story_log_func_before_in_api_exception_handler(
|
246
241
|
ignore_api_error_codes=[
|
@@ -1,8 +1,11 @@
|
|
1
1
|
import fastapi
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
+
from project.api.auth import require_user_token_dbm_api_middleware, APIAuthData, \
|
5
|
+
api_auth, require_correct_api_key_or_api_key_dbm_api_middleware
|
4
6
|
from project.api.schema.out.common.error import ErrorCommonSO
|
5
7
|
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
8
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
6
9
|
from project.util.arpakitlib_project_template import get_arpakitlib_project_template_info
|
7
10
|
|
8
11
|
api_router = APIRouter()
|
@@ -17,7 +20,14 @@ api_router = APIRouter()
|
|
17
20
|
async def _(
|
18
21
|
*,
|
19
22
|
request: fastapi.requests.Request,
|
20
|
-
response: fastapi.responses.Response
|
23
|
+
response: fastapi.responses.Response,
|
24
|
+
api_auth_data: APIAuthData = fastapi.Depends(api_auth(middlewares=[
|
25
|
+
require_correct_api_key_or_api_key_dbm_api_middleware(require_active_api_key_dbm=True),
|
26
|
+
require_user_token_dbm_api_middleware(
|
27
|
+
require_active_user_token=True,
|
28
|
+
require_user_roles=[UserDBM.Roles.admin]
|
29
|
+
)
|
30
|
+
]))
|
21
31
|
):
|
22
32
|
arpakitlib_project_template_data = get_arpakitlib_project_template_info()
|
23
33
|
return RawDataCommonSO(data=arpakitlib_project_template_data)
|
@@ -2,9 +2,11 @@ import fastapi.requests
|
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
4
|
from arpakitlib.ar_json_util import transfer_data_to_json_str_to_data
|
5
|
-
from project.api.auth import APIAuthData, api_auth
|
5
|
+
from project.api.auth import APIAuthData, api_auth, require_user_token_dbm_api_middleware, \
|
6
|
+
require_correct_api_key_or_api_key_dbm_api_middleware
|
6
7
|
from project.api.schema.out.common.error import ErrorCommonSO
|
7
8
|
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
9
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
8
10
|
|
9
11
|
api_router = APIRouter()
|
10
12
|
|
@@ -19,12 +21,12 @@ async def _(
|
|
19
21
|
*,
|
20
22
|
request: fastapi.requests.Request,
|
21
23
|
response: fastapi.responses.Response,
|
22
|
-
api_auth_data: APIAuthData = fastapi.Depends(api_auth(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
))
|
24
|
+
api_auth_data: APIAuthData = fastapi.Depends(api_auth(middlewares=[
|
25
|
+
require_correct_api_key_or_api_key_dbm_api_middleware(require_active_api_key_dbm=True),
|
26
|
+
require_user_token_dbm_api_middleware(
|
27
|
+
require_active_user_token=True,
|
28
|
+
require_user_roles=[UserDBM.Roles.admin]
|
29
|
+
)
|
30
|
+
]))
|
29
31
|
):
|
30
32
|
return RawDataCommonSO(data=transfer_data_to_json_str_to_data(api_auth_data.model_dump()))
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import fastapi.requests
|
2
|
+
import sqlalchemy
|
3
|
+
from fastapi import APIRouter
|
4
|
+
|
5
|
+
from project.api.auth import APIAuthData, api_auth, require_user_token_dbm_api_middleware, \
|
6
|
+
require_correct_api_key_or_api_key_dbm_api_middleware
|
7
|
+
from project.api.schema.out.admin.story_log import StoryLogAdminSO
|
8
|
+
from project.api.schema.out.common.error import ErrorCommonSO
|
9
|
+
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
10
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM, StoryLogDBM
|
11
|
+
|
12
|
+
api_router = APIRouter()
|
13
|
+
|
14
|
+
|
15
|
+
@api_router.get(
|
16
|
+
"",
|
17
|
+
name="Get story log",
|
18
|
+
status_code=fastapi.status.HTTP_200_OK,
|
19
|
+
response_model=StoryLogAdminSO | None | ErrorCommonSO,
|
20
|
+
)
|
21
|
+
async def _(
|
22
|
+
*,
|
23
|
+
request: fastapi.requests.Request,
|
24
|
+
response: fastapi.responses.Response,
|
25
|
+
api_auth_data: APIAuthData = fastapi.Depends(api_auth(middlewares=[
|
26
|
+
require_correct_api_key_or_api_key_dbm_api_middleware(require_active_api_key_dbm=True),
|
27
|
+
require_user_token_dbm_api_middleware(
|
28
|
+
require_active_user_token=True,
|
29
|
+
require_user_roles=[UserDBM.Roles.admin]
|
30
|
+
)
|
31
|
+
])),
|
32
|
+
filter_id: int | None = fastapi.Query(default=None),
|
33
|
+
filter_long_id: str | None = fastapi.Query(default=None),
|
34
|
+
):
|
35
|
+
if filter_id is None and filter_long_id is None:
|
36
|
+
return None
|
37
|
+
|
38
|
+
query = sqlalchemy.select(StoryLogDBM)
|
39
|
+
if filter_id is not None:
|
40
|
+
query = query.filter(StoryLogDBM.id == filter_id)
|
41
|
+
if filter_long_id is not None:
|
42
|
+
query = query.filter(StoryLogDBM.long_id == filter_long_id)
|
43
|
+
|
44
|
+
async with get_cached_sqlalchemy_db().new_async_session() as async_session:
|
45
|
+
result = await async_session.scalar(query)
|
46
|
+
return StoryLogAdminSO.from_dbm(simple_dbm=result)
|