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
@@ -1 +1 @@
1
- __VERSION__ = "2.18.6"
1
+ __VERSION__ = "2.19.0a0"
@@ -7,6 +7,8 @@ from sqlalchemy import Column
7
7
  from sqlalchemy import String
8
8
  from sqlalchemy.dialects.postgresql import ARRAY
9
9
  from sqlalchemy.types import DateTime
10
+ from sqlmodel import BOOLEAN
11
+ from sqlmodel import CheckConstraint
10
12
  from sqlmodel import Field
11
13
  from sqlmodel import Relationship
12
14
  from sqlmodel import SQLModel
@@ -100,6 +102,13 @@ class UserOAuth(SQLModel, table=True):
100
102
  is_active: bool = Field(default=True, nullable=False)
101
103
  is_superuser: bool = Field(default=False, nullable=False)
102
104
  is_verified: bool = Field(default=False, nullable=False)
105
+ is_guest: bool = Field(
106
+ sa_column=Column(
107
+ BOOLEAN,
108
+ server_default="false",
109
+ nullable=False,
110
+ ),
111
+ )
103
112
 
104
113
  oauth_accounts: list["OAuthAccount"] = Relationship(
105
114
  back_populates="user",
@@ -120,6 +129,13 @@ class UserOAuth(SQLModel, table=True):
120
129
  sa_column=Column(ARRAY(String), server_default="{}"),
121
130
  )
122
131
 
132
+ __table_args__ = (
133
+ CheckConstraint(
134
+ "NOT (is_superuser AND is_guest)",
135
+ name="superuser_is_not_guest",
136
+ ),
137
+ )
138
+
123
139
 
124
140
  class UserGroup(SQLModel, table=True):
125
141
  """
@@ -1,5 +1,8 @@
1
1
  from fastapi import APIRouter
2
2
  from fastapi import Depends
3
+ from fastapi import HTTPException
4
+ from fastapi import Response
5
+ from fastapi import status
3
6
  from sqlalchemy import func
4
7
  from sqlmodel import select
5
8
 
@@ -8,11 +11,16 @@ from fractal_server.app.db import get_async_db
8
11
  from fractal_server.app.models import LinkUserProjectV2
9
12
  from fractal_server.app.models import UserOAuth
10
13
  from fractal_server.app.models.v2 import ProjectV2
14
+ from fractal_server.app.routes.api.v2._aux_functions_sharing import (
15
+ get_pending_invitation_or_404,
16
+ )
11
17
  from fractal_server.app.routes.auth import current_superuser_act
18
+ from fractal_server.app.routes.auth._aux_auth import _user_or_404
12
19
  from fractal_server.app.routes.pagination import PaginationRequest
13
20
  from fractal_server.app.routes.pagination import PaginationResponse
14
21
  from fractal_server.app.routes.pagination import get_pagination_params
15
22
  from fractal_server.app.schemas.v2 import LinkUserProjectRead
23
+ from fractal_server.app.schemas.v2.sharing import ProjectPermissions
16
24
 
17
25
  router = APIRouter()
18
26
 
@@ -101,3 +109,42 @@ async def view_link_user_project(
101
109
  for linkuserproject, user_email, project_name in items
102
110
  ],
103
111
  )
112
+
113
+
114
+ @router.post("/verify/", status_code=200)
115
+ async def verify_invitation_for_guest(
116
+ guest_user_id: int,
117
+ project_id: int,
118
+ superuser: UserOAuth = Depends(current_superuser_act),
119
+ db: AsyncSession = Depends(get_async_db),
120
+ ) -> None:
121
+ """
122
+ Verify the invitation to join a project for a guest user
123
+
124
+ Note that a guest user would not be allowed to do this themselves.
125
+ """
126
+ # Get user and verify that they actually are a guest
127
+ guest_user = await _user_or_404(guest_user_id, db)
128
+ if not guest_user.is_guest:
129
+ raise HTTPException(
130
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
131
+ detail="Cannot accept invitations for non-guest users.",
132
+ )
133
+
134
+ # Find verification and check that permissions are set to R
135
+ link = await get_pending_invitation_or_404(
136
+ user_id=guest_user_id, project_id=project_id, db=db
137
+ )
138
+ if link.permissions != ProjectPermissions.READ:
139
+ raise HTTPException(
140
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
141
+ detail=(
142
+ "Guest users can only have 'r' permission "
143
+ f"(given: '{link.permissions}')"
144
+ ),
145
+ )
146
+ # Mark the invitation as verified
147
+ link.is_verified = True
148
+ await db.commit()
149
+
150
+ return Response(status_code=status.HTTP_200_OK)
@@ -1,57 +1,9 @@
1
- """
2
- `api` module
3
- """
4
-
5
1
  from fastapi import APIRouter
6
- from fastapi import Depends
7
2
 
8
- import fractal_server
9
- from fractal_server.app.models import UserOAuth
10
- from fractal_server.app.routes.auth import current_superuser_act
11
- from fractal_server.config import get_db_settings
12
- from fractal_server.config import get_email_settings
13
- from fractal_server.config import get_oauth_settings
14
- from fractal_server.config import get_settings
15
- from fractal_server.syringe import Inject
3
+ from .alive import router as router_alive
4
+ from .settings import router as router_settings
16
5
 
17
6
  router_api = APIRouter()
18
7
 
19
-
20
- @router_api.get("/alive/")
21
- async def alive():
22
- return dict(
23
- alive=True,
24
- version=fractal_server.__VERSION__,
25
- )
26
-
27
-
28
- @router_api.get("/settings/app/")
29
- async def view_settings(
30
- user: UserOAuth = Depends(current_superuser_act),
31
- ):
32
- settings = Inject(get_settings)
33
- return settings.model_dump()
34
-
35
-
36
- @router_api.get("/settings/database/")
37
- async def view_db_settings(
38
- user: UserOAuth = Depends(current_superuser_act),
39
- ):
40
- settings = Inject(get_db_settings)
41
- return settings.model_dump()
42
-
43
-
44
- @router_api.get("/settings/email/")
45
- async def view_email_settings(
46
- user: UserOAuth = Depends(current_superuser_act),
47
- ):
48
- settings = Inject(get_email_settings)
49
- return settings.model_dump()
50
-
51
-
52
- @router_api.get("/settings/oauth/")
53
- async def view_oauth_settings(
54
- user: UserOAuth = Depends(current_superuser_act),
55
- ):
56
- settings = Inject(get_oauth_settings)
57
- return settings.model_dump()
8
+ router_api.include_router(router_alive)
9
+ router_api.include_router(router_settings)
@@ -0,0 +1,13 @@
1
+ from fastapi import APIRouter
2
+
3
+ import fractal_server
4
+
5
+ router = APIRouter()
6
+
7
+
8
+ @router.get("/alive/")
9
+ async def alive():
10
+ return dict(
11
+ alive=True,
12
+ version=fractal_server.__VERSION__,
13
+ )
@@ -0,0 +1,44 @@
1
+ from fastapi import APIRouter
2
+ from fastapi import Depends
3
+
4
+ from fractal_server.app.models import UserOAuth
5
+ from fractal_server.app.routes.auth import current_superuser_act
6
+ from fractal_server.config import get_db_settings
7
+ from fractal_server.config import get_email_settings
8
+ from fractal_server.config import get_oauth_settings
9
+ from fractal_server.config import get_settings
10
+ from fractal_server.syringe import Inject
11
+
12
+ router = APIRouter()
13
+
14
+
15
+ @router.get("/settings/app/")
16
+ async def view_settings(
17
+ user: UserOAuth = Depends(current_superuser_act),
18
+ ):
19
+ settings = Inject(get_settings)
20
+ return settings.model_dump()
21
+
22
+
23
+ @router.get("/settings/database/")
24
+ async def view_db_settings(
25
+ user: UserOAuth = Depends(current_superuser_act),
26
+ ):
27
+ settings = Inject(get_db_settings)
28
+ return settings.model_dump()
29
+
30
+
31
+ @router.get("/settings/email/")
32
+ async def view_email_settings(
33
+ user: UserOAuth = Depends(current_superuser_act),
34
+ ):
35
+ settings = Inject(get_email_settings)
36
+ return settings.model_dump()
37
+
38
+
39
+ @router.get("/settings/oauth/")
40
+ async def view_oauth_settings(
41
+ user: UserOAuth = Depends(current_superuser_act),
42
+ ):
43
+ settings = Inject(get_oauth_settings)
44
+ return settings.model_dump()
@@ -13,7 +13,8 @@ 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 DatasetV2
15
15
  from fractal_server.app.models.v2 import JobV2
16
- from fractal_server.app.routes.auth import current_user_act_ver_prof
16
+ from fractal_server.app.routes.auth import get_api_guest
17
+ from fractal_server.app.routes.auth import get_api_user
17
18
  from fractal_server.app.schemas.v2 import DatasetCreate
18
19
  from fractal_server.app.schemas.v2 import DatasetRead
19
20
  from fractal_server.app.schemas.v2 import DatasetUpdate
@@ -38,7 +39,7 @@ router = APIRouter()
38
39
  async def create_dataset(
39
40
  project_id: int,
40
41
  dataset: DatasetCreate,
41
- user: UserOAuth = Depends(current_user_act_ver_prof),
42
+ user: UserOAuth = Depends(get_api_user),
42
43
  db: AsyncSession = Depends(get_async_db),
43
44
  ) -> DatasetRead | None:
44
45
  """
@@ -96,7 +97,7 @@ async def create_dataset(
96
97
  )
97
98
  async def read_dataset_list(
98
99
  project_id: int,
99
- user: UserOAuth = Depends(current_user_act_ver_prof),
100
+ user: UserOAuth = Depends(get_api_guest),
100
101
  db: AsyncSession = Depends(get_async_db),
101
102
  ) -> list[DatasetRead] | None:
102
103
  """
@@ -126,7 +127,7 @@ async def read_dataset_list(
126
127
  async def read_dataset(
127
128
  project_id: int,
128
129
  dataset_id: int,
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
  ) -> DatasetRead | None:
132
133
  """
@@ -151,7 +152,7 @@ async def update_dataset(
151
152
  project_id: int,
152
153
  dataset_id: int,
153
154
  dataset_update: DatasetUpdate,
154
- user: UserOAuth = Depends(current_user_act_ver_prof),
155
+ user: UserOAuth = Depends(get_api_user),
155
156
  db: AsyncSession = Depends(get_async_db),
156
157
  ) -> DatasetRead | None:
157
158
  """
@@ -182,7 +183,7 @@ async def update_dataset(
182
183
  async def delete_dataset(
183
184
  project_id: int,
184
185
  dataset_id: int,
185
- user: UserOAuth = Depends(current_user_act_ver_prof),
186
+ user: UserOAuth = Depends(get_api_user),
186
187
  db: AsyncSession = Depends(get_async_db),
187
188
  ) -> Response:
188
189
  """
@@ -226,7 +227,7 @@ async def delete_dataset(
226
227
  async def export_dataset(
227
228
  project_id: int,
228
229
  dataset_id: int,
229
- user: UserOAuth = Depends(current_user_act_ver_prof),
230
+ user: UserOAuth = Depends(get_api_guest),
230
231
  db: AsyncSession = Depends(get_async_db),
231
232
  ) -> DatasetExport | None:
232
233
  """
@@ -252,7 +253,7 @@ async def export_dataset(
252
253
  async def import_dataset(
253
254
  project_id: int,
254
255
  dataset: DatasetImport,
255
- user: UserOAuth = Depends(current_user_act_ver_prof),
256
+ user: UserOAuth = Depends(get_api_user),
256
257
  db: AsyncSession = Depends(get_async_db),
257
258
  ) -> DatasetRead | None:
258
259
  """
@@ -14,7 +14,7 @@ from fractal_server.app.models.v2 import HistoryRun
14
14
  from fractal_server.app.models.v2 import HistoryUnit
15
15
  from fractal_server.app.models.v2 import JobV2
16
16
  from fractal_server.app.models.v2 import TaskV2
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
18
  from fractal_server.app.routes.pagination import PaginationRequest
19
19
  from fractal_server.app.routes.pagination import PaginationResponse
20
20
  from fractal_server.app.routes.pagination import get_pagination_params
@@ -71,7 +71,7 @@ async def get_workflow_tasks_statuses(
71
71
  project_id: int,
72
72
  dataset_id: int,
73
73
  workflow_id: int,
74
- user: UserOAuth = Depends(current_user_act_ver_prof),
74
+ user: UserOAuth = Depends(get_api_guest),
75
75
  db: AsyncSession = Depends(get_async_db),
76
76
  ) -> JSONResponse:
77
77
  # Access control
@@ -186,7 +186,7 @@ async def get_history_run_list(
186
186
  project_id: int,
187
187
  dataset_id: int,
188
188
  workflowtask_id: int,
189
- user: UserOAuth = Depends(current_user_act_ver_prof),
189
+ user: UserOAuth = Depends(get_api_guest),
190
190
  db: AsyncSession = Depends(get_async_db),
191
191
  ) -> list[HistoryRunReadAggregated]:
192
192
  # Access control
@@ -279,7 +279,7 @@ async def get_history_run_units(
279
279
  workflowtask_id: int,
280
280
  history_run_id: int,
281
281
  unit_status: HistoryUnitStatus | None = None,
282
- user: UserOAuth = Depends(current_user_act_ver_prof),
282
+ user: UserOAuth = Depends(get_api_guest),
283
283
  db: AsyncSession = Depends(get_async_db),
284
284
  pagination: PaginationRequest = Depends(get_pagination_params),
285
285
  ) -> PaginationResponse[HistoryUnitRead]:
@@ -339,7 +339,7 @@ async def get_history_images(
339
339
  dataset_id: int,
340
340
  workflowtask_id: int,
341
341
  request_body: ImageQuery,
342
- user: UserOAuth = Depends(current_user_act_ver_prof),
342
+ user: UserOAuth = Depends(get_api_guest),
343
343
  db: AsyncSession = Depends(get_async_db),
344
344
  pagination: PaginationRequest = Depends(get_pagination_params),
345
345
  ) -> ImagePage:
@@ -423,7 +423,7 @@ async def get_history_images(
423
423
  async def get_image_log(
424
424
  project_id: int,
425
425
  request_data: ImageLogsRequest,
426
- user: UserOAuth = Depends(current_user_act_ver_prof),
426
+ user: UserOAuth = Depends(get_api_guest),
427
427
  db: AsyncSession = Depends(get_async_db),
428
428
  ) -> JSONResponse:
429
429
  # Access control
@@ -482,7 +482,7 @@ async def get_history_unit_log(
482
482
  history_unit_id: int,
483
483
  workflowtask_id: int,
484
484
  dataset_id: int,
485
- user: UserOAuth = Depends(current_user_act_ver_prof),
485
+ user: UserOAuth = Depends(get_api_guest),
486
486
  db: AsyncSession = Depends(get_async_db),
487
487
  ) -> JSONResponse:
488
488
  # Access control
@@ -535,7 +535,7 @@ async def get_history_unit_log(
535
535
  async def get_dataset_history(
536
536
  project_id: int,
537
537
  dataset_id: int,
538
- user: UserOAuth = Depends(current_user_act_ver_prof),
538
+ user: UserOAuth = Depends(get_api_guest),
539
539
  db: AsyncSession = Depends(get_async_db),
540
540
  ) -> list[HistoryRunRead]:
541
541
  """
@@ -12,7 +12,8 @@ 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 HistoryImageCache
14
14
  from fractal_server.app.models import UserOAuth
15
- from fractal_server.app.routes.auth import current_user_act_ver_prof
15
+ from fractal_server.app.routes.auth import get_api_guest
16
+ from fractal_server.app.routes.auth import get_api_user
16
17
  from fractal_server.app.routes.pagination import PaginationRequest
17
18
  from fractal_server.app.routes.pagination import PaginationResponse
18
19
  from fractal_server.app.routes.pagination import get_pagination_params
@@ -62,7 +63,7 @@ async def post_new_image(
62
63
  project_id: int,
63
64
  dataset_id: int,
64
65
  new_image: SingleImage,
65
- user: UserOAuth = Depends(current_user_act_ver_prof),
66
+ user: UserOAuth = Depends(get_api_user),
66
67
  db: AsyncSession = Depends(get_async_db),
67
68
  ) -> Response:
68
69
  output = await _get_dataset_check_access(
@@ -118,7 +119,7 @@ async def query_dataset_images(
118
119
  dataset_id: int,
119
120
  query: ImageQueryWithZarrUrl | None = None,
120
121
  pagination: PaginationRequest = Depends(get_pagination_params),
121
- user: UserOAuth = Depends(current_user_act_ver_prof),
122
+ user: UserOAuth = Depends(get_api_guest),
122
123
  db: AsyncSession = Depends(get_async_db),
123
124
  ) -> ImagePage:
124
125
  page = pagination.page
@@ -193,7 +194,7 @@ async def delete_dataset_images(
193
194
  project_id: int,
194
195
  dataset_id: int,
195
196
  zarr_url: str,
196
- user: UserOAuth = Depends(current_user_act_ver_prof),
197
+ user: UserOAuth = Depends(get_api_user),
197
198
  db: AsyncSession = Depends(get_async_db),
198
199
  ) -> Response:
199
200
  output = await _get_dataset_check_access(
@@ -241,7 +242,7 @@ async def patch_dataset_image(
241
242
  project_id: int,
242
243
  dataset_id: int,
243
244
  image_update: SingleImageUpdate,
244
- user: UserOAuth = Depends(current_user_act_ver_prof),
245
+ user: UserOAuth = Depends(get_api_user),
245
246
  db: AsyncSession = Depends(get_async_db),
246
247
  ):
247
248
  output = await _get_dataset_check_access(
@@ -15,7 +15,8 @@ from fractal_server.app.db import get_async_db
15
15
  from fractal_server.app.models import UserOAuth
16
16
  from fractal_server.app.models.v2 import JobV2
17
17
  from fractal_server.app.models.v2 import LinkUserProjectV2
18
- from fractal_server.app.routes.auth import current_user_act_ver_prof
18
+ from fractal_server.app.routes.auth import get_api_guest
19
+ from fractal_server.app.routes.auth import get_api_user
19
20
  from fractal_server.app.routes.aux._job import _write_shutdown_file
20
21
  from fractal_server.app.routes.aux._runner import _check_shutdown_is_supported
21
22
  from fractal_server.app.schemas.v2 import JobRead
@@ -41,12 +42,12 @@ router = APIRouter()
41
42
 
42
43
  @router.get("/job/", response_model=list[JobRead])
43
44
  async def get_user_jobs(
44
- user: UserOAuth = Depends(current_user_act_ver_prof),
45
+ user: UserOAuth = Depends(get_api_guest),
45
46
  log: bool = True,
46
47
  db: AsyncSession = Depends(get_async_db),
47
48
  ) -> list[JobRead]:
48
49
  """
49
- Returns all the jobs of the current user
50
+ Returns all the jobs from projects linked to the current user
50
51
  """
51
52
  stm = (
52
53
  select(JobV2)
@@ -73,7 +74,7 @@ async def get_user_jobs(
73
74
  async def get_workflow_jobs(
74
75
  project_id: int,
75
76
  workflow_id: int,
76
- user: UserOAuth = Depends(current_user_act_ver_prof),
77
+ user: UserOAuth = Depends(get_api_guest),
77
78
  db: AsyncSession = Depends(get_async_db),
78
79
  ) -> list[JobRead] | None:
79
80
  """
@@ -100,7 +101,7 @@ async def get_latest_job(
100
101
  project_id: int,
101
102
  workflow_id: int,
102
103
  dataset_id: int,
103
- user: UserOAuth = Depends(current_user_act_ver_prof),
104
+ user: UserOAuth = Depends(get_api_guest),
104
105
  db: AsyncSession = Depends(get_async_db),
105
106
  ) -> JobRead:
106
107
  await _get_workflow_check_access(
@@ -136,7 +137,7 @@ async def read_job(
136
137
  project_id: int,
137
138
  job_id: int,
138
139
  show_tmp_logs: bool = False,
139
- user: UserOAuth = Depends(current_user_act_ver_prof),
140
+ user: UserOAuth = Depends(get_api_guest),
140
141
  db: AsyncSession = Depends(get_async_db),
141
142
  ) -> JobRead | None:
142
143
  """
@@ -169,7 +170,7 @@ async def read_job(
169
170
  async def download_job_logs(
170
171
  project_id: int,
171
172
  job_id: int,
172
- user: UserOAuth = Depends(current_user_act_ver_prof),
173
+ user: UserOAuth = Depends(get_api_guest),
173
174
  db: AsyncSession = Depends(get_async_db),
174
175
  ) -> StreamingResponse:
175
176
  """
@@ -200,7 +201,7 @@ async def download_job_logs(
200
201
  )
201
202
  async def get_job_list(
202
203
  project_id: int,
203
- user: UserOAuth = Depends(current_user_act_ver_prof),
204
+ user: UserOAuth = Depends(get_api_guest),
204
205
  log: bool = True,
205
206
  db: AsyncSession = Depends(get_async_db),
206
207
  ) -> list[JobRead] | None:
@@ -234,7 +235,7 @@ async def get_job_list(
234
235
  async def stop_job(
235
236
  project_id: int,
236
237
  job_id: int,
237
- user: UserOAuth = Depends(current_user_act_ver_prof),
238
+ user: UserOAuth = Depends(get_api_user),
238
239
  db: AsyncSession = Depends(get_async_db),
239
240
  ) -> Response:
240
241
  """
@@ -8,7 +8,7 @@ from pydantic import Field
8
8
  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
- from fractal_server.app.routes.auth import current_user_act_ver_prof
11
+ from fractal_server.app.routes.auth import get_api_guest
12
12
  from fractal_server.app.schemas.v2 import HistoryUnitStatus
13
13
  from fractal_server.app.schemas.v2 import TaskType
14
14
  from fractal_server.app.schemas.v2.sharing import ProjectPermissions
@@ -34,7 +34,7 @@ async def verify_unique_types(
34
34
  dataset_id: int,
35
35
  workflowtask_id: int,
36
36
  query: ImageQuery | None = None,
37
- user: UserOAuth = Depends(current_user_act_ver_prof),
37
+ user: UserOAuth = Depends(get_api_guest),
38
38
  db: AsyncSession = Depends(get_async_db),
39
39
  ) -> list[str]:
40
40
  # Get dataset
@@ -99,7 +99,7 @@ async def check_non_processed_images(
99
99
  workflow_id: int,
100
100
  workflowtask_id: int,
101
101
  filters: NonProcessedImagesPayload,
102
- user: UserOAuth = Depends(current_user_act_ver_prof),
102
+ user: UserOAuth = Depends(get_api_guest),
103
103
  db: AsyncSession = Depends(get_async_db),
104
104
  ) -> JSONResponse:
105
105
  db_workflow_task, db_workflow = await _get_workflow_task_check_access(
@@ -11,7 +11,8 @@ from fractal_server.app.models import UserOAuth
11
11
  from fractal_server.app.models.v2 import JobV2
12
12
  from fractal_server.app.models.v2 import LinkUserProjectV2
13
13
  from fractal_server.app.models.v2 import ProjectV2
14
- from fractal_server.app.routes.auth import current_user_act_ver_prof
14
+ from fractal_server.app.routes.auth import get_api_guest
15
+ from fractal_server.app.routes.auth import get_api_user
15
16
  from fractal_server.app.routes.aux.validate_user_profile import (
16
17
  validate_user_profile,
17
18
  )
@@ -32,7 +33,7 @@ router = APIRouter()
32
33
  @router.get("/project/", response_model=list[ProjectRead])
33
34
  async def get_list_project(
34
35
  is_owner: bool = True,
35
- user: UserOAuth = Depends(current_user_act_ver_prof),
36
+ user: UserOAuth = Depends(get_api_guest),
36
37
  db: AsyncSession = Depends(get_async_db),
37
38
  ) -> list[ProjectV2]:
38
39
  """
@@ -53,7 +54,7 @@ async def get_list_project(
53
54
  @router.post("/project/", response_model=ProjectRead, status_code=201)
54
55
  async def create_project(
55
56
  project: ProjectCreate,
56
- user: UserOAuth = Depends(current_user_act_ver_prof),
57
+ user: UserOAuth = Depends(get_api_user),
57
58
  db: AsyncSession = Depends(get_async_db),
58
59
  ) -> ProjectRead | None:
59
60
  """
@@ -94,7 +95,7 @@ async def create_project(
94
95
  @router.get("/project/{project_id}/", response_model=ProjectRead)
95
96
  async def read_project(
96
97
  project_id: int,
97
- user: UserOAuth = Depends(current_user_act_ver_prof),
98
+ user: UserOAuth = Depends(get_api_guest),
98
99
  db: AsyncSession = Depends(get_async_db),
99
100
  ) -> ProjectRead | None:
100
101
  """
@@ -113,7 +114,7 @@ async def read_project(
113
114
  async def update_project(
114
115
  project_id: int,
115
116
  project_update: ProjectUpdate,
116
- user: UserOAuth = Depends(current_user_act_ver_prof),
117
+ user: UserOAuth = Depends(get_api_user),
117
118
  db: AsyncSession = Depends(get_async_db),
118
119
  ):
119
120
  project = await _get_project_check_access(
@@ -140,7 +141,7 @@ async def update_project(
140
141
  @router.delete("/project/{project_id}/", status_code=204)
141
142
  async def delete_project(
142
143
  project_id: int,
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
  ) -> Response:
146
147
  """
@@ -11,7 +11,8 @@ from fractal_server.app.db import get_async_db
11
11
  from fractal_server.app.models import UserOAuth
12
12
  from fractal_server.app.models.v2 import LinkUserProjectV2
13
13
  from fractal_server.app.models.v2 import ProjectV2
14
- from fractal_server.app.routes.auth import current_user_act_ver_prof
14
+ from fractal_server.app.routes.auth import get_api_guest
15
+ from fractal_server.app.routes.auth import get_api_user
15
16
  from fractal_server.app.schemas.v2 import ProjectAccessRead
16
17
  from fractal_server.app.schemas.v2 import ProjectGuestCreate
17
18
  from fractal_server.app.schemas.v2 import ProjectGuestRead
@@ -33,7 +34,7 @@ router = APIRouter()
33
34
  )
34
35
  async def get_project_guests(
35
36
  project_id: int,
36
- owner: UserOAuth = Depends(current_user_act_ver_prof),
37
+ owner: UserOAuth = Depends(get_api_guest),
37
38
  db: AsyncSession = Depends(get_async_db),
38
39
  ) -> list[ProjectGuestRead]:
39
40
  """
@@ -68,7 +69,7 @@ async def invite_guest(
68
69
  project_id: int,
69
70
  email: EmailStr,
70
71
  project_invitation: ProjectGuestCreate,
71
- owner: UserOAuth = Depends(current_user_act_ver_prof),
72
+ owner: UserOAuth = Depends(get_api_user),
72
73
  db: AsyncSession = Depends(get_async_db),
73
74
  ) -> Response:
74
75
  """
@@ -103,7 +104,7 @@ async def patch_guest(
103
104
  project_id: int,
104
105
  email: EmailStr,
105
106
  update: ProjectGuestUpdate,
106
- owner: UserOAuth = Depends(current_user_act_ver_prof),
107
+ owner: UserOAuth = Depends(get_api_user),
107
108
  db: AsyncSession = Depends(get_async_db),
108
109
  ) -> Response:
109
110
  """
@@ -137,7 +138,7 @@ async def patch_guest(
137
138
  async def revoke_guest_access(
138
139
  project_id: int,
139
140
  email: EmailStr,
140
- owner: UserOAuth = Depends(current_user_act_ver_prof),
141
+ owner: UserOAuth = Depends(get_api_user),
141
142
  db: AsyncSession = Depends(get_async_db),
142
143
  ) -> Response:
143
144
  """
@@ -171,13 +172,20 @@ async def revoke_guest_access(
171
172
  response_model=list[ProjectInvitationRead],
172
173
  )
173
174
  async def get_pending_invitations(
174
- user: UserOAuth = Depends(current_user_act_ver_prof),
175
+ user: UserOAuth = Depends(get_api_guest),
175
176
  db: AsyncSession = Depends(get_async_db),
176
177
  ) -> list[ProjectInvitationRead]:
177
178
  """
178
179
  See your current invitations.
179
180
  """
180
181
 
182
+ if user.is_guest:
183
+ # The user's attribute `is_guest` is used to identify guest accounts,
184
+ # i.e. accounts with read only permissions on the API.
185
+ # This is a different concept from a project guest, which is a regular
186
+ # account with which a project has been shared.
187
+ return []
188
+
181
189
  res = await db.execute(
182
190
  select(
183
191
  ProjectV2.id,
@@ -225,7 +233,7 @@ async def get_pending_invitations(
225
233
  )
226
234
  async def get_access_info(
227
235
  project_id: int,
228
- user: UserOAuth = Depends(current_user_act_ver_prof),
236
+ user: UserOAuth = Depends(get_api_guest),
229
237
  db: AsyncSession = Depends(get_async_db),
230
238
  ) -> ProjectAccessRead:
231
239
  """
@@ -272,7 +280,7 @@ async def get_access_info(
272
280
  @router.post("/project/{project_id}/access/accept/", status_code=200)
273
281
  async def accept_project_invitation(
274
282
  project_id: int,
275
- user: UserOAuth = Depends(current_user_act_ver_prof),
283
+ user: UserOAuth = Depends(get_api_user),
276
284
  db: AsyncSession = Depends(get_async_db),
277
285
  ) -> Response:
278
286
  """
@@ -290,7 +298,7 @@ async def accept_project_invitation(
290
298
  @router.delete("/project/{project_id}/access/", status_code=204)
291
299
  async def leave_project(
292
300
  project_id: int,
293
- user: UserOAuth = Depends(current_user_act_ver_prof),
301
+ user: UserOAuth = Depends(get_api_user),
294
302
  db: AsyncSession = Depends(get_async_db),
295
303
  ) -> Response:
296
304
  """