fractal-server 2.17.0a3__py3-none-any.whl → 2.17.0a5__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.
- fractal_server/__init__.py +1 -1
- fractal_server/__main__.py +137 -120
- fractal_server/app/models/security.py +19 -21
- fractal_server/app/models/user_settings.py +1 -0
- fractal_server/app/models/v2/task_group.py +1 -0
- fractal_server/app/routes/admin/v2/accounting.py +3 -3
- fractal_server/app/routes/admin/v2/impersonate.py +2 -2
- fractal_server/app/routes/admin/v2/job.py +6 -6
- fractal_server/app/routes/admin/v2/profile.py +4 -4
- fractal_server/app/routes/admin/v2/project.py +2 -2
- fractal_server/app/routes/admin/v2/resource.py +42 -8
- fractal_server/app/routes/admin/v2/task.py +2 -2
- fractal_server/app/routes/admin/v2/task_group.py +5 -5
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +4 -4
- fractal_server/app/routes/api/__init__.py +5 -5
- fractal_server/app/routes/api/v2/dataset.py +10 -19
- fractal_server/app/routes/api/v2/history.py +8 -8
- fractal_server/app/routes/api/v2/images.py +5 -5
- fractal_server/app/routes/api/v2/job.py +8 -8
- fractal_server/app/routes/api/v2/pre_submission_checks.py +3 -3
- fractal_server/app/routes/api/v2/project.py +6 -6
- fractal_server/app/routes/api/v2/status_legacy.py +2 -2
- fractal_server/app/routes/api/v2/submit.py +24 -26
- fractal_server/app/routes/api/v2/task.py +6 -7
- fractal_server/app/routes/api/v2/task_collection.py +4 -3
- fractal_server/app/routes/api/v2/task_collection_custom.py +4 -3
- fractal_server/app/routes/api/v2/task_collection_pixi.py +2 -2
- fractal_server/app/routes/api/v2/task_group.py +6 -6
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +4 -4
- fractal_server/app/routes/api/v2/task_version_update.py +3 -3
- fractal_server/app/routes/api/v2/workflow.py +9 -9
- fractal_server/app/routes/api/v2/workflow_import.py +2 -2
- fractal_server/app/routes/api/v2/workflowtask.py +5 -5
- fractal_server/app/routes/auth/__init__.py +34 -5
- fractal_server/app/routes/auth/current_user.py +22 -67
- fractal_server/app/routes/auth/group.py +8 -35
- fractal_server/app/routes/auth/register.py +2 -2
- fractal_server/app/routes/auth/users.py +5 -46
- fractal_server/app/schemas/__init__.py +0 -1
- fractal_server/app/schemas/user.py +23 -0
- fractal_server/app/schemas/v2/task_group.py +1 -0
- fractal_server/app/security/__init__.py +134 -46
- fractal_server/app/security/signup_email.py +52 -34
- fractal_server/config/__init__.py +1 -7
- fractal_server/config/_email.py +10 -47
- fractal_server/config/_main.py +14 -3
- fractal_server/migrations/versions/f65ee53991e3_user_settings_related.py +67 -0
- fractal_server/runner/config/_slurm.py +3 -2
- fractal_server/runner/executors/slurm_common/base_slurm_runner.py +2 -2
- fractal_server/runner/executors/slurm_common/get_slurm_config.py +1 -1
- fractal_server/runner/executors/slurm_common/slurm_config.py +7 -13
- fractal_server/runner/executors/slurm_ssh/runner.py +1 -1
- fractal_server/runner/executors/slurm_sudo/runner.py +1 -1
- fractal_server/runner/v2/_slurm_ssh.py +2 -1
- fractal_server/runner/v2/_slurm_sudo.py +1 -1
- fractal_server/runner/v2/submit_workflow.py +12 -12
- {fractal_server-2.17.0a3.dist-info → fractal_server-2.17.0a5.dist-info}/METADATA +1 -2
- {fractal_server-2.17.0a3.dist-info → fractal_server-2.17.0a5.dist-info}/RECORD +61 -64
- fractal_server/app/routes/aux/validate_user_settings.py +0 -76
- fractal_server/app/schemas/user_settings.py +0 -63
- fractal_server/app/user_settings.py +0 -32
- fractal_server/config/_init_data.py +0 -27
- {fractal_server-2.17.0a3.dist-info → fractal_server-2.17.0a5.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.0a3.dist-info → fractal_server-2.17.0a5.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.0a3.dist-info → fractal_server-2.17.0a5.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,23 +15,18 @@ from fractal_server.app.models import Profile
|
|
|
15
15
|
from fractal_server.app.models import Resource
|
|
16
16
|
from fractal_server.app.models import UserGroup
|
|
17
17
|
from fractal_server.app.models import UserOAuth
|
|
18
|
-
from fractal_server.app.
|
|
19
|
-
from fractal_server.app.routes.auth import current_active_user
|
|
18
|
+
from fractal_server.app.routes.auth import current_user_act
|
|
20
19
|
from fractal_server.app.routes.auth._aux_auth import (
|
|
21
20
|
_get_single_user_with_groups,
|
|
22
21
|
)
|
|
23
|
-
from fractal_server.app.routes.aux.validate_user_settings import (
|
|
24
|
-
verify_user_has_settings,
|
|
25
|
-
)
|
|
26
22
|
from fractal_server.app.schemas import UserProfileInfo
|
|
27
|
-
from fractal_server.app.schemas import UserSettingsReadStrict
|
|
28
|
-
from fractal_server.app.schemas import UserSettingsUpdateStrict
|
|
29
23
|
from fractal_server.app.schemas.user import UserRead
|
|
30
24
|
from fractal_server.app.schemas.user import UserUpdate
|
|
31
25
|
from fractal_server.app.schemas.user import UserUpdateStrict
|
|
32
26
|
from fractal_server.app.security import get_user_manager
|
|
33
27
|
from fractal_server.app.security import UserManager
|
|
34
28
|
from fractal_server.config import get_settings
|
|
29
|
+
from fractal_server.config import ViewerAuthScheme
|
|
35
30
|
from fractal_server.syringe import Inject
|
|
36
31
|
|
|
37
32
|
router_current_user = APIRouter()
|
|
@@ -40,7 +35,7 @@ router_current_user = APIRouter()
|
|
|
40
35
|
@router_current_user.get("/current-user/", response_model=UserRead)
|
|
41
36
|
async def get_current_user(
|
|
42
37
|
group_ids_names: bool = False,
|
|
43
|
-
user: UserOAuth = Depends(
|
|
38
|
+
user: UserOAuth = Depends(current_user_act),
|
|
44
39
|
db: AsyncSession = Depends(get_async_db),
|
|
45
40
|
):
|
|
46
41
|
"""
|
|
@@ -56,7 +51,7 @@ async def get_current_user(
|
|
|
56
51
|
@router_current_user.patch("/current-user/", response_model=UserRead)
|
|
57
52
|
async def patch_current_user(
|
|
58
53
|
user_update: UserUpdateStrict,
|
|
59
|
-
current_user: UserOAuth = Depends(
|
|
54
|
+
current_user: UserOAuth = Depends(current_user_act),
|
|
60
55
|
user_manager: UserManager = Depends(get_user_manager),
|
|
61
56
|
db: AsyncSession = Depends(get_async_db),
|
|
62
57
|
):
|
|
@@ -82,47 +77,12 @@ async def patch_current_user(
|
|
|
82
77
|
return patched_user_with_groups
|
|
83
78
|
|
|
84
79
|
|
|
85
|
-
@router_current_user.get(
|
|
86
|
-
"/current-user/settings/", response_model=UserSettingsReadStrict
|
|
87
|
-
)
|
|
88
|
-
async def get_current_user_settings(
|
|
89
|
-
current_user: UserOAuth = Depends(current_active_user),
|
|
90
|
-
db: AsyncSession = Depends(get_async_db),
|
|
91
|
-
) -> UserSettingsReadStrict:
|
|
92
|
-
verify_user_has_settings(current_user)
|
|
93
|
-
user_settings = await db.get(UserSettings, current_user.user_settings_id)
|
|
94
|
-
return user_settings
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@router_current_user.patch(
|
|
98
|
-
"/current-user/settings/", response_model=UserSettingsReadStrict
|
|
99
|
-
)
|
|
100
|
-
async def patch_current_user_settings(
|
|
101
|
-
settings_update: UserSettingsUpdateStrict,
|
|
102
|
-
current_user: UserOAuth = Depends(current_active_user),
|
|
103
|
-
db: AsyncSession = Depends(get_async_db),
|
|
104
|
-
) -> UserSettingsReadStrict:
|
|
105
|
-
verify_user_has_settings(current_user)
|
|
106
|
-
current_user_settings = await db.get(
|
|
107
|
-
UserSettings, current_user.user_settings_id
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
for k, v in settings_update.model_dump(exclude_unset=True).items():
|
|
111
|
-
setattr(current_user_settings, k, v)
|
|
112
|
-
|
|
113
|
-
db.add(current_user_settings)
|
|
114
|
-
await db.commit()
|
|
115
|
-
await db.refresh(current_user_settings)
|
|
116
|
-
|
|
117
|
-
return current_user_settings
|
|
118
|
-
|
|
119
|
-
|
|
120
80
|
@router_current_user.get(
|
|
121
81
|
"/current-user/profile-info/",
|
|
122
82
|
response_model=UserProfileInfo,
|
|
123
83
|
)
|
|
124
84
|
async def get_current_user_profile_info(
|
|
125
|
-
current_user: UserOAuth = Depends(
|
|
85
|
+
current_user: UserOAuth = Depends(current_user_act),
|
|
126
86
|
db: AsyncSession = Depends(get_async_db),
|
|
127
87
|
) -> UserProfileInfo:
|
|
128
88
|
stm = (
|
|
@@ -152,7 +112,7 @@ async def get_current_user_profile_info(
|
|
|
152
112
|
"/current-user/allowed-viewer-paths/", response_model=list[str]
|
|
153
113
|
)
|
|
154
114
|
async def get_current_user_allowed_viewer_paths(
|
|
155
|
-
current_user: UserOAuth = Depends(
|
|
115
|
+
current_user: UserOAuth = Depends(current_user_act),
|
|
156
116
|
db: AsyncSession = Depends(get_async_db),
|
|
157
117
|
) -> list[str]:
|
|
158
118
|
"""
|
|
@@ -162,35 +122,31 @@ async def get_current_user_allowed_viewer_paths(
|
|
|
162
122
|
|
|
163
123
|
settings = Inject(get_settings)
|
|
164
124
|
|
|
165
|
-
if settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME == "none":
|
|
166
|
-
return []
|
|
167
|
-
|
|
168
125
|
authorized_paths = []
|
|
169
126
|
|
|
170
|
-
|
|
171
|
-
|
|
127
|
+
if settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME == ViewerAuthScheme.NONE:
|
|
128
|
+
return authorized_paths
|
|
172
129
|
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
UserSettings, current_user.user_settings_id
|
|
176
|
-
)
|
|
177
|
-
# If project_dir is set, append it to the list of authorized paths
|
|
178
|
-
if current_user_settings.project_dir is not None:
|
|
179
|
-
authorized_paths.append(current_user_settings.project_dir)
|
|
130
|
+
# Append `project_dir` to the list of authorized paths
|
|
131
|
+
authorized_paths.append(current_user.project_dir)
|
|
180
132
|
|
|
181
133
|
# If auth scheme is "users-folders" and `slurm_user` is set,
|
|
182
134
|
# build and append the user folder
|
|
183
135
|
if (
|
|
184
|
-
settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME
|
|
185
|
-
|
|
136
|
+
settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME
|
|
137
|
+
== ViewerAuthScheme.USERS_FOLDERS
|
|
138
|
+
and current_user.profile_id is not None
|
|
186
139
|
):
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
base_folder
|
|
190
|
-
|
|
191
|
-
|
|
140
|
+
profile = await db.get(Profile, current_user.profile_id)
|
|
141
|
+
if profile is not None and profile.username is not None:
|
|
142
|
+
base_folder = settings.FRACTAL_VIEWER_BASE_FOLDER
|
|
143
|
+
user_folder = os.path.join(base_folder, profile.username)
|
|
144
|
+
authorized_paths.append(user_folder)
|
|
192
145
|
|
|
193
|
-
if
|
|
146
|
+
if (
|
|
147
|
+
settings.FRACTAL_VIEWER_AUTHORIZATION_SCHEME
|
|
148
|
+
== ViewerAuthScheme.VIEWER_PATHS
|
|
149
|
+
):
|
|
194
150
|
# Returns the union of `viewer_paths` for all user's groups
|
|
195
151
|
cmd = (
|
|
196
152
|
select(UserGroup.viewer_paths)
|
|
@@ -207,7 +163,6 @@ async def get_current_user_allowed_viewer_paths(
|
|
|
207
163
|
for _viewer_paths in viewer_paths_nested
|
|
208
164
|
for path in _viewer_paths
|
|
209
165
|
}
|
|
210
|
-
|
|
211
166
|
authorized_paths.extend(all_viewer_paths_set)
|
|
212
167
|
|
|
213
168
|
return authorized_paths
|
|
@@ -9,7 +9,7 @@ from fastapi import status
|
|
|
9
9
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
10
10
|
from sqlmodel import select
|
|
11
11
|
|
|
12
|
-
from . import
|
|
12
|
+
from . import current_superuser_act
|
|
13
13
|
from ._aux_auth import _get_default_usergroup_id
|
|
14
14
|
from ._aux_auth import _get_single_usergroup_with_user_ids
|
|
15
15
|
from ._aux_auth import _user_or_404
|
|
@@ -18,11 +18,9 @@ from fractal_server.app.db import get_async_db
|
|
|
18
18
|
from fractal_server.app.models import LinkUserGroup
|
|
19
19
|
from fractal_server.app.models import UserGroup
|
|
20
20
|
from fractal_server.app.models import UserOAuth
|
|
21
|
-
from fractal_server.app.models import UserSettings
|
|
22
21
|
from fractal_server.app.schemas.user_group import UserGroupCreate
|
|
23
22
|
from fractal_server.app.schemas.user_group import UserGroupRead
|
|
24
23
|
from fractal_server.app.schemas.user_group import UserGroupUpdate
|
|
25
|
-
from fractal_server.app.schemas.user_settings import UserSettingsUpdate
|
|
26
24
|
from fractal_server.app.security import FRACTAL_DEFAULT_GROUP_NAME
|
|
27
25
|
from fractal_server.logger import set_logger
|
|
28
26
|
|
|
@@ -36,7 +34,7 @@ router_group = APIRouter()
|
|
|
36
34
|
)
|
|
37
35
|
async def get_list_user_groups(
|
|
38
36
|
user_ids: bool = False,
|
|
39
|
-
user: UserOAuth = Depends(
|
|
37
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
40
38
|
db: AsyncSession = Depends(get_async_db),
|
|
41
39
|
) -> list[UserGroupRead]:
|
|
42
40
|
# Get all groups
|
|
@@ -70,7 +68,7 @@ async def get_list_user_groups(
|
|
|
70
68
|
)
|
|
71
69
|
async def get_single_user_group(
|
|
72
70
|
group_id: int,
|
|
73
|
-
user: UserOAuth = Depends(
|
|
71
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
74
72
|
db: AsyncSession = Depends(get_async_db),
|
|
75
73
|
) -> UserGroupRead:
|
|
76
74
|
group = await _get_single_usergroup_with_user_ids(group_id=group_id, db=db)
|
|
@@ -84,7 +82,7 @@ async def get_single_user_group(
|
|
|
84
82
|
)
|
|
85
83
|
async def create_single_group(
|
|
86
84
|
group_create: UserGroupCreate,
|
|
87
|
-
user: UserOAuth = Depends(
|
|
85
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
88
86
|
db: AsyncSession = Depends(get_async_db),
|
|
89
87
|
) -> UserGroupRead:
|
|
90
88
|
# Check that name is not already in use
|
|
@@ -116,7 +114,7 @@ async def create_single_group(
|
|
|
116
114
|
async def update_single_group(
|
|
117
115
|
group_id: int,
|
|
118
116
|
group_update: UserGroupUpdate,
|
|
119
|
-
user: UserOAuth = Depends(
|
|
117
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
120
118
|
db: AsyncSession = Depends(get_async_db),
|
|
121
119
|
) -> UserGroupRead:
|
|
122
120
|
group = await _usergroup_or_404(group_id, db)
|
|
@@ -137,7 +135,7 @@ async def update_single_group(
|
|
|
137
135
|
@router_group.delete("/group/{group_id}/", status_code=204)
|
|
138
136
|
async def delete_single_group(
|
|
139
137
|
group_id: int,
|
|
140
|
-
user: UserOAuth = Depends(
|
|
138
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
141
139
|
db: AsyncSession = Depends(get_async_db),
|
|
142
140
|
) -> Response:
|
|
143
141
|
group = await _usergroup_or_404(group_id, db)
|
|
@@ -159,36 +157,11 @@ async def delete_single_group(
|
|
|
159
157
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
160
158
|
|
|
161
159
|
|
|
162
|
-
@router_group.patch("/group/{group_id}/user-settings/", status_code=200)
|
|
163
|
-
async def patch_user_settings_bulk(
|
|
164
|
-
group_id: int,
|
|
165
|
-
settings_update: UserSettingsUpdate,
|
|
166
|
-
superuser: UserOAuth = Depends(current_active_superuser),
|
|
167
|
-
db: AsyncSession = Depends(get_async_db),
|
|
168
|
-
):
|
|
169
|
-
await _usergroup_or_404(group_id, db)
|
|
170
|
-
res = await db.execute(
|
|
171
|
-
select(UserSettings)
|
|
172
|
-
.join(UserOAuth)
|
|
173
|
-
.where(LinkUserGroup.user_id == UserOAuth.id)
|
|
174
|
-
.where(LinkUserGroup.group_id == group_id)
|
|
175
|
-
)
|
|
176
|
-
settings_list = res.scalars().all()
|
|
177
|
-
update = settings_update.model_dump(exclude_unset=True)
|
|
178
|
-
for settings in settings_list:
|
|
179
|
-
for k, v in update.items():
|
|
180
|
-
setattr(settings, k, v)
|
|
181
|
-
db.add(settings)
|
|
182
|
-
await db.commit()
|
|
183
|
-
|
|
184
|
-
return Response(status_code=status.HTTP_200_OK)
|
|
185
|
-
|
|
186
|
-
|
|
187
160
|
@router_group.post("/group/{group_id}/add-user/{user_id}/", status_code=200)
|
|
188
161
|
async def add_user_to_group(
|
|
189
162
|
group_id: int,
|
|
190
163
|
user_id: int,
|
|
191
|
-
superuser: UserOAuth = Depends(
|
|
164
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
192
165
|
db: AsyncSession = Depends(get_async_db),
|
|
193
166
|
) -> UserGroupRead:
|
|
194
167
|
await _usergroup_or_404(group_id, db)
|
|
@@ -212,7 +185,7 @@ async def add_user_to_group(
|
|
|
212
185
|
async def remove_user_from_group(
|
|
213
186
|
group_id: int,
|
|
214
187
|
user_id: int,
|
|
215
|
-
superuser: UserOAuth = Depends(
|
|
188
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
216
189
|
db: AsyncSession = Depends(get_async_db),
|
|
217
190
|
) -> UserGroupRead:
|
|
218
191
|
# Check that user and group exist
|
|
@@ -4,7 +4,7 @@ Definition of `/auth/register/` routes.
|
|
|
4
4
|
from fastapi import APIRouter
|
|
5
5
|
from fastapi import Depends
|
|
6
6
|
|
|
7
|
-
from . import
|
|
7
|
+
from . import current_superuser_act
|
|
8
8
|
from . import fastapi_users
|
|
9
9
|
from ...schemas.user import UserCreate
|
|
10
10
|
from ...schemas.user import UserRead
|
|
@@ -13,7 +13,7 @@ router_register = APIRouter()
|
|
|
13
13
|
|
|
14
14
|
router_register.include_router(
|
|
15
15
|
fastapi_users.get_register_router(UserRead, UserCreate),
|
|
16
|
-
dependencies=[Depends(
|
|
16
|
+
dependencies=[Depends(current_superuser_act)],
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
|
|
@@ -12,22 +12,18 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
12
12
|
from sqlmodel import func
|
|
13
13
|
from sqlmodel import select
|
|
14
14
|
|
|
15
|
-
from . import
|
|
15
|
+
from . import current_superuser_act
|
|
16
16
|
from ...db import get_async_db
|
|
17
17
|
from ...schemas.user import UserRead
|
|
18
18
|
from ...schemas.user import UserUpdate
|
|
19
|
-
from ..aux.validate_user_settings import verify_user_has_settings
|
|
20
19
|
from ._aux_auth import _get_default_usergroup_id
|
|
21
20
|
from ._aux_auth import _get_single_user_with_groups
|
|
22
21
|
from ._aux_auth import FRACTAL_DEFAULT_GROUP_NAME
|
|
23
22
|
from fractal_server.app.models import LinkUserGroup
|
|
24
23
|
from fractal_server.app.models import UserGroup
|
|
25
24
|
from fractal_server.app.models import UserOAuth
|
|
26
|
-
from fractal_server.app.models import UserSettings
|
|
27
25
|
from fractal_server.app.models.v2 import Profile
|
|
28
26
|
from fractal_server.app.routes.auth._aux_auth import _user_or_404
|
|
29
|
-
from fractal_server.app.schemas import UserSettingsRead
|
|
30
|
-
from fractal_server.app.schemas import UserSettingsUpdate
|
|
31
27
|
from fractal_server.app.schemas.user import UserUpdateGroups
|
|
32
28
|
from fractal_server.app.security import get_user_manager
|
|
33
29
|
from fractal_server.app.security import UserManager
|
|
@@ -43,7 +39,7 @@ logger = set_logger(__name__)
|
|
|
43
39
|
async def get_user(
|
|
44
40
|
user_id: int,
|
|
45
41
|
group_ids_names: bool = True,
|
|
46
|
-
superuser: UserOAuth = Depends(
|
|
42
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
47
43
|
db: AsyncSession = Depends(get_async_db),
|
|
48
44
|
) -> UserRead:
|
|
49
45
|
user = await _user_or_404(user_id, db)
|
|
@@ -57,7 +53,7 @@ async def get_user(
|
|
|
57
53
|
async def patch_user(
|
|
58
54
|
user_id: int,
|
|
59
55
|
user_update: UserUpdate,
|
|
60
|
-
current_superuser: UserOAuth = Depends(
|
|
56
|
+
current_superuser: UserOAuth = Depends(current_superuser_act),
|
|
61
57
|
user_manager: UserManager = Depends(get_user_manager),
|
|
62
58
|
db: AsyncSession = Depends(get_async_db),
|
|
63
59
|
):
|
|
@@ -113,7 +109,7 @@ async def patch_user(
|
|
|
113
109
|
@router_users.get("/users/", response_model=list[UserRead])
|
|
114
110
|
async def list_users(
|
|
115
111
|
profile_id: int | None = None,
|
|
116
|
-
user: UserOAuth = Depends(
|
|
112
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
117
113
|
db: AsyncSession = Depends(get_async_db),
|
|
118
114
|
):
|
|
119
115
|
"""
|
|
@@ -148,7 +144,7 @@ async def list_users(
|
|
|
148
144
|
async def set_user_groups(
|
|
149
145
|
user_id: int,
|
|
150
146
|
user_update: UserUpdateGroups,
|
|
151
|
-
superuser: UserOAuth = Depends(
|
|
147
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
152
148
|
db: AsyncSession = Depends(get_async_db),
|
|
153
149
|
) -> UserRead:
|
|
154
150
|
# Preliminary check that all objects exist in the db
|
|
@@ -210,40 +206,3 @@ async def set_user_groups(
|
|
|
210
206
|
user_with_groups = await _get_single_user_with_groups(user, db)
|
|
211
207
|
|
|
212
208
|
return user_with_groups
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
@router_users.get(
|
|
216
|
-
"/users/{user_id}/settings/", response_model=UserSettingsRead
|
|
217
|
-
)
|
|
218
|
-
async def get_user_settings(
|
|
219
|
-
user_id: int,
|
|
220
|
-
superuser: UserOAuth = Depends(current_active_superuser),
|
|
221
|
-
db: AsyncSession = Depends(get_async_db),
|
|
222
|
-
) -> UserSettingsRead:
|
|
223
|
-
user = await _user_or_404(user_id=user_id, db=db)
|
|
224
|
-
verify_user_has_settings(user)
|
|
225
|
-
user_settings = await db.get(UserSettings, user.user_settings_id)
|
|
226
|
-
return user_settings
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@router_users.patch(
|
|
230
|
-
"/users/{user_id}/settings/", response_model=UserSettingsRead
|
|
231
|
-
)
|
|
232
|
-
async def patch_user_settings(
|
|
233
|
-
user_id: int,
|
|
234
|
-
settings_update: UserSettingsUpdate,
|
|
235
|
-
superuser: UserOAuth = Depends(current_active_superuser),
|
|
236
|
-
db: AsyncSession = Depends(get_async_db),
|
|
237
|
-
) -> UserSettingsRead:
|
|
238
|
-
user = await _user_or_404(user_id=user_id, db=db)
|
|
239
|
-
verify_user_has_settings(user)
|
|
240
|
-
user_settings = await db.get(UserSettings, user.user_settings_id)
|
|
241
|
-
|
|
242
|
-
for k, v in settings_update.model_dump(exclude_unset=True).items():
|
|
243
|
-
setattr(user_settings, k, v)
|
|
244
|
-
|
|
245
|
-
db.add(user_settings)
|
|
246
|
-
await db.commit()
|
|
247
|
-
await db.refresh(user_settings)
|
|
248
|
-
|
|
249
|
-
return user_settings
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
1
3
|
from fastapi_users import schemas
|
|
4
|
+
from pydantic import AfterValidator
|
|
2
5
|
from pydantic import BaseModel
|
|
3
6
|
from pydantic import ConfigDict
|
|
4
7
|
from pydantic import EmailStr
|
|
5
8
|
from pydantic import Field
|
|
6
9
|
|
|
10
|
+
from fractal_server.string_tools import validate_cmd
|
|
11
|
+
from fractal_server.types import AbsolutePathStr
|
|
12
|
+
from fractal_server.types import ListUniqueNonEmptyString
|
|
7
13
|
from fractal_server.types import ListUniqueNonNegativeInt
|
|
8
14
|
from fractal_server.types import NonEmptyStr
|
|
9
15
|
|
|
@@ -37,6 +43,13 @@ class UserRead(schemas.BaseUser[int]):
|
|
|
37
43
|
group_ids_names: list[tuple[int, str]] | None = None
|
|
38
44
|
oauth_accounts: list[OAuthAccountRead]
|
|
39
45
|
profile_id: int | None = None
|
|
46
|
+
project_dir: str
|
|
47
|
+
slurm_accounts: list[str]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _validate_cmd(value: str) -> str:
|
|
51
|
+
validate_cmd(value)
|
|
52
|
+
return value
|
|
40
53
|
|
|
41
54
|
|
|
42
55
|
class UserUpdate(schemas.BaseUserUpdate):
|
|
@@ -50,6 +63,8 @@ class UserUpdate(schemas.BaseUserUpdate):
|
|
|
50
63
|
is_superuser:
|
|
51
64
|
is_verified:
|
|
52
65
|
profile_id:
|
|
66
|
+
project_dir:
|
|
67
|
+
slurm_accounts:
|
|
53
68
|
"""
|
|
54
69
|
|
|
55
70
|
model_config = ConfigDict(extra="forbid")
|
|
@@ -59,6 +74,10 @@ class UserUpdate(schemas.BaseUserUpdate):
|
|
|
59
74
|
is_superuser: bool = None
|
|
60
75
|
is_verified: bool = None
|
|
61
76
|
profile_id: int | None = None
|
|
77
|
+
project_dir: Annotated[
|
|
78
|
+
AbsolutePathStr, AfterValidator(_validate_cmd)
|
|
79
|
+
] | None = None
|
|
80
|
+
slurm_accounts: ListUniqueNonEmptyString | None = None
|
|
62
81
|
|
|
63
82
|
|
|
64
83
|
class UserUpdateStrict(BaseModel):
|
|
@@ -66,9 +85,11 @@ class UserUpdateStrict(BaseModel):
|
|
|
66
85
|
Schema for `User` self-editing.
|
|
67
86
|
|
|
68
87
|
Attributes:
|
|
88
|
+
slurm_accounts:
|
|
69
89
|
"""
|
|
70
90
|
|
|
71
91
|
model_config = ConfigDict(extra="forbid")
|
|
92
|
+
slurm_accounts: ListUniqueNonEmptyString | None = None
|
|
72
93
|
|
|
73
94
|
|
|
74
95
|
class UserCreate(schemas.BaseUserCreate):
|
|
@@ -80,6 +101,8 @@ class UserCreate(schemas.BaseUserCreate):
|
|
|
80
101
|
"""
|
|
81
102
|
|
|
82
103
|
profile_id: int | None = None
|
|
104
|
+
project_dir: Annotated[AbsolutePathStr, AfterValidator(_validate_cmd)]
|
|
105
|
+
slurm_accounts: list[str] = Field(default_factory=list)
|
|
83
106
|
|
|
84
107
|
|
|
85
108
|
class UserUpdateGroups(BaseModel):
|