phase1: separate DB roles, RLS restoration, login on crm_auth
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
- config.py: add auth_database_url, worker_database_url, migration_database_url - db/__init__.py: separate engines for auth/worker/migration + get_auth_db/get_worker_db - auth.py: all auth endpoints use get_auth_db (crm_auth role) - auth_service.py: remove login fallback, require active membership, check status - auth_service.py: switch_tenant checks active membership status - alembic/env.py: use migration_database_url for Alembic - docker-compose.yml: add AUTH_DATABASE_URL, WORKER_DATABASE_URL - .env.example: add all 4 DB URLs with separate roles - migration 0085: transfer ownership to crm_migration, fix BYPASSRLS, enable RLS+FORCE on all tenant tables, drop old policies, create new fail-closed policies scoped to crm_api+crm_worker, revoke excessive grants, grant minimal crm_auth access, drop crm_runtime, set default privileges - tests/test_rls_coverage.py: automated RLS coverage check (13 tests) - tests/test_cross_tenant_security_v2.py: RLS tests with unprivileged role
This commit is contained in:
+8
-8
@@ -9,7 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import get_settings
|
||||
from app.core.auth import get_redis
|
||||
from app.core.db import get_db
|
||||
from app.core.db import get_auth_db
|
||||
from app.core.rate_limit import check_rate_limit, get_client_ip, reset_rate_limit
|
||||
from app.schemas.auth import (
|
||||
AuthResponse,
|
||||
@@ -30,7 +30,7 @@ settings = get_settings()
|
||||
async def login(
|
||||
request: Request,
|
||||
body: LoginRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
):
|
||||
"""Login with email+password. Sets session cookie."""
|
||||
ip = get_client_ip(request)
|
||||
@@ -95,7 +95,7 @@ async def login(
|
||||
@router.post("/logout", response_model=MessageResponse)
|
||||
async def logout(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
):
|
||||
"""Logout — invalidate session, clear cookie."""
|
||||
session_id = request.cookies.get(settings.session_cookie_name)
|
||||
@@ -116,7 +116,7 @@ async def logout(
|
||||
@router.get("/me", response_model=AuthResponse)
|
||||
async def me(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
):
|
||||
"""Get current user + active tenant."""
|
||||
session_id = request.cookies.get(settings.session_cookie_name)
|
||||
@@ -151,7 +151,7 @@ async def me(
|
||||
@router.get("/me/permissions")
|
||||
async def me_permissions(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get resolved permissions for the current user.
|
||||
@@ -171,7 +171,7 @@ async def me_permissions(
|
||||
async def switch_tenant(
|
||||
request: Request,
|
||||
body: SwitchTenantRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
):
|
||||
"""Switch active tenant for current session."""
|
||||
session_id = request.cookies.get(settings.session_cookie_name)
|
||||
@@ -211,7 +211,7 @@ async def switch_tenant(
|
||||
async def password_reset_request(
|
||||
request: Request,
|
||||
body: PasswordResetRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
):
|
||||
"""Request password reset. Always returns 200 (no user enumeration)."""
|
||||
ip = get_client_ip(request)
|
||||
@@ -231,7 +231,7 @@ async def password_reset_request(
|
||||
async def password_reset_confirm(
|
||||
request: Request,
|
||||
body: PasswordResetConfirm,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_auth_db),
|
||||
):
|
||||
"""Reset password with a valid token."""
|
||||
ip = get_client_ip(request)
|
||||
|
||||
Reference in New Issue
Block a user