Skip to content

Commit 1ef4437

Browse files
committed
drafted sharing
1 parent 354bff5 commit 1ef4437

File tree

3 files changed

+182
-33
lines changed

3 files changed

+182
-33
lines changed

packages/postgres-database/src/simcore_postgres_database/utils_tags.py

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
from .utils_tags_sql import (
1111
count_groups_with_given_access_rights_stmt,
1212
create_tag_stmt,
13+
delete_tag_sharing_stmt,
1314
delete_tag_stmt,
1415
get_tag_stmt,
16+
has_access_rights_stmt,
17+
list_tag_group_access_stmt,
1518
list_tags_stmt,
16-
set_tag_access_rights_stmt,
19+
share_tag_stmt,
1720
update_tag_stmt,
1821
)
1922

@@ -109,7 +112,7 @@ async def create(
109112
assert tag # nosec
110113

111114
# take tag ownership
112-
access_stmt = set_tag_access_rights_stmt(
115+
access_stmt = share_tag_stmt(
113116
tag_id=tag.id,
114117
user_id=user_id,
115118
read=read,
@@ -231,37 +234,100 @@ async def delete(
231234
# ACCESS RIGHTS
232235
#
233236

234-
async def create_access_rights(
237+
async def _has_access_rights(
238+
self,
239+
connection: AsyncConnection,
240+
*,
241+
caller_id: int,
242+
tag_id: int,
243+
read: bool = False,
244+
write: bool = False,
245+
delete: bool = False,
246+
) -> bool:
247+
result = await connection.execute(
248+
has_access_rights_stmt(
249+
tag_id=tag_id,
250+
caller_user_id=caller_id,
251+
read=read,
252+
write=write,
253+
delete=delete,
254+
)
255+
)
256+
return result.fetchone() is not None
257+
258+
async def list_tag_group_access(
235259
self,
236260
connection: AsyncConnection | None = None,
237261
*,
262+
# caller
238263
user_id: int,
264+
# target
239265
tag_id: int,
240-
group_id: int,
241-
read: bool,
242-
write: bool,
243-
delete: bool,
244266
):
245-
raise NotImplementedError
267+
async with pass_or_acquire_connection(self.engine, connection) as conn:
268+
if not await self._has_access_rights(
269+
conn, caller_id=user_id, tag_id=tag_id, read=True
270+
):
271+
# TODO: empyt list or error?
272+
raise TagOperationNotAllowedError(
273+
operation="share.list", user_id=user_id, tag_id=tag_id
274+
)
246275

247-
async def update_access_rights(
276+
result = await conn.execute(list_tag_group_access_stmt(tag_id=tag_id))
277+
return [dict(row) for row in result.fetchall()]
278+
279+
async def create_or_update_access_rights(
248280
self,
249281
connection: AsyncConnection | None = None,
250282
*,
283+
# caller
251284
user_id: int,
285+
# target
252286
tag_id: int,
253287
group_id: int,
288+
# access-rights
254289
read: bool,
255290
write: bool,
256291
delete: bool,
257292
):
258-
raise NotImplementedError
293+
async with transaction_context(self.engine, connection) as conn:
294+
if not await self._has_access_rights(
295+
conn, caller_id=user_id, tag_id=tag_id, write=True
296+
):
297+
raise TagOperationNotAllowedError(
298+
operation="share.write", user_id=user_id, tag_id=tag_id
299+
)
300+
301+
result = await conn.execute(
302+
share_tag_stmt(
303+
tag_id=tag_id,
304+
group_id=group_id,
305+
read=read,
306+
write=write,
307+
delete=delete,
308+
)
309+
)
310+
row = result.first()
311+
return dict(row)
259312

260313
async def delete_access_rights(
261314
self,
262315
connection: AsyncConnection | None = None,
263316
*,
264317
user_id: int,
318+
# target
265319
tag_id: int,
266-
):
267-
raise NotImplementedError
320+
group_id: int,
321+
) -> bool:
322+
async with transaction_context(self.engine, connection) as conn:
323+
if not await self._has_access_rights(
324+
conn, caller_id=user_id, tag_id=tag_id, write=True
325+
):
326+
raise TagOperationNotAllowedError(
327+
operation="share.delete", user_id=user_id, tag_id=tag_id
328+
)
329+
330+
deleted: bool = await conn.scalar(
331+
delete_tag_sharing_stmt(tag_id=tag_id, group_id=group_id)
332+
)
333+
return deleted

packages/postgres-database/src/simcore_postgres_database/utils_tags_sql.py

Lines changed: 102 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -130,25 +130,6 @@ def count_groups_with_given_access_rights_stmt(
130130
return sa.select(sa.func.count(user_to_groups.c.uid)).select_from(j)
131131

132132

133-
def set_tag_access_rights_stmt(
134-
*, tag_id: int, user_id: int, read: bool, write: bool, delete: bool
135-
):
136-
scalar_subq = (
137-
sa.select(users.c.primary_gid).where(users.c.id == user_id).scalar_subquery()
138-
)
139-
return (
140-
tags_access_rights.insert()
141-
.values(
142-
tag_id=tag_id,
143-
group_id=scalar_subq,
144-
read=read,
145-
write=write,
146-
delete=delete,
147-
)
148-
.returning(*_ACCESS_RIGHTS_COLUMNS)
149-
)
150-
151-
152133
def update_tag_stmt(*, user_id: int, tag_id: int, **updates):
153134
return (
154135
tags.update()
@@ -182,6 +163,108 @@ def delete_tag_stmt(*, user_id: int, tag_id: int):
182163
)
183164

184165

166+
#
167+
# ACCESS RIGHTS AND SHARING: GROUP<--> TAGS
168+
#
169+
170+
171+
def list_tag_group_access_stmt(*, tag_id: int):
172+
return sa.select(tags_access_rights.c.group_id, *_ACCESS_RIGHTS_COLUMNS).where(
173+
tags_access_rights.c.tag_id == tag_id
174+
)
175+
176+
177+
def share_tag_stmt(
178+
*,
179+
tag_id: int,
180+
group_id: int | None = None,
181+
user_id: int | None = None,
182+
read: bool,
183+
write: bool,
184+
delete: bool,
185+
):
186+
assert (user_id and group_id) or (not user_id and not group_id) # nosec
187+
188+
if user_id:
189+
assert not group_id # nosec
190+
target_group_id = (
191+
sa.select(users.c.primary_gid)
192+
.where(users.c.id == user_id)
193+
.scalar_subquery()
194+
)
195+
else:
196+
assert group_id # nosec
197+
target_group_id = group_id
198+
199+
return (
200+
pg_insert(tags_access_rights)
201+
.values(
202+
tag_id=tag_id,
203+
group_id=target_group_id,
204+
read=read,
205+
write=write,
206+
delete=delete,
207+
)
208+
.on_conflict_do_update(
209+
index_elements=["tag_id", "group_id"],
210+
set_={"read": read, "write": write, "delete": delete},
211+
)
212+
.returning(tags_access_rights.c.group_id, *_ACCESS_RIGHTS_COLUMNS)
213+
)
214+
215+
216+
def delete_tag_sharing_stmt(*, tag_id: int, group_id: int):
217+
return (
218+
sa.delete(tags_access_rights)
219+
.where(
220+
(tags_access_rights.c.tag_id == tag_id)
221+
& (tags_access_rights.c.group_id == group_id)
222+
)
223+
.returning(tags_access_rights.c.tag_id.is_not(None))
224+
)
225+
226+
227+
def has_access_rights_stmt(
228+
*,
229+
tag_id: int,
230+
caller_user_id: int | None = None,
231+
caller_group_id: int | None = None,
232+
read: bool = False,
233+
write: bool = False,
234+
delete: bool = False,
235+
):
236+
conditions = []
237+
238+
# caller
239+
if caller_user_id is not None:
240+
group_condition = (
241+
tags_access_rights.c.group_id
242+
== sa.select(users.c.primary_gid)
243+
.where(users.c.id == caller_user_id)
244+
.scalar_subquery()
245+
)
246+
elif caller_group_id is not None:
247+
group_condition = tags_access_rights.c.group_id == caller_group_id
248+
else:
249+
msg = "Either caller_user_id or caller_group_id must be provided."
250+
raise ValueError(msg)
251+
252+
conditions.append(group_condition)
253+
if read:
254+
conditions.append(tags_access_rights.c.read.is_(True))
255+
if write:
256+
conditions.append(tags_access_rights.c.write.is_(True))
257+
if delete:
258+
conditions.append(tags_access_rights.c.delete.is_(True))
259+
260+
return sa.select(tags_access_rights).where(
261+
sa.and_(
262+
tags_access_rights.c.tag_id == tag_id,
263+
*conditions,
264+
)
265+
)
266+
267+
185268
#
186269
# PROJECT TAGS
187270
#

packages/postgres-database/tests/test_utils_tags.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
get_tags_for_project_stmt,
2929
get_tags_for_services_stmt,
3030
list_tags_stmt,
31-
set_tag_access_rights_stmt,
31+
share_tag_stmt,
3232
update_tag_stmt,
3333
)
3434
from sqlalchemy.ext.asyncio import AsyncEngine
@@ -690,7 +690,7 @@ def _check(func_smt, **kwargs):
690690
)
691691

692692
_check(
693-
set_tag_access_rights_stmt,
693+
share_tag_stmt,
694694
tag_id=tag_id,
695695
user_id=user_id,
696696
read=True,

0 commit comments

Comments
 (0)