fractal-server 2.18.6__py3-none-any.whl → 2.19.0a0__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 (34) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/security.py +16 -0
  3. fractal_server/app/routes/admin/v2/sharing.py +47 -0
  4. fractal_server/app/routes/api/__init__.py +4 -52
  5. fractal_server/app/routes/api/alive.py +13 -0
  6. fractal_server/app/routes/api/settings.py +44 -0
  7. fractal_server/app/routes/api/v2/dataset.py +9 -8
  8. fractal_server/app/routes/api/v2/history.py +8 -8
  9. fractal_server/app/routes/api/v2/images.py +6 -5
  10. fractal_server/app/routes/api/v2/job.py +10 -9
  11. fractal_server/app/routes/api/v2/pre_submission_checks.py +3 -3
  12. fractal_server/app/routes/api/v2/project.py +7 -6
  13. fractal_server/app/routes/api/v2/sharing.py +17 -9
  14. fractal_server/app/routes/api/v2/status_legacy.py +2 -2
  15. fractal_server/app/routes/api/v2/submit.py +2 -2
  16. fractal_server/app/routes/api/v2/task.py +7 -6
  17. fractal_server/app/routes/api/v2/task_collection.py +2 -2
  18. fractal_server/app/routes/api/v2/task_collection_custom.py +2 -2
  19. fractal_server/app/routes/api/v2/task_collection_pixi.py +2 -2
  20. fractal_server/app/routes/api/v2/task_group.py +7 -6
  21. fractal_server/app/routes/api/v2/task_group_lifecycle.py +4 -4
  22. fractal_server/app/routes/api/v2/task_version_update.py +4 -3
  23. fractal_server/app/routes/api/v2/workflow.py +9 -8
  24. fractal_server/app/routes/api/v2/workflow_import.py +2 -2
  25. fractal_server/app/routes/api/v2/workflowtask.py +6 -5
  26. fractal_server/app/routes/auth/__init__.py +18 -1
  27. fractal_server/app/routes/auth/users.py +11 -0
  28. fractal_server/app/schemas/user.py +7 -0
  29. fractal_server/migrations/versions/e53dc51fdf93_add_useroauth_is_guest.py +36 -0
  30. {fractal_server-2.18.6.dist-info → fractal_server-2.19.0a0.dist-info}/METADATA +1 -1
  31. {fractal_server-2.18.6.dist-info → fractal_server-2.19.0a0.dist-info}/RECORD +34 -31
  32. {fractal_server-2.18.6.dist-info → fractal_server-2.19.0a0.dist-info}/WHEEL +0 -0
  33. {fractal_server-2.18.6.dist-info → fractal_server-2.19.0a0.dist-info}/entry_points.txt +0 -0
  34. {fractal_server-2.18.6.dist-info → fractal_server-2.19.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,7 @@ from fractal_server.app.db import AsyncSession
5
5
  from fractal_server.app.db import get_async_db
6
6
  from fractal_server.app.models import UserOAuth
7
7
  from fractal_server.app.models.v2 import JobV2
8
- from fractal_server.app.routes.auth import current_user_act_ver_prof
8
+ from fractal_server.app.routes.auth import get_api_guest
9
9
  from fractal_server.app.schemas.v2.sharing import ProjectPermissions
10
10
  from fractal_server.app.schemas.v2.status_legacy import LegacyStatusRead
11
11
  from fractal_server.app.schemas.v2.status_legacy import WorkflowTaskStatusType
@@ -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_user_act_ver_prof),
31
+ user: UserOAuth = Depends(get_api_guest),
32
32
  db: AsyncSession = Depends(get_async_db),
33
33
  ) -> LegacyStatusRead | None:
34
34
  """
@@ -20,7 +20,7 @@ from fractal_server.app.models.v2 import JobV2
20
20
  from fractal_server.app.routes.api.v2._aux_functions_tasks import (
21
21
  _get_task_read_access,
22
22
  )
23
- from fractal_server.app.routes.auth import current_user_act_ver_prof
23
+ from fractal_server.app.routes.auth import get_api_user
24
24
  from fractal_server.app.routes.aux.validate_user_profile import (
25
25
  validate_user_profile,
26
26
  )
@@ -59,7 +59,7 @@ async def submit_job(
59
59
  job_create: JobCreate,
60
60
  background_tasks: BackgroundTasks,
61
61
  request: Request,
62
- user: UserOAuth = Depends(current_user_act_ver_prof),
62
+ user: UserOAuth = Depends(get_api_user),
63
63
  db: AsyncSession = Depends(get_async_db),
64
64
  ) -> JobRead | None:
65
65
  # Remove non-submitted Jobs from the app state when the list grows
@@ -24,7 +24,8 @@ from fractal_server.app.models import LinkUserGroup
24
24
  from fractal_server.app.models import UserOAuth
25
25
  from fractal_server.app.models.v2 import TaskGroupV2
26
26
  from fractal_server.app.models.v2 import TaskV2
27
- from fractal_server.app.routes.auth import current_user_act_ver_prof
27
+ from fractal_server.app.routes.auth import get_api_guest
28
+ from fractal_server.app.routes.auth import get_api_user
28
29
  from fractal_server.app.schemas.v2 import TaskCreate
29
30
  from fractal_server.app.schemas.v2 import TaskGroupOriginEnum
30
31
  from fractal_server.app.schemas.v2 import TaskRead
@@ -43,7 +44,7 @@ async def get_list_task(
43
44
  category: str | None = None,
44
45
  modality: str | None = None,
45
46
  author: str | None = None,
46
- user: UserOAuth = Depends(current_user_act_ver_prof),
47
+ user: UserOAuth = Depends(get_api_guest),
47
48
  db: AsyncSession = Depends(get_async_db),
48
49
  ) -> list[TaskRead]:
49
50
  """
@@ -88,7 +89,7 @@ async def get_list_task(
88
89
  @router.get("/{task_id}/", response_model=TaskRead)
89
90
  async def get_task(
90
91
  task_id: int,
91
- user: UserOAuth = Depends(current_user_act_ver_prof),
92
+ user: UserOAuth = Depends(get_api_guest),
92
93
  db: AsyncSession = Depends(get_async_db),
93
94
  ) -> TaskRead:
94
95
  """
@@ -102,7 +103,7 @@ async def get_task(
102
103
  async def patch_task(
103
104
  task_id: int,
104
105
  task_update: TaskUpdate,
105
- user: UserOAuth = Depends(current_user_act_ver_prof),
106
+ user: UserOAuth = Depends(get_api_user),
106
107
  db: AsyncSession = Depends(get_async_db),
107
108
  ) -> TaskRead | None:
108
109
  """
@@ -140,7 +141,7 @@ async def create_task(
140
141
  task: TaskCreate,
141
142
  user_group_id: int | None = None,
142
143
  private: bool = False,
143
- user: UserOAuth = Depends(current_user_act_ver_prof),
144
+ user: UserOAuth = Depends(get_api_user),
144
145
  db: AsyncSession = Depends(get_async_db),
145
146
  ) -> TaskRead | None:
146
147
  """
@@ -221,7 +222,7 @@ async def create_task(
221
222
  @router.delete("/{task_id}/", status_code=204)
222
223
  async def delete_task(
223
224
  task_id: int,
224
- user: UserOAuth = Depends(current_user_act_ver_prof),
225
+ user: UserOAuth = Depends(get_api_user),
225
226
  db: AsyncSession = Depends(get_async_db),
226
227
  ) -> Response:
227
228
  """
@@ -19,7 +19,7 @@ from fractal_server.app.db import get_async_db
19
19
  from fractal_server.app.models import UserOAuth
20
20
  from fractal_server.app.models.v2 import TaskGroupActivityV2
21
21
  from fractal_server.app.models.v2 import TaskGroupV2
22
- from fractal_server.app.routes.auth import current_user_act_ver_prof
22
+ from fractal_server.app.routes.auth import get_api_user
23
23
  from fractal_server.app.routes.aux.validate_user_profile import (
24
24
  validate_user_profile,
25
25
  )
@@ -158,7 +158,7 @@ async def collect_tasks_pip(
158
158
  request_data: CollectionRequestData = Depends(parse_request_data),
159
159
  private: bool = False,
160
160
  user_group_id: int | None = None,
161
- user: UserOAuth = Depends(current_user_act_ver_prof),
161
+ user: UserOAuth = Depends(get_api_user),
162
162
  db: AsyncSession = Depends(get_async_db),
163
163
  ) -> TaskGroupActivityRead:
164
164
  """
@@ -12,7 +12,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
12
12
  from fractal_server.app.db import get_async_db
13
13
  from fractal_server.app.models import UserOAuth
14
14
  from fractal_server.app.models.v2 import TaskGroupV2
15
- from fractal_server.app.routes.auth import current_user_act_ver_prof
15
+ from fractal_server.app.routes.auth import get_api_user
16
16
  from fractal_server.app.routes.aux.validate_user_profile import (
17
17
  validate_user_profile,
18
18
  )
@@ -43,7 +43,7 @@ async def collect_task_custom(
43
43
  task_collect: TaskCollectCustom,
44
44
  private: bool = False,
45
45
  user_group_id: int | None = None,
46
- user: UserOAuth = Depends(current_user_act_ver_prof),
46
+ user: UserOAuth = Depends(get_api_user),
47
47
  db: AsyncSession = Depends(get_async_db),
48
48
  ) -> list[TaskRead]:
49
49
  # Get validated resource and profile
@@ -27,7 +27,7 @@ from fractal_server.app.routes.api.v2._aux_functions_tasks import (
27
27
  from fractal_server.app.routes.api.v2._aux_functions_tasks import (
28
28
  _verify_non_duplication_user_constraint,
29
29
  )
30
- from fractal_server.app.routes.auth import current_user_act_ver_prof
30
+ from fractal_server.app.routes.auth import get_api_user
31
31
  from fractal_server.app.routes.aux.validate_user_profile import (
32
32
  validate_user_profile,
33
33
  )
@@ -83,7 +83,7 @@ async def collect_task_pixi(
83
83
  pixi_version: NonEmptyStr | None = Form(None),
84
84
  private: bool = False,
85
85
  user_group_id: int | None = None,
86
- user: UserOAuth = Depends(current_user_act_ver_prof),
86
+ user: UserOAuth = Depends(get_api_user),
87
87
  db: AsyncSession = Depends(get_async_db),
88
88
  ) -> TaskGroupActivityRead:
89
89
  # Get validated resource and profile
@@ -17,7 +17,8 @@ from fractal_server.app.models import LinkUserGroup
17
17
  from fractal_server.app.models import UserOAuth
18
18
  from fractal_server.app.models.v2 import TaskGroupActivityV2
19
19
  from fractal_server.app.models.v2 import TaskGroupV2
20
- from fractal_server.app.routes.auth import current_user_act_ver_prof
20
+ from fractal_server.app.routes.auth import get_api_guest
21
+ from fractal_server.app.routes.auth import get_api_user
21
22
  from fractal_server.app.routes.auth._aux_auth import (
22
23
  _get_default_usergroup_id_or_none,
23
24
  )
@@ -70,7 +71,7 @@ async def get_task_group_activity_list(
70
71
  status: TaskGroupActivityStatus | None = None,
71
72
  action: TaskGroupActivityAction | None = None,
72
73
  timestamp_started_min: AwareDatetime | None = None,
73
- user: UserOAuth = Depends(current_user_act_ver_prof),
74
+ user: UserOAuth = Depends(get_api_guest),
74
75
  db: AsyncSession = Depends(get_async_db),
75
76
  ) -> list[TaskGroupActivityRead]:
76
77
  stm = select(TaskGroupActivityV2).where(
@@ -102,7 +103,7 @@ async def get_task_group_activity_list(
102
103
  )
103
104
  async def get_task_group_activity(
104
105
  task_group_activity_id: int,
105
- user: UserOAuth = Depends(current_user_act_ver_prof),
106
+ user: UserOAuth = Depends(get_api_guest),
106
107
  db: AsyncSession = Depends(get_async_db),
107
108
  ) -> TaskGroupActivityRead:
108
109
  activity = await db.get(TaskGroupActivityV2, task_group_activity_id)
@@ -126,7 +127,7 @@ async def get_task_group_activity(
126
127
 
127
128
  @router.get("/", response_model=list[tuple[str, list[TaskGroupRead]]])
128
129
  async def get_task_group_list(
129
- user: UserOAuth = Depends(current_user_act_ver_prof),
130
+ user: UserOAuth = Depends(get_api_guest),
130
131
  db: AsyncSession = Depends(get_async_db),
131
132
  only_active: bool = False,
132
133
  only_owner: bool = False,
@@ -194,7 +195,7 @@ async def get_task_group_list(
194
195
  @router.get("/{task_group_id}/", response_model=TaskGroupRead)
195
196
  async def get_task_group(
196
197
  task_group_id: int,
197
- user: UserOAuth = Depends(current_user_act_ver_prof),
198
+ user: UserOAuth = Depends(get_api_guest),
198
199
  db: AsyncSession = Depends(get_async_db),
199
200
  ) -> TaskGroupRead:
200
201
  """
@@ -212,7 +213,7 @@ async def get_task_group(
212
213
  async def patch_task_group(
213
214
  task_group_id: int,
214
215
  task_group_update: TaskGroupUpdate,
215
- user: UserOAuth = Depends(current_user_act_ver_prof),
216
+ user: UserOAuth = Depends(get_api_user),
216
217
  db: AsyncSession = Depends(get_async_db),
217
218
  ) -> TaskGroupRead:
218
219
  """
@@ -9,7 +9,7 @@ from fractal_server.app.db import AsyncSession
9
9
  from fractal_server.app.db import get_async_db
10
10
  from fractal_server.app.models import UserOAuth
11
11
  from fractal_server.app.models.v2 import TaskGroupActivityV2
12
- from fractal_server.app.routes.auth import current_user_act_ver_prof
12
+ from fractal_server.app.routes.auth import get_api_user
13
13
  from fractal_server.app.routes.aux.validate_user_profile import (
14
14
  validate_user_profile,
15
15
  )
@@ -51,7 +51,7 @@ async def deactivate_task_group(
51
51
  task_group_id: int,
52
52
  background_tasks: BackgroundTasks,
53
53
  response: Response,
54
- user: UserOAuth = Depends(current_user_act_ver_prof),
54
+ user: UserOAuth = Depends(get_api_user),
55
55
  db: AsyncSession = Depends(get_async_db),
56
56
  ) -> TaskGroupActivityRead:
57
57
  """
@@ -155,7 +155,7 @@ async def reactivate_task_group(
155
155
  task_group_id: int,
156
156
  background_tasks: BackgroundTasks,
157
157
  response: Response,
158
- user: UserOAuth = Depends(current_user_act_ver_prof),
158
+ user: UserOAuth = Depends(get_api_user),
159
159
  db: AsyncSession = Depends(get_async_db),
160
160
  ) -> TaskGroupRead:
161
161
  """
@@ -263,7 +263,7 @@ async def delete_task_group(
263
263
  task_group_id: int,
264
264
  background_tasks: BackgroundTasks,
265
265
  response: Response,
266
- user: UserOAuth = Depends(current_user_act_ver_prof),
266
+ user: UserOAuth = Depends(get_api_user),
267
267
  db: AsyncSession = Depends(get_async_db),
268
268
  ) -> TaskGroupActivityRead:
269
269
  """
@@ -17,7 +17,8 @@ from fractal_server.app.models import LinkUserGroup
17
17
  from fractal_server.app.models import UserOAuth
18
18
  from fractal_server.app.models.v2 import TaskGroupV2
19
19
  from fractal_server.app.models.v2 import TaskV2
20
- from fractal_server.app.routes.auth import current_user_act_ver_prof
20
+ from fractal_server.app.routes.auth import get_api_guest
21
+ from fractal_server.app.routes.auth import get_api_user
21
22
  from fractal_server.app.schemas.v2 import TaskType
22
23
  from fractal_server.app.schemas.v2 import WorkflowTaskRead
23
24
  from fractal_server.app.schemas.v2 import WorkflowTaskReplace
@@ -77,7 +78,7 @@ class TaskVersionRead(BaseModel):
77
78
  async def get_workflow_version_update_candidates(
78
79
  project_id: int,
79
80
  workflow_id: int,
80
- user: UserOAuth = Depends(current_user_act_ver_prof),
81
+ user: UserOAuth = Depends(get_api_guest),
81
82
  db: AsyncSession = Depends(get_async_db),
82
83
  ) -> list[list[TaskVersionRead]]:
83
84
  workflow = await _get_workflow_check_access(
@@ -180,7 +181,7 @@ async def replace_workflowtask(
180
181
  workflow_task_id: int,
181
182
  task_id: int,
182
183
  replace: WorkflowTaskReplace,
183
- user: UserOAuth = Depends(current_user_act_ver_prof),
184
+ user: UserOAuth = Depends(get_api_user),
184
185
  db: AsyncSession = Depends(get_async_db),
185
186
  ) -> WorkflowTaskRead:
186
187
  # Get objects from database
@@ -14,7 +14,8 @@ from fractal_server.app.models import UserOAuth
14
14
  from fractal_server.app.models.v2 import JobV2
15
15
  from fractal_server.app.models.v2 import TaskGroupV2
16
16
  from fractal_server.app.models.v2 import WorkflowV2
17
- from fractal_server.app.routes.auth import current_user_act_ver_prof
17
+ from fractal_server.app.routes.auth import get_api_guest
18
+ from fractal_server.app.routes.auth import get_api_user
18
19
  from fractal_server.app.schemas.v2 import WorkflowCreate
19
20
  from fractal_server.app.schemas.v2 import WorkflowExport
20
21
  from fractal_server.app.schemas.v2 import WorkflowRead
@@ -39,7 +40,7 @@ router = APIRouter()
39
40
  )
40
41
  async def get_workflow_list(
41
42
  project_id: int,
42
- user: UserOAuth = Depends(current_user_act_ver_prof),
43
+ user: UserOAuth = Depends(get_api_guest),
43
44
  db: AsyncSession = Depends(get_async_db),
44
45
  ) -> list[WorkflowRead] | None:
45
46
  """
@@ -69,7 +70,7 @@ async def get_workflow_list(
69
70
  async def create_workflow(
70
71
  project_id: int,
71
72
  workflow: WorkflowCreate,
72
- user: UserOAuth = Depends(current_user_act_ver_prof),
73
+ user: UserOAuth = Depends(get_api_user),
73
74
  db: AsyncSession = Depends(get_async_db),
74
75
  ) -> WorkflowRead | None:
75
76
  """
@@ -99,7 +100,7 @@ async def create_workflow(
99
100
  async def read_workflow(
100
101
  project_id: int,
101
102
  workflow_id: int,
102
- user: UserOAuth = Depends(current_user_act_ver_prof),
103
+ user: UserOAuth = Depends(get_api_guest),
103
104
  db: AsyncSession = Depends(get_async_db),
104
105
  ) -> WorkflowReadWithWarnings | None:
105
106
  """
@@ -134,7 +135,7 @@ async def update_workflow(
134
135
  project_id: int,
135
136
  workflow_id: int,
136
137
  patch: WorkflowUpdate,
137
- user: UserOAuth = Depends(current_user_act_ver_prof),
138
+ user: UserOAuth = Depends(get_api_user),
138
139
  db: AsyncSession = Depends(get_async_db),
139
140
  ) -> WorkflowReadWithWarnings | None:
140
141
  """
@@ -208,7 +209,7 @@ async def update_workflow(
208
209
  async def delete_workflow(
209
210
  project_id: int,
210
211
  workflow_id: int,
211
- user: UserOAuth = Depends(current_user_act_ver_prof),
212
+ user: UserOAuth = Depends(get_api_user),
212
213
  db: AsyncSession = Depends(get_async_db),
213
214
  ) -> Response:
214
215
  """
@@ -254,7 +255,7 @@ async def delete_workflow(
254
255
  async def export_workflow(
255
256
  project_id: int,
256
257
  workflow_id: int,
257
- user: UserOAuth = Depends(current_user_act_ver_prof),
258
+ user: UserOAuth = Depends(get_api_guest),
258
259
  db: AsyncSession = Depends(get_async_db),
259
260
  ) -> WorkflowExport | None:
260
261
  """
@@ -295,7 +296,7 @@ class WorkflowTaskTypeFiltersInfo(BaseModel):
295
296
  async def get_workflow_type_filters(
296
297
  project_id: int,
297
298
  workflow_id: int,
298
- user: UserOAuth = Depends(current_user_act_ver_prof),
299
+ user: UserOAuth = Depends(get_api_guest),
299
300
  db: AsyncSession = Depends(get_async_db),
300
301
  ) -> list[WorkflowTaskTypeFiltersInfo]:
301
302
  """
@@ -15,7 +15,7 @@ from fractal_server.app.models.v2 import WorkflowV2
15
15
  from fractal_server.app.routes.api.v2._aux_task_group_disambiguation import (
16
16
  _disambiguate_task_groups,
17
17
  )
18
- from fractal_server.app.routes.auth import current_user_act_ver_prof
18
+ from fractal_server.app.routes.auth import get_api_user
19
19
  from fractal_server.app.routes.auth._aux_auth import (
20
20
  _get_default_usergroup_id_or_none,
21
21
  )
@@ -213,7 +213,7 @@ async def _get_task_by_taskimport(
213
213
  async def import_workflow(
214
214
  project_id: int,
215
215
  workflow_import: WorkflowImport,
216
- user: UserOAuth = Depends(current_user_act_ver_prof),
216
+ user: UserOAuth = Depends(get_api_user),
217
217
  db: AsyncSession = Depends(get_async_db),
218
218
  ) -> WorkflowReadWithWarnings:
219
219
  """
@@ -9,7 +9,8 @@ from fastapi import status
9
9
  from fractal_server.app.db import AsyncSession
10
10
  from fractal_server.app.db import get_async_db
11
11
  from fractal_server.app.models import UserOAuth
12
- from fractal_server.app.routes.auth import current_user_act_ver_prof
12
+ from fractal_server.app.routes.auth import get_api_guest
13
+ from fractal_server.app.routes.auth import get_api_user
13
14
  from fractal_server.app.schemas.v2 import TaskType
14
15
  from fractal_server.app.schemas.v2 import WorkflowTaskCreate
15
16
  from fractal_server.app.schemas.v2 import WorkflowTaskRead
@@ -36,7 +37,7 @@ async def create_workflowtask(
36
37
  workflow_id: int,
37
38
  task_id: int,
38
39
  wftask: WorkflowTaskCreate,
39
- user: UserOAuth = Depends(current_user_act_ver_prof),
40
+ user: UserOAuth = Depends(get_api_user),
40
41
  db: AsyncSession = Depends(get_async_db),
41
42
  ) -> WorkflowTaskRead | None:
42
43
  """
@@ -106,7 +107,7 @@ async def read_workflowtask(
106
107
  project_id: int,
107
108
  workflow_id: int,
108
109
  workflow_task_id: int,
109
- user: UserOAuth = Depends(current_user_act_ver_prof),
110
+ user: UserOAuth = Depends(get_api_guest),
110
111
  db: AsyncSession = Depends(get_async_db),
111
112
  ):
112
113
  workflow_task, _ = await _get_workflow_task_check_access(
@@ -129,7 +130,7 @@ async def update_workflowtask(
129
130
  workflow_id: int,
130
131
  workflow_task_id: int,
131
132
  workflow_task_update: WorkflowTaskUpdate,
132
- user: UserOAuth = Depends(current_user_act_ver_prof),
133
+ user: UserOAuth = Depends(get_api_user),
133
134
  db: AsyncSession = Depends(get_async_db),
134
135
  ) -> WorkflowTaskRead | None:
135
136
  """
@@ -214,7 +215,7 @@ async def delete_workflowtask(
214
215
  project_id: int,
215
216
  workflow_id: int,
216
217
  workflow_task_id: int,
217
- user: UserOAuth = Depends(current_user_act_ver_prof),
218
+ user: UserOAuth = Depends(get_api_user),
218
219
  db: AsyncSession = Depends(get_async_db),
219
220
  ) -> Response:
220
221
  """
@@ -57,7 +57,7 @@ current_user_act_ver = fastapi_users.current_user(
57
57
  )
58
58
 
59
59
 
60
- async def current_user_act_ver_prof(
60
+ async def get_api_guest(
61
61
  user: UserOAuth = Depends(current_user_act_ver),
62
62
  ) -> UserOAuth:
63
63
  """
@@ -76,6 +76,23 @@ async def current_user_act_ver_prof(
76
76
  return user
77
77
 
78
78
 
79
+ async def get_api_user(
80
+ user: UserOAuth = Depends(get_api_guest),
81
+ ) -> UserOAuth:
82
+ """
83
+ Require a active&verified non-guest user, with a non-null `profile_id`.
84
+
85
+ Raises 401 if user does not exist or is not active.
86
+ Raises 403 if user is not verified, is a guest or has null `profile_id`.
87
+ """
88
+ if user.is_guest:
89
+ raise HTTPException(
90
+ status_code=status.HTTP_403_FORBIDDEN,
91
+ detail="This feature is not available for guest users.",
92
+ )
93
+ return user
94
+
95
+
79
96
  current_superuser_act = fastapi_users.current_user(
80
97
  active=True,
81
98
  superuser=True,
@@ -83,6 +83,17 @@ async def patch_user(
83
83
  db=db,
84
84
  )
85
85
 
86
+ will_be_superuser = (
87
+ user_update.is_superuser
88
+ if user_update.is_superuser is not None
89
+ else user_to_patch.is_superuser
90
+ )
91
+ if user_update.is_guest and will_be_superuser:
92
+ raise HTTPException(
93
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
94
+ detail="Superuser cannot be guest.",
95
+ )
96
+
86
97
  # Modify user attributes
87
98
  try:
88
99
  user = await user_manager.update(
@@ -41,13 +41,16 @@ class UserRead(schemas.BaseUser[int]):
41
41
  Schema for `User` read from database.
42
42
 
43
43
  Attributes:
44
+ is_guest:
44
45
  group_ids_names:
45
46
  oauth_accounts:
46
47
  profile_id:
47
48
  project_dirs:
48
49
  slurm_accounts:
50
+
49
51
  """
50
52
 
53
+ is_guest: bool
51
54
  group_ids_names: list[tuple[int, str]] | None = None
52
55
  oauth_accounts: list[OAuthAccountRead]
53
56
  profile_id: int | None = None
@@ -65,6 +68,7 @@ class UserUpdate(schemas.BaseUserUpdate):
65
68
  is_active:
66
69
  is_superuser:
67
70
  is_verified:
71
+ is_guest:
68
72
  profile_id:
69
73
  project_dirs:
70
74
  slurm_accounts:
@@ -76,6 +80,7 @@ class UserUpdate(schemas.BaseUserUpdate):
76
80
  is_active: bool = None
77
81
  is_superuser: bool = None
78
82
  is_verified: bool = None
83
+ is_guest: bool = None
79
84
  profile_id: int | None = None
80
85
  project_dirs: Annotated[
81
86
  ListUniqueAbsolutePathStr, AfterValidator(_validate_cmd_list)
@@ -100,11 +105,13 @@ class UserCreate(schemas.BaseUserCreate):
100
105
  Schema for `User` creation.
101
106
 
102
107
  Attributes:
108
+ is_guest:
103
109
  profile_id:
104
110
  project_dirs:
105
111
  slurm_accounts:
106
112
  """
107
113
 
114
+ is_guest: bool = False
108
115
  profile_id: int | None = None
109
116
  project_dirs: Annotated[
110
117
  ListUniqueAbsolutePathStr, AfterValidator(_validate_cmd_list)
@@ -0,0 +1,36 @@
1
+ """add UserOAuth.is_guest
2
+
3
+ Revision ID: e53dc51fdf93
4
+ Revises: 068496367952
5
+ Create Date: 2026-01-14 14:32:25.044504
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "e53dc51fdf93"
14
+ down_revision = "068496367952"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ # ### commands auto generated by Alembic - please adjust! ###
21
+ with op.batch_alter_table("user_oauth", schema=None) as batch_op:
22
+ batch_op.add_column(
23
+ sa.Column(
24
+ "is_guest", sa.BOOLEAN(), server_default="false", nullable=False
25
+ )
26
+ )
27
+
28
+ # ### end Alembic commands ###
29
+
30
+
31
+ def downgrade() -> None:
32
+ # ### commands auto generated by Alembic - please adjust! ###
33
+ with op.batch_alter_table("user_oauth", schema=None) as batch_op:
34
+ batch_op.drop_column("is_guest")
35
+
36
+ # ### end Alembic commands ###
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractal-server
3
- Version: 2.18.6
3
+ Version: 2.19.0a0
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  Author: Tommaso Comparin, Marco Franzon, Yuri Chiucconi, Jacopo Nespolo
6
6
  Author-email: Tommaso Comparin <tommaso.comparin@exact-lab.it>, Marco Franzon <marco.franzon@exact-lab.it>, Yuri Chiucconi <yuri.chiucconi@exact-lab.it>, Jacopo Nespolo <jacopo.nespolo@exact-lab.it>