Phase 3 harden CSRF invalid POST and work access handling

This commit is contained in:
A R R R Associates
2026-06-26 11:41:00 +05:30
parent 4ce5556e9d
commit fc31a5f4e9
7 changed files with 503 additions and 94 deletions
+69 -11
View File
@@ -25,6 +25,12 @@ from app.modules.system_settings.year_backup_service import build_year_backup_ex
router = APIRouter(prefix="/system-settings", tags=["system-settings-ui"])
def _csrf_rejected(request: Request):
from app.core.http_responses import forbidden_response
return forbidden_response(request, "CSRF validation failed")
def _base_ctx(request: Request, user, db, **ctx):
base = {
"request": request,
@@ -172,6 +178,25 @@ def dashboard(request: Request):
# -----------------------------
# Tenant - System Admin only
# -----------------------------
# Phase 3 security hardening: list/index pages must not accept direct unsafe POSTs.
# Existing create/update features continue to use their dedicated /new or action routes.
@router.post("/tenants")
def tenants_list_post_rejected(request: Request):
return _csrf_rejected(request)
@router.post("/branches")
def branches_list_post_rejected(request: Request):
return _csrf_rejected(request)
@router.post("/financial-years")
def financial_years_list_post_rejected(request: Request):
return _csrf_rejected(request)
@router.get("/tenants")
def tenants_list(request: Request, q: str = "", page: int = 1, per_page: int = 10):
db = CommonSessionLocal()
@@ -222,7 +247,10 @@ def tenant_create_submit(
default_storage_mode: str = Form("local_only"),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -296,7 +324,10 @@ def tenant_edit_submit(
is_active: str | None = Form(None),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -432,7 +463,10 @@ def branch_create_submit(
attendance_allowed_ip_csv: str = Form(""),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -627,7 +661,10 @@ def branch_edit_submit(
lockout_attempts: str = Form("5"),
lockout_minutes: str = Form("15"),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -881,7 +918,10 @@ async def branding_save(
favicon_file: UploadFile | None = File(None),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -1043,7 +1083,10 @@ def financial_year_create_submit(
is_current: str | None = Form(None),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -1140,7 +1183,10 @@ def financial_year_edit_submit(
end_date: str = Form(...),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -1182,7 +1228,10 @@ def financial_year_edit_submit(
@router.post("/financial-years/{fy_id}/make-current")
def financial_year_make_current(request: Request, fy_id: int, csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -1209,7 +1258,10 @@ def financial_year_make_current(request: Request, fy_id: int, csrf_token: str =
@router.post("/financial-years/{fy_id}/lock")
def financial_year_lock(request: Request, fy_id: int, csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -1232,7 +1284,10 @@ def financial_year_lock(request: Request, fy_id: int, csrf_token: str = Form(...
@router.post("/financial-years/{fy_id}/unlock")
def financial_year_unlock(request: Request, fy_id: int, csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
@@ -1289,7 +1344,10 @@ def financial_year_backup_page(request: Request, fy_id: int):
@router.post("/financial-years/{fy_id}/backup/export")
def financial_year_backup_export_submit(request: Request, fy_id: int, csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
try:
validate_csrf(request, csrf_token)
except PermissionError:
return _csrf_rejected(request)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)