Skip to content

Commit 7ba8f8e

Browse files
committed
draft
1 parent bc24799 commit 7ba8f8e

File tree

12 files changed

+432
-442
lines changed

12 files changed

+432
-442
lines changed

api/specs/web-server/_projects_groups.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import Annotated
88

99
from fastapi import APIRouter, Depends, status
10+
from models_library.api_schemas_webserver.projects_groups import ProjectGroupGet
1011
from models_library.generics import Envelope
1112
from simcore_service_webserver._meta import API_VTAG
1213
from simcore_service_webserver.projects._controller._rest_schemas import (
@@ -16,7 +17,6 @@
1617
_ProjectsGroupsBodyParams,
1718
_ProjectsGroupsPathParams,
1819
)
19-
from simcore_service_webserver.projects._groups_service import ProjectGroupGet
2020

2121
router = APIRouter(
2222
prefix=f"/{API_VTAG}",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from datetime import datetime
2+
3+
from models_library.groups import GroupID
4+
5+
from ._base import OutputSchema
6+
7+
8+
class ProjectGroupGet(OutputSchema):
9+
gid: GroupID
10+
read: bool
11+
write: bool
12+
delete: bool
13+
created: datetime
14+
modified: datetime

services/web/server/src/simcore_service_webserver/garbage_collector/_core_utils.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@
88
from simcore_postgres_database.errors import DatabaseError
99

1010
from ..groups.api import get_group_from_gid
11+
from ..projects import projects_groups_service
1112
from ..projects._projects_repository_legacy import (
1213
APP_PROJECT_DBAPI,
1314
ProjectAccessRights,
1415
)
1516
from ..projects.exceptions import ProjectNotFoundError
16-
from ..projects.projects_groups_service import (
17-
create_project_group_without_checking_permissions,
18-
delete_project_group_without_checking_permissions,
19-
)
2017
from ..users.api import get_user, get_user_id_from_gid, get_users_in_group
2118
from ..users.exceptions import UserNotFoundError
2219

@@ -164,7 +161,7 @@ async def replace_current_owner(
164161
# syncing back project data
165162
try:
166163
# Remove previous owner access rights
167-
await delete_project_group_without_checking_permissions(
164+
await projects_groups_service.delete_project_group_without_checking_permissions(
168165
app, project_id=ProjectID(project_uuid), group_id=user_primary_gid
169166
)
170167
# Update project owner in projects table
@@ -174,7 +171,7 @@ async def replace_current_owner(
174171
project_uuid=project_uuid,
175172
)
176173
# Add new owner access rights
177-
await create_project_group_without_checking_permissions(
174+
await projects_groups_service.create_project_group_without_checking_permissions(
178175
app,
179176
project_id=ProjectID(project_uuid),
180177
group_id=new_project_owner_gid,

services/web/server/src/simcore_service_webserver/projects/_access_rights_repository.py

Lines changed: 212 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1+
import logging
2+
from datetime import datetime
3+
14
import sqlalchemy
5+
from aiohttp import web
26
from aiopg.sa.engine import Engine
7+
from models_library.groups import GroupID
38
from models_library.projects import ProjectID
49
from models_library.users import UserID
10+
from pydantic import BaseModel, ConfigDict, TypeAdapter
11+
from simcore_postgres_database.models.project_to_groups import project_to_groups
512
from simcore_postgres_database.models.projects import projects
13+
from simcore_postgres_database.utils_repos import transaction_context
14+
from sqlalchemy import func, literal_column
15+
from sqlalchemy.dialects.postgresql import insert as pg_insert
16+
from sqlalchemy.ext.asyncio import AsyncConnection
17+
from sqlalchemy.sql import select
18+
19+
from ..db.plugin import get_asyncpg_engine
20+
from .exceptions import ProjectGroupNotFoundError, ProjectNotFoundError
621

7-
from .exceptions import ProjectNotFoundError
22+
_logger = logging.getLogger(__name__)
823

924

1025
async def get_project_owner(engine: Engine, project_uuid: ProjectID) -> UserID:
@@ -18,3 +33,199 @@ async def get_project_owner(engine: Engine, project_uuid: ProjectID) -> UserID:
1833
raise ProjectNotFoundError(project_uuid=project_uuid)
1934
assert isinstance(owner_id, int)
2035
return owner_id
36+
37+
38+
class ProjectGroupGetDB(BaseModel):
39+
gid: GroupID
40+
read: bool
41+
write: bool
42+
delete: bool
43+
created: datetime
44+
modified: datetime
45+
46+
model_config = ConfigDict(from_attributes=True)
47+
48+
49+
async def create_project_group(
50+
app: web.Application,
51+
connection: AsyncConnection | None = None,
52+
*,
53+
project_id: ProjectID,
54+
group_id: GroupID,
55+
read: bool,
56+
write: bool,
57+
delete: bool,
58+
) -> ProjectGroupGetDB:
59+
query = (
60+
project_to_groups.insert()
61+
.values(
62+
project_uuid=f"{project_id}",
63+
gid=group_id,
64+
read=read,
65+
write=write,
66+
delete=delete,
67+
created=func.now(),
68+
modified=func.now(),
69+
)
70+
.returning(literal_column("*"))
71+
)
72+
73+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
74+
result = await conn.stream(query)
75+
row = await result.first()
76+
return ProjectGroupGetDB.model_validate(row)
77+
78+
79+
async def list_project_groups(
80+
app: web.Application,
81+
connection: AsyncConnection | None = None,
82+
*,
83+
project_id: ProjectID,
84+
) -> list[ProjectGroupGetDB]:
85+
stmt = (
86+
select(
87+
project_to_groups.c.gid,
88+
project_to_groups.c.read,
89+
project_to_groups.c.write,
90+
project_to_groups.c.delete,
91+
project_to_groups.c.created,
92+
project_to_groups.c.modified,
93+
)
94+
.select_from(project_to_groups)
95+
.where(project_to_groups.c.project_uuid == f"{project_id}")
96+
)
97+
98+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
99+
result = await conn.stream(stmt)
100+
rows = await result.all() or []
101+
return TypeAdapter(list[ProjectGroupGetDB]).validate_python(rows)
102+
103+
104+
async def get_project_group(
105+
app: web.Application,
106+
connection: AsyncConnection | None = None,
107+
*,
108+
project_id: ProjectID,
109+
group_id: GroupID,
110+
) -> ProjectGroupGetDB:
111+
stmt = (
112+
select(
113+
project_to_groups.c.gid,
114+
project_to_groups.c.read,
115+
project_to_groups.c.write,
116+
project_to_groups.c.delete,
117+
project_to_groups.c.created,
118+
project_to_groups.c.modified,
119+
)
120+
.select_from(project_to_groups)
121+
.where(
122+
(project_to_groups.c.project_uuid == f"{project_id}")
123+
& (project_to_groups.c.gid == group_id)
124+
)
125+
)
126+
127+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
128+
result = await conn.stream(stmt)
129+
row = await result.first()
130+
if row is None:
131+
raise ProjectGroupNotFoundError(
132+
reason=f"Project {project_id} group {group_id} not found"
133+
)
134+
return ProjectGroupGetDB.model_validate(row)
135+
136+
137+
async def replace_project_group(
138+
app: web.Application,
139+
connection: AsyncConnection | None = None,
140+
*,
141+
project_id: ProjectID,
142+
group_id: GroupID,
143+
read: bool,
144+
write: bool,
145+
delete: bool,
146+
) -> ProjectGroupGetDB:
147+
148+
query = (
149+
project_to_groups.update()
150+
.values(
151+
read=read,
152+
write=write,
153+
delete=delete,
154+
)
155+
.where(
156+
(project_to_groups.c.project_uuid == f"{project_id}")
157+
& (project_to_groups.c.gid == group_id)
158+
)
159+
.returning(literal_column("*"))
160+
)
161+
162+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
163+
result = await conn.stream(query)
164+
row = await result.first()
165+
if row is None:
166+
raise ProjectGroupNotFoundError(
167+
reason=f"Project {project_id} group {group_id} not found"
168+
)
169+
return ProjectGroupGetDB.model_validate(row)
170+
171+
172+
async def update_or_insert_project_group(
173+
app: web.Application,
174+
connection: AsyncConnection | None = None,
175+
*,
176+
project_id: ProjectID,
177+
group_id: GroupID,
178+
read: bool,
179+
write: bool,
180+
delete: bool,
181+
) -> None:
182+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
183+
insert_stmt = pg_insert(project_to_groups).values(
184+
project_uuid=f"{project_id}",
185+
gid=group_id,
186+
read=read,
187+
write=write,
188+
delete=delete,
189+
created=func.now(),
190+
modified=func.now(),
191+
)
192+
on_update_stmt = insert_stmt.on_conflict_do_update(
193+
index_elements=[project_to_groups.c.project_uuid, project_to_groups.c.gid],
194+
set_={
195+
"read": insert_stmt.excluded.read,
196+
"write": insert_stmt.excluded.write,
197+
"delete": insert_stmt.excluded.delete,
198+
"modified": func.now(),
199+
},
200+
)
201+
await conn.stream(on_update_stmt)
202+
203+
204+
async def delete_project_group(
205+
app: web.Application,
206+
connection: AsyncConnection | None = None,
207+
*,
208+
project_id: ProjectID,
209+
group_id: GroupID,
210+
) -> None:
211+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
212+
await conn.stream(
213+
project_to_groups.delete().where(
214+
(project_to_groups.c.project_uuid == f"{project_id}")
215+
& (project_to_groups.c.gid == group_id)
216+
)
217+
)
218+
219+
220+
async def delete_all_project_groups(
221+
app: web.Application,
222+
connection: AsyncConnection | None = None,
223+
*,
224+
project_id: ProjectID,
225+
) -> None:
226+
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
227+
await conn.stream(
228+
project_to_groups.delete().where(
229+
project_to_groups.c.project_uuid == f"{project_id}"
230+
)
231+
)

0 commit comments

Comments
 (0)