arpakitlib 1.8.22__py3-none-any.whl → 1.8.23__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/project/api/{auth.py → authorize.py} +28 -28
- arpakitlib/_arpakit_project_template_v_5/project/api/create_api_app.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_arpakitlib_project_template_info.py +6 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_auth_data.py +7 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_story_log.py +12 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/init_sqlalchemy_db.py +36 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/main_router.py +6 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/raise_fake_error.py +7 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/reinit_sqlalchemy_db.py +9 -7
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_api_key.py +35 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user.py +7 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user_token.py +7 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_errors_info.py +7 -5
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/main_router.py +6 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/api_key.py +14 -0
- arpakitlib/_arpakit_project_template_v_5/project/core/settings.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/resource/static/sqladmin-favicon.png +0 -0
- arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_1.py +1 -3
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/add_admin_in_app.py +9 -3
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/admin_authorize.py +86 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/__init__.py +3 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/api_key.py +33 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/user.py +40 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/user_token.py +34 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py +0 -4
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py +0 -4
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user_token.py +2 -2
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_api_keys.py +2 -0
- {arpakitlib-1.8.22.dist-info → arpakitlib-1.8.23.dist-info}/METADATA +1 -1
- {arpakitlib-1.8.22.dist-info → arpakitlib-1.8.23.dist-info}/RECORD +34 -27
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/admin_auth.py +0 -66
- {arpakitlib-1.8.22.dist-info → arpakitlib-1.8.23.dist-info}/LICENSE +0 -0
- {arpakitlib-1.8.22.dist-info → arpakitlib-1.8.23.dist-info}/WHEEL +0 -0
- {arpakitlib-1.8.22.dist-info → arpakitlib-1.8.23.dist-info}/entry_points.txt +0 -0
@@ -19,7 +19,7 @@ from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
|
19
19
|
from project.sqlalchemy_db_.sqlalchemy_model import ApiKeyDBM, UserTokenDBM
|
20
20
|
|
21
21
|
|
22
|
-
class
|
22
|
+
class APIAuthorizeData(BaseModel):
|
23
23
|
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, from_attributes=True)
|
24
24
|
|
25
25
|
api_key_string: str | None = None
|
@@ -35,7 +35,7 @@ class APIAuthData(BaseModel):
|
|
35
35
|
extra_data: dict[str, Any] = {}
|
36
36
|
|
37
37
|
|
38
|
-
def
|
38
|
+
def api_authorize(
|
39
39
|
*,
|
40
40
|
middlewares: list[Callable] | None = None
|
41
41
|
) -> Callable:
|
@@ -51,9 +51,9 @@ def api_auth(
|
|
51
51
|
APIKeyHeader(name="apikey", auto_error=False)
|
52
52
|
),
|
53
53
|
request: fastapi.requests.Request
|
54
|
-
) ->
|
54
|
+
) -> APIAuthorizeData:
|
55
55
|
|
56
|
-
api_auth_data =
|
56
|
+
api_auth_data = APIAuthorizeData(
|
57
57
|
prod_mode=get_cached_settings().prod_mode
|
58
58
|
)
|
59
59
|
|
@@ -167,8 +167,8 @@ def api_auth(
|
|
167
167
|
return async_func
|
168
168
|
|
169
169
|
|
170
|
-
def
|
171
|
-
def func(*, api_auth_data:
|
170
|
+
def require_prod_mode_api_authorize_middleware():
|
171
|
+
def func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
172
172
|
if not get_cached_settings().prod_mode:
|
173
173
|
raise APIException(
|
174
174
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -177,13 +177,13 @@ def require_prod_mode_api_middleware():
|
|
177
177
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
178
178
|
)
|
179
179
|
|
180
|
-
func.__name__ =
|
180
|
+
func.__name__ = require_prod_mode_api_authorize_middleware.__name__
|
181
181
|
|
182
182
|
return func
|
183
183
|
|
184
184
|
|
185
|
-
def
|
186
|
-
def func(*, api_auth_data:
|
185
|
+
def require_not_prod_mode_api_authorize_middleware():
|
186
|
+
def func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
187
187
|
if get_cached_settings().prod_mode:
|
188
188
|
raise APIException(
|
189
189
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -192,13 +192,13 @@ def require_not_prod_mode_api_middleware():
|
|
192
192
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
193
193
|
)
|
194
194
|
|
195
|
-
func.__name__ =
|
195
|
+
func.__name__ = require_not_prod_mode_api_authorize_middleware.__name__
|
196
196
|
|
197
197
|
return func
|
198
198
|
|
199
199
|
|
200
|
-
def
|
201
|
-
def func(*, api_auth_data:
|
200
|
+
def require_api_key_string_api_authorize_middleware():
|
201
|
+
def func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
202
202
|
if api_auth_data.api_key_string is None:
|
203
203
|
raise APIException(
|
204
204
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -207,13 +207,13 @@ def require_api_key_string_api_middleware():
|
|
207
207
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
208
208
|
)
|
209
209
|
|
210
|
-
func.__name__ =
|
210
|
+
func.__name__ = require_api_key_string_api_authorize_middleware.__name__
|
211
211
|
|
212
212
|
return func
|
213
213
|
|
214
214
|
|
215
|
-
def
|
216
|
-
def func(*, api_auth_data:
|
215
|
+
def require_user_token_string_api_authorize_middleware():
|
216
|
+
def func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
217
217
|
if api_auth_data.user_token_string is None:
|
218
218
|
raise APIException(
|
219
219
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -222,13 +222,13 @@ def require_user_token_string_api_middleware():
|
|
222
222
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
223
223
|
)
|
224
224
|
|
225
|
-
func.__name__ =
|
225
|
+
func.__name__ = require_user_token_string_api_authorize_middleware.__name__
|
226
226
|
|
227
227
|
return func
|
228
228
|
|
229
229
|
|
230
|
-
def
|
231
|
-
def func(*, api_auth_data:
|
230
|
+
def require_correct_api_key_api_authorize_middleware():
|
231
|
+
def func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
232
232
|
if not api_auth_data.is_api_key_correct:
|
233
233
|
raise APIException(
|
234
234
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -237,13 +237,13 @@ def require_correct_api_key_api_middleware():
|
|
237
237
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
238
238
|
)
|
239
239
|
|
240
|
-
func.__name__ =
|
240
|
+
func.__name__ = require_correct_api_key_api_authorize_middleware.__name__
|
241
241
|
|
242
242
|
return func
|
243
243
|
|
244
244
|
|
245
|
-
def
|
246
|
-
async def async_func(*, api_auth_data:
|
245
|
+
def require_api_key_dbm_api_authorize_middleware(*, require_active: bool = True):
|
246
|
+
async def async_func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
247
247
|
if api_auth_data.api_key_dbm is None:
|
248
248
|
raise APIException(
|
249
249
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -259,13 +259,13 @@ def require_api_key_dbm_api_middleware(*, require_active: bool = True):
|
|
259
259
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
260
260
|
)
|
261
261
|
|
262
|
-
async_func.__name__ =
|
262
|
+
async_func.__name__ = require_api_key_dbm_api_authorize_middleware.__name__
|
263
263
|
|
264
264
|
return async_func
|
265
265
|
|
266
266
|
|
267
|
-
def
|
268
|
-
async def async_func(*, api_auth_data:
|
267
|
+
def require_correct_api_key_or_api_key_dbm_api_authorize_middleware(*, require_active_api_key_dbm: bool = True):
|
268
|
+
async def async_func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
269
269
|
if not api_auth_data.is_api_key_correct and (
|
270
270
|
api_auth_data.api_key_dbm is None
|
271
271
|
or (require_active_api_key_dbm and not api_auth_data.api_key_dbm.is_active)
|
@@ -279,16 +279,16 @@ def require_correct_api_key_or_api_key_dbm_api_middleware(*, require_active_api_
|
|
279
279
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
280
280
|
)
|
281
281
|
|
282
|
-
async_func.__name__ =
|
282
|
+
async_func.__name__ = require_correct_api_key_or_api_key_dbm_api_authorize_middleware.__name__
|
283
283
|
|
284
284
|
return async_func
|
285
285
|
|
286
286
|
|
287
|
-
def
|
287
|
+
def require_user_token_dbm_api_authorize_middleware(
|
288
288
|
*, require_active_user_token: bool = True,
|
289
289
|
require_user_roles: list[str] | None = None
|
290
290
|
):
|
291
|
-
async def async_func(*, api_auth_data:
|
291
|
+
async def async_func(*, api_auth_data: APIAuthorizeData, request: fastapi.requests.Request):
|
292
292
|
if api_auth_data.user_token_dbm is None:
|
293
293
|
raise APIException(
|
294
294
|
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
@@ -312,6 +312,6 @@ def require_user_token_dbm_api_middleware(
|
|
312
312
|
error_data=transfer_data_to_json_str_to_data(api_auth_data.model_dump())
|
313
313
|
)
|
314
314
|
|
315
|
-
async_func.__name__ =
|
315
|
+
async_func.__name__ = require_user_token_dbm_api_authorize_middleware.__name__
|
316
316
|
|
317
317
|
return async_func
|
@@ -64,7 +64,7 @@ def create_api_app(*, prefix: str = "/api") -> FastAPI:
|
|
64
64
|
|
65
65
|
if get_cached_settings().api_enable_sqladmin:
|
66
66
|
from project.sqladmin_.add_admin_in_app import add_sqladmin_in_app
|
67
|
-
add_sqladmin_in_app(app=api_app)
|
67
|
+
add_sqladmin_in_app(app=api_app, favicon_url="/static/openapi-favicon.png")
|
68
68
|
|
69
69
|
_logger.info("finish")
|
70
70
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import fastapi
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
-
from project.api.
|
5
|
-
|
4
|
+
from project.api.authorize import require_user_token_dbm_api_authorize_middleware, APIAuthorizeData, \
|
5
|
+
api_authorize, require_api_key_dbm_api_authorize_middleware
|
6
6
|
from project.api.schema.out.common.error import ErrorCommonSO
|
7
7
|
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
8
8
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -21,9 +21,10 @@ async def _(
|
|
21
21
|
*,
|
22
22
|
request: fastapi.requests.Request,
|
23
23
|
response: fastapi.responses.Response,
|
24
|
-
api_auth_data:
|
25
|
-
|
26
|
-
|
24
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
25
|
+
require_api_key_dbm_api_authorize_middleware(
|
26
|
+
require_active=True
|
27
|
+
), require_user_token_dbm_api_authorize_middleware(
|
27
28
|
require_active_user_token=True,
|
28
29
|
require_user_roles=[UserDBM.Roles.admin]
|
29
30
|
)
|
@@ -2,8 +2,8 @@ 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.
|
6
|
-
|
5
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
6
|
+
require_api_key_dbm_api_authorize_middleware
|
7
7
|
from project.api.schema.out.common.error import ErrorCommonSO
|
8
8
|
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
9
9
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -21,9 +21,11 @@ async def _(
|
|
21
21
|
*,
|
22
22
|
request: fastapi.requests.Request,
|
23
23
|
response: fastapi.responses.Response,
|
24
|
-
api_auth_data:
|
25
|
-
|
26
|
-
|
24
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
25
|
+
require_api_key_dbm_api_authorize_middleware(
|
26
|
+
require_active=True
|
27
|
+
),
|
28
|
+
require_user_token_dbm_api_authorize_middleware(
|
27
29
|
require_active_user_token=True,
|
28
30
|
require_user_roles=[UserDBM.Roles.admin]
|
29
31
|
)
|
@@ -2,8 +2,8 @@ import fastapi.requests
|
|
2
2
|
import sqlalchemy
|
3
3
|
from fastapi import APIRouter
|
4
4
|
|
5
|
-
from project.api.
|
6
|
-
|
5
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
6
|
+
require_api_key_dbm_api_authorize_middleware
|
7
7
|
from project.api.schema.out.admin.story_log import StoryLogAdminSO
|
8
8
|
from project.api.schema.out.common.error import ErrorCommonSO
|
9
9
|
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
@@ -22,15 +22,18 @@ async def _(
|
|
22
22
|
*,
|
23
23
|
request: fastapi.requests.Request,
|
24
24
|
response: fastapi.responses.Response,
|
25
|
-
api_auth_data:
|
26
|
-
|
27
|
-
|
25
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
26
|
+
require_api_key_dbm_api_authorize_middleware(
|
27
|
+
require_active=True
|
28
|
+
),
|
29
|
+
require_user_token_dbm_api_authorize_middleware(
|
28
30
|
require_active_user_token=True,
|
29
31
|
require_user_roles=[UserDBM.Roles.admin]
|
30
32
|
)
|
31
33
|
])),
|
32
34
|
filter_id: int | None = fastapi.Query(default=None),
|
33
35
|
filter_long_id: str | None = fastapi.Query(default=None),
|
36
|
+
filter_slug: str | None = fastapi.Query(default=None),
|
34
37
|
):
|
35
38
|
if filter_id is None and filter_long_id is None:
|
36
39
|
return None
|
@@ -40,7 +43,11 @@ async def _(
|
|
40
43
|
query = query.filter(StoryLogDBM.id == filter_id)
|
41
44
|
if filter_long_id is not None:
|
42
45
|
query = query.filter(StoryLogDBM.long_id == filter_long_id)
|
46
|
+
if filter_slug is not None:
|
47
|
+
query = query.filter(StoryLogDBM.slug == filter_slug)
|
43
48
|
|
44
49
|
async with get_cached_sqlalchemy_db().new_async_session() as async_session:
|
45
50
|
result = await async_session.scalar(query)
|
51
|
+
if result is None:
|
52
|
+
return None
|
46
53
|
return StoryLogAdminSO.from_dbm(simple_dbm=result)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import fastapi.requests
|
2
|
+
from fastapi import APIRouter
|
3
|
+
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_api_key_dbm_api_authorize_middleware, require_not_prod_mode_api_authorize_middleware
|
6
|
+
from project.api.schema.out.common.error import ErrorCommonSO
|
7
|
+
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
8
|
+
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
9
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
10
|
+
|
11
|
+
api_router = APIRouter()
|
12
|
+
|
13
|
+
|
14
|
+
@api_router.get(
|
15
|
+
path="",
|
16
|
+
name="Init sqlalchemy db",
|
17
|
+
status_code=fastapi.status.HTTP_200_OK,
|
18
|
+
response_model=RawDataCommonSO | ErrorCommonSO,
|
19
|
+
)
|
20
|
+
async def _(
|
21
|
+
*,
|
22
|
+
request: fastapi.requests.Request,
|
23
|
+
response: fastapi.responses.Response,
|
24
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
25
|
+
require_not_prod_mode_api_authorize_middleware(),
|
26
|
+
require_api_key_dbm_api_authorize_middleware(
|
27
|
+
require_active=True
|
28
|
+
),
|
29
|
+
require_user_token_dbm_api_authorize_middleware(
|
30
|
+
require_active_user_token=True,
|
31
|
+
require_user_roles=[UserDBM.Roles.admin],
|
32
|
+
),
|
33
|
+
]))
|
34
|
+
):
|
35
|
+
get_cached_sqlalchemy_db().init()
|
36
|
+
return RawDataCommonSO()
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from fastapi import APIRouter
|
2
2
|
|
3
3
|
from project.api.router.admin import get_auth_data, get_arpakitlib_project_template_info, raise_fake_error, \
|
4
|
-
reinit_sqlalchemy_db, get_story_log
|
4
|
+
reinit_sqlalchemy_db, get_story_log, init_sqlalchemy_db
|
5
5
|
|
6
6
|
main_admin_api_router = APIRouter()
|
7
7
|
|
@@ -29,3 +29,8 @@ main_admin_api_router.include_router(
|
|
29
29
|
router=get_story_log.api_router,
|
30
30
|
prefix="/get_story_log"
|
31
31
|
)
|
32
|
+
|
33
|
+
main_admin_api_router.include_router(
|
34
|
+
router=init_sqlalchemy_db.api_router,
|
35
|
+
prefix="/init_sqlalchemy_db"
|
36
|
+
)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import fastapi.requests
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
-
from project.api.
|
5
|
-
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_api_key_dbm_api_authorize_middleware
|
6
6
|
from project.api.schema.out.common.error import ErrorCommonSO
|
7
7
|
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
8
8
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -20,9 +20,11 @@ async def _(
|
|
20
20
|
*,
|
21
21
|
request: fastapi.requests.Request,
|
22
22
|
response: fastapi.responses.Response,
|
23
|
-
api_auth_data:
|
24
|
-
|
25
|
-
|
23
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
24
|
+
require_api_key_dbm_api_authorize_middleware(
|
25
|
+
require_active=True
|
26
|
+
),
|
27
|
+
require_user_token_dbm_api_authorize_middleware(
|
26
28
|
require_active_user_token=True,
|
27
29
|
require_user_roles=[UserDBM.Roles.admin]
|
28
30
|
)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import fastapi.requests
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
-
from project.api.
|
5
|
-
|
6
|
-
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_not_prod_mode_api_authorize_middleware, \
|
6
|
+
require_api_key_dbm_api_authorize_middleware
|
7
7
|
from project.api.schema.out.common.error import ErrorCommonSO
|
8
8
|
from project.api.schema.out.common.raw_data import RawDataCommonSO
|
9
9
|
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
@@ -22,10 +22,12 @@ async def _(
|
|
22
22
|
*,
|
23
23
|
request: fastapi.requests.Request,
|
24
24
|
response: fastapi.responses.Response,
|
25
|
-
api_auth_data:
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
26
|
+
require_not_prod_mode_api_authorize_middleware(),
|
27
|
+
require_api_key_dbm_api_authorize_middleware(
|
28
|
+
require_active=True
|
29
|
+
),
|
30
|
+
require_user_token_dbm_api_authorize_middleware(
|
29
31
|
require_active_user_token=True,
|
30
32
|
require_user_roles=[UserDBM.Roles.admin],
|
31
33
|
),
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import fastapi.requests
|
2
|
+
from fastapi import APIRouter
|
3
|
+
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_api_key_dbm_api_authorize_middleware
|
6
|
+
from project.api.schema.out.client.api_key import ApiKeyClientSO
|
7
|
+
from project.api.schema.out.common.error import ErrorCommonSO
|
8
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
9
|
+
|
10
|
+
api_router = APIRouter()
|
11
|
+
|
12
|
+
|
13
|
+
@api_router.get(
|
14
|
+
"",
|
15
|
+
name="Get current api key",
|
16
|
+
status_code=fastapi.status.HTTP_200_OK,
|
17
|
+
response_model=ApiKeyClientSO | str | ErrorCommonSO,
|
18
|
+
)
|
19
|
+
async def _(
|
20
|
+
*,
|
21
|
+
request: fastapi.requests.Request,
|
22
|
+
response: fastapi.responses.Response,
|
23
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
24
|
+
require_api_key_dbm_api_authorize_middleware(
|
25
|
+
require_active=True
|
26
|
+
),
|
27
|
+
require_user_token_dbm_api_authorize_middleware(
|
28
|
+
require_active_user_token=True,
|
29
|
+
require_user_roles=[UserDBM.Roles.client]
|
30
|
+
)
|
31
|
+
]))
|
32
|
+
):
|
33
|
+
return ApiKeyClientSO.from_dbm(
|
34
|
+
simple_dbm=api_auth_data.api_key_dbm
|
35
|
+
)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import fastapi.requests
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
-
from project.api.
|
5
|
-
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_api_key_dbm_api_authorize_middleware
|
6
6
|
from project.api.schema.out.client.user import UserClientSO
|
7
7
|
from project.api.schema.out.common.error import ErrorCommonSO
|
8
8
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -20,9 +20,11 @@ async def _(
|
|
20
20
|
*,
|
21
21
|
request: fastapi.requests.Request,
|
22
22
|
response: fastapi.responses.Response,
|
23
|
-
api_auth_data:
|
24
|
-
|
25
|
-
|
23
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
24
|
+
require_api_key_dbm_api_authorize_middleware(
|
25
|
+
require_active=True
|
26
|
+
),
|
27
|
+
require_user_token_dbm_api_authorize_middleware(
|
26
28
|
require_active_user_token=True,
|
27
29
|
require_user_roles=[UserDBM.Roles.client]
|
28
30
|
)
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user_token.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import fastapi.requests
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
-
from project.api.
|
5
|
-
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_api_key_dbm_api_authorize_middleware
|
6
6
|
from project.api.schema.out.client.user_token import UserTokenClientSO
|
7
7
|
from project.api.schema.out.common.error import ErrorCommonSO
|
8
8
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -20,9 +20,11 @@ async def _(
|
|
20
20
|
*,
|
21
21
|
request: fastapi.requests.Request,
|
22
22
|
response: fastapi.responses.Response,
|
23
|
-
api_auth_data:
|
24
|
-
|
25
|
-
|
23
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
24
|
+
require_api_key_dbm_api_authorize_middleware(
|
25
|
+
require_active=True
|
26
|
+
),
|
27
|
+
require_user_token_dbm_api_authorize_middleware(
|
26
28
|
require_active_user_token=True,
|
27
29
|
require_user_roles=[UserDBM.Roles.client]
|
28
30
|
)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import fastapi.requests
|
2
2
|
from fastapi import APIRouter
|
3
3
|
|
4
|
-
from project.api.
|
5
|
-
|
4
|
+
from project.api.authorize import APIAuthorizeData, api_authorize, require_user_token_dbm_api_authorize_middleware, \
|
5
|
+
require_api_key_dbm_api_authorize_middleware
|
6
6
|
from project.api.const import APIErrorCodes, APIErrorSpecificationCodes
|
7
7
|
from project.api.schema.out.common.error import ErrorCommonSO
|
8
8
|
from project.api.schema.out.general.errors_info_general import ErrorsInfoGeneralSO
|
@@ -20,9 +20,11 @@ async def _(
|
|
20
20
|
*,
|
21
21
|
request: fastapi.requests.Request,
|
22
22
|
response: fastapi.responses.Response,
|
23
|
-
api_auth_data:
|
24
|
-
|
25
|
-
|
23
|
+
api_auth_data: APIAuthorizeData = fastapi.Depends(api_authorize(middlewares=[
|
24
|
+
require_api_key_dbm_api_authorize_middleware(
|
25
|
+
require_active=True
|
26
|
+
),
|
27
|
+
require_user_token_dbm_api_authorize_middleware(
|
26
28
|
require_active_user_token=True
|
27
29
|
)
|
28
30
|
]))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from fastapi import APIRouter
|
2
2
|
|
3
|
-
from project.api.router.client import get_errors_info, get_current_user, get_current_user_token
|
3
|
+
from project.api.router.client import get_errors_info, get_current_user, get_current_user_token, get_current_api_key
|
4
4
|
|
5
5
|
main_client_api_router = APIRouter()
|
6
6
|
|
@@ -18,3 +18,8 @@ main_client_api_router.include_router(
|
|
18
18
|
router=get_current_user_token.api_router,
|
19
19
|
prefix="/get_current_user_token"
|
20
20
|
)
|
21
|
+
|
22
|
+
main_client_api_router.include_router(
|
23
|
+
router=get_current_api_key.api_router,
|
24
|
+
prefix="/get_current_api_key"
|
25
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from project.api.schema.out.client.common import SimpleDBMClientSO
|
4
|
+
from project.sqlalchemy_db_.sqlalchemy_model import ApiKeyDBM
|
5
|
+
|
6
|
+
|
7
|
+
class ApiKeyClientSO(SimpleDBMClientSO):
|
8
|
+
title: str | None
|
9
|
+
value: str
|
10
|
+
is_active: bool
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def from_dbm(cls, *, simple_dbm: ApiKeyDBM) -> ApiKeyClientSO:
|
14
|
+
return cls.model_validate(simple_dbm.simple_dict_with_sd_properties())
|
@@ -106,10 +106,10 @@ class Settings(SimpleSettings):
|
|
106
106
|
|
107
107
|
sqladmin_secret_key: str | None = "85a9583cb91c4de7a78d7eb1e5306a04418c9c43014c447ea8ec8dd5deb4cf71"
|
108
108
|
|
109
|
-
|
109
|
+
sqladmin_authorize_keys: list[str] | None = ["1"]
|
110
110
|
|
111
|
-
@field_validator("
|
112
|
-
def
|
111
|
+
@field_validator("sqladmin_authorize_keys", mode="before")
|
112
|
+
def validate_sqladmin_authorize_keys(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> list[str] | None:
|
113
113
|
if isinstance(v, str):
|
114
114
|
v = [v]
|
115
115
|
if isinstance(v, int):
|
Binary file
|
@@ -2,12 +2,17 @@ from fastapi import FastAPI
|
|
2
2
|
from sqladmin import Admin
|
3
3
|
|
4
4
|
from project.core.settings import get_cached_settings
|
5
|
-
from project.sqladmin_.
|
5
|
+
from project.sqladmin_.admin_authorize import SQLAdminAuth
|
6
6
|
from project.sqladmin_.model_view import SimpleMV
|
7
7
|
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
8
8
|
|
9
9
|
|
10
|
-
def add_sqladmin_in_app(
|
10
|
+
def add_sqladmin_in_app(
|
11
|
+
*,
|
12
|
+
app: FastAPI,
|
13
|
+
base_url: str = "/sqladmin",
|
14
|
+
favicon_url: str | None = None
|
15
|
+
) -> FastAPI:
|
11
16
|
authentication_backend = SQLAdminAuth()
|
12
17
|
|
13
18
|
admin = Admin(
|
@@ -15,7 +20,8 @@ def add_sqladmin_in_app(*, app: FastAPI, base_url: str = "/sqladmin") -> FastAPI
|
|
15
20
|
engine=get_cached_sqlalchemy_db().engine,
|
16
21
|
base_url=base_url,
|
17
22
|
authentication_backend=authentication_backend,
|
18
|
-
title=get_cached_settings().project_name
|
23
|
+
title=get_cached_settings().project_name,
|
24
|
+
favicon_url=favicon_url
|
19
25
|
)
|
20
26
|
|
21
27
|
for model_view in SimpleMV.__subclasses__():
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
import fastapi
|
4
|
+
from sqladmin.authentication import AuthenticationBackend
|
5
|
+
|
6
|
+
from arpakitlib.ar_str_util import make_none_if_blank
|
7
|
+
from project.core.settings import get_cached_settings
|
8
|
+
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
9
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserTokenDBM
|
10
|
+
|
11
|
+
SQLADMIN_AUTH_KEY = "sqladmin_auth_key"
|
12
|
+
|
13
|
+
|
14
|
+
class SQLAdminAuth(AuthenticationBackend):
|
15
|
+
def __init__(self):
|
16
|
+
self._logger = logging.getLogger(self.__class__.__name__)
|
17
|
+
super().__init__(secret_key=get_cached_settings().sqladmin_secret_key)
|
18
|
+
|
19
|
+
async def login(self, request: fastapi.Request) -> bool:
|
20
|
+
form = await request.form()
|
21
|
+
|
22
|
+
username = form.get("username")
|
23
|
+
if username:
|
24
|
+
username = username.strip()
|
25
|
+
username = make_none_if_blank(username)
|
26
|
+
|
27
|
+
password = form.get("password")
|
28
|
+
if password:
|
29
|
+
password = password.strip()
|
30
|
+
password = make_none_if_blank(password)
|
31
|
+
|
32
|
+
if (
|
33
|
+
get_cached_settings().sqladmin_authorize_keys is not None
|
34
|
+
and (username is not None or password is not None)
|
35
|
+
):
|
36
|
+
if (
|
37
|
+
(
|
38
|
+
is_username_correct := username in get_cached_settings().sqladmin_authorize_keys
|
39
|
+
)
|
40
|
+
or
|
41
|
+
(
|
42
|
+
is_password_correct := password in get_cached_settings().sqladmin_authorize_keys
|
43
|
+
)
|
44
|
+
):
|
45
|
+
if is_username_correct is True:
|
46
|
+
request.session.update({SQLADMIN_AUTH_KEY: username})
|
47
|
+
elif is_password_correct is True:
|
48
|
+
request.session.update({SQLADMIN_AUTH_KEY: password})
|
49
|
+
return True
|
50
|
+
|
51
|
+
if get_cached_sqlalchemy_db() is not None and (username is not None or password is not None):
|
52
|
+
with get_cached_sqlalchemy_db().new_session() as session:
|
53
|
+
query = session.query(UserTokenDBM)
|
54
|
+
if username is not None:
|
55
|
+
query = query.filter(UserTokenDBM.value == username)
|
56
|
+
elif password is not None:
|
57
|
+
query = query.filter(UserTokenDBM.value == password)
|
58
|
+
user_token = query.one_or_none()
|
59
|
+
if user_token is not None:
|
60
|
+
request.session.update({SQLADMIN_AUTH_KEY: user_token.value})
|
61
|
+
return True
|
62
|
+
|
63
|
+
return False
|
64
|
+
|
65
|
+
async def logout(self, request: fastapi.Request) -> bool:
|
66
|
+
request.session.clear()
|
67
|
+
return True
|
68
|
+
|
69
|
+
async def authenticate(self, request: fastapi.Request) -> bool:
|
70
|
+
sqladmin_auth_key = request.session.get(SQLADMIN_AUTH_KEY)
|
71
|
+
if sqladmin_auth_key:
|
72
|
+
sqladmin_auth_key = sqladmin_auth_key.strip()
|
73
|
+
sqladmin_auth_key = make_none_if_blank(sqladmin_auth_key)
|
74
|
+
|
75
|
+
if get_cached_settings().sqladmin_authorize_keys is not None and sqladmin_auth_key is not None:
|
76
|
+
if sqladmin_auth_key in get_cached_settings().sqladmin_authorize_keys:
|
77
|
+
return True
|
78
|
+
|
79
|
+
if get_cached_sqlalchemy_db() is not None and sqladmin_auth_key is not None:
|
80
|
+
with get_cached_sqlalchemy_db().new_session() as session:
|
81
|
+
query = session.query(UserTokenDBM).filter(UserTokenDBM.value == sqladmin_auth_key)
|
82
|
+
user_token = query.one_or_none()
|
83
|
+
if user_token is not None:
|
84
|
+
return True
|
85
|
+
|
86
|
+
return False
|
@@ -1,3 +1,6 @@
|
|
1
|
+
from project.sqladmin_.model_view.api_key import ApiKeyMV
|
1
2
|
from project.sqladmin_.model_view.common import SimpleMV
|
2
3
|
from project.sqladmin_.model_view.operation import OperationMV
|
3
4
|
from project.sqladmin_.model_view.story_log import StoryLogMV
|
5
|
+
from project.sqladmin_.model_view.user import UserMV
|
6
|
+
from project.sqladmin_.model_view.user_token import UserTokenMV
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from project.sqladmin_.model_view import SimpleMV
|
2
|
+
from project.sqlalchemy_db_.sqlalchemy_model import ApiKeyDBM
|
3
|
+
|
4
|
+
|
5
|
+
class ApiKeyMV(SimpleMV, model=ApiKeyDBM):
|
6
|
+
name = "ApiKey"
|
7
|
+
name_plural = "ApiKeys"
|
8
|
+
column_list = [
|
9
|
+
ApiKeyDBM.id,
|
10
|
+
ApiKeyDBM.long_id,
|
11
|
+
ApiKeyDBM.slug,
|
12
|
+
ApiKeyDBM.creation_dt,
|
13
|
+
ApiKeyDBM.title,
|
14
|
+
ApiKeyDBM.value,
|
15
|
+
ApiKeyDBM.is_active,
|
16
|
+
]
|
17
|
+
form_columns = [
|
18
|
+
ApiKeyDBM.slug,
|
19
|
+
ApiKeyDBM.title,
|
20
|
+
ApiKeyDBM.value,
|
21
|
+
ApiKeyDBM.is_active,
|
22
|
+
]
|
23
|
+
column_default_sort = [
|
24
|
+
(ApiKeyDBM.creation_dt, True)
|
25
|
+
]
|
26
|
+
column_searchable_list = [
|
27
|
+
ApiKeyDBM.id,
|
28
|
+
ApiKeyDBM.long_id,
|
29
|
+
ApiKeyDBM.slug,
|
30
|
+
ApiKeyDBM.title,
|
31
|
+
ApiKeyDBM.value,
|
32
|
+
ApiKeyDBM.is_active,
|
33
|
+
]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from project.sqladmin_.model_view import SimpleMV
|
2
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
3
|
+
|
4
|
+
|
5
|
+
class UserMV(SimpleMV, model=UserDBM):
|
6
|
+
name = "User"
|
7
|
+
name_plural = "Users"
|
8
|
+
column_list = [
|
9
|
+
UserDBM.id,
|
10
|
+
UserDBM.long_id,
|
11
|
+
UserDBM.slug,
|
12
|
+
UserDBM.creation_dt,
|
13
|
+
UserDBM.email,
|
14
|
+
UserDBM.roles,
|
15
|
+
UserDBM.is_active,
|
16
|
+
UserDBM.tg_id,
|
17
|
+
UserDBM.tg_bot_last_action_dt,
|
18
|
+
UserDBM.tg_data,
|
19
|
+
]
|
20
|
+
form_columns = [
|
21
|
+
UserDBM.slug,
|
22
|
+
UserDBM.email,
|
23
|
+
UserDBM.roles,
|
24
|
+
UserDBM.is_active,
|
25
|
+
UserDBM.tg_id,
|
26
|
+
UserDBM.tg_bot_last_action_dt,
|
27
|
+
UserDBM.tg_data,
|
28
|
+
]
|
29
|
+
column_default_sort = [
|
30
|
+
(UserDBM.creation_dt, True)
|
31
|
+
]
|
32
|
+
column_searchable_list = [
|
33
|
+
UserDBM.id,
|
34
|
+
UserDBM.long_id,
|
35
|
+
UserDBM.slug,
|
36
|
+
UserDBM.email,
|
37
|
+
UserDBM.roles,
|
38
|
+
UserDBM.is_active,
|
39
|
+
UserDBM.tg_id,
|
40
|
+
]
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from project.sqladmin_.model_view import SimpleMV
|
2
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserTokenDBM
|
3
|
+
|
4
|
+
|
5
|
+
class UserTokenMV(SimpleMV, model=UserTokenDBM):
|
6
|
+
name = "UserToken"
|
7
|
+
name_plural = "UserTokens"
|
8
|
+
column_list = [
|
9
|
+
UserTokenDBM.id,
|
10
|
+
UserTokenDBM.long_id,
|
11
|
+
UserTokenDBM.slug,
|
12
|
+
UserTokenDBM.creation_dt,
|
13
|
+
UserTokenDBM.value,
|
14
|
+
UserTokenDBM.user,
|
15
|
+
UserTokenDBM.is_active,
|
16
|
+
]
|
17
|
+
form_columns = [
|
18
|
+
UserTokenDBM.slug,
|
19
|
+
UserTokenDBM.creation_dt,
|
20
|
+
UserTokenDBM.value,
|
21
|
+
UserTokenDBM.user,
|
22
|
+
UserTokenDBM.is_active,
|
23
|
+
]
|
24
|
+
column_default_sort = [
|
25
|
+
(UserTokenDBM.creation_dt, True)
|
26
|
+
]
|
27
|
+
column_searchable_list = [
|
28
|
+
UserTokenDBM.id,
|
29
|
+
UserTokenDBM.long_id,
|
30
|
+
UserTokenDBM.slug,
|
31
|
+
UserTokenDBM.value,
|
32
|
+
UserTokenDBM.user_id,
|
33
|
+
UserTokenDBM.is_active,
|
34
|
+
]
|
@@ -11,6 +11,8 @@ _logger = logging.getLogger(__name__)
|
|
11
11
|
def make_test_data_1():
|
12
12
|
get_cached_settings().raise_if_prod_mode()
|
13
13
|
with get_cached_sqlalchemy_db().new_session() as session:
|
14
|
+
session.query(ApiKeyDBM).delete()
|
15
|
+
session.commit()
|
14
16
|
for i in range(10):
|
15
17
|
api_key = ApiKeyDBM(value=str(i))
|
16
18
|
session.add(api_key)
|
@@ -78,9 +78,9 @@ arpakitlib/_arpakit_project_template_v_5/project/additional_model/__init__.py,sh
|
|
78
78
|
arpakitlib/_arpakit_project_template_v_5/project/additional_model/common.py,sha256=BRz-B699ZY52DfUD6kmhpuThBWpPzpZpD0QXIjlkwC8,168
|
79
79
|
arpakitlib/_arpakit_project_template_v_5/project/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
80
80
|
arpakitlib/_arpakit_project_template_v_5/project/api/asgi.py,sha256=ES3YGwNxWUHVyD6e2ii6QkvTyB-vlVmr8_PhfVIXQ4Y,78
|
81
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/
|
81
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/authorize.py,sha256=86NyfOV0_SQcyXFrZoJph27bTK4IqtfETl46yOPfRWs,14141
|
82
82
|
arpakitlib/_arpakit_project_template_v_5/project/api/const.py,sha256=J9bqaRRiIc3RLn6SJTvdfDvFrSsM_Ixii9t2M8dA5Jc,433
|
83
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/create_api_app.py,sha256
|
83
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/create_api_app.py,sha256=C7VhAh7cTYLUBg8HyEgNTCdzvRlAC6hGYKdDrweWq6I,2327
|
84
84
|
arpakitlib/_arpakit_project_template_v_5/project/api/event.py,sha256=xKfJ3UnOx_g1s7KNZjWRp0eZbVRTsSlyJhT3wkfwT6k,2414
|
85
85
|
arpakitlib/_arpakit_project_template_v_5/project/api/exception.py,sha256=cNZaI2DacGLl8Hyn1qIfFpVjvQzOQjwXWsVW4auBrCo,1280
|
86
86
|
arpakitlib/_arpakit_project_template_v_5/project/api/exception_handler.py,sha256=Uyt0gB_8QnMFcZuNoUaEiAAJEjziWQpsXY1OM36e3YU,11125
|
@@ -88,17 +88,19 @@ arpakitlib/_arpakit_project_template_v_5/project/api/openapi_ui.py,sha256=PLhH-W
|
|
88
88
|
arpakitlib/_arpakit_project_template_v_5/project/api/response.py,sha256=xZMymP2BuQaRNVWLeIp3UgUUo-MFN8MJnsn9Al4vOb8,1028
|
89
89
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
90
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
91
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_arpakitlib_project_template_info.py,sha256=
|
92
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_auth_data.py,sha256=
|
93
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_story_log.py,sha256=
|
94
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/
|
95
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/
|
96
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/
|
91
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_arpakitlib_project_template_info.py,sha256=qF1ClV0D8mYi3mfk_BbJqyTVa7Yor7ULJ9RCxCVhC1A,1326
|
92
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_auth_data.py,sha256=ml2IanYaDow2nWhCCVeGEp1PkTZAem5I5XQowdQV_Bg,1261
|
93
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_story_log.py,sha256=y0YcC0i7XSPJz0Doy8atxBilRY2FJZn2IdzKsCQFVKs,2038
|
94
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/init_sqlalchemy_db.py,sha256=fO6FqSFxSGEvmiCbuTAgVo2wrg9iFxqEmFz0DNWx6bg,1354
|
95
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/main_router.py,sha256=I_M-ipqOnrY-sVi0jCNhSreQQi1Y8dRGqpj0koT-Vd0,950
|
96
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/raise_fake_error.py,sha256=-6zsihzxSUVve-5M71y5NDKXywlMSRIsDQn19vQ3yJo,1133
|
97
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/reinit_sqlalchemy_db.py,sha256=KoCoKndAdJi0xkuOhVv9toC0ZWvwstrOtdtgp_Bw-0Q,1364
|
97
98
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
98
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/
|
99
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/
|
100
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/
|
101
|
-
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/
|
99
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_api_key.py,sha256=gS8JJLpuqaq83BBsEh5MOLgse4i3t4Z9stSmTBd9m70,1188
|
100
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user.py,sha256=txTSIee352vky9xIuQpptncAV7gpzC9WgRxc_0Ib9WY,1178
|
101
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user_token.py,sha256=lRkChLNHiSdiAgPTPNn7GBU1vrPYIUoJ_fXBq1Sc2Jc,1200
|
102
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_errors_info.py,sha256=BIGsJIxjE5Mh4fILbfvf9yGZi2J-pxtEUF-9CZ9CYiY,1237
|
103
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/router/client/main_router.py,sha256=u5boqPilcW9ivGuOyNKRoang8-Vp3Faovu6R34RpRBY,654
|
102
104
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
103
105
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/general/healthcheck.py,sha256=xQNMi81IaroNE2H3LvYNUYV6X1dcGSeS2kAUSvGEcWA,523
|
104
106
|
arpakitlib/_arpakit_project_template_v_5/project/api/router/general/main_router.py,sha256=OPKujYKPInDozmfZrffQnMGtdwyxMBQv1k2f-LttMDY,356
|
@@ -120,6 +122,7 @@ arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/common.py,
|
|
120
122
|
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/operation.py,sha256=8oPqCofV5KJMB6JB-Cozi_5l-2t8EPSvfossT0wIZBM,699
|
121
123
|
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/story_log.py,sha256=EenBUvs9qENEGuCpZrBRSd4TNVjR-ER55g44QGPLAzY,482
|
122
124
|
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
125
|
+
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/api_key.py,sha256=opXOQvpC3turivtLkf-BOJ9Octq5HW8HaM-oRTLhrX0,429
|
123
126
|
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/common.py,sha256=cm7FAsZf5GlGAcR_w0tr2fR9gUMNcY--ifqE-BuFGpU,447
|
124
127
|
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/user.py,sha256=S93SizBPg5evgycr3Y_AF0Ds0M5gvO2o_W2VsDZXFeY,629
|
125
128
|
arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/user_token.py,sha256=XRUs3ieMDZGGnMMst-QXptBWXqBL-qPQa25NJVbSI9I,436
|
@@ -142,7 +145,7 @@ arpakitlib/_arpakit_project_template_v_5/project/core/const.py,sha256=hgiiPIYL95
|
|
142
145
|
arpakitlib/_arpakit_project_template_v_5/project/core/dump_file_storage_in_dir.py,sha256=u3-vStMGaseMsRLuJmQK04UDhaez9vw6o5jyHb1fwNg,617
|
143
146
|
arpakitlib/_arpakit_project_template_v_5/project/core/jinja2_templates.py,sha256=jCNLaBauGC7YNvZdTLNHuPp7hmRGt94O23Skg6ewo7o,352
|
144
147
|
arpakitlib/_arpakit_project_template_v_5/project/core/media_file_storage_in_dir.py,sha256=fMofTsfJtA8pp5lEUhucEUu3PBsmj-elaRZzExDsdLI,623
|
145
|
-
arpakitlib/_arpakit_project_template_v_5/project/core/settings.py,sha256=
|
148
|
+
arpakitlib/_arpakit_project_template_v_5/project/core/settings.py,sha256=MRf_7zUPGI11zw2PfbY1dgPJV7ZUIVA5kuALyNmyJkI,5843
|
146
149
|
arpakitlib/_arpakit_project_template_v_5/project/core/util.py,sha256=1ha9UrguVPsTSjoMHhVZVCD0_mNBfhIDGEvcG1nA4Zw,667
|
147
150
|
arpakitlib/_arpakit_project_template_v_5/project/json_db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
148
151
|
arpakitlib/_arpakit_project_template_v_5/project/json_db/json_db.py,sha256=tBML-z4Y7uY8f_0ggcxvlDNt15Sf93Jr_OUeHwWxqOA,724
|
@@ -159,6 +162,7 @@ arpakitlib/_arpakit_project_template_v_5/project/resource/static/healthcheck,sha
|
|
159
162
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/helloworld,sha256=eH7Hbcr9IMGQjrCTahL5Ht0QWrXNfswrGuIDJkg0Xf8,11
|
160
163
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/openapi-favicon.png,sha256=CuptowzQaCQsbXlunWIr_9akhvJ0TDIXHyuXBAFtM4I,5279
|
161
164
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/redoc/redoc.standalone.js,sha256=WCuodUNv1qVh0oW5fjnJDwb5AwOue73jKHdI9z8iGKU,909365
|
165
|
+
arpakitlib/_arpakit_project_template_v_5/project/resource/static/sqladmin-favicon.png,sha256=CuptowzQaCQsbXlunWIr_9akhvJ0TDIXHyuXBAFtM4I,5279
|
162
166
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/swagger-ui/favicon-16x16.png,sha256=ryStYE3Xs7zaj5dauXMHX0ovcKQIeUShL474tjo-B8I,665
|
163
167
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/swagger-ui/favicon-32x32.png,sha256=PtYS9B4FDKXnAAytbxy-fn2jn2X8qZwC6Z5lkQVuWDc,628
|
164
168
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/swagger-ui/index.css,sha256=kySAfUJFZaFjm7KfN1TI1NRcEAnGdnTpluMzVfaSnOc,202
|
@@ -178,7 +182,7 @@ arpakitlib/_arpakit_project_template_v_5/project/resource/static/swagger-ui/swag
|
|
178
182
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/swagger-ui/swagger-ui.js,sha256=yHKu9z0C2kOO5j9n9D8oQzuBLeEoMGrFcMWQN27V554,339285
|
179
183
|
arpakitlib/_arpakit_project_template_v_5/project/resource/static/swagger-ui/swagger-ui.js.map,sha256=jeX-b8zAm2jsTGGCznrc49McbLKSfXspwECPhEAp0Xc,1159444
|
180
184
|
arpakitlib/_arpakit_project_template_v_5/project/sandbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
181
|
-
arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_1.py,sha256=
|
185
|
+
arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_1.py,sha256=xKSp7tIBu3Ffp_kgJkwVtdam3BcoFZ44JPVHoRRaP0E,163
|
182
186
|
arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_2.py,sha256=xKSp7tIBu3Ffp_kgJkwVtdam3BcoFZ44JPVHoRRaP0E,163
|
183
187
|
arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_3.py,sha256=xKSp7tIBu3Ffp_kgJkwVtdam3BcoFZ44JPVHoRRaP0E,163
|
184
188
|
arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_4.py,sha256=xKSp7tIBu3Ffp_kgJkwVtdam3BcoFZ44JPVHoRRaP0E,163
|
@@ -194,28 +198,31 @@ arpakitlib/_arpakit_project_template_v_5/project/site/exception_handler.py,sha25
|
|
194
198
|
arpakitlib/_arpakit_project_template_v_5/project/site/router/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
195
199
|
arpakitlib/_arpakit_project_template_v_5/project/site/router/main_router.py,sha256=J5jNfgd-3OiWTtuubimEUz6wrOogupj9PkUdDmwz-2U,62
|
196
200
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
197
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/add_admin_in_app.py,sha256=
|
198
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/
|
201
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/add_admin_in_app.py,sha256=JTXLf18DQVsZtoe6yFpZIVJI95rgJofnTe9YKyVwwhQ,851
|
202
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/admin_authorize.py,sha256=otas9GzslibQlW4DpCguMnXy2UKMOvR9adULHCcF7E0,3485
|
199
203
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/asgi.py,sha256=DRlRPkcOGXOccfP73oKEXolZTsc5VWdQgEG-qoW03ms,99
|
200
204
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/create_sqladmin_app.py,sha256=x5F8--5KA4cmTrb6kAAmp6fVd2TiqxPOzxqUkSEhSG4,1298
|
201
205
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/event.py,sha256=LjAUYNlsX9Sj_QLMzYuTQbbYYbIMHhbsSjTXt-G7lOE,849
|
202
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/__init__.py,sha256=
|
206
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/__init__.py,sha256=MtEk37LR9xg5XltEbIAw1hh74BsS70I2G3C21QSbjVQ,357
|
207
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/api_key.py,sha256=t4IBg1k1pgTpAEGPMZ3TYDWfsZZcK2fCZz5C9aQ-YSA,805
|
203
208
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/common.py,sha256=WSlZ2wOyrUVX-6nO0NRCZJ9dgPrY2ytANrhkooYq_w8,560
|
204
209
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/operation.py,sha256=UILyxYDPi1jbjoBEV6ipSBUvswWd0gCbfBD8pAhpZHE,1225
|
205
210
|
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/story_log.py,sha256=vHTewzi0zo6Eg06Jci9wyswSHqyxRbyiHsbYDtNXeeM,911
|
211
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/user.py,sha256=lT0zEjIcnm_Y3N-f4TM5145H_65XFOtNyWUQKjEUjuA,956
|
212
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/user_token.py,sha256=0oSZLYneZSDSl3dnqaojHc1ldZSJCc7dPw4_i1N8Uvk,908
|
206
213
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
207
214
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/const.py,sha256=dcvj5C9E2F2KCsGZPBBncQf_EvVJAC1qQgnyD8P4ZEw,6
|
208
215
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_db.py,sha256=1y3FaMFzm_5UM2poqtBve_UP_mh1vjs--krq6yO8PsA,742
|
209
216
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/__init__.py,sha256=btSjCQ7RJqomeXzYs9HePCu1dZHs0z6-RuboYKG89_8,598
|
210
217
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/api_key.py,sha256=a-xSHsnE8EkiH9g1ZoxaQQ7Y7q0urIm3SynlGYiSE8E,1038
|
211
218
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/common.py,sha256=yK0fFdRYZUpjWl8Vq9S9DKMLBkj9ZT9TXhGyPuR6asU,1876
|
212
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py,sha256=
|
213
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py,sha256=
|
214
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py,sha256=
|
215
|
-
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user_token.py,sha256=
|
219
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py,sha256=xm_0X5DNoLtw_2HkDVd386cleOFBeA-Ll7Cr2hw-fgk,3288
|
220
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py,sha256=uB7nKEqvUKJyE_xybJUjXIJtVXh0IKyoPDnDUogq8lE,1306
|
221
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py,sha256=GaOJkxsQ6B3B4pO43_MOOrJSUE8d7VzXyYLQcoEvCCw,2355
|
222
|
+
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user_token.py,sha256=k3_p7S_oNDfzDIQ1daLYL9TgGn-nZRug_Puwsn73RTs,1015
|
216
223
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/util.py,sha256=QSA_nT6aWdtE412-Uj3VTd7yh3dzS4HugDK9FivjTPo,570
|
217
224
|
arpakitlib/_arpakit_project_template_v_5/project/test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
218
|
-
arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_api_keys.py,sha256=
|
225
|
+
arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_api_keys.py,sha256=RXIEB5zOFHtQ_B0t_JlQ0ihjFbkVtW7six6CLdxft3w,901
|
219
226
|
arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_1.py,sha256=fioAaw5sWtZdOuHsl1kbVxuU_g0IK8N-gyKlMzVi8FE,416
|
220
227
|
arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_2.py,sha256=VcCvxRLA0XnLaQcsaoZkIVV8rTO3uMyLTYoEsdxKtYE,416
|
221
228
|
arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_3.py,sha256=9QJTgM4qsJXPgFhfVueQ9vObXr_dAldzP_p8dPO42uA,416
|
@@ -346,8 +353,8 @@ arpakitlib/ar_type_util.py,sha256=Cs_tef-Fc5xeyAF54KgISCsP11NHyzIsglm4S3Xx7iM,40
|
|
346
353
|
arpakitlib/ar_wata_api_client.py,sha256=gdHOqDbuqxhTjVDtRW1DvkRJLdDofCrOq51GTctzLns,242
|
347
354
|
arpakitlib/ar_yookassa_api_client_util.py,sha256=VozuZeCJjmLd1zj2BdC9WfiAQ3XYOrIMsdpNK-AUlm0,5347
|
348
355
|
arpakitlib/ar_zabbix_api_client_util.py,sha256=Q-VR4MvoZ9aHwZeYZr9G3LwN-ANx1T5KFmF6pvPM-9M,6402
|
349
|
-
arpakitlib-1.8.
|
350
|
-
arpakitlib-1.8.
|
351
|
-
arpakitlib-1.8.
|
352
|
-
arpakitlib-1.8.
|
353
|
-
arpakitlib-1.8.
|
356
|
+
arpakitlib-1.8.23.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
357
|
+
arpakitlib-1.8.23.dist-info/METADATA,sha256=_GjT6D8Z0BfsOBLxwfXwQMEPNivlHJgUrbaKs7zBXdk,3432
|
358
|
+
arpakitlib-1.8.23.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
359
|
+
arpakitlib-1.8.23.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
|
360
|
+
arpakitlib-1.8.23.dist-info/RECORD,,
|
@@ -1,66 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
import fastapi
|
4
|
-
from sqladmin.authentication import AuthenticationBackend
|
5
|
-
|
6
|
-
from arpakitlib.ar_str_util import make_none_if_blank
|
7
|
-
from project.core.settings import get_cached_settings
|
8
|
-
|
9
|
-
|
10
|
-
class SQLAdminAuth(AuthenticationBackend):
|
11
|
-
def __init__(self):
|
12
|
-
self._logger = logging.getLogger(self.__class__.__name__)
|
13
|
-
super().__init__(secret_key=get_cached_settings().sqladmin_secret_key)
|
14
|
-
|
15
|
-
async def login(self, request: fastapi.Request) -> bool:
|
16
|
-
form = await request.form()
|
17
|
-
|
18
|
-
username = form.get("username")
|
19
|
-
if username:
|
20
|
-
username = username.strip()
|
21
|
-
username = make_none_if_blank(username)
|
22
|
-
|
23
|
-
password = form.get("password")
|
24
|
-
if password:
|
25
|
-
password = password.strip()
|
26
|
-
password = make_none_if_blank(password)
|
27
|
-
|
28
|
-
if get_cached_settings().sqladmin_auth_keys is not None:
|
29
|
-
if (
|
30
|
-
(
|
31
|
-
is_username_correct := (
|
32
|
-
username is not None
|
33
|
-
and username in get_cached_settings().sqladmin_auth_keys
|
34
|
-
)
|
35
|
-
)
|
36
|
-
or
|
37
|
-
(
|
38
|
-
is_password_correct := (
|
39
|
-
password is not None
|
40
|
-
and password in get_cached_settings().sqladmin_auth_keys
|
41
|
-
)
|
42
|
-
)
|
43
|
-
):
|
44
|
-
if is_username_correct is True:
|
45
|
-
request.session.update({"sqladmin_auth_key": username})
|
46
|
-
elif is_password_correct is True:
|
47
|
-
request.session.update({"sqladmin_auth_key": password})
|
48
|
-
return True
|
49
|
-
|
50
|
-
return False
|
51
|
-
|
52
|
-
async def logout(self, request: fastapi.Request) -> bool:
|
53
|
-
request.session.clear()
|
54
|
-
return True
|
55
|
-
|
56
|
-
async def authenticate(self, request: fastapi.Request) -> bool:
|
57
|
-
sqladmin_auth_key = request.session.get("sqladmin_auth_key")
|
58
|
-
if sqladmin_auth_key:
|
59
|
-
sqladmin_auth_key = sqladmin_auth_key.strip()
|
60
|
-
sqladmin_auth_key = make_none_if_blank(sqladmin_auth_key)
|
61
|
-
|
62
|
-
if get_cached_settings().sqladmin_auth_keys is not None:
|
63
|
-
if sqladmin_auth_key is not None and sqladmin_auth_key in get_cached_settings().sqladmin_auth_keys:
|
64
|
-
return True
|
65
|
-
|
66
|
-
return False
|
File without changes
|
File without changes
|
File without changes
|