feat: implement comprehensive RBAC checks for courses, chapters, collections, and activities, enhancing user rights management and security documentation

This commit is contained in:
swve 2025-08-09 12:13:12 +02:00
parent 887046203e
commit 3ce019abec
22 changed files with 1788 additions and 598 deletions

View file

@ -4,7 +4,8 @@ from sqlmodel import Session, select, and_
from src.db.users import PublicUser, AnonymousUser, User, UserRead
from src.db.courses.courses import Course
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum, ResourceAuthorshipStatusEnum
from src.security.rbac.rbac import authorization_verify_if_user_is_anon, authorization_verify_based_on_roles_and_authorship
from src.security.rbac.rbac import authorization_verify_if_user_is_anon, authorization_verify_based_on_org_admin_status
from src.security.courses_security import courses_rbac_check
from typing import List
@ -14,6 +15,14 @@ async def apply_course_contributor(
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
"""
Apply to become a course contributor
SECURITY NOTES:
- Any authenticated user can apply to become a contributor
- Applications are created with PENDING status
- Only course owners (CREATOR, MAINTAINER) or admins can approve applications
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
@ -73,21 +82,17 @@ async def update_course_contributor(
):
"""
Update a course contributor's role and status
Only administrators can perform this action
SECURITY NOTES:
- Only course owners (CREATOR, MAINTAINER) or admins can update contributors
- Cannot modify the role of the course creator
- Requires strict course ownership checks
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
# RBAC check - verify if user has admin rights
authorized = await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, "update", course_uuid, db_session
)
if not authorized:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not authorized to update course contributors",
)
# SECURITY: Require course ownership or admin role for updating contributors
await courses_rbac_check(request, course_uuid, current_user, "update", db_session)
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@ -115,7 +120,7 @@ async def update_course_contributor(
detail="Contributor not found for this course",
)
# Don't allow changing the role of the creator
# SECURITY: Don't allow changing the role of the creator
if existing_authorship.authorship == ResourceAuthorshipEnum.CREATOR:
raise HTTPException(
status_code=400,
@ -144,6 +149,10 @@ async def get_course_contributors(
) -> List[dict]:
"""
Get all contributors for a course with their user information
SECURITY NOTES:
- Requires read access to the course
- Contributors are visible to anyone with course read access
"""
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@ -155,6 +164,9 @@ async def get_course_contributors(
detail="Course not found",
)
# SECURITY: Require read access to the course
await courses_rbac_check(request, course_uuid, current_user, "read", db_session)
# Get all contributors for this course with user information
statement = (
select(ResourceAuthor, User)
@ -184,21 +196,17 @@ async def add_bulk_course_contributors(
):
"""
Add multiple contributors to a course by their usernames
Only administrators can perform this action
SECURITY NOTES:
- Only course owners (CREATOR, MAINTAINER) or admins can add contributors
- Requires strict course ownership checks
- Cannot add contributors to courses the user doesn't own
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
# RBAC check - verify if user has admin rights
authorized = await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, "update", course_uuid, db_session
)
if not authorized:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not authorized to add contributors",
)
# SECURITY: Require course ownership or admin role for adding contributors
await courses_rbac_check(request, course_uuid, current_user, "update", db_session)
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@ -284,21 +292,18 @@ async def remove_bulk_course_contributors(
):
"""
Remove multiple contributors from a course by their usernames
Only administrators can perform this action
SECURITY NOTES:
- Only course owners (CREATOR, MAINTAINER) or admins can remove contributors
- Requires strict course ownership checks
- Cannot remove contributors from courses the user doesn't own
- Cannot remove the course creator
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
# RBAC check - verify if user has admin rights
authorized = await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, "update", course_uuid, db_session
)
if not authorized:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not authorized to remove contributors",
)
# SECURITY: Require course ownership or admin role for removing contributors
await courses_rbac_check(request, course_uuid, current_user, "update", db_session)
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@ -346,7 +351,7 @@ async def remove_bulk_course_contributors(
})
continue
# Don't allow removing the creator
# SECURITY: Don't allow removing the creator
if existing_authorship.authorship == ResourceAuthorshipEnum.CREATOR:
results["failed"].append({
"username": username,