diff --git a/backend/app.py b/backend/app.py index d488b4c7..cebe2379 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,6 +1,7 @@ import uvicorn from fastapi import FastAPI +from routes.groups import groups_router from routes.projects import projects_router from routes.subjects import subjects_router from routes.users import users_router @@ -11,6 +12,8 @@ app.include_router(subjects_router, prefix="/api") app.include_router(users_router, prefix="/api") app.include_router(projects_router, prefix="/api") +app.include_router(groups_router, prefix="/api") + if __name__ == "__main__": uvicorn.run("app:app") diff --git a/backend/domain/logic/GroupLogic.py b/backend/domain/logic/GroupLogic.py new file mode 100644 index 00000000..2cd8ce13 --- /dev/null +++ b/backend/domain/logic/GroupLogic.py @@ -0,0 +1,12 @@ +from db.interface.DAOProvider import DAOProvider +from domain.logic.SubjectLogic import is_user_authorized_for_subject +from domain.models.GroupDataclass import GroupDataclass +from domain.models.UserDataclass import UserDataclass + + +def is_user_authorized_for_group(group: GroupDataclass, user: UserDataclass, dao_provider: DAOProvider) -> bool: + subject_dao = dao_provider.get_subject_dao() + project_dao = dao_provider.get_project_dao() + project = project_dao.get(group.project_id) + subject = subject_dao.get(project.subject_id) + return is_user_authorized_for_subject(subject, user, dao_provider) diff --git a/backend/domain/logic/ProjectLogic.py b/backend/domain/logic/ProjectLogic.py new file mode 100644 index 00000000..d8bf4fc6 --- /dev/null +++ b/backend/domain/logic/ProjectLogic.py @@ -0,0 +1,10 @@ +from db.interface.DAOProvider import DAOProvider +from domain.logic.SubjectLogic import is_user_authorized_for_subject +from domain.models.ProjectDataclass import ProjectDataclass +from domain.models.UserDataclass import UserDataclass + + +def is_user_authorized_for_project(project: ProjectDataclass, user: UserDataclass, dao_provider: DAOProvider) -> bool: + subject_dao = dao_provider.get_subject_dao() + subject = subject_dao.get(project.subject_id) + return is_user_authorized_for_subject(subject, user, dao_provider) diff --git a/backend/fill_database_mock.py b/backend/fill_database_mock.py index da04f028..4886fe1c 100644 --- a/backend/fill_database_mock.py +++ b/backend/fill_database_mock.py @@ -48,6 +48,7 @@ # voeg teacher toe aan objeprog subject_dao.add_teacher_to_subject(teacher1.id, objeprog.id) + subject_dao.add_student_to_subject(student1.id, objeprog.id) # voeg studenten toe aan de groep group_dao.add_student_to_group(student1.id, groep1.id) diff --git a/backend/routes/groups.py b/backend/routes/groups.py new file mode 100644 index 00000000..67f4fd74 --- /dev/null +++ b/backend/routes/groups.py @@ -0,0 +1,49 @@ +from fastapi import APIRouter, HTTPException, status + +from db.errors.database_errors import ItemNotFoundError +from domain.logic.GroupLogic import is_user_authorized_for_group +from domain.models.GroupDataclass import GroupDataclass +from domain.models.StudentDataclass import StudentDataclass +from routes.db import get_dao_provider +from routes.login import get_authenticated_user + +groups_router = APIRouter() + + +@groups_router.get("/groups") +def get_groups() -> list[GroupDataclass]: + user = get_authenticated_user() + group_dao = get_dao_provider().get_group_dao() + try: + groups = group_dao.get_groups_of_student(user.id) + except ItemNotFoundError as err: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err + return groups + + +def ensure_group_authorized(group: GroupDataclass) -> None: + user = get_authenticated_user() + if not is_user_authorized_for_group(group, user, get_dao_provider()): + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not allowed to access this group") + + +@groups_router.get("/groups/{group_id}") +def get_group(group_id: int) -> GroupDataclass: + group_dao = get_dao_provider().get_group_dao() + try: + group = group_dao.get(group_id) + except ItemNotFoundError as err: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err + ensure_group_authorized(group) + return group + + +@groups_router.get("/groups/{group_id}/students") +def get_group_students(group_id: int) -> list[StudentDataclass]: + group_dao = get_dao_provider().get_group_dao() + try: + group = group_dao.get(group_id) + except ItemNotFoundError as err: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err + ensure_group_authorized(group) + return group_dao.get_students_of_group(group.id) diff --git a/backend/routes/login.py b/backend/routes/login.py index d2e32ced..83b3ce80 100644 --- a/backend/routes/login.py +++ b/backend/routes/login.py @@ -1,3 +1,5 @@ +from fastapi import HTTPException, status + from domain.models.UserDataclass import UserDataclass from routes.db import get_dao_provider @@ -10,3 +12,8 @@ def is_user_admin() -> bool: user = get_authenticated_user() admin_dao = get_dao_provider().get_admin_dao() return admin_dao.is_user_admin(user.id) + + +def ensure_admin_access() -> None: + if not is_user_admin(): + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required") diff --git a/backend/routes/projects.py b/backend/routes/projects.py index e73b6574..fbe669cb 100644 --- a/backend/routes/projects.py +++ b/backend/routes/projects.py @@ -1,7 +1,8 @@ from fastapi import APIRouter, HTTPException, status from db.errors.database_errors import ItemNotFoundError -from domain.logic.SubjectLogic import is_user_authorized_for_subject +from domain.logic.ProjectLogic import is_user_authorized_for_project +from domain.models.GroupDataclass import GroupDataclass from domain.models.ProjectDataclass import ProjectDataclass from routes.db import get_dao_provider from routes.login import get_authenticated_user @@ -10,7 +11,7 @@ @projects_router.get("/projects") -def get_subjects(teacher: bool = False) -> list[ProjectDataclass]: +def get_projects(teacher: bool = False) -> list[ProjectDataclass]: user = get_authenticated_user() project_dao = get_dao_provider().get_project_dao() subject_dao = get_dao_provider().get_subject_dao() @@ -23,20 +24,34 @@ def get_subjects(teacher: bool = False) -> list[ProjectDataclass]: for i in subjects: projects += project_dao.get_projects_of_subject(i.id) except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err return projects +def ensure_project_authorized(project: ProjectDataclass) -> None: + user = get_authenticated_user() + if not is_user_authorized_for_project(project, user, get_dao_provider()): + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not allowed to access this project") + + @projects_router.get("/projects/{project_id}") def get_project(project_id: int) -> ProjectDataclass: project_dao = get_dao_provider().get_project_dao() - subject_dao = get_dao_provider().get_subject_dao() - user = get_authenticated_user() try: project = project_dao.get(project_id) except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err - subject = subject_dao.get(project.subject_id) - if not is_user_authorized_for_subject(subject, user, get_dao_provider()): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err + ensure_project_authorized(project) return project + + +@projects_router.get("/projects/{project_id}/groups") +def get_project_groups(project_id: int) -> list[GroupDataclass]: + project_dao = get_dao_provider().get_project_dao() + group_dao = get_dao_provider().get_group_dao() + try: + project = project_dao.get(project_id) + except ItemNotFoundError as err: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err + ensure_project_authorized(project) + return group_dao.get_groups_of_project(project.id) diff --git a/backend/routes/subjects.py b/backend/routes/subjects.py index db602a81..fdc43492 100644 --- a/backend/routes/subjects.py +++ b/backend/routes/subjects.py @@ -19,7 +19,7 @@ def get_subjects(teacher: bool = False) -> list[SubjectDataclass]: return subject_dao.get_subjects_of_teacher(user.id) return subject_dao.get_subjects_of_student(user.id) except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err @subjects_router.get("/subjects/{subject_id}") @@ -28,7 +28,7 @@ def get_subject(subject_id: int) -> SubjectDataclass: try: return subject_dao.get(subject_id) except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err @subjects_router.get("/subjects/{subject_id}/projects") @@ -38,7 +38,7 @@ def get_subject_projects(subject_id: int) -> list[ProjectDataclass]: try: subject = subject_dao.get(subject_id) if not is_user_authorized_for_subject(subject, get_authenticated_user(), get_dao_provider()): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not allowed to access this subject") return project_dao.get_projects_of_subject(subject_id) except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err diff --git a/backend/routes/users.py b/backend/routes/users.py index 0c08846e..6e9a0fb5 100644 --- a/backend/routes/users.py +++ b/backend/routes/users.py @@ -4,7 +4,7 @@ from domain.logic.UserLogic import convert_user from domain.models.APIUser import APIUser from routes.db import get_dao_provider -from routes.login import get_authenticated_user, is_user_admin +from routes.login import ensure_admin_access, get_authenticated_user users_router = APIRouter() @@ -17,8 +17,7 @@ def get_current_user() -> APIUser: @users_router.get("/users") def get_users() -> list[APIUser]: - if not is_user_admin(): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + ensure_admin_access() user_dao = get_dao_provider().get_user_dao() users = user_dao.get_all() return [convert_user(user, get_dao_provider()) for user in users] @@ -26,11 +25,10 @@ def get_users() -> list[APIUser]: @users_router.get("/users/{uid}") def get_user(uid: int) -> APIUser: - if not is_user_admin(): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + ensure_admin_access() user_dao = get_dao_provider().get_user_dao() try: user = user_dao.get(uid) except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(err)) from err return convert_user(user, get_dao_provider())