Add pyproject.toml for consolidated project configuration and replace Black, isort, and Flake8 with Ruff
- Introduced `pyproject.toml` to centralize backend tool configurations (e.g., Ruff, mypy, coverage, pytest). - Replaced Black, isort, and Flake8 with Ruff for linting, formatting, and import sorting. - Updated `requirements.txt` to include Ruff and remove replaced tools. - Added `Makefile` to streamline development workflows with commands for linting, formatting, type-checking, testing, and cleanup.
This commit is contained in:
@@ -7,13 +7,13 @@ Critical security tests covering:
|
||||
|
||||
These tests prevent unauthorized access and privilege escalation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.user import User
|
||||
from app.models.organization import Organization
|
||||
from app.crud.user import user as user_crud
|
||||
from app.models.organization import Organization
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
class TestInactiveUserBlocking:
|
||||
@@ -29,11 +29,7 @@ class TestInactiveUserBlocking:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_inactive_user_cannot_access_protected_endpoints(
|
||||
self,
|
||||
client: AsyncClient,
|
||||
async_test_db,
|
||||
async_test_user: User,
|
||||
user_token: str
|
||||
self, client: AsyncClient, async_test_db, async_test_user: User, user_token: str
|
||||
):
|
||||
"""
|
||||
Test that inactive users are blocked from protected endpoints.
|
||||
@@ -44,12 +40,11 @@ class TestInactiveUserBlocking:
|
||||
3. User tries to access protected endpoint with valid token
|
||||
4. System MUST reject (account inactive)
|
||||
"""
|
||||
test_engine, SessionLocal = async_test_db
|
||||
_test_engine, SessionLocal = async_test_db
|
||||
|
||||
# Step 1: Verify user can access endpoint while active
|
||||
response = await client.get(
|
||||
"/api/v1/users/me",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
"/api/v1/users/me", headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
assert response.status_code == 200, "Active user should have access"
|
||||
|
||||
@@ -61,8 +56,7 @@ class TestInactiveUserBlocking:
|
||||
|
||||
# Step 3: User tries to access endpoint with same token
|
||||
response = await client.get(
|
||||
"/api/v1/users/me",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
"/api/v1/users/me", headers={"Authorization": f"Bearer {user_token}"}
|
||||
)
|
||||
|
||||
# Step 4: System MUST reject (covers lines 52-57)
|
||||
@@ -75,18 +69,14 @@ class TestInactiveUserBlocking:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_inactive_user_blocked_from_organization_endpoints(
|
||||
self,
|
||||
client: AsyncClient,
|
||||
async_test_db,
|
||||
async_test_user: User,
|
||||
user_token: str
|
||||
self, client: AsyncClient, async_test_db, async_test_user: User, user_token: str
|
||||
):
|
||||
"""
|
||||
Test that inactive users can't access organization endpoints.
|
||||
|
||||
Ensures the inactive check applies to ALL protected endpoints.
|
||||
"""
|
||||
test_engine, SessionLocal = async_test_db
|
||||
_test_engine, SessionLocal = async_test_db
|
||||
|
||||
# Deactivate user
|
||||
async with SessionLocal() as session:
|
||||
@@ -97,7 +87,7 @@ class TestInactiveUserBlocking:
|
||||
# Try to list organizations
|
||||
response = await client.get(
|
||||
"/api/v1/organizations/me",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
headers={"Authorization": f"Bearer {user_token}"},
|
||||
)
|
||||
|
||||
# Must be blocked
|
||||
@@ -122,7 +112,7 @@ class TestSuperuserPrivilegeEscalation:
|
||||
client: AsyncClient,
|
||||
async_test_db,
|
||||
async_test_superuser: User,
|
||||
superuser_token: str
|
||||
superuser_token: str,
|
||||
):
|
||||
"""
|
||||
Test that superusers automatically get OWNER role in organizations.
|
||||
@@ -131,14 +121,11 @@ class TestSuperuserPrivilegeEscalation:
|
||||
Superusers can manage any organization without being explicitly added.
|
||||
This is for platform administration.
|
||||
"""
|
||||
test_engine, SessionLocal = async_test_db
|
||||
_test_engine, SessionLocal = async_test_db
|
||||
|
||||
# Step 1: Create an organization (owned by someone else)
|
||||
async with SessionLocal() as session:
|
||||
org = Organization(
|
||||
name="Test Organization",
|
||||
slug="test-org"
|
||||
)
|
||||
org = Organization(name="Test Organization", slug="test-org")
|
||||
session.add(org)
|
||||
await session.commit()
|
||||
await session.refresh(org)
|
||||
@@ -148,7 +135,7 @@ class TestSuperuserPrivilegeEscalation:
|
||||
# (They're not a member, but should auto-get OWNER role)
|
||||
response = await client.get(
|
||||
f"/api/v1/organizations/{org_id}",
|
||||
headers={"Authorization": f"Bearer {superuser_token}"}
|
||||
headers={"Authorization": f"Bearer {superuser_token}"},
|
||||
)
|
||||
|
||||
# Step 3: Should have access (covers lines 154-157)
|
||||
@@ -161,21 +148,18 @@ class TestSuperuserPrivilegeEscalation:
|
||||
client: AsyncClient,
|
||||
async_test_db,
|
||||
async_test_superuser: User,
|
||||
superuser_token: str
|
||||
superuser_token: str,
|
||||
):
|
||||
"""
|
||||
Test that superusers have full management access to all organizations.
|
||||
|
||||
Ensures the OWNER role privilege escalation works end-to-end.
|
||||
"""
|
||||
test_engine, SessionLocal = async_test_db
|
||||
_test_engine, SessionLocal = async_test_db
|
||||
|
||||
# Create an organization
|
||||
async with SessionLocal() as session:
|
||||
org = Organization(
|
||||
name="Test Organization",
|
||||
slug="test-org"
|
||||
)
|
||||
org = Organization(name="Test Organization", slug="test-org")
|
||||
session.add(org)
|
||||
await session.commit()
|
||||
await session.refresh(org)
|
||||
@@ -185,34 +169,29 @@ class TestSuperuserPrivilegeEscalation:
|
||||
response = await client.put(
|
||||
f"/api/v1/organizations/{org_id}",
|
||||
headers={"Authorization": f"Bearer {superuser_token}"},
|
||||
json={"name": "Updated Name"}
|
||||
json={"name": "Updated Name"},
|
||||
)
|
||||
|
||||
# Should succeed (superuser has OWNER privileges)
|
||||
assert response.status_code in [200, 404], "Superuser should be able to manage any org"
|
||||
assert response.status_code in [200, 404], (
|
||||
"Superuser should be able to manage any org"
|
||||
)
|
||||
# Note: Might be 404 if org endpoints require membership, but the role check passes
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_user_does_not_get_owner_role(
|
||||
self,
|
||||
client: AsyncClient,
|
||||
async_test_db,
|
||||
async_test_user: User,
|
||||
user_token: str
|
||||
self, client: AsyncClient, async_test_db, async_test_user: User, user_token: str
|
||||
):
|
||||
"""
|
||||
Sanity check: Regular users don't get automatic OWNER role.
|
||||
|
||||
Ensures the superuser check is working correctly (line 154).
|
||||
"""
|
||||
test_engine, SessionLocal = async_test_db
|
||||
_test_engine, SessionLocal = async_test_db
|
||||
|
||||
# Create an organization
|
||||
async with SessionLocal() as session:
|
||||
org = Organization(
|
||||
name="Test Organization",
|
||||
slug="test-org"
|
||||
)
|
||||
org = Organization(name="Test Organization", slug="test-org")
|
||||
session.add(org)
|
||||
await session.commit()
|
||||
await session.refresh(org)
|
||||
@@ -221,8 +200,10 @@ class TestSuperuserPrivilegeEscalation:
|
||||
# Regular user tries to access it (not a member)
|
||||
response = await client.get(
|
||||
f"/api/v1/organizations/{org_id}",
|
||||
headers={"Authorization": f"Bearer {user_token}"}
|
||||
headers={"Authorization": f"Bearer {user_token}"},
|
||||
)
|
||||
|
||||
# Should be denied (not a member, not a superuser)
|
||||
assert response.status_code in [403, 404], "Regular user shouldn't access non-member org"
|
||||
assert response.status_code in [403, 404], (
|
||||
"Regular user shouldn't access non-member org"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user