Files
xamxam/TODO.md
Pontoporeia 27e1b6828d Implement TFE file access restriction feature (complete)
Requirements:
- parametres.php toggle: 'restricted_files_enabled' enables/disables the feature
- Public TFE page: when enabled + access_type=Interne, hides files, shows French
  restriction message + access request form (metadata/synopsis still visible)
- ERG emails (@erg.school / @erg.be): auto-approve, send 24h access link immediately
- External emails: show justification textarea, create pending request, notify admin
- Admin panel /admin/file-access.php: approve/reject requests with optional notes,
  sends access email on approval (linked from admin nav with pending count badge)

Security:
- One-time 24h email tokens (used_at + is_valid=0 on first click)
- Token redeemed via POST /validate-access (GET shows confirmation page only)
- Long-lived 30-day browser session in file_access_sessions table
- Cookie: HttpOnly + Secure + SameSite=Strict
- CSRF on all mutations, rate limiting on request submission
- Audit trail: IP, UA, event, timestamp in file_access_audit

Bug fixes:
- admin/file-access.php: $vars never extract()ed → page was blank
- Template had self-contained head/footer includes (double-include)
- Admin approval URL used $requestId instead of $request['thesis_id']
- App::boot() now starts session so CSRF token works on public pages
- Dispatcher routes /validate-access and /request-access through front controller
2026-04-27 20:20:52 +02:00

3.5 KiB

TFE Access Restriction Feature

Overview

Add access restriction for TFE attached files based on user email domain, with admin validation workflow.

Implementation Plan

1. Database Changes

  • Add restricted_files_enabled setting to site_settings table
  • Create file_access_requests table
    • id, thesis_id, email, justification, status (pending/approved/rejected), admin_notes, created_at, approved_at, approved_by_admin_id
  • Create file_access_tokens table (short-lived, one-time email links, 24h)
    • id, request_id, token (unique), expires_at, used_at (one-time mark)
  • Create file_access_sessions table (long-lived browser sessions, 30 days)
    • id, request_id, session_token, expires_at, is_valid
  • Create file_access_audit table (IP, UA, timestamp on redemption)

2. Configuration

  • Add restricted_files_enabled checkbox in parametres.php (Formulaire section)
  • Update settings.php action handler to persist the setting

3. Public TFE View (tfe.php) - Restricted Access UI

  • TfeController checks restricted flag + access_type_id=2 + cookie session
  • French text: "Accès restreint — Les fichiers attachés à ce TFE sont réservés aux utilisateurs autorisés."
  • Request form: email input + conditional justification textarea (non-ERG only)
  • JS: shows/hides justification textarea based on email domain (@erg.school / @erg.be)
  • Form submits via fetch POST to /request-access.php with CSRF token
  • Metadata, title, authors, synopsis all remain visible regardless of restriction

4. Email Flow

  • @erg.school / @erg.be → auto-approve, generate 24h one-time token, send email immediately
  • External email → create pending request, notify admin by email
  • Admin approves → generate 24h token, send email to requester
  • Email contains link to GET /validate-access (confirmation page) → POST to redeem

5. Secure Token Redemption

  • One-time email token (24h, marked used_at on first click, is_valid=0)
  • GET /validate-access → shows confirmation page (no side effects)
  • POST /validate-access → redeems token (CSRF required), creates browser session cookie
  • Cookie: HttpOnly; Secure; SameSite=Strict; 30 days
  • Session stored in file_access_sessions (separate from one-time email token)
  • TfeController checks file_access_sessions via hasValidCookieAccess()
  • Audit trail: IP, User-Agent, timestamp in file_access_audit on every redemption attempt

6. Admin Panel - Access Requests Management

  • Admin page: /admin/file-access.php (linked from admin nav with pending badge)
  • List pending/approved/rejected requests with tab filters and pagination
  • Approve dialog (with optional note) → sends 24h access email
  • Reject dialog (with optional note)
  • Bug fix: $vars was never extract()ed — page was blank
  • Bug fix: template included head.php/header.php/footer.php itself (double-include)
  • Bug fix: admin approval URL used $requestId instead of $request['thesis_id']

7. Security

  • One-time use tokens (used_at + is_valid=0)
  • POST-based redemption (token in hidden form field, not URL action)
  • 256-bit random tokens, rate limiting on request submission (3/10min per IP)
  • HttpOnly cookie + SameSite=Strict
  • CSRF on all mutations
  • Audit trail (IP, UA, event, timestamp)
  • Short-lived email links (24h), long-lived browser sessions (30 days)
  • App::boot() starts session for all public requests (CSRF token available everywhere)