Skip to content

Commit ab4f40f

Browse files
authored
Merge branch 'master' into dependabot/github_actions/actions/setup-node-4.4.0
2 parents 3423e95 + 2c40792 commit ab4f40f

File tree

160 files changed

+5660
-1186
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+5660
-1186
lines changed

api/specs/web-server/_users.py

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,23 @@
44
# pylint: disable=too-many-arguments
55

66

7-
from enum import Enum
87
from typing import Annotated
98

10-
from _common import as_query
119
from fastapi import APIRouter, Depends, status
1210
from models_library.api_schemas_webserver.users import (
11+
MyFunctionPermissionsGet,
1312
MyPermissionGet,
1413
MyProfileGet,
1514
MyProfilePatch,
1615
MyTokenCreate,
1716
MyTokenGet,
18-
UserAccountApprove,
19-
UserAccountGet,
20-
UserAccountReject,
21-
UserAccountSearchQueryParams,
2217
UserGet,
23-
UsersAccountListQueryParams,
2418
UsersSearch,
2519
)
2620
from models_library.api_schemas_webserver.users_preferences import PatchRequestBody
2721
from models_library.generics import Envelope
28-
from models_library.rest_pagination import Page
2922
from models_library.user_preferences import PreferenceIdentifier
3023
from simcore_service_webserver._meta import API_VTAG
31-
from simcore_service_webserver.users._common.schemas import PreRegisteredUserGet
3224
from simcore_service_webserver.users._notifications import (
3325
UserNotification,
3426
UserNotificationCreate,
@@ -128,6 +120,13 @@ async def mark_notification_as_read(
128120
async def list_user_permissions(): ...
129121

130122

123+
@router.get(
124+
"/me/function-permissions",
125+
response_model=Envelope[MyFunctionPermissionsGet],
126+
)
127+
async def list_user_functions_permissions(): ...
128+
129+
131130
#
132131
# USERS public
133132
#
@@ -139,56 +138,3 @@ async def list_user_permissions(): ...
139138
description="Search among users who are publicly visible to the caller (i.e., me) based on their privacy settings.",
140139
)
141140
async def search_users(_body: UsersSearch): ...
142-
143-
144-
#
145-
# USERS admin
146-
#
147-
148-
_extra_tags: list[str | Enum] = ["admin"]
149-
150-
151-
@router.get(
152-
"/admin/user-accounts",
153-
response_model=Page[UserAccountGet],
154-
tags=_extra_tags,
155-
)
156-
async def list_users_accounts(
157-
_query: Annotated[as_query(UsersAccountListQueryParams), Depends()],
158-
): ...
159-
160-
161-
@router.post(
162-
"/admin/user-accounts:approve",
163-
status_code=status.HTTP_204_NO_CONTENT,
164-
tags=_extra_tags,
165-
)
166-
async def approve_user_account(_body: UserAccountApprove): ...
167-
168-
169-
@router.post(
170-
"/admin/user-accounts:reject",
171-
status_code=status.HTTP_204_NO_CONTENT,
172-
tags=_extra_tags,
173-
)
174-
async def reject_user_account(_body: UserAccountReject): ...
175-
176-
177-
@router.get(
178-
"/admin/user-accounts:search",
179-
response_model=Envelope[list[UserAccountGet]],
180-
tags=_extra_tags,
181-
)
182-
async def search_user_accounts(
183-
_query: Annotated[UserAccountSearchQueryParams, Depends()],
184-
):
185-
# NOTE: see `Search` in `Common Custom Methods` in https://cloud.google.com/apis/design/custom_methods
186-
...
187-
188-
189-
@router.post(
190-
"/admin/user-accounts:pre-register",
191-
response_model=Envelope[UserAccountGet],
192-
tags=_extra_tags,
193-
)
194-
async def pre_register_user_account(_body: PreRegisteredUserGet): ...
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# pylint: disable=redefined-outer-name
2+
# pylint: disable=unused-argument
3+
# pylint: disable=unused-variable
4+
# pylint: disable=too-many-arguments
5+
6+
7+
from enum import Enum
8+
from typing import Annotated
9+
10+
from _common import as_query
11+
from fastapi import APIRouter, Depends, status
12+
from models_library.api_schemas_webserver.users import (
13+
UserAccountApprove,
14+
UserAccountGet,
15+
UserAccountReject,
16+
UserAccountSearchQueryParams,
17+
UsersAccountListQueryParams,
18+
)
19+
from models_library.generics import Envelope
20+
from models_library.rest_pagination import Page
21+
from simcore_service_webserver._meta import API_VTAG
22+
from simcore_service_webserver.users._common.schemas import PreRegisteredUserGet
23+
24+
router = APIRouter(prefix=f"/{API_VTAG}", tags=["users"])
25+
26+
_extra_tags: list[str | Enum] = ["admin"]
27+
28+
29+
@router.get(
30+
"/admin/user-accounts",
31+
response_model=Page[UserAccountGet],
32+
tags=_extra_tags,
33+
)
34+
async def list_users_accounts(
35+
_query: Annotated[as_query(UsersAccountListQueryParams), Depends()],
36+
): ...
37+
38+
39+
@router.post(
40+
"/admin/user-accounts:approve",
41+
status_code=status.HTTP_204_NO_CONTENT,
42+
tags=_extra_tags,
43+
)
44+
async def approve_user_account(_body: UserAccountApprove): ...
45+
46+
47+
@router.post(
48+
"/admin/user-accounts:reject",
49+
status_code=status.HTTP_204_NO_CONTENT,
50+
tags=_extra_tags,
51+
)
52+
async def reject_user_account(_body: UserAccountReject): ...
53+
54+
55+
@router.get(
56+
"/admin/user-accounts:search",
57+
response_model=Envelope[list[UserAccountGet]],
58+
tags=_extra_tags,
59+
)
60+
async def search_user_accounts(
61+
_query: Annotated[UserAccountSearchQueryParams, Depends()],
62+
):
63+
# NOTE: see `Search` in `Common Custom Methods` in https://cloud.google.com/apis/design/custom_methods
64+
...
65+
66+
67+
@router.post(
68+
"/admin/user-accounts:pre-register",
69+
response_model=Envelope[UserAccountGet],
70+
tags=_extra_tags,
71+
)
72+
async def pre_register_user_account(_body: PreRegisteredUserGet): ...

api/specs/web-server/openapi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"_tags_groups", # after _tags
2727
"_products",
2828
"_users",
29+
"_users_admin", # after _users
2930
"_wallets",
3031
# add-ons ---
3132
"_activity",

packages/models-library/src/models_library/api_schemas_webserver/users.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,7 @@ class MyPermissionGet(OutputSchema):
377377
@classmethod
378378
def from_domain_model(cls, permission: UserPermission) -> Self:
379379
return cls(name=permission.name, allowed=permission.allowed)
380+
381+
382+
class MyFunctionPermissionsGet(OutputSchema):
383+
write_functions: bool

packages/models-library/src/models_library/functions.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from models_library.products import ProductName
1212
from models_library.services_types import ServiceKey, ServiceVersion
1313
from models_library.users import UserID
14+
from models_library.utils.enums import StrAutoEnum
1415
from pydantic import BaseModel, ConfigDict, Field
1516

1617
from .projects import ProjectID
@@ -301,6 +302,25 @@ class FunctionAccessRightsDB(BaseModel):
301302
)
302303

303304

305+
class FunctionUserApiAccessRights(BaseModel):
306+
user_id: UserID
307+
read_functions: bool = False
308+
write_functions: bool = False
309+
execute_functions: bool = False
310+
read_function_jobs: bool = False
311+
write_function_jobs: bool = False
312+
execute_function_jobs: bool = False
313+
read_function_job_collections: bool = False
314+
write_function_job_collections: bool = False
315+
execute_function_job_collections: bool = False
316+
317+
model_config = ConfigDict(
318+
alias_generator=snake_to_camel,
319+
populate_by_name=True,
320+
extra="forbid",
321+
)
322+
323+
304324
FunctionJobAccessRights: TypeAlias = FunctionAccessRights
305325
FunctionJobAccessRightsDB: TypeAlias = FunctionAccessRightsDB
306326
FunctionJobUserAccessRights: TypeAlias = FunctionUserAccessRights
@@ -310,3 +330,15 @@ class FunctionAccessRightsDB(BaseModel):
310330
FunctionJobCollectionAccessRightsDB: TypeAlias = FunctionAccessRightsDB
311331
FunctionJobCollectionUserAccessRights: TypeAlias = FunctionUserAccessRights
312332
FunctionJobCollectionGroupAccessRights: TypeAlias = FunctionGroupAccessRights
333+
334+
335+
class FunctionsApiAccessRights(StrAutoEnum):
336+
READ_FUNCTIONS = "read_functions"
337+
WRITE_FUNCTIONS = "write_functions"
338+
EXECUTE_FUNCTIONS = "execute_functions"
339+
READ_FUNCTION_JOBS = "read_function_jobs"
340+
WRITE_FUNCTION_JOBS = "write_function_jobs"
341+
EXECUTE_FUNCTION_JOBS = "execute_function_jobs"
342+
READ_FUNCTION_JOB_COLLECTIONS = "read_function_job_collections"
343+
WRITE_FUNCTION_JOB_COLLECTIONS = "write_function_job_collections"
344+
EXECUTE_FUNCTION_JOB_COLLECTIONS = "execute_function_job_collections"

packages/models-library/src/models_library/functions_errors.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,62 @@ class FunctionJobCollectionExecuteAccessDeniedError(FunctionBaseError):
9999
"Function job collection {function_job_collection_id} execute access denied for user {user_id}"
100100
)
101101
status_code: int = 403 # Forbidden
102+
103+
104+
class FunctionsReadApiAccessDeniedError(FunctionBaseError):
105+
msg_template: str = "User {user_id} does not have the permission to read functions"
106+
status_code: int = 403 # Forbidden
107+
108+
109+
class FunctionsWriteApiAccessDeniedError(FunctionBaseError):
110+
msg_template: str = "User {user_id} does not have the permission to write functions"
111+
status_code: int = 403 # Forbidden
112+
113+
114+
class FunctionsExecuteApiAccessDeniedError(FunctionBaseError):
115+
msg_template: str = (
116+
"User {user_id} does not have the permission to execute functions"
117+
)
118+
status_code: int = 403 # Forbidden
119+
120+
121+
class FunctionJobsReadApiAccessDeniedError(FunctionBaseError):
122+
msg_template: str = (
123+
"User {user_id} does not have the permission to read function jobs"
124+
)
125+
status_code: int = 403 # Forbidden
126+
127+
128+
class FunctionJobsWriteApiAccessDeniedError(FunctionBaseError):
129+
msg_template: str = (
130+
"User {user_id} does not have the permission to write function jobs"
131+
)
132+
status_code: int = 403 # Forbidden
133+
134+
135+
class FunctionJobsExecuteApiAccessDeniedError(FunctionBaseError):
136+
msg_template: str = (
137+
"User {user_id} does not have the permission to execute function jobs"
138+
)
139+
status_code: int = 403 # Forbidden
140+
141+
142+
class FunctionJobCollectionsReadApiAccessDeniedError(FunctionBaseError):
143+
msg_template: str = (
144+
"User {user_id} does not have the permission to read function job collections"
145+
)
146+
status_code: int = 403 # Forbidden
147+
148+
149+
class FunctionJobCollectionsWriteApiAccessDeniedError(FunctionBaseError):
150+
msg_template: str = (
151+
"User {user_id} does not have the permission to write function job collections"
152+
)
153+
status_code: int = 403 # Forbidden
154+
155+
156+
class FunctionJobCollectionsExecuteApiAccessDeniedError(FunctionBaseError):
157+
msg_template: str = (
158+
"User {user_id} does not have the permission to execute function job collections"
159+
)
160+
status_code: int = 403 # Forbidden
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Add functions api access rights
2+
3+
Revision ID: 4f6fd2586491
4+
Revises: afb1ba08f3c2
5+
Create Date: 2025-06-13 12:14:59.317685+00:00
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "4f6fd2586491"
14+
down_revision = "afb1ba08f3c2"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table(
22+
"funcapi_group_api_access_rights",
23+
sa.Column("group_id", sa.BigInteger(), nullable=False),
24+
sa.Column("product_name", sa.String(), nullable=False),
25+
sa.Column("read_functions", sa.Boolean(), nullable=True),
26+
sa.Column("write_functions", sa.Boolean(), nullable=True),
27+
sa.Column("execute_functions", sa.Boolean(), nullable=True),
28+
sa.Column("read_function_jobs", sa.Boolean(), nullable=True),
29+
sa.Column("write_function_jobs", sa.Boolean(), nullable=True),
30+
sa.Column("execute_function_jobs", sa.Boolean(), nullable=True),
31+
sa.Column("read_function_job_collections", sa.Boolean(), nullable=True),
32+
sa.Column("write_function_job_collections", sa.Boolean(), nullable=True),
33+
sa.Column("execute_function_job_collections", sa.Boolean(), nullable=True),
34+
sa.Column(
35+
"created",
36+
sa.DateTime(timezone=True),
37+
server_default=sa.text("now()"),
38+
nullable=False,
39+
),
40+
sa.Column(
41+
"modified",
42+
sa.DateTime(timezone=True),
43+
server_default=sa.text("now()"),
44+
nullable=False,
45+
),
46+
sa.ForeignKeyConstraint(
47+
["group_id"],
48+
["groups.gid"],
49+
name="fk_func_access_to_groups_group_id",
50+
onupdate="CASCADE",
51+
ondelete="CASCADE",
52+
),
53+
sa.ForeignKeyConstraint(
54+
["product_name"],
55+
["products.name"],
56+
name="fk_func_access_to_products_product_name",
57+
onupdate="CASCADE",
58+
ondelete="CASCADE",
59+
),
60+
sa.PrimaryKeyConstraint(
61+
"group_id",
62+
"product_name",
63+
name="pk_func_group_product_name_to_api_access_rights",
64+
),
65+
)
66+
# ### end Alembic commands ###
67+
68+
69+
def downgrade():
70+
# ### commands auto generated by Alembic - please adjust! ###
71+
op.drop_table("funcapi_group_api_access_rights")
72+
# ### end Alembic commands ###

0 commit comments

Comments
 (0)