fix: high-severity security hardening (Phase B permissions audit)
Phase B addresses 7 high-severity gaps from the permissions audit: - B1: Enforce tree access check on session start via can_access_tree - B2: Replace all inline permission helpers with centralized permissions.py - B3: Fix require_engineer_or_admin to check is_team_admin before role - B4: Add is_active field on User with enforcement in get_current_active_user - B5: Add admin user management endpoints (list, get, role, team-admin, deactivate, activate) - B6: Add rate limiting on auth/invite endpoints via slowapi (disabled in DEBUG) - B7: Implement refresh token rotation with JTI-based revocation and meaningful logout Also reduces access token TTL from 15 to 5 minutes and updates CLAUDE.md with SaaS/MSP context for future planning sessions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -684,3 +684,40 @@ class TestSessions:
|
||||
content = response.text
|
||||
assert '<script>' not in content
|
||||
assert '<script>' in content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_session_on_others_private_tree_forbidden(
|
||||
self, client: AsyncClient, auth_headers: dict, test_tree: dict
|
||||
):
|
||||
"""Test that a user cannot start a session on another user's private tree."""
|
||||
# Register a second user
|
||||
await client.post("/api/v1/auth/register", json={
|
||||
"email": "other@example.com",
|
||||
"password": "OtherPassword123!",
|
||||
"name": "Other User"
|
||||
})
|
||||
login_resp = await client.post("/api/v1/auth/login/json", json={
|
||||
"email": "other@example.com",
|
||||
"password": "OtherPassword123!"
|
||||
})
|
||||
other_headers = {"Authorization": f"Bearer {login_resp.json()['access_token']}"}
|
||||
|
||||
# test_tree is owned by test_user (not public, not default)
|
||||
response = await client.post(
|
||||
"/api/v1/sessions",
|
||||
json={"tree_id": test_tree["id"]},
|
||||
headers=other_headers
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_session_super_admin_any_tree(
|
||||
self, client: AsyncClient, admin_auth_headers: dict, test_tree: dict
|
||||
):
|
||||
"""Test that a super admin can start a session on any tree."""
|
||||
response = await client.post(
|
||||
"/api/v1/sessions",
|
||||
json={"tree_id": test_tree["id"]},
|
||||
headers=admin_auth_headers
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
Reference in New Issue
Block a user