diff --git a/README.md b/README.md index 6c0a99a..9d38ba7 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,23 @@ # Job Application System -A simple, accessible Flask web application for job applications with email-based resume functionality and file uploads. +A professional, accessible Flask web application for job applications with email-based resume functionality, file uploads, and HR notifications. ## Features -- **Multi-step application process**: 5-page workflow from email capture to confirmation +- **Multi-step application process**: 5-page workflow with progress navigation +- **Progress navigation bar**: Visual indicator with backward navigation to edit previous pages - **Email-based resume**: Applicants receive a unique link to resume their application +- **HR notifications**: Automated email alerts to HR when applications are submitted +- **Application review page**: Dedicated HR interface to view applications and download attachments - **Stateful sessions**: Applications can be paused and resumed at any time - **File uploads**: Support for CV, cover letter, and other documents (up to 3 files, 4 MB each) - **German interface**: All user-facing content in German - **File-based storage**: No database required - uses YAML for data serialization +- **Security features**: CSRF protection and rate limiting (5-second cooldown) +- **Modular architecture**: Clean separation of concerns with application factory pattern - **Accessible design**: Minimal CSS, no JavaScript required, keyboard-friendly - **Validation**: Comprehensive server-side validation for all inputs +- **Full test coverage**: 102 automated tests with 100% pass rate ## Application Workflow @@ -21,12 +27,29 @@ A simple, accessible Flask web application for job applications with email-based 4. **Document Upload**: Upload up to 3 documents (CV, cover letter, etc.) 5. **Confirmation**: Success message with next steps +### Navigation Features + +- **Progress bar**: Visual indicator shows current step and completion status +- **Backward navigation**: Users can click on completed steps to return and edit data +- **Sequential progression**: Users must complete pages in order (cannot skip ahead) +- **Data preservation**: All entered information is automatically saved when navigating + +### HR Features + +- **Email notifications**: HR receives an email when an application is submitted +- **Review interface**: Access applications at `/application/{session-id}/` +- **Download attachments**: HR can download all uploaded documents +- **Complete data view**: See all applicant information in one place + ## Technology Stack -- Python 3 -- Flask 3.0.0 -- PyYAML for data serialization -- Flask-Mail for email sending +- **Python** 3.8+ +- **Flask** 3.0.0 (with application factory pattern) +- **Flask-WTF** 1.2.1 (CSRF protection) +- **Flask-Mail** 0.9.1 (email sending) +- **PyYAML** 6.0.1 (data serialization) +- **Werkzeug** 3.0.1 (file handling) +- **pytest** 7.4.3 (testing framework) - Minimal CSS for styling - No JavaScript required @@ -60,18 +83,44 @@ A simple, accessible Flask web application for job applications with email-based - Edit `.env` and configure your settings: ``` SECRET_KEY=your-secret-key-here - MAIL_SERVER=smtp.gmail.com - MAIL_PORT=587 - MAIL_USE_TLS=True - MAIL_USERNAME=your-email@example.com - MAIL_PASSWORD=your-app-password - MAIL_DEFAULT_SENDER=noreply@example.com COMPANY_NAME=Your Company Name + HR_EMAIL=hr@example.com + APPLICATION_URL_BASE=http://localhost:5000 ``` ### Email Configuration -For **Gmail**, you need to: +The application defaults to **mailcatcher** for development (localhost:1025, no authentication required). + +#### For Development (Mailcatcher) + +1. **Install mailcatcher**: + ```bash + gem install mailcatcher + ``` + +2. **Run mailcatcher**: + ```bash + mailcatcher + ``` + +3. **View emails**: Open http://localhost:1080 in your browser + +4. **No .env changes needed** - the application uses mailcatcher by default + +#### For Production (Real SMTP) + +Add to your `.env` file: + +``` +MAIL_SERVER=smtp.gmail.com +MAIL_PORT=587 +MAIL_USE_TLS=True +MAIL_USERNAME=your-email@example.com +MAIL_PASSWORD=your-app-password +``` + +For **Gmail**: 1. Enable 2-factor authentication on your Google account 2. Generate an "App Password" at https://myaccount.google.com/apppasswords 3. Use the app password (not your regular password) in `MAIL_PASSWORD` @@ -83,11 +132,32 @@ For **other email providers**, adjust `MAIL_SERVER` and `MAIL_PORT` accordingly. ### Development Mode ```bash -python app.py +python run.py +``` + +Or using Flask CLI: + +```bash +flask run ``` The application will run at `http://127.0.0.1:5000` +### Running Tests + +The application includes a comprehensive test suite with 102 tests: + +```bash +# Run all tests +pytest tests/ -v + +# Run with coverage report +pytest tests/ --cov=app --cov-report=html + +# Run specific test file +pytest tests/test_routes.py -v +``` + ### Production Deployment For production, use a WSGI server like Gunicorn: @@ -99,11 +169,18 @@ For production, use a WSGI server like Gunicorn: 2. **Run with Gunicorn**: ```bash - gunicorn -w 4 -b 0.0.0.0:8000 app:app + gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()" ``` 3. **Use a reverse proxy** (e.g., Nginx) in front of Gunicorn +4. **Update production settings** in `.env`: + ``` + WTF_CSRF_SSL_STRICT=True + SESSION_COOKIE_SECURE=True + MAIL_SERVER=your-production-smtp-server + ``` + ## Usage ### Linking from Job Postings @@ -130,24 +207,44 @@ This link allows them to resume their application at any time. ``` . -├── app.py # Main Flask application -├── config.py # Configuration settings -├── requirements.txt # Python dependencies -├── .env.example # Example environment variables -├── README.md # This file -├── templates/ # HTML templates -│ ├── base.html -│ ├── page1_email.html -│ ├── page2_personal.html -│ ├── page3_motivation.html -│ ├── page4_upload.html -│ └── page5_confirmation.html -├── static/ # Static files -│ └── style.css -└── applications/ # Application data (created automatically) +├── run.py # Application entry point +├── config.py # Configuration settings +├── requirements.txt # Python dependencies +├── .env.example # Example environment variables +├── README.md # This file +├── app/ # Main application package +│ ├── __init__.py # Application factory +│ ├── routes.py # Route handlers (all endpoints) +│ ├── models.py # Data persistence layer +│ ├── validators.py # Input validation functions +│ ├── email_service.py # Email sending functions +│ └── utils.py # Utility functions +├── templates/ # HTML templates +│ ├── base.html # Base template with navigation +│ ├── _navigation.html # Progress bar component +│ ├── page1_email.html # Email capture page +│ ├── page2_personal.html # Personal information page +│ ├── page3_motivation.html # Motivation questions page +│ ├── page4_upload.html # Document upload page +│ ├── page5_confirmation.html # Confirmation page +│ └── application_view.html # HR application review page +├── static/ # Static files +│ └── style.css # Styles (including navigation) +├── tests/ # Test suite (102 tests) +│ ├── conftest.py # Test configuration and fixtures +│ ├── test_routes.py # Route handler tests +│ ├── test_validation.py # Validation function tests +│ ├── test_email.py # Email functionality tests +│ ├── test_storage.py # Data persistence tests +│ ├── test_uploads.py # File upload tests +│ ├── test_integration.py # End-to-end workflow tests +│ ├── test_hr_notifications.py # HR email notification tests +│ ├── test_application_view.py # Application review page tests +│ └── test_config.py # Configuration tests +└── applications/ # Application data (created automatically) └── {session-id}/ - ├── data.yaml - └── attachments/ + ├── data.yaml # Application data + └── attachments/ # Uploaded files ``` ## Data Storage @@ -163,9 +260,11 @@ Each application is stored in its own folder under `./applications/{session-id}/ session_id: "abc123-def456-..." email: "applicant@example.com" job_name: "Junior Marketing Assistant" -current_page: 4 +current_page: 5 +status: "submitted" created_at: "2025-01-15T10:30:00" updated_at: "2025-01-15T11:45:00" +submitted_at: "2025-01-15T11:45:00" personal_info: name: "Müller" firstname: "Anna" @@ -211,13 +310,93 @@ uploaded_files: - Maximum 4 MB per file - Allowed formats: PDF, DOC, DOCX, TXT, JPG, JPEG, PNG -## Security Considerations +## Security Features -- Session IDs use cryptographically secure UUIDs -- File uploads are validated for size and type -- Filenames are sanitized to prevent path traversal -- All validation is server-side -- Email configuration uses environment variables +### CSRF Protection + +All forms are protected against Cross-Site Request Forgery (CSRF) attacks using Flask-WTF: + +- CSRF tokens required on all POST requests +- Tokens included automatically in all forms +- No token expiration (users can take time filling forms) +- Configurable for production HTTPS environments + +### Rate Limiting + +Built-in rate limiting prevents form spam and abuse: + +- 5-second cooldown between form submissions (configurable) +- Session-based tracking +- Customizable in `config.py`: `RATE_LIMIT_SECONDS = 5` + +### Data Security + +- **Session IDs**: Cryptographically secure UUIDs (not guessable) +- **File uploads**: Validated for size, type, and sanitized filenames +- **Path traversal protection**: Secure filename handling prevents directory attacks +- **Server-side validation**: All input validated on the server +- **Environment variables**: Sensitive configuration kept in `.env` file +- **No SQL injection**: File-based storage eliminates SQL vulnerabilities + +### Production Security Checklist + +When deploying to production, update `.env`: + +``` +SECRET_KEY= +WTF_CSRF_SSL_STRICT=True +SESSION_COOKIE_SECURE=True +SESSION_COOKIE_HTTPONLY=True +SESSION_COOKIE_SAMESITE=Lax +``` + +## HR Features + +### Accessing Applications + +HR staff can review applications using the direct URL: + +``` +https://your-domain.com/application/{session-id}/ +``` + +The session ID is included in the HR notification email sent when an application is submitted. + +### HR Notification Emails + +Configure HR email notifications in `.env`: + +``` +HR_EMAIL=hr@example.com +APPLICATION_URL_BASE=https://your-domain.com +``` + +When an applicant submits their application (page 4), an email is automatically sent to the HR email address containing: + +- Applicant name and email +- Position applied for +- Submission timestamp +- Number of uploaded documents +- Direct link to the application review page + +### Application Review Page + +The review page (`/application/{session-id}/`) displays: + +- Complete applicant information (all form fields) +- All uploaded documents with download links +- Submission timestamp and metadata +- Professional formatting for easy review + +### Downloading Attachments + +HR can download individual files directly from the review page: + +``` +https://your-domain.com/application/{session-id}/download/{filename} +``` + +Security: Only files listed in the application's uploaded_files list can be downloaded (prevents unauthorized access). ## Customization @@ -244,26 +423,119 @@ ALLOWED_EXTENSIONS = {'pdf', 'doc', 'docx', 'txt', 'jpg', 'jpeg', 'png'} Edit all templates in `./templates/` - they contain German text that can be translated. +## Architecture + +### Application Factory Pattern + +The application uses Flask's application factory pattern for better testability and flexibility: + +- `app/__init__.py` contains the `create_app()` function +- Extensions (Flask-Mail, CSRF) are initialized within the factory +- Multiple app instances can be created (useful for testing) +- Configuration can be passed as a parameter + +### Modular Structure + +Code is organized by responsibility: + +- **routes.py** (473 lines): All route handlers and request processing +- **models.py** (41 lines): Data persistence layer (YAML operations) +- **validators.py** (48 lines): Input validation functions +- **email_service.py** (85 lines): Email sending logic +- **utils.py** (30 lines): Utility functions (rate limiting, etc.) + +Benefits: +- Easy to test individual modules in isolation +- Clear separation of concerns +- Easier to navigate and maintain +- Supports team collaboration + +### Testing Strategy + +The application includes 102 automated tests organized by functionality: + +- **test_routes.py**: Route handler behavior +- **test_validation.py**: Input validation logic +- **test_email.py**: Email sending functionality +- **test_storage.py**: YAML data persistence +- **test_uploads.py**: File upload handling +- **test_integration.py**: End-to-end workflows +- **test_hr_notifications.py**: HR email notifications +- **test_application_view.py**: Application review page +- **test_config.py**: Configuration settings + +Run tests frequently during development to catch regressions early. + ## Troubleshooting -### Email not sending +### Email not sending in development + +**Using Mailcatcher (recommended for development):** + +1. Verify mailcatcher is running: + ```bash + mailcatcher + ``` + +2. Check mailcatcher web interface at http://localhost:1080 + +3. Verify application is configured for mailcatcher: + - Default settings work out of the box (localhost:1025) + - No `.env` email configuration needed for development + +**Using real SMTP:** - Check your email credentials in `.env` - For Gmail, ensure you're using an app password, not your regular password - Check that `MAIL_USE_TLS` is set correctly for your email provider - Check application logs for error messages +### Tests failing + +If tests fail after making changes: + +```bash +# Run specific test file to identify the issue +pytest tests/test_routes.py -v + +# Run with detailed output +pytest tests/ -vv + +# Check test coverage +pytest tests/ --cov=app --cov-report=term-missing +``` + +Common issues: +- Import errors: Check module structure in `app/` package +- Template errors: Verify templates exist in `templates/` directory +- CSRF errors: Tests disable CSRF in `conftest.py` + ### File upload errors -- Ensure the `./applications/` folder is writable +- Ensure the `./applications/` folder exists and is writable - Check file size limits in your web server configuration - Verify allowed file extensions in `config.py` +- Check that storage path is correct in config ### Session not resuming - Check that the `./applications/{session-id}/` folder exists - Verify that `data.yaml` is valid YAML format - Check application logs for errors +- Ensure session ID in URL is correct (UUID format) + +### Navigation not working + +- Verify `current_page` is being passed to templates +- Check that navigation component is included in `base.html` +- Ensure session data contains valid `current_page` value +- Check browser console for CSS/styling issues + +### Rate limiting preventing form submission + +- Check `RATE_LIMIT_SECONDS` in `config.py` (default: 5 seconds) +- Wait the specified time between submissions +- For testing, set to 0 in `.env`: `RATE_LIMIT_SECONDS=0` ## License