Skip to content

Commit 1de9053

Browse files
authored
Give membership manager read/write access for user membership (#783)
### Description Please explain the changes you made here. ### Checklist - [ ] Created tests which fail without the change (if possible) - [ ] All tests passing - [ ] Extended the documentation, if necessary
1 parent b7cbffd commit 1de9053

File tree

3 files changed

+194
-23
lines changed

3 files changed

+194
-23
lines changed

app/core/memberships/cruds_memberships.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,14 @@ async def get_user_memberships_by_user_id(
124124
maximal_start_date: date | None = None,
125125
minimal_end_date: date | None = None,
126126
maximal_end_date: date | None = None,
127+
manager_restriction: list[str] | None = None,
127128
) -> Sequence[schemas_memberships.UserMembershipComplete]:
128129
result = (
129130
(
130131
await db.execute(
131-
select(models_memberships.CoreAssociationUserMembership).where(
132+
select(models_memberships.CoreAssociationUserMembership)
133+
.join(models_memberships.CoreAssociationMembership)
134+
.where(
132135
models_memberships.CoreAssociationUserMembership.user_id == user_id,
133136
models_memberships.CoreAssociationUserMembership.end_date
134137
>= minimal_end_date
@@ -146,6 +149,11 @@ async def get_user_memberships_by_user_id(
146149
<= maximal_start_date
147150
if maximal_start_date
148151
else and_(True),
152+
models_memberships.CoreAssociationMembership.manager_group_id.in_(
153+
manager_restriction,
154+
)
155+
if manager_restriction
156+
else and_(True),
149157
),
150158
)
151159
)

app/core/memberships/endpoints_memberships.py

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
is_user_in,
2121
)
2222
from app.types.module import CoreModule
23+
from app.utils.tools import is_user_member_of_any_group
2324

2425
router = APIRouter(tags=["Memberships"])
2526

@@ -227,17 +228,18 @@ async def read_user_memberships(
227228
):
228229
"""
229230
Return all memberships for a user.
230-
231-
**This endpoint is only usable by administrators**
232231
"""
232+
# Check if the user is trying to access their own memberships or if they are an admin
233+
# If the user is not an admin or the user_id does not match the current user,
234+
# filter the query to get the managed memberships from user's groups.
233235
if user_id != user.id and GroupType.admin not in [
234236
group.id for group in user.groups
235237
]:
236-
raise HTTPException(
237-
status_code=403,
238-
detail="User is not allowed to access other users' memberships",
238+
return await cruds_memberships.get_user_memberships_by_user_id(
239+
db,
240+
user_id,
241+
manager_restriction=[group.id for group in user.groups],
239242
)
240-
241243
return await cruds_memberships.get_user_memberships_by_user_id(
242244
db,
243245
user_id,
@@ -253,14 +255,27 @@ async def read_user_association_membership_history(
253255
user_id: str,
254256
association_membership_id: uuid.UUID,
255257
db: AsyncSession = Depends(get_db),
256-
user: models_users.CoreUser = Depends(is_user_in(GroupType.admin)),
258+
user: models_users.CoreUser = Depends(is_user()),
257259
):
258260
"""
259261
Return all user memberships for a specific association membership for a user.
260262
261-
**This endpoint is only usable by administrators**
263+
**This endpoint is only usable by administrators and membership managers**
262264
"""
263265

266+
db_membership = await cruds_memberships.get_association_membership_by_id(
267+
db,
268+
association_membership_id,
269+
)
270+
if db_membership is None:
271+
raise HTTPException(status_code=404, detail="Association membership not found")
272+
273+
if not is_user_member_of_any_group(
274+
user,
275+
[GroupType.admin, GroupType.admin_cdr, db_membership.manager_group_id],
276+
):
277+
raise HTTPException(status_code=403, detail="Unauthorized")
278+
264279
return await cruds_memberships.get_user_memberships_by_user_id_and_association_membership_id(
265280
db,
266281
user_id,
@@ -277,12 +292,12 @@ async def create_user_membership(
277292
user_id: str,
278293
user_membership: schemas_memberships.UserMembershipBase,
279294
db: AsyncSession = Depends(get_db),
280-
user: models_users.CoreUser = Depends(is_user_in(GroupType.admin)),
295+
user: models_users.CoreUser = Depends(is_user()),
281296
):
282297
"""
283298
Create a new user membership.
284299
285-
**This endpoint is only usable by administrators**
300+
**This endpoint is only usable by administrators and membership managers**
286301
"""
287302

288303
db_association_membership = (
@@ -296,6 +311,15 @@ async def create_user_membership(
296311
status_code=404,
297312
detail="Association membership not found",
298313
)
314+
if not is_user_member_of_any_group(
315+
user,
316+
[
317+
GroupType.admin,
318+
GroupType.admin_cdr,
319+
db_association_membership.manager_group_id,
320+
],
321+
):
322+
raise HTTPException(status_code=403, detail="Unauthorized")
299323

300324
db_user = await cruds_users.get_user_by_id(db=db, user_id=user_id)
301325
if db_user is None:
@@ -339,14 +363,14 @@ async def add_batch_membership(
339363
association_membership_id: uuid.UUID,
340364
memberships_details: list[schemas_memberships.MembershipUserMappingEmail],
341365
db: AsyncSession = Depends(get_db),
342-
user: models_users.CoreUser = Depends(is_user_in(GroupType.admin)),
366+
user: models_users.CoreUser = Depends(is_user()),
343367
):
344368
"""
345369
Add a batch of user to a membership.
346370
347371
Return the list of unknown users whose email is not in the database.
348372
349-
**User must be an administrator to use this endpoint.**
373+
**User must be an administrator or a membership manager to use this endpoint.**
350374
"""
351375
db_association_membership = (
352376
await cruds_memberships.get_association_membership_by_id(
@@ -360,6 +384,16 @@ async def add_batch_membership(
360384
detail="Association membership not found",
361385
)
362386

387+
if not is_user_member_of_any_group(
388+
user,
389+
[
390+
GroupType.admin,
391+
GroupType.admin_cdr,
392+
db_association_membership.manager_group_id,
393+
],
394+
):
395+
raise HTTPException(status_code=403, detail="Unauthorized")
396+
363397
unknown_users: list[schemas_memberships.MembershipUserMappingEmail] = []
364398
for detail in memberships_details:
365399
detail_user = await cruds_users.get_user_by_email(
@@ -399,12 +433,12 @@ async def update_user_membership(
399433
membership_id: uuid.UUID,
400434
user_membership: schemas_memberships.UserMembershipEdit,
401435
db: AsyncSession = Depends(get_db),
402-
user: models_users.CoreUser = Depends(is_user_in(GroupType.admin)),
436+
user: models_users.CoreUser = Depends(is_user()),
403437
):
404438
"""
405439
Update a user membership.
406440
407-
**This endpoint is only usable by administrators**
441+
**This endpoint is only usable by administrators and membership managers**
408442
"""
409443

410444
db_user_membership = await cruds_memberships.get_user_membership_by_id(
@@ -413,6 +447,18 @@ async def update_user_membership(
413447
)
414448
if db_user_membership is None:
415449
raise HTTPException(status_code=404, detail="User membership not found")
450+
db_membership = await cruds_memberships.get_association_membership_by_id(
451+
db,
452+
db_user_membership.association_membership_id,
453+
)
454+
if db_membership is None:
455+
raise ValueError
456+
457+
if not is_user_member_of_any_group(
458+
user,
459+
[GroupType.admin, GroupType.admin_cdr, db_membership.manager_group_id],
460+
):
461+
raise HTTPException(status_code=403, detail="Unauthorized")
416462

417463
new_membership = schemas_memberships.UserMembershipSimple(
418464
id=db_user_membership.id,
@@ -440,12 +486,12 @@ async def update_user_membership(
440486
async def delete_user_membership(
441487
membership_id: uuid.UUID,
442488
db: AsyncSession = Depends(get_db),
443-
user: models_users.CoreUser = Depends(is_user_in(GroupType.admin)),
489+
user: models_users.CoreUser = Depends(is_user()),
444490
):
445491
"""
446492
Delete a user membership.
447493
448-
**This endpoint is only usable by administrators**
494+
**This endpoint is only usable by administrators and membership managers**
449495
"""
450496

451497
db_user_membership = await cruds_memberships.get_user_membership_by_id(
@@ -454,6 +500,18 @@ async def delete_user_membership(
454500
)
455501
if db_user_membership is None:
456502
raise HTTPException(status_code=404, detail="User membership not found")
503+
db_membership = await cruds_memberships.get_association_membership_by_id(
504+
db,
505+
db_user_membership.association_membership_id,
506+
)
507+
if db_membership is None:
508+
raise ValueError
509+
510+
if not is_user_member_of_any_group(
511+
user,
512+
[GroupType.admin, GroupType.admin_cdr, db_membership.manager_group_id],
513+
):
514+
raise HTTPException(status_code=403, detail="Unauthorized")
457515

458516
await cruds_memberships.delete_user_membership(
459517
db=db,

0 commit comments

Comments
 (0)