fractal-server 2.16.6__py3-none-any.whl → 2.17.0__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.
Files changed (142) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +178 -52
  3. fractal_server/app/db/__init__.py +9 -11
  4. fractal_server/app/models/security.py +30 -22
  5. fractal_server/app/models/user_settings.py +5 -4
  6. fractal_server/app/models/v2/__init__.py +4 -0
  7. fractal_server/app/models/v2/profile.py +16 -0
  8. fractal_server/app/models/v2/project.py +5 -0
  9. fractal_server/app/models/v2/resource.py +130 -0
  10. fractal_server/app/models/v2/task_group.py +4 -0
  11. fractal_server/app/routes/admin/v2/__init__.py +4 -0
  12. fractal_server/app/routes/admin/v2/_aux_functions.py +55 -0
  13. fractal_server/app/routes/admin/v2/accounting.py +3 -3
  14. fractal_server/app/routes/admin/v2/impersonate.py +2 -2
  15. fractal_server/app/routes/admin/v2/job.py +51 -15
  16. fractal_server/app/routes/admin/v2/profile.py +100 -0
  17. fractal_server/app/routes/admin/v2/project.py +2 -2
  18. fractal_server/app/routes/admin/v2/resource.py +222 -0
  19. fractal_server/app/routes/admin/v2/task.py +59 -32
  20. fractal_server/app/routes/admin/v2/task_group.py +17 -12
  21. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +52 -86
  22. fractal_server/app/routes/api/__init__.py +45 -8
  23. fractal_server/app/routes/api/v2/_aux_functions.py +17 -1
  24. fractal_server/app/routes/api/v2/_aux_functions_history.py +2 -2
  25. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
  26. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +55 -19
  27. fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +21 -17
  28. fractal_server/app/routes/api/v2/dataset.py +10 -19
  29. fractal_server/app/routes/api/v2/history.py +8 -8
  30. fractal_server/app/routes/api/v2/images.py +5 -5
  31. fractal_server/app/routes/api/v2/job.py +8 -8
  32. fractal_server/app/routes/api/v2/pre_submission_checks.py +3 -3
  33. fractal_server/app/routes/api/v2/project.py +15 -7
  34. fractal_server/app/routes/api/v2/status_legacy.py +2 -2
  35. fractal_server/app/routes/api/v2/submit.py +49 -42
  36. fractal_server/app/routes/api/v2/task.py +26 -8
  37. fractal_server/app/routes/api/v2/task_collection.py +39 -50
  38. fractal_server/app/routes/api/v2/task_collection_custom.py +10 -6
  39. fractal_server/app/routes/api/v2/task_collection_pixi.py +34 -42
  40. fractal_server/app/routes/api/v2/task_group.py +19 -9
  41. fractal_server/app/routes/api/v2/task_group_lifecycle.py +43 -86
  42. fractal_server/app/routes/api/v2/task_version_update.py +3 -3
  43. fractal_server/app/routes/api/v2/workflow.py +9 -9
  44. fractal_server/app/routes/api/v2/workflow_import.py +25 -13
  45. fractal_server/app/routes/api/v2/workflowtask.py +5 -5
  46. fractal_server/app/routes/auth/__init__.py +34 -5
  47. fractal_server/app/routes/auth/_aux_auth.py +39 -20
  48. fractal_server/app/routes/auth/current_user.py +56 -67
  49. fractal_server/app/routes/auth/group.py +29 -46
  50. fractal_server/app/routes/auth/oauth.py +55 -38
  51. fractal_server/app/routes/auth/register.py +2 -2
  52. fractal_server/app/routes/auth/router.py +4 -2
  53. fractal_server/app/routes/auth/users.py +29 -53
  54. fractal_server/app/routes/aux/_runner.py +2 -1
  55. fractal_server/app/routes/aux/validate_user_profile.py +62 -0
  56. fractal_server/app/schemas/__init__.py +0 -1
  57. fractal_server/app/schemas/user.py +43 -13
  58. fractal_server/app/schemas/user_group.py +2 -1
  59. fractal_server/app/schemas/v2/__init__.py +12 -0
  60. fractal_server/app/schemas/v2/profile.py +78 -0
  61. fractal_server/app/schemas/v2/resource.py +137 -0
  62. fractal_server/app/schemas/v2/task_collection.py +11 -3
  63. fractal_server/app/schemas/v2/task_group.py +5 -0
  64. fractal_server/app/security/__init__.py +174 -75
  65. fractal_server/app/security/signup_email.py +52 -34
  66. fractal_server/config/__init__.py +27 -0
  67. fractal_server/config/_data.py +68 -0
  68. fractal_server/config/_database.py +59 -0
  69. fractal_server/config/_email.py +133 -0
  70. fractal_server/config/_main.py +78 -0
  71. fractal_server/config/_oauth.py +69 -0
  72. fractal_server/config/_settings_config.py +7 -0
  73. fractal_server/data_migrations/2_17_0.py +339 -0
  74. fractal_server/images/tools.py +3 -3
  75. fractal_server/logger.py +3 -3
  76. fractal_server/main.py +17 -23
  77. fractal_server/migrations/naming_convention.py +1 -1
  78. fractal_server/migrations/versions/83bc2ad3ffcc_2_17_0.py +195 -0
  79. fractal_server/runner/config/__init__.py +2 -0
  80. fractal_server/runner/config/_local.py +21 -0
  81. fractal_server/runner/config/_slurm.py +129 -0
  82. fractal_server/runner/config/slurm_mem_to_MB.py +63 -0
  83. fractal_server/runner/exceptions.py +4 -0
  84. fractal_server/runner/executors/base_runner.py +17 -7
  85. fractal_server/runner/executors/local/get_local_config.py +21 -86
  86. fractal_server/runner/executors/local/runner.py +48 -5
  87. fractal_server/runner/executors/slurm_common/_batching.py +2 -2
  88. fractal_server/runner/executors/slurm_common/base_slurm_runner.py +60 -26
  89. fractal_server/runner/executors/slurm_common/get_slurm_config.py +39 -55
  90. fractal_server/runner/executors/slurm_common/remote.py +1 -1
  91. fractal_server/runner/executors/slurm_common/slurm_config.py +214 -0
  92. fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -1
  93. fractal_server/runner/executors/slurm_ssh/runner.py +12 -14
  94. fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +2 -2
  95. fractal_server/runner/executors/slurm_sudo/runner.py +12 -12
  96. fractal_server/runner/v2/_local.py +36 -21
  97. fractal_server/runner/v2/_slurm_ssh.py +41 -4
  98. fractal_server/runner/v2/_slurm_sudo.py +42 -12
  99. fractal_server/runner/v2/db_tools.py +1 -1
  100. fractal_server/runner/v2/runner.py +3 -11
  101. fractal_server/runner/v2/runner_functions.py +42 -28
  102. fractal_server/runner/v2/submit_workflow.py +88 -109
  103. fractal_server/runner/versions.py +8 -3
  104. fractal_server/ssh/_fabric.py +6 -6
  105. fractal_server/tasks/config/__init__.py +3 -0
  106. fractal_server/tasks/config/_pixi.py +127 -0
  107. fractal_server/tasks/config/_python.py +51 -0
  108. fractal_server/tasks/v2/local/_utils.py +7 -7
  109. fractal_server/tasks/v2/local/collect.py +13 -5
  110. fractal_server/tasks/v2/local/collect_pixi.py +26 -10
  111. fractal_server/tasks/v2/local/deactivate.py +7 -1
  112. fractal_server/tasks/v2/local/deactivate_pixi.py +5 -1
  113. fractal_server/tasks/v2/local/delete.py +5 -1
  114. fractal_server/tasks/v2/local/reactivate.py +13 -5
  115. fractal_server/tasks/v2/local/reactivate_pixi.py +27 -9
  116. fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py +11 -10
  117. fractal_server/tasks/v2/ssh/_utils.py +6 -7
  118. fractal_server/tasks/v2/ssh/collect.py +19 -12
  119. fractal_server/tasks/v2/ssh/collect_pixi.py +34 -16
  120. fractal_server/tasks/v2/ssh/deactivate.py +12 -8
  121. fractal_server/tasks/v2/ssh/deactivate_pixi.py +14 -10
  122. fractal_server/tasks/v2/ssh/delete.py +12 -9
  123. fractal_server/tasks/v2/ssh/reactivate.py +18 -12
  124. fractal_server/tasks/v2/ssh/reactivate_pixi.py +36 -17
  125. fractal_server/tasks/v2/templates/4_pip_show.sh +4 -6
  126. fractal_server/tasks/v2/utils_database.py +2 -2
  127. fractal_server/tasks/v2/utils_pixi.py +3 -0
  128. fractal_server/tasks/v2/utils_python_interpreter.py +8 -16
  129. fractal_server/tasks/v2/utils_templates.py +7 -10
  130. fractal_server/utils.py +1 -1
  131. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/METADATA +4 -6
  132. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/RECORD +136 -117
  133. fractal_server/app/routes/aux/validate_user_settings.py +0 -73
  134. fractal_server/app/schemas/user_settings.py +0 -67
  135. fractal_server/app/user_settings.py +0 -42
  136. fractal_server/config.py +0 -906
  137. fractal_server/data_migrations/2_14_10.py +0 -48
  138. fractal_server/runner/executors/slurm_common/_slurm_config.py +0 -471
  139. /fractal_server/{runner → app}/shutdown.py +0 -0
  140. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/WHEEL +0 -0
  141. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/entry_points.txt +0 -0
  142. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,11 +12,17 @@ from fractal_server.app.db import AsyncSession
12
12
  from fractal_server.app.models import LinkUserGroup
13
13
  from fractal_server.app.models import UserGroup
14
14
  from fractal_server.app.models import UserOAuth
15
+ from fractal_server.app.models.v2 import Profile
15
16
  from fractal_server.app.models.v2 import TaskGroupActivityV2
16
17
  from fractal_server.app.models.v2 import TaskGroupV2
17
18
  from fractal_server.app.models.v2 import TaskV2
18
19
  from fractal_server.app.models.v2 import WorkflowTaskV2
19
- from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
20
+ from fractal_server.app.routes.api.v2._aux_functions import (
21
+ _get_user_resource_id,
22
+ )
23
+ from fractal_server.app.routes.auth._aux_auth import (
24
+ _get_default_usergroup_id_or_none,
25
+ )
20
26
  from fractal_server.app.routes.auth._aux_auth import (
21
27
  _verify_user_belongs_to_group,
22
28
  )
@@ -33,7 +39,7 @@ async def _get_task_group_or_404(
33
39
  """
34
40
  Get an existing task group or raise a 404.
35
41
 
36
- Arguments:
42
+ Args:
37
43
  task_group_id: The TaskGroupV2 id
38
44
  db: An asynchronous db session
39
45
  """
@@ -55,7 +61,7 @@ async def _get_task_group_read_access(
55
61
  """
56
62
  Get a task group or raise a 403 if user has no read access.
57
63
 
58
- Arguments:
64
+ Args:
59
65
  task_group_id: ID of the required task group.
60
66
  user_id: ID of the current user.
61
67
  db: An asynchronous db session.
@@ -69,7 +75,7 @@ async def _get_task_group_read_access(
69
75
  status_code=status.HTTP_403_FORBIDDEN,
70
76
  detail=(
71
77
  "Current user has no read access to TaskGroupV2 "
72
- f"{task_group_id}.",
78
+ f"{task_group_id}."
73
79
  ),
74
80
  )
75
81
 
@@ -80,11 +86,16 @@ async def _get_task_group_read_access(
80
86
  else:
81
87
  stm = (
82
88
  select(LinkUserGroup)
89
+ .join(UserOAuth)
90
+ .join(Profile)
83
91
  .where(LinkUserGroup.group_id == task_group.user_group_id)
84
92
  .where(LinkUserGroup.user_id == user_id)
93
+ .where(UserOAuth.id == user_id)
94
+ .where(Profile.id == UserOAuth.profile_id)
95
+ .where(task_group.resource_id == Profile.resource_id)
85
96
  )
86
97
  res = await db.execute(stm)
87
- link = res.scalar_one_or_none()
98
+ link = res.unique().scalars().one_or_none()
88
99
  if link is None:
89
100
  raise forbidden_exception
90
101
  else:
@@ -100,7 +111,7 @@ async def _get_task_group_full_access(
100
111
  """
101
112
  Get a task group or raise a 403 if user has no full access.
102
113
 
103
- Arguments:
114
+ Args:
104
115
  task_group_id: ID of the required task group.
105
116
  user_id: ID of the current user.
106
117
  db: An asynchronous db session
@@ -125,7 +136,7 @@ async def _get_task_or_404(*, task_id: int, db: AsyncSession) -> TaskV2:
125
136
  """
126
137
  Get an existing task or raise a 404.
127
138
 
128
- Arguments:
139
+ Args:
129
140
  task_id: ID of the required task.
130
141
  db: An asynchronous db session
131
142
  """
@@ -147,15 +158,23 @@ async def _get_task_full_access(
147
158
  """
148
159
  Get an existing task or raise a 404.
149
160
 
150
- Arguments:
161
+ Args:
151
162
  task_id: ID of the required task.
152
163
  user_id: ID of the current user.
153
164
  db: An asynchronous db session.
154
165
  """
155
166
  task = await _get_task_or_404(task_id=task_id, db=db)
156
- await _get_task_group_full_access(
167
+ task_group = await _get_task_group_full_access(
157
168
  task_group_id=task.taskgroupv2_id, user_id=user_id, db=db
158
169
  )
170
+
171
+ resource_id = await _get_user_resource_id(user_id=user_id, db=db)
172
+ if resource_id is None or resource_id != task_group.resource_id:
173
+ raise HTTPException(
174
+ status_code=status.HTTP_403_FORBIDDEN,
175
+ detail=f"User {user_id} has no access to TaskGroup's Resource.",
176
+ )
177
+
159
178
  return task
160
179
 
161
180
 
@@ -169,7 +188,7 @@ async def _get_task_read_access(
169
188
  """
170
189
  Get an existing task or raise a 404.
171
190
 
172
- Arguments:
191
+ Args:
173
192
  task_id: ID of the required task.
174
193
  user_id: ID of the current user.
175
194
  db: An asynchronous db session.
@@ -179,12 +198,20 @@ async def _get_task_read_access(
179
198
  task_group = await _get_task_group_read_access(
180
199
  task_group_id=task.taskgroupv2_id, user_id=user_id, db=db
181
200
  )
182
- if require_active:
183
- if not task_group.active:
184
- raise HTTPException(
185
- status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
186
- detail=f"Error: task {task_id} ({task.name}) is not active.",
187
- )
201
+
202
+ resource_id = await _get_user_resource_id(user_id=user_id, db=db)
203
+ if resource_id is None or resource_id != task_group.resource_id:
204
+ raise HTTPException(
205
+ status_code=status.HTTP_403_FORBIDDEN,
206
+ detail=f"User {user_id} has no access to TaskGroup's Resource.",
207
+ )
208
+
209
+ if require_active and not task_group.active:
210
+ raise HTTPException(
211
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
212
+ detail=f"Error: task {task_id} ({task.name}) is not active.",
213
+ )
214
+
188
215
  return task
189
216
 
190
217
 
@@ -198,7 +225,7 @@ async def _get_valid_user_group_id(
198
225
  """
199
226
  Validate query parameters for endpoints that create some task(s).
200
227
 
201
- Arguments:
228
+ Args:
202
229
  user_group_id:
203
230
  private:
204
231
  user_id: ID of the current user
@@ -212,7 +239,7 @@ async def _get_valid_user_group_id(
212
239
  elif private is True:
213
240
  user_group_id = None
214
241
  elif user_group_id is None:
215
- user_group_id = await _get_default_usergroup_id(db=db)
242
+ user_group_id = await _get_default_usergroup_id_or_none(db=db)
216
243
  else:
217
244
  await _verify_user_belongs_to_group(
218
245
  user_id=user_id, user_group_id=user_group_id, db=db
@@ -255,16 +282,19 @@ async def _get_collection_task_group_activity_status_message(
255
282
 
256
283
 
257
284
  async def _verify_non_duplication_user_constraint(
285
+ *,
258
286
  db: AsyncSession,
259
287
  user_id: int,
260
288
  pkg_name: str,
261
289
  version: str | None,
290
+ user_resource_id: int,
262
291
  ):
263
292
  stm = (
264
293
  select(TaskGroupV2)
265
294
  .where(TaskGroupV2.user_id == user_id)
266
295
  .where(TaskGroupV2.pkg_name == pkg_name)
267
296
  .where(TaskGroupV2.version == version)
297
+ .where(TaskGroupV2.resource_id == user_resource_id)
268
298
  )
269
299
  res = await db.execute(stm)
270
300
  duplicate = res.scalars().all()
@@ -342,7 +372,9 @@ async def _verify_non_duplication_group_constraint(
342
372
 
343
373
 
344
374
  async def _verify_non_duplication_group_path(
375
+ *,
345
376
  path: str | None,
377
+ resource_id: int,
346
378
  db: AsyncSession,
347
379
  ) -> None:
348
380
  """
@@ -350,7 +382,11 @@ async def _verify_non_duplication_group_path(
350
382
  """
351
383
  if path is None:
352
384
  return
353
- stm = select(TaskGroupV2.id).where(TaskGroupV2.path == path)
385
+ stm = (
386
+ select(TaskGroupV2.id)
387
+ .where(TaskGroupV2.path == path)
388
+ .where(TaskGroupV2.resource_id == resource_id)
389
+ )
354
390
  res = await db.execute(stm)
355
391
  duplicate_ids = res.scalars().all()
356
392
  if duplicate_ids:
@@ -5,8 +5,10 @@ from sqlmodel import select
5
5
  from fractal_server.app.db import AsyncSession
6
6
  from fractal_server.app.models import LinkUserGroup
7
7
  from fractal_server.app.models.v2 import TaskGroupV2
8
+ from fractal_server.config import get_settings
8
9
  from fractal_server.exceptions import UnreachableBranchError
9
10
  from fractal_server.logger import set_logger
11
+ from fractal_server.syringe import Inject
10
12
 
11
13
 
12
14
  logger = set_logger(__name__)
@@ -16,7 +18,7 @@ async def _disambiguate_task_groups(
16
18
  *,
17
19
  matching_task_groups: list[TaskGroupV2],
18
20
  user_id: int,
19
- default_group_id: int,
21
+ default_group_id: int | None,
20
22
  db: AsyncSession,
21
23
  ) -> TaskGroupV2 | None:
22
24
  """
@@ -49,21 +51,23 @@ async def _disambiguate_task_groups(
49
51
  )
50
52
 
51
53
  # Medium priority: task groups owned by default user group
54
+ settings = Inject(get_settings)
52
55
  list_user_group_ids = [tg.user_group_id for tg in matching_task_groups]
53
- try:
54
- ind_user_group_id = list_user_group_ids.index(default_group_id)
55
- task_group = matching_task_groups[ind_user_group_id]
56
- logger.debug(
57
- "[_disambiguate_task_groups] "
58
- f"Found task group {task_group.id} with {user_id=}, return."
59
- )
60
- return task_group
61
- except ValueError:
62
- logger.debug(
63
- "[_disambiguate_task_groups] "
64
- "No task group with user_group_id="
65
- f"{default_group_id}, continue."
66
- )
56
+ if settings.FRACTAL_DEFAULT_GROUP_NAME is not None:
57
+ try:
58
+ ind_user_group_id = list_user_group_ids.index(default_group_id)
59
+ task_group = matching_task_groups[ind_user_group_id]
60
+ logger.debug(
61
+ "[_disambiguate_task_groups] "
62
+ f"Found task group {task_group.id} with {user_id=}, return."
63
+ )
64
+ return task_group
65
+ except ValueError:
66
+ logger.debug(
67
+ "[_disambiguate_task_groups] "
68
+ "No task group with user_group_id="
69
+ f"{default_group_id}, continue."
70
+ )
67
71
 
68
72
  # Lowest priority: task groups owned by other groups, sorted
69
73
  # according to age of the user/usergroup link
@@ -97,7 +101,7 @@ async def _disambiguate_task_groups_not_none(
97
101
  *,
98
102
  matching_task_groups: list[TaskGroupV2],
99
103
  user_id: int,
100
- default_group_id: int,
104
+ default_group_id: int | None,
101
105
  db: AsyncSession,
102
106
  ) -> TaskGroupV2:
103
107
  """
@@ -133,7 +137,7 @@ async def remove_duplicate_task_groups(
133
137
  *,
134
138
  task_groups: list[TaskGroupV2],
135
139
  user_id: int,
136
- default_group_id: int,
140
+ default_group_id: int | None,
137
141
  db: AsyncSession,
138
142
  ) -> list[TaskGroupV2]:
139
143
  """
@@ -19,7 +19,7 @@ from ._aux_functions import _get_dataset_check_owner
19
19
  from ._aux_functions import _get_project_check_owner
20
20
  from ._aux_functions import _get_submitted_jobs_statement
21
21
  from fractal_server.app.models import UserOAuth
22
- from fractal_server.app.routes.auth import current_active_user
22
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
23
23
  from fractal_server.string_tools import sanitize_string
24
24
  from fractal_server.urls import normalize_url
25
25
 
@@ -34,7 +34,7 @@ router = APIRouter()
34
34
  async def create_dataset(
35
35
  project_id: int,
36
36
  dataset: DatasetCreateV2,
37
- user: UserOAuth = Depends(current_active_user),
37
+ user: UserOAuth = Depends(current_user_act_ver_prof),
38
38
  db: AsyncSession = Depends(get_async_db),
39
39
  ) -> DatasetReadV2 | None:
40
40
  """
@@ -45,15 +45,6 @@ async def create_dataset(
45
45
  )
46
46
 
47
47
  if dataset.zarr_dir is None:
48
- if user.settings.project_dir is None:
49
- raise HTTPException(
50
- status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
51
- detail=(
52
- "Both 'dataset.zarr_dir' and 'user.settings.project_dir' "
53
- "are null"
54
- ),
55
- )
56
-
57
48
  db_dataset = DatasetV2(
58
49
  project_id=project_id,
59
50
  zarr_dir="__PLACEHOLDER__",
@@ -63,7 +54,7 @@ async def create_dataset(
63
54
  await db.commit()
64
55
  await db.refresh(db_dataset)
65
56
  path = (
66
- f"{user.settings.project_dir}/fractal/"
57
+ f"{user.project_dir}/fractal/"
67
58
  f"{project_id}_{sanitize_string(project.name)}/"
68
59
  f"{db_dataset.id}_{sanitize_string(db_dataset.name)}"
69
60
  )
@@ -88,7 +79,7 @@ async def create_dataset(
88
79
  )
89
80
  async def read_dataset_list(
90
81
  project_id: int,
91
- user: UserOAuth = Depends(current_active_user),
82
+ user: UserOAuth = Depends(current_user_act_ver_prof),
92
83
  db: AsyncSession = Depends(get_async_db),
93
84
  ) -> list[DatasetReadV2] | None:
94
85
  """
@@ -116,7 +107,7 @@ async def read_dataset_list(
116
107
  async def read_dataset(
117
108
  project_id: int,
118
109
  dataset_id: int,
119
- user: UserOAuth = Depends(current_active_user),
110
+ user: UserOAuth = Depends(current_user_act_ver_prof),
120
111
  db: AsyncSession = Depends(get_async_db),
121
112
  ) -> DatasetReadV2 | None:
122
113
  """
@@ -141,7 +132,7 @@ async def update_dataset(
141
132
  project_id: int,
142
133
  dataset_id: int,
143
134
  dataset_update: DatasetUpdateV2,
144
- user: UserOAuth = Depends(current_active_user),
135
+ user: UserOAuth = Depends(current_user_act_ver_prof),
145
136
  db: AsyncSession = Depends(get_async_db),
146
137
  ) -> DatasetReadV2 | None:
147
138
  """
@@ -181,7 +172,7 @@ async def update_dataset(
181
172
  async def delete_dataset(
182
173
  project_id: int,
183
174
  dataset_id: int,
184
- user: UserOAuth = Depends(current_active_user),
175
+ user: UserOAuth = Depends(current_user_act_ver_prof),
185
176
  db: AsyncSession = Depends(get_async_db),
186
177
  ) -> Response:
187
178
  """
@@ -219,7 +210,7 @@ async def delete_dataset(
219
210
 
220
211
  @router.get("/dataset/", response_model=list[DatasetReadV2])
221
212
  async def get_user_datasets(
222
- user: UserOAuth = Depends(current_active_user),
213
+ user: UserOAuth = Depends(current_user_act_ver_prof),
223
214
  db: AsyncSession = Depends(get_async_db),
224
215
  ) -> list[DatasetReadV2]:
225
216
  """
@@ -243,7 +234,7 @@ async def get_user_datasets(
243
234
  async def export_dataset(
244
235
  project_id: int,
245
236
  dataset_id: int,
246
- user: UserOAuth = Depends(current_active_user),
237
+ user: UserOAuth = Depends(current_user_act_ver_prof),
247
238
  db: AsyncSession = Depends(get_async_db),
248
239
  ) -> DatasetExportV2 | None:
249
240
  """
@@ -270,7 +261,7 @@ async def export_dataset(
270
261
  async def import_dataset(
271
262
  project_id: int,
272
263
  dataset: DatasetImportV2,
273
- user: UserOAuth = Depends(current_active_user),
264
+ user: UserOAuth = Depends(current_user_act_ver_prof),
274
265
  db: AsyncSession = Depends(get_async_db),
275
266
  ) -> DatasetReadV2 | None:
276
267
  """
@@ -23,7 +23,7 @@ from fractal_server.app.models.v2 import HistoryImageCache
23
23
  from fractal_server.app.models.v2 import HistoryRun
24
24
  from fractal_server.app.models.v2 import HistoryUnit
25
25
  from fractal_server.app.models.v2 import TaskV2
26
- from fractal_server.app.routes.auth import current_active_user
26
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
27
27
  from fractal_server.app.routes.pagination import get_pagination_params
28
28
  from fractal_server.app.routes.pagination import PaginationRequest
29
29
  from fractal_server.app.routes.pagination import PaginationResponse
@@ -68,7 +68,7 @@ async def get_workflow_tasks_statuses(
68
68
  project_id: int,
69
69
  dataset_id: int,
70
70
  workflow_id: int,
71
- user: UserOAuth = Depends(current_active_user),
71
+ user: UserOAuth = Depends(current_user_act_ver_prof),
72
72
  db: AsyncSession = Depends(get_async_db),
73
73
  ) -> JSONResponse:
74
74
  # Access control
@@ -179,7 +179,7 @@ async def get_history_run_list(
179
179
  project_id: int,
180
180
  dataset_id: int,
181
181
  workflowtask_id: int,
182
- user: UserOAuth = Depends(current_active_user),
182
+ user: UserOAuth = Depends(current_user_act_ver_prof),
183
183
  db: AsyncSession = Depends(get_async_db),
184
184
  ) -> list[HistoryRunReadAggregated]:
185
185
  # Access control
@@ -271,7 +271,7 @@ async def get_history_run_units(
271
271
  workflowtask_id: int,
272
272
  history_run_id: int,
273
273
  unit_status: HistoryUnitStatus | None = None,
274
- user: UserOAuth = Depends(current_active_user),
274
+ user: UserOAuth = Depends(current_user_act_ver_prof),
275
275
  db: AsyncSession = Depends(get_async_db),
276
276
  pagination: PaginationRequest = Depends(get_pagination_params),
277
277
  ) -> PaginationResponse[HistoryUnitRead]:
@@ -330,7 +330,7 @@ async def get_history_images(
330
330
  dataset_id: int,
331
331
  workflowtask_id: int,
332
332
  request_body: ImageQuery,
333
- user: UserOAuth = Depends(current_active_user),
333
+ user: UserOAuth = Depends(current_user_act_ver_prof),
334
334
  db: AsyncSession = Depends(get_async_db),
335
335
  pagination: PaginationRequest = Depends(get_pagination_params),
336
336
  ) -> ImagePage:
@@ -412,7 +412,7 @@ async def get_history_images(
412
412
  async def get_image_log(
413
413
  project_id: int,
414
414
  request_data: ImageLogsRequest,
415
- user: UserOAuth = Depends(current_active_user),
415
+ user: UserOAuth = Depends(current_user_act_ver_prof),
416
416
  db: AsyncSession = Depends(get_async_db),
417
417
  ) -> JSONResponse:
418
418
  # Access control
@@ -460,7 +460,7 @@ async def get_history_unit_log(
460
460
  history_unit_id: int,
461
461
  workflowtask_id: int,
462
462
  dataset_id: int,
463
- user: UserOAuth = Depends(current_active_user),
463
+ user: UserOAuth = Depends(current_user_act_ver_prof),
464
464
  db: AsyncSession = Depends(get_async_db),
465
465
  ) -> JSONResponse:
466
466
  # Access control
@@ -508,7 +508,7 @@ async def get_history_unit_log(
508
508
  async def get_dataset_history(
509
509
  project_id: int,
510
510
  dataset_id: int,
511
- user: UserOAuth = Depends(current_active_user),
511
+ user: UserOAuth = Depends(current_user_act_ver_prof),
512
512
  db: AsyncSession = Depends(get_async_db),
513
513
  ) -> list[HistoryRunRead]:
514
514
  """
@@ -13,7 +13,7 @@ from fractal_server.app.db import AsyncSession
13
13
  from fractal_server.app.db import get_async_db
14
14
  from fractal_server.app.models import HistoryImageCache
15
15
  from fractal_server.app.models import UserOAuth
16
- from fractal_server.app.routes.auth import current_active_user
16
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
17
17
  from fractal_server.app.routes.pagination import get_pagination_params
18
18
  from fractal_server.app.routes.pagination import PaginationRequest
19
19
  from fractal_server.app.routes.pagination import PaginationResponse
@@ -60,7 +60,7 @@ async def post_new_image(
60
60
  project_id: int,
61
61
  dataset_id: int,
62
62
  new_image: SingleImage,
63
- user: UserOAuth = Depends(current_active_user),
63
+ user: UserOAuth = Depends(current_user_act_ver_prof),
64
64
  db: AsyncSession = Depends(get_async_db),
65
65
  ) -> Response:
66
66
  output = await _get_dataset_check_owner(
@@ -112,7 +112,7 @@ async def query_dataset_images(
112
112
  dataset_id: int,
113
113
  query: ImageQueryWithZarrUrl | None = None,
114
114
  pagination: PaginationRequest = Depends(get_pagination_params),
115
- user: UserOAuth = Depends(current_active_user),
115
+ user: UserOAuth = Depends(current_user_act_ver_prof),
116
116
  db: AsyncSession = Depends(get_async_db),
117
117
  ) -> ImagePage:
118
118
  page = pagination.page
@@ -183,7 +183,7 @@ async def delete_dataset_images(
183
183
  project_id: int,
184
184
  dataset_id: int,
185
185
  zarr_url: str,
186
- user: UserOAuth = Depends(current_active_user),
186
+ user: UserOAuth = Depends(current_user_act_ver_prof),
187
187
  db: AsyncSession = Depends(get_async_db),
188
188
  ) -> Response:
189
189
  output = await _get_dataset_check_owner(
@@ -227,7 +227,7 @@ async def patch_dataset_image(
227
227
  project_id: int,
228
228
  dataset_id: int,
229
229
  image_update: SingleImageUpdate,
230
- user: UserOAuth = Depends(current_active_user),
230
+ user: UserOAuth = Depends(current_user_act_ver_prof),
231
231
  db: AsyncSession = Depends(get_async_db),
232
232
  ):
233
233
  output = await _get_dataset_check_owner(
@@ -23,7 +23,7 @@ from ._aux_functions import _get_job_check_owner
23
23
  from ._aux_functions import _get_project_check_owner
24
24
  from ._aux_functions import _get_workflow_check_owner
25
25
  from fractal_server.app.models import UserOAuth
26
- from fractal_server.app.routes.auth import current_active_user
26
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
27
27
  from fractal_server.runner.filenames import WORKFLOW_LOG_FILENAME
28
28
 
29
29
 
@@ -39,7 +39,7 @@ router = APIRouter()
39
39
 
40
40
  @router.get("/job/", response_model=list[JobReadV2])
41
41
  async def get_user_jobs(
42
- user: UserOAuth = Depends(current_active_user),
42
+ user: UserOAuth = Depends(current_user_act_ver_prof),
43
43
  log: bool = True,
44
44
  db: AsyncSession = Depends(get_async_db),
45
45
  ) -> list[JobReadV2]:
@@ -68,7 +68,7 @@ async def get_user_jobs(
68
68
  async def get_workflow_jobs(
69
69
  project_id: int,
70
70
  workflow_id: int,
71
- user: UserOAuth = Depends(current_active_user),
71
+ user: UserOAuth = Depends(current_user_act_ver_prof),
72
72
  db: AsyncSession = Depends(get_async_db),
73
73
  ) -> list[JobReadV2] | None:
74
74
  """
@@ -88,7 +88,7 @@ async def get_latest_job(
88
88
  project_id: int,
89
89
  workflow_id: int,
90
90
  dataset_id: int,
91
- user: UserOAuth = Depends(current_active_user),
91
+ user: UserOAuth = Depends(current_user_act_ver_prof),
92
92
  db: AsyncSession = Depends(get_async_db),
93
93
  ) -> JobReadV2:
94
94
  await _get_workflow_check_owner(
@@ -120,7 +120,7 @@ async def read_job(
120
120
  project_id: int,
121
121
  job_id: int,
122
122
  show_tmp_logs: bool = False,
123
- user: UserOAuth = Depends(current_active_user),
123
+ user: UserOAuth = Depends(current_user_act_ver_prof),
124
124
  db: AsyncSession = Depends(get_async_db),
125
125
  ) -> JobReadV2 | None:
126
126
  """
@@ -153,7 +153,7 @@ async def read_job(
153
153
  async def download_job_logs(
154
154
  project_id: int,
155
155
  job_id: int,
156
- user: UserOAuth = Depends(current_active_user),
156
+ user: UserOAuth = Depends(current_user_act_ver_prof),
157
157
  db: AsyncSession = Depends(get_async_db),
158
158
  ) -> StreamingResponse:
159
159
  """
@@ -183,7 +183,7 @@ async def download_job_logs(
183
183
  )
184
184
  async def get_job_list(
185
185
  project_id: int,
186
- user: UserOAuth = Depends(current_active_user),
186
+ user: UserOAuth = Depends(current_user_act_ver_prof),
187
187
  log: bool = True,
188
188
  db: AsyncSession = Depends(get_async_db),
189
189
  ) -> list[JobReadV2] | None:
@@ -212,7 +212,7 @@ async def get_job_list(
212
212
  async def stop_job(
213
213
  project_id: int,
214
214
  job_id: int,
215
- user: UserOAuth = Depends(current_active_user),
215
+ user: UserOAuth = Depends(current_user_act_ver_prof),
216
216
  db: AsyncSession = Depends(get_async_db),
217
217
  ) -> Response:
218
218
  """
@@ -11,7 +11,7 @@ from .images import ImageQuery
11
11
  from fractal_server.app.db import AsyncSession
12
12
  from fractal_server.app.db import get_async_db
13
13
  from fractal_server.app.models import UserOAuth
14
- from fractal_server.app.routes.auth import current_active_user
14
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
15
15
  from fractal_server.app.schemas.v2 import HistoryUnitStatus
16
16
  from fractal_server.app.schemas.v2 import TaskType
17
17
  from fractal_server.images.status_tools import enrich_images_unsorted_async
@@ -32,7 +32,7 @@ async def verify_unique_types(
32
32
  dataset_id: int,
33
33
  workflowtask_id: int,
34
34
  query: ImageQuery | None = None,
35
- user: UserOAuth = Depends(current_active_user),
35
+ user: UserOAuth = Depends(current_user_act_ver_prof),
36
36
  db: AsyncSession = Depends(get_async_db),
37
37
  ) -> list[str]:
38
38
  # Get dataset
@@ -93,7 +93,7 @@ async def check_non_processed_images(
93
93
  workflow_id: int,
94
94
  workflowtask_id: int,
95
95
  filters: NonProcessedImagesPayload,
96
- user: UserOAuth = Depends(current_active_user),
96
+ user: UserOAuth = Depends(current_user_act_ver_prof),
97
97
  db: AsyncSession = Depends(get_async_db),
98
98
  ) -> JSONResponse:
99
99
  db_workflow_task, db_workflow = await _get_workflow_task_check_owner(
@@ -15,18 +15,19 @@ from ....models.v2 import ProjectV2
15
15
  from ....schemas.v2 import ProjectCreateV2
16
16
  from ....schemas.v2 import ProjectReadV2
17
17
  from ....schemas.v2 import ProjectUpdateV2
18
+ from ...aux.validate_user_profile import validate_user_profile
18
19
  from ._aux_functions import _check_project_exists
19
20
  from ._aux_functions import _get_project_check_owner
20
21
  from ._aux_functions import _get_submitted_jobs_statement
21
22
  from fractal_server.app.models import UserOAuth
22
- from fractal_server.app.routes.auth import current_active_user
23
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
23
24
 
24
25
  router = APIRouter()
25
26
 
26
27
 
27
28
  @router.get("/project/", response_model=list[ProjectReadV2])
28
29
  async def get_list_project(
29
- user: UserOAuth = Depends(current_active_user),
30
+ user: UserOAuth = Depends(current_user_act_ver_prof),
30
31
  db: AsyncSession = Depends(get_async_db),
31
32
  ) -> list[ProjectV2]:
32
33
  """
@@ -46,19 +47,26 @@ async def get_list_project(
46
47
  @router.post("/project/", response_model=ProjectReadV2, status_code=201)
47
48
  async def create_project(
48
49
  project: ProjectCreateV2,
49
- user: UserOAuth = Depends(current_active_user),
50
+ user: UserOAuth = Depends(current_user_act_ver_prof),
50
51
  db: AsyncSession = Depends(get_async_db),
51
52
  ) -> ProjectReadV2 | None:
52
53
  """
53
54
  Create new project
54
55
  """
55
56
 
57
+ # Get validated resource and profile
58
+ resource, profile = await validate_user_profile(
59
+ user=user,
60
+ db=db,
61
+ )
62
+ resource_id = resource.id
63
+
56
64
  # Check that there is no project with the same user and name
57
65
  await _check_project_exists(
58
66
  project_name=project.name, user_id=user.id, db=db
59
67
  )
60
68
 
61
- db_project = ProjectV2(**project.model_dump())
69
+ db_project = ProjectV2(**project.model_dump(), resource_id=resource_id)
62
70
  db_project.user_list.append(user)
63
71
 
64
72
  db.add(db_project)
@@ -72,7 +80,7 @@ async def create_project(
72
80
  @router.get("/project/{project_id}/", response_model=ProjectReadV2)
73
81
  async def read_project(
74
82
  project_id: int,
75
- user: UserOAuth = Depends(current_active_user),
83
+ user: UserOAuth = Depends(current_user_act_ver_prof),
76
84
  db: AsyncSession = Depends(get_async_db),
77
85
  ) -> ProjectReadV2 | None:
78
86
  """
@@ -89,7 +97,7 @@ async def read_project(
89
97
  async def update_project(
90
98
  project_id: int,
91
99
  project_update: ProjectUpdateV2,
92
- user: UserOAuth = Depends(current_active_user),
100
+ user: UserOAuth = Depends(current_user_act_ver_prof),
93
101
  db: AsyncSession = Depends(get_async_db),
94
102
  ):
95
103
  project = await _get_project_check_owner(
@@ -114,7 +122,7 @@ async def update_project(
114
122
  @router.delete("/project/{project_id}/", status_code=204)
115
123
  async def delete_project(
116
124
  project_id: int,
117
- user: UserOAuth = Depends(current_active_user),
125
+ user: UserOAuth = Depends(current_user_act_ver_prof),
118
126
  db: AsyncSession = Depends(get_async_db),
119
127
  ) -> Response:
120
128
  """
@@ -13,7 +13,7 @@ from ._aux_functions import _get_dataset_check_owner
13
13
  from ._aux_functions import _get_submitted_jobs_statement
14
14
  from ._aux_functions import _get_workflow_check_owner
15
15
  from fractal_server.app.models import UserOAuth
16
- from fractal_server.app.routes.auth import current_active_user
16
+ from fractal_server.app.routes.auth import current_user_act_ver_prof
17
17
 
18
18
  router = APIRouter()
19
19
 
@@ -28,7 +28,7 @@ async def get_workflowtask_status(
28
28
  project_id: int,
29
29
  dataset_id: int,
30
30
  workflow_id: int,
31
- user: UserOAuth = Depends(current_active_user),
31
+ user: UserOAuth = Depends(current_user_act_ver_prof),
32
32
  db: AsyncSession = Depends(get_async_db),
33
33
  ) -> LegacyStatusReadV2 | None:
34
34
  """