Phase 3 harden CSRF invalid POST and work access handling
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user