From 7270cc62cdec0755ac48768ff35036d041f7a13f Mon Sep 17 00:00:00 2001 From: Liam Keegan Date: Thu, 26 Sep 2024 09:19:08 +0200 Subject: [PATCH] Add is_researcher boolean flag to User - add empty `/researcher/auth` endpoint to test researcher authentification --- .../src/mondey_backend/dependencies.py | 12 +++++++++ mondey_backend/src/mondey_backend/main.py | 2 ++ .../src/mondey_backend/models/users.py | 8 +++--- .../src/mondey_backend/routers/research.py | 17 ++++++++++++ mondey_backend/tests/conftest.py | 26 +++++++++++++++++++ mondey_backend/tests/routers/test_admin.py | 13 ++++++++-- .../tests/routers/test_milestones.py | 4 ++- mondey_backend/tests/routers/test_research.py | 11 ++++++++ 8 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 mondey_backend/src/mondey_backend/routers/research.py create mode 100644 mondey_backend/tests/routers/test_research.py diff --git a/mondey_backend/src/mondey_backend/dependencies.py b/mondey_backend/src/mondey_backend/dependencies.py index cf596f65..f3505894 100644 --- a/mondey_backend/src/mondey_backend/dependencies.py +++ b/mondey_backend/src/mondey_backend/dependencies.py @@ -3,6 +3,7 @@ from typing import Annotated from fastapi import Depends +from fastapi import HTTPException from sqlmodel import Session from .databases.milestones import engine as milestones_engine @@ -21,6 +22,17 @@ def get_session(): CurrentActiveUserDep = Annotated[User, Depends(current_active_user)] + +def current_active_researcher( + user: Annotated[User, Depends(current_active_user)], +) -> User: + if user and user.is_researcher: + return user + raise HTTPException(401) + + +ResearchDep = Depends(current_active_researcher) + current_active_superuser = fastapi_users.current_user(active=True, superuser=True) AdminDep = Depends(current_active_superuser) diff --git a/mondey_backend/src/mondey_backend/main.py b/mondey_backend/src/mondey_backend/main.py index 450c8db5..ea93808b 100644 --- a/mondey_backend/src/mondey_backend/main.py +++ b/mondey_backend/src/mondey_backend/main.py @@ -14,6 +14,7 @@ from .routers import admin from .routers import auth from .routers import milestones +from .routers import research from .routers import users from .settings import app_settings @@ -33,6 +34,7 @@ def create_app() -> FastAPI: app.include_router(admin.create_router()) app.include_router(users.create_router()) app.include_router(auth.create_router()) + app.include_router(research.create_router()) app.mount( "/static", StaticFiles(directory=app_settings.STATIC_FILES_PATH), name="static" ) diff --git a/mondey_backend/src/mondey_backend/models/users.py b/mondey_backend/src/mondey_backend/models/users.py index 8bded93d..dbccc141 100644 --- a/mondey_backend/src/mondey_backend/models/users.py +++ b/mondey_backend/src/mondey_backend/models/users.py @@ -3,6 +3,7 @@ from fastapi_users import schemas from fastapi_users.db import SQLAlchemyBaseUserTable from fastapi_users_db_sqlalchemy.access_token import SQLAlchemyBaseAccessTokenTable +from sqlalchemy import Boolean from sqlalchemy import ForeignKey from sqlalchemy import Integer from sqlalchemy.orm import DeclarativeBase @@ -17,18 +18,19 @@ class Base(DeclarativeBase): class User(SQLAlchemyBaseUserTable[int], Base): id: Mapped[int] = mapped_column(Integer, primary_key=True) + is_researcher: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) class UserRead(schemas.BaseUser[int]): - pass + is_researcher: bool class UserCreate(schemas.BaseUserCreate): - pass + is_researcher: bool | None = False class UserUpdate(schemas.BaseUserUpdate): - pass + is_researcher: bool | None = None class AccessToken(SQLAlchemyBaseAccessTokenTable[int], Base): diff --git a/mondey_backend/src/mondey_backend/routers/research.py b/mondey_backend/src/mondey_backend/routers/research.py new file mode 100644 index 00000000..52f53074 --- /dev/null +++ b/mondey_backend/src/mondey_backend/routers/research.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from fastapi import APIRouter + +from ..dependencies import ResearchDep + + +def create_router() -> APIRouter: + router = APIRouter( + prefix="/research", tags=["research"], dependencies=[ResearchDep] + ) + + @router.get("/auth/") + def auth(): + return {"ok": True} + + return router diff --git a/mondey_backend/tests/conftest.py b/mondey_backend/tests/conftest.py index 7fff77f3..16a2f64a 100644 --- a/mondey_backend/tests/conftest.py +++ b/mondey_backend/tests/conftest.py @@ -6,6 +6,7 @@ from fastapi import FastAPI from fastapi.testclient import TestClient from mondey_backend import settings +from mondey_backend.dependencies import current_active_researcher from mondey_backend.dependencies import current_active_superuser from mondey_backend.dependencies import current_active_user from mondey_backend.dependencies import get_session @@ -123,6 +124,19 @@ def admin(): email="admin@mondey.de", is_active=True, is_superuser=True, + is_researcher=False, + is_verified=True, + ) + + +@pytest.fixture +def research(): + UserRead( + id=2, + email="user@mondey.de", + is_active=True, + is_superuser=False, + is_researcher=True, is_verified=True, ) @@ -134,6 +148,7 @@ def user(): email="user@mondey.de", is_active=True, is_superuser=False, + is_researcher=False, is_verified=True, ) @@ -156,6 +171,17 @@ def user_client( app.dependency_overrides.clear() +@pytest.fixture +def research_client( + app: FastAPI, static_dir: pathlib.Path, session: Session, research: UserRead +): + app.dependency_overrides[get_session] = lambda: session + app.dependency_overrides[current_active_researcher] = lambda: research + client = TestClient(app) + yield client + app.dependency_overrides.clear() + + @pytest.fixture def admin_client( app: FastAPI, static_dir: pathlib.Path, session: Session, admin: UserRead diff --git a/mondey_backend/tests/routers/test_admin.py b/mondey_backend/tests/routers/test_admin.py index d9441b0b..b3116de1 100644 --- a/mondey_backend/tests/routers/test_admin.py +++ b/mondey_backend/tests/routers/test_admin.py @@ -1,5 +1,6 @@ import pathlib +import pytest from fastapi.testclient import TestClient @@ -80,8 +81,16 @@ def test_delete_milestone_group_invalid_group_id(admin_client: TestClient): assert response.status_code == 404 -def test_delete_milestone_group_invalid_admin_user(user_client: TestClient): - response = user_client.delete("/admin/milestone-groups/2") +@pytest.mark.parametrize( + "endpoint", + ["/admin/languages/1", "/admin/milestone-groups/1", "/admin/milestones/1"], +) +@pytest.mark.parametrize("client_type", ["user_client", "research_client"]) +def test_delete_endpoints_invalid_admin_user( + endpoint: str, client_type: str, request: pytest.FixtureRequest +): + client = request.getfixturevalue(client_type) + response = client.delete(endpoint) assert response.status_code == 401 diff --git a/mondey_backend/tests/routers/test_milestones.py b/mondey_backend/tests/routers/test_milestones.py index 2636def2..b05806ab 100644 --- a/mondey_backend/tests/routers/test_milestones.py +++ b/mondey_backend/tests/routers/test_milestones.py @@ -1,7 +1,9 @@ import pytest -@pytest.mark.parametrize("client_type", ["user_client", "admin_client"]) +@pytest.mark.parametrize( + "client_type", ["user_client", "research_client", "admin_client"] +) class TestMilestones: def test_get_languages(self, client_type: str, request: pytest.FixtureRequest): client = request.getfixturevalue(client_type) diff --git a/mondey_backend/tests/routers/test_research.py b/mondey_backend/tests/routers/test_research.py new file mode 100644 index 00000000..eea27233 --- /dev/null +++ b/mondey_backend/tests/routers/test_research.py @@ -0,0 +1,11 @@ +from fastapi.testclient import TestClient + + +def test_auth_invalid_user(user_client: TestClient): + response = user_client.get("/research/auth/") + assert response.status_code == 401 + + +def test_auth(research_client: TestClient): + response = research_client.get("/research/auth/") + assert response.status_code == 200