Skip to content

Commit 06011ed

Browse files
committed
drafted groups
1 parent f0d4a54 commit 06011ed

File tree

5 files changed

+76
-10
lines changed

5 files changed

+76
-10
lines changed

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,14 @@ class GroupUserGet(OutputSchemaWithoutCamelCase):
274274
] = None
275275

276276
# Access Rights
277-
access_rights: GroupAccessRights = Field(..., alias="accessRights")
277+
access_rights: Annotated[
278+
GroupAccessRights | None,
279+
Field(
280+
alias="accessRights",
281+
description="If group is standard, these are these are the access rights of the user to it."
282+
"None if primary group.",
283+
),
284+
] = None
278285

279286
model_config = ConfigDict(
280287
populate_by_name=True,
@@ -292,7 +299,23 @@ class GroupUserGet(OutputSchemaWithoutCamelCase):
292299
"write": False,
293300
"delete": False,
294301
},
295-
}
302+
},
303+
"examples": [
304+
# unique member on a primary group with two different primacy settings
305+
{
306+
"id": "16",
307+
"userName": "mrprivate",
308+
"gid": "55",
309+
},
310+
{
311+
"id": "56",
312+
"userName": "mrpublic",
313+
"login": "[email protected]",
314+
"first_name": "Mr",
315+
"last_name": "Public",
316+
"gid": "42",
317+
},
318+
],
296319
},
297320
)
298321

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class GroupMember(BaseModel):
108108
last_name: str | None
109109

110110
# group access
111-
access_rights: AccessRightsDict
111+
access_rights: AccessRightsDict | None = None
112112

113113
model_config = ConfigDict(from_attributes=True)
114114

services/web/server/src/simcore_service_webserver/groups/_groups_repository.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
22
from copy import deepcopy
3+
from typing import Literal
34

45
import sqlalchemy as sa
56
from aiohttp import web
@@ -89,11 +90,14 @@ def _to_group_info_tuple(group: Row) -> GroupInfoTuple:
8990

9091

9192
def _check_group_permissions(
92-
group: Row, user_id: int, gid: int, permission: str
93+
group: Row,
94+
caller_id: UserID,
95+
group_id: GroupID,
96+
permission: Literal["read", "write", "delete"],
9397
) -> None:
9498
if not group.access_rights[permission]:
9599
raise UserInsufficientRightsError(
96-
user_id=user_id, gid=gid, permission=permission
100+
user_id=caller_id, gid=group_id, permission=permission
97101
)
98102

99103

@@ -487,18 +491,37 @@ async def list_users_in_group(
487491
) -> list[GroupMember]:
488492
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
489493
# first check if the group exists
490-
group = await _get_group_and_access_rights_or_raise(
491-
conn, user_id=user_id, group_id=group_id
494+
result = await conn.execute(
495+
sa.select(
496+
*_GROUP_COLUMNS,
497+
user_to_groups.c.access_rights,
498+
)
499+
.select_from(
500+
groups.join(
501+
user_to_groups, user_to_groups.c.gid == groups.c.gid, isouter=True
502+
)
503+
)
504+
.where(
505+
((user_to_groups.c.uid == user_id) & (user_to_groups.c.gid == group_id))
506+
| (groups.c.type == GroupType.PRIMARY)
507+
)
492508
)
493-
_check_group_permissions(group, user_id, group_id, "read")
509+
group_row = result.first()
510+
if not group_row:
511+
raise GroupNotFoundError(gid=group_id)
512+
513+
if group_row.type != GroupType.PRIMARY:
514+
_check_group_permissions(
515+
group_row, caller_id=user_id, group_id=group_id, permission="read"
516+
)
494517

495518
# now get the list
496519
query = (
497520
sa.select(
498521
*_group_user_cols(user_id),
499522
user_to_groups.c.access_rights,
500523
)
501-
.select_from(users.join(user_to_groups))
524+
.select_from(users.join(user_to_groups, isouter=True))
502525
.where(user_to_groups.c.gid == group_id)
503526
)
504527

services/web/server/src/simcore_service_webserver/groups/_groups_rest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ async def delete_group(request: web.Request):
164164
@permission_required("groups.*")
165165
@handle_plugin_requests_exceptions
166166
async def get_all_group_users(request: web.Request):
167-
"""Gets users in organization groups"""
167+
"""Gets users in organization or primary groups"""
168168
req_ctx = GroupsRequestContext.model_validate(request)
169169
path_params = parse_request_path_parameters_as(GroupsPathParams, request)
170170

services/web/server/tests/unit/with_dbs/03/test_users.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from common_library.users_enums import UserRole, UserStatus
2121
from faker import Faker
2222
from models_library.api_schemas_webserver.auth import AccountRequestInfo
23+
from models_library.api_schemas_webserver.groups import GroupUserGet
2324
from models_library.api_schemas_webserver.users import (
2425
MyProfileGet,
2526
UserForAdminGet,
@@ -156,6 +157,25 @@ async def test_get_and_search_public_users(
156157
resp = await client.get(f"{url}")
157158
await assert_status(resp, status.HTTP_403_FORBIDDEN)
158159

160+
# GET user by primary GID
161+
url = client.app.router["get_all_group_users"].url_for(
162+
gid=f"{public_user['id']}"
163+
)
164+
resp = await client.get(f"{url}")
165+
data, _ = await assert_status(resp, status.HTTP_200_OK)
166+
167+
user = GroupUserGet.model_validate(data)
168+
assert user.id == public_user["id"]
169+
170+
url = client.app.router["get_all_group_users"].url_for(
171+
gid=f"{private_user['id']}"
172+
)
173+
resp = await client.get(f"{url}")
174+
data, _ = await assert_status(resp, status.HTTP_200_OK)
175+
176+
user = GroupUserGet.model_validate(data)
177+
assert user.id == private_user["id"]
178+
159179

160180
@pytest.mark.parametrize(
161181
"user_role,expected",

0 commit comments

Comments
 (0)