From 2969fb41c995f9188acfb68b4643a361d1cb447e Mon Sep 17 00:00:00 2001 From: Jens Luedicke Date: Sun, 13 Jul 2025 12:57:52 +0200 Subject: [PATCH] Add Forget Password feature. --- CLAUDE.md | 226 +++++++++++++ README.md | 302 ++++++++++++++---- app.py | 100 ++++++ ...add_password_reset_fields_to_user_model.py | 29 ++ models/user.py | 26 ++ security_headers.py | 4 +- templates/forgot_password.html | 135 ++++++++ templates/login.html | 6 + templates/reset_password.html | 291 +++++++++++++++++ 9 files changed, 1063 insertions(+), 56 deletions(-) create mode 100644 CLAUDE.md create mode 100644 migrations/versions/85d490db548b_add_password_reset_fields_to_user_model.py create mode 100644 templates/forgot_password.html create mode 100644 templates/reset_password.html diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0e1b6dc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,226 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +TimeTrack is a comprehensive web-based time tracking application built with Flask that provides enterprise-level time management capabilities for teams and organizations. It features multi-tenancy, role-based access control, project/team management, billing/invoicing, and secure authentication with 2FA. + +## Tech Stack + +- **Backend**: Flask 2.0.1 with SQLAlchemy ORM +- **Database**: PostgreSQL (production) / SQLite (development) +- **Migrations**: Flask-Migrate (Alembic-based) +- **Frontend**: Server-side rendered Jinja2 templates with vanilla JavaScript +- **Authentication**: Session-based with TOTP 2FA support and password reset via email +- **Export**: Pandas for CSV/Excel, ReportLab for PDF generation +- **Mobile**: Progressive Web App (PWA) support with optimized mobile UI + +## Development Setup + +### Local Development + +```bash +# Using virtual environment +source .venv/bin/activate # or pipenv shell + +# Set environment variables (PostgreSQL example) +export DATABASE_URL="postgresql://timetrack:timetrack123@localhost:5432/timetrack" + +# Run the application +python app.py +``` + +### Docker Development + +```bash +# Standard docker-compose (uses PostgreSQL) +docker-compose up + +# Debug mode with hot-reload +docker-compose -f docker-compose.debug.yml up +``` + +## Database Operations + +### Flask-Migrate Commands + +```bash +# Create a new migration +python create_migration.py "Description of changes" + +# Apply pending migrations +python apply_migration.py + +# Check current migration state +python check_migration_state.py + +# Clean migration state (CAUTION: destructive) +python clean_migration_state.py + +# For Docker environments +docker exec timetrack-timetrack-1 python create_migration.py "Description" +docker exec timetrack-timetrack-1 python apply_migration.py +``` + +### Standard Flask-Migrate Commands + +```bash +# Create migration +flask db migrate -m "Description" + +# Apply migrations +flask db upgrade + +# Rollback one revision +flask db downgrade + +# Show current revision +flask db current + +# Show migration history +flask db history +``` + +## Key Architecture Patterns + +### 1. Blueprint-Based Modular Architecture + +Routes are organized into blueprints by feature domain: +- `/routes/auth.py` - Authentication and authorization decorators +- `/routes/projects.py` - Project management +- `/routes/invoice.py` - Billing and invoicing +- `/routes/tax_configuration.py` - Tax management +- `/routes/teams.py` - Team management +- `/routes/export.py` - Data export functionality + +### 2. Model Organization + +Models are split by domain in `/models/`: +- `user.py` - User, Role, UserPreferences +- `company.py` - Company, CompanySettings, CompanyWorkConfig +- `project.py` - Project, ProjectCategory +- `team.py` - Team +- `time_entry.py` - TimeEntry +- `invoice.py` - Invoice, InvoiceLineItem, InvoiceStatus +- `tax_configuration.py` - TaxConfiguration +- `enums.py` - BillingType, AccountType, etc. + +### 3. Multi-Tenancy Pattern + +All data is scoped by company_id with automatic filtering: +```python +# Common pattern in routes +projects = Project.query.filter_by(company_id=g.user.company_id).all() +``` + +### 4. Role-Based Access Control + +Decorators enforce permissions: +```python +@role_required(Role.SUPERVISOR) # Supervisor and above +@admin_required # Admin and System Admin only +@company_required # Ensures user has company context +``` + +### 5. Billing Architecture + +- Projects support multiple billing types: NON_BILLABLE, HOURLY, DAILY_RATE, FIXED_RATE +- Invoices support net/gross pricing with country-specific tax configurations +- TimeEntry calculates billing based on project settings (8-hour day for daily rates) + +## Common Development Tasks + +### Adding a New Feature + +1. Create model in appropriate file under `/models/` +2. Create blueprint in `/routes/` with proper decorators +3. Register blueprint in `app.py` +4. Create templates in `/templates/` +5. Run migration: `python create_migration.py "Add feature X"` + +### Testing Database Changes + +```bash +# Check what will be migrated +python check_migration_state.py + +# Test in isolated environment +docker-compose -f docker-compose.debug.yml up + +# Apply to development database +DATABASE_URL="postgresql://..." python apply_migration.py +``` + +### Working with Billing Features + +When modifying billing/invoice features: +1. Check `models/enums.py` for BillingType values +2. Update TimeEntry.calculate_billing_amount() for new billing logic +3. Update Invoice.calculate_totals() for tax calculations +4. Ensure templates handle all billing types (hourly/daily rate display) + +## Important Implementation Details + +### Session Management +- Sessions are permanent with 7-day lifetime +- User context loaded in `app.before_request()` via `g.user` +- Company context available via `g.company` + +### Time Calculations +- Time entries use UTC internally, converted for display +- Rounding rules configured per company/user +- Break time calculations handled in TimeEntry model + +### Export System +- Pandas handles CSV/Excel generation +- ReportLab for PDF invoices +- Export routes handle large datasets with streaming + +### Email Configuration +- Flask-Mail configured via environment variables +- MAIL_SERVER, MAIL_PORT, MAIL_USERNAME required +- Used for user invitations and password resets + +### Authentication Features +- Password reset functionality with secure tokens (1-hour expiry) +- Two-factor authentication (2FA) using TOTP +- Session-based authentication with "Remember Me" option +- Email verification for new accounts (configurable) + +### Mobile UI Features +- Progressive Web App (PWA) manifest for installability +- Mobile-optimized navigation with hamburger menu and bottom nav +- Touch-friendly form inputs and buttons (44px minimum touch targets) +- Responsive tables with card view on small screens +- Pull-to-refresh functionality +- Mobile gestures support (swipe, pinch-to-zoom) +- Date/time pickers that respect user preferences + +## Environment Variables + +Required for production: +- `DATABASE_URL` - PostgreSQL connection string +- `SECRET_KEY` - Flask session secret +- `MAIL_SERVER`, `MAIL_PORT`, `MAIL_USERNAME`, `MAIL_PASSWORD` - Email config + +Optional: +- `FLASK_ENV` - Set to 'development' for debug mode +- `FORCE_HTTPS` - Set to 'true' for HTTPS enforcement +- `TRUST_PROXY_HEADERS` - Set to 'true' when behind reverse proxy + +## Troubleshooting + +### Migration Issues +1. Run `python check_migration_state.py` to verify database state +2. Check `/migrations/versions/` for migration files +3. If stuck, use `clean_migration_state.py` (careful - destructive) + +### Import Errors +- Ensure all model imports in routes use: `from models import ModelName` +- Blueprint registration order matters in `app.py` + +### Database Connection +- PostgreSQL requires running container or local instance +- Default fallback to SQLite at `/data/timetrack.db` +- Check `docker-compose.yml` for PostgreSQL credentials \ No newline at end of file diff --git a/README.md b/README.md index fad0fc3..69a33a8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TimeTrack -TimeTrack is a comprehensive web-based time tracking application built with Flask that provides enterprise-level time management capabilities for teams and organizations. The application features role-based access control, project management, team collaboration, and secure authentication. +TimeTrack is a comprehensive web-based time tracking application built with Flask that provides enterprise-level time management capabilities for teams and organizations. The application features multi-tenancy support, role-based access control, project management, team collaboration, billing/invoicing, and secure authentication with 2FA and password reset functionality. It includes a Progressive Web App (PWA) interface with mobile-optimized features. ## Features @@ -13,59 +13,99 @@ TimeTrack is a comprehensive web-based time tracking application built with Flas ### User Management & Security - **Two-Factor Authentication (2FA)**: TOTP-based authentication with QR codes -- **Role-Based Access Control**: Admin, Supervisor, Team Leader, and Team Member roles -- **User Administration**: Create, edit, block/unblock users with verification system -- **Profile Management**: Update email, password, and personal settings +- **Password Reset**: Secure email-based password recovery (1-hour token expiry) +- **Role-Based Access Control**: System Admin, Admin, Supervisor, Team Leader, and Team Member roles +- **Multi-Tenancy**: Complete data isolation between companies +- **User Administration**: Create, edit, block/unblock users with email verification +- **Profile Management**: Update email, password, avatar, and personal settings +- **Company Invitations**: Invite users to join your company via email ### Team & Project Management - **Team Management**: Create teams, assign members, and track team hours -- **Project Management**: Create projects with codes, assign to teams, set active/inactive status +- **Project Management**: Create projects with codes, categories, billing types (hourly/daily/fixed) +- **Sprint Management**: Agile sprint planning and tracking +- **Task Management**: Create and manage tasks with subtasks and dependencies +- **Notes System**: Create, organize, and share notes with folder structure - **Team Hours Tracking**: View team member working hours with date filtering - **Project Assignment**: Flexible project-team assignments and access control ### Export & Reporting -- **Multiple Export Formats**: CSV and Excel export capabilities +- **Multiple Export Formats**: CSV, Excel, and PDF export capabilities - **Individual Time Export**: Personal time entries with date range selection - **Team Hours Export**: Export team member hours with filtering options +- **Invoice Generation**: Create and export invoices with tax configurations +- **Analytics Dashboard**: Visual analytics with charts and statistics - **Quick Export Options**: Today, week, month, or all-time data exports ### Administrative Features +- **Company Management**: Multi-company support with separate settings - **Admin Dashboard**: System overview with user, team, and activity statistics -- **System Settings**: Configure registration settings and global preferences +- **System Settings**: Configure registration, email verification, tracking scripts +- **Work Configuration**: Set work hours, break rules, and rounding preferences +- **Tax Configuration**: Country-specific tax rates for invoicing +- **Branding**: Custom logos, colors, and application naming - **User Administration**: Complete user lifecycle management - **Team & Project Administration**: Full CRUD operations for teams and projects +- **System Admin Tools**: Multi-company oversight and system health monitoring ## Tech Stack - **Backend**: Flask 2.0.1 with SQLAlchemy ORM -- **Database**: SQLite with comprehensive relational schema -- **Authentication**: Flask session management with 2FA support -- **Frontend**: Responsive HTML, CSS, JavaScript with real-time updates -- **Security**: TOTP-based two-factor authentication, role-based access control -- **Export**: CSV and Excel export capabilities -- **Dependencies**: See Pipfile for complete dependency list +- **Database**: PostgreSQL (production) +- **Migrations**: Flask-Migrate (Alembic-based) with custom helpers +- **Authentication**: Session-based with 2FA and password reset via Flask-Mail +- **Frontend**: Server-side Jinja2 templates with vanilla JavaScript +- **Mobile**: Progressive Web App (PWA) with mobile-optimized UI +- **Export**: Pandas for CSV/Excel, ReportLab for PDF generation +- **Containerization**: Docker and Docker Compose for easy deployment +- **Dependencies**: See requirements.txt for complete dependency list ## Installation ### Prerequisites -- Python 3.12 -- pip or pipenv +- Python 3.9+ +- PostgreSQL 15+ (for production) +- Docker & Docker Compose (recommended) -### Setup with pipenv (recommended) +### Quick Start with Docker (Recommended) ```bash # Clone the repository git clone https://github.com/nullmedium/TimeTrack.git cd TimeTrack -# Install dependencies using pipenv -pipenv install +# Copy and configure environment variables +cp .env.example .env +# Edit .env with your settings -# Activate the virtual environment -pipenv shell +# Start the application +docker-compose up -d -# Run the application (migrations run automatically on first startup) +# Access at http://localhost:5000 +``` + +### Local Development Setup + +```bash +# Create virtual environment +python -m venv .venv +source .venv/bin/activate # On Windows: .venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt + +# Set environment variables +export DATABASE_URL="postgresql://user:pass@localhost/timetrack" +export SECRET_KEY="your-secret-key" +export MAIL_SERVER="smtp.example.com" +# ... other environment variables + +# Run migrations +python create_migration.py "Initial setup" +python apply_migration.py + +# Run the application python app.py ``` @@ -81,22 +121,60 @@ python app.py ### Database Migrations -**Automatic Migration System**: All database migrations now run automatically when the application starts. No manual migration scripts need to be run. +**Flask-Migrate System**: The application uses Flask-Migrate (Alembic-based) for database version control. -The integrated migration system handles: -- Database schema creation for new installations -- Automatic schema updates for existing databases -- User table enhancements (verification, roles, teams, 2FA) -- Project and team management table creation -- Sample data initialization -- Data integrity maintenance during upgrades +```bash +# Create a new migration +python create_migration.py "Description of changes" + +# Apply migrations +python apply_migration.py + +# Check migration status +python check_migration_state.py + +# For Docker environments +docker exec timetrack-timetrack-1 python create_migration.py "Description" +docker exec timetrack-timetrack-1 python apply_migration.py +``` + +The migration system handles: +- Automatic schema updates on container startup +- PostgreSQL-specific features (enums, constraints) +- Safe rollback capabilities +- Multi-tenancy data isolation +- Preservation of existing data during schema changes ### Configuration -The application can be configured through: -- **Admin Dashboard**: System-wide settings and user management -- **User Profiles**: Individual work hour and break preferences -- **Environment Variables**: Database and Flask configuration +#### Environment Variables + +```bash +# Database +DATABASE_URL=postgresql://user:password@host:5432/dbname + +# Flask +SECRET_KEY=your-secret-key-here +FLASK_ENV=development # or production + +# Email (required for invitations and password reset) +MAIL_SERVER=smtp.example.com +MAIL_PORT=587 +MAIL_USE_TLS=true +MAIL_USERNAME=your-email@example.com +MAIL_PASSWORD=your-password + +# Optional +FORCE_HTTPS=true # Force HTTPS in production +TRUST_PROXY_HEADERS=true # When behind reverse proxy +``` + +#### Application Settings + +- **Company Settings**: Work hours, break requirements, time rounding +- **User Preferences**: Date/time formats, timezone, UI preferences +- **System Settings**: Registration, email verification, tracking scripts +- **Branding**: Custom logos, colors, and naming ## Usage @@ -123,36 +201,152 @@ The application can be configured through: ## Security Features - **Two-Factor Authentication**: TOTP-based 2FA with QR code setup -- **Role-Based Access Control**: Four distinct user roles with appropriate permissions -- **Session Management**: Secure login/logout with "remember me" functionality -- **Data Validation**: Comprehensive input validation and error handling -- **Account Verification**: Email verification system for new accounts +- **Password Reset**: Secure email-based recovery with time-limited tokens +- **Role-Based Access Control**: Five distinct user roles with granular permissions +- **Multi-Tenancy**: Complete data isolation between companies +- **Session Management**: Secure sessions with configurable lifetime +- **Email Verification**: Optional email verification for new accounts +- **Password Strength**: Enforced password complexity requirements +- **Security Headers**: HSTS, CSP, X-Frame-Options, and more +- **Input Validation**: Comprehensive server-side validation +- **CSRF Protection**: Built-in Flask CSRF protection + +## Features for Mobile Users + +- **Progressive Web App**: Install TimeTrack as an app on mobile devices +- **Mobile Navigation**: Bottom navigation bar and hamburger menu +- **Touch Optimization**: 44px minimum touch targets throughout +- **Responsive Tables**: Automatic card view on small screens +- **Mobile Gestures**: Swipe navigation and pull-to-refresh +- **Date/Time Pickers**: Mobile-friendly pickers respecting user preferences +- **Offline Support**: Basic offline functionality with service workers +- **Performance**: Lazy loading and optimized for mobile networks + +## Project Structure + +``` +TimeTrack/ +├── app.py # Main Flask application +├── models/ # Database models organized by domain +│ ├── user.py # User, Role, UserPreferences +│ ├── company.py # Company, CompanySettings +│ ├── project.py # Project, ProjectCategory +│ ├── time_entry.py # TimeEntry model +│ └── ... # Other domain models +├── routes/ # Blueprint-based route handlers +│ ├── auth.py # Authentication routes +│ ├── projects.py # Project management +│ ├── teams.py # Team management +│ └── ... # Other route modules +├── templates/ # Jinja2 templates +├── static/ # CSS, JS, images +│ ├── css/ # Stylesheets including mobile +│ ├── js/ # JavaScript including PWA support +│ └── manifest.json # PWA manifest +├── migrations/ # Flask-Migrate/Alembic migrations +├── docker-compose.yml # Docker configuration +└── requirements.txt # Python dependencies +``` ## API Endpoints -The application provides various endpoints for different user roles: -- `/admin/*`: Administrative functions (Admin only) -- `/supervisor/*`: Supervisor functions (Supervisor+ roles) -- `/team/*`: Team management (Team Leader+ roles) -- `/export/*`: Data export functionality -- `/auth/*`: Authentication and profile management +Key application routes organized by function: +- `/` - Home dashboard +- `/login`, `/logout`, `/register` - Authentication +- `/forgot_password`, `/reset_password/` - Password recovery +- `/time-tracking` - Main time tracking interface +- `/projects/*` - Project management +- `/teams/*` - Team management +- `/tasks/*` - Task and sprint management +- `/notes/*` - Notes system +- `/invoices/*` - Billing and invoicing +- `/export/*` - Data export functionality +- `/admin/*` - Administrative functions +- `/system-admin/*` - System administration (multi-company) -## File Structure +## Deployment -- `app.py`: Main Flask application with integrated migration system -- `models.py`: Database models and relationships -- `templates/`: HTML templates for all pages -- `static/`: CSS and JavaScript files -- `migrate_*.py`: Legacy migration scripts (no longer needed) +### Production Deployment with Docker + +```bash +# Clone and configure +git clone https://github.com/nullmedium/TimeTrack.git +cd TimeTrack +cp .env.example .env +# Edit .env with production values + +# Build and start +docker-compose -f docker-compose.yml up -d + +# View logs +docker-compose logs -f + +# Backup database +docker exec timetrack-db-1 pg_dump -U timetrack timetrack > backup.sql +``` + +### Reverse Proxy Configuration (Nginx) + +```nginx +server { + listen 80; + server_name timetrack.example.com; + + location / { + proxy_pass http://localhost:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +## Troubleshooting + +### Common Issues + +1. **Migration Errors** + - Check current state: `python check_migration_state.py` + - View migration history: `flask db history` + - Fix revision conflicts in alembic_version table + +2. **Docker Issues** + - Container not starting: `docker logs timetrack-timetrack-1` + - Database connection: Ensure PostgreSQL is healthy + - Permission errors: Check volume permissions + +3. **Email Not Sending** + - Verify MAIL_* environment variables + - Check firewall rules for SMTP port + - Enable "less secure apps" if using Gmail + +4. **2FA Issues** + - Ensure system time is synchronized + - QR code not scanning: Check for firewall blocking images ## Contributing 1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Test thoroughly -5. Submit a pull request +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +### Development Guidelines + +- Follow PEP 8 for Python code +- Write tests for new features +- Update documentation as needed +- Ensure migrations are reversible +- Test on both PostgreSQL and SQLite ## License -This project is licensed under the MIT License. +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Acknowledgments + +- Flask community for the excellent framework +- Contributors and testers +- Open source projects that made this possible diff --git a/app.py b/app.py index 9687ea0..570971c 100644 --- a/app.py +++ b/app.py @@ -909,6 +909,106 @@ def verify_email(token): return redirect(url_for('login')) +@app.route('/forgot_password', methods=['GET', 'POST']) +def forgot_password(): + """Handle forgot password requests""" + if request.method == 'POST': + username_or_email = request.form.get('username_or_email', '').strip() + + if not username_or_email: + flash('Please enter your username or email address.', 'error') + return render_template('forgot_password.html', title='Forgot Password') + + # Try to find user by username or email + user = User.query.filter( + db.or_( + User.username == username_or_email, + User.email == username_or_email + ) + ).first() + + if user and user.email: + # Generate reset token + token = user.generate_password_reset_token() + + # Send reset email + reset_url = url_for('reset_password', token=token, _external=True) + msg = Message( + f'Password Reset Request - {g.branding.app_name if g.branding else "TimeTrack"}', + recipients=[user.email] + ) + msg.body = f'''Hello {user.username}, + +You have requested to reset your password for {g.branding.app_name if g.branding else "TimeTrack"}. + +To reset your password, please click on the link below: +{reset_url} + +This link will expire in 1 hour. + +If you did not request a password reset, please ignore this email. + +Best regards, +The {g.branding.app_name if g.branding else "TimeTrack"} Team +''' + + try: + mail.send(msg) + logger.info(f"Password reset email sent to user {user.username}") + except Exception as e: + logger.error(f"Failed to send password reset email: {str(e)}") + flash('Failed to send reset email. Please contact support.', 'error') + return render_template('forgot_password.html', title='Forgot Password') + + # Always show success message to prevent user enumeration + flash('If an account exists with that username or email address, we have sent a password reset link.', 'success') + return redirect(url_for('login')) + + return render_template('forgot_password.html', title='Forgot Password') + +@app.route('/reset_password/', methods=['GET', 'POST']) +def reset_password(token): + """Handle password reset with token""" + # Find user by reset token + user = User.query.filter_by(password_reset_token=token).first() + + if not user or not user.verify_password_reset_token(token): + flash('Invalid or expired reset link.', 'error') + return redirect(url_for('login')) + + if request.method == 'POST': + password = request.form.get('password') + confirm_password = request.form.get('confirm_password') + + # Validate input + error = None + if not password: + error = 'Password is required' + elif password != confirm_password: + error = 'Passwords do not match' + + # Validate password strength + if not error: + validator = PasswordValidator() + is_valid, password_errors = validator.validate(password) + if not is_valid: + error = password_errors[0] + + if error: + flash(error, 'error') + return render_template('reset_password.html', token=token, title='Reset Password') + + # Update password + user.set_password(password) + user.clear_password_reset_token() + db.session.commit() + + logger.info(f"Password reset successful for user {user.username}") + flash('Your password has been reset successfully. Please log in with your new password.', 'success') + return redirect(url_for('login')) + + return render_template('reset_password.html', token=token, title='Reset Password') + @app.route('/dashboard') @role_required(Role.TEAM_MEMBER) @company_required diff --git a/migrations/versions/85d490db548b_add_password_reset_fields_to_user_model.py b/migrations/versions/85d490db548b_add_password_reset_fields_to_user_model.py new file mode 100644 index 0000000..e2cdcca --- /dev/null +++ b/migrations/versions/85d490db548b_add_password_reset_fields_to_user_model.py @@ -0,0 +1,29 @@ +"""Add password reset fields to user model + +Revision ID: 85d490db548b +Revises: c72667903a91 +Create Date: 2025-07-13 12:24:14.261548 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '85d490db548b' +down_revision = 'c72667903a91' +branch_labels = None +depends_on = None + + +def upgrade(): + # Add password reset fields to user table + op.add_column('user', sa.Column('password_reset_token', sa.String(length=100), nullable=True)) + op.add_column('user', sa.Column('password_reset_expiry', sa.DateTime(), nullable=True)) + op.create_unique_constraint('uq_user_password_reset_token', 'user', ['password_reset_token']) + + +def downgrade(): + # Remove password reset fields from user table + op.drop_constraint('uq_user_password_reset_token', 'user', type_='unique') + op.drop_column('user', 'password_reset_expiry') + op.drop_column('user', 'password_reset_token') \ No newline at end of file diff --git a/models/user.py b/models/user.py index 4795de7..a806c34 100644 --- a/models/user.py +++ b/models/user.py @@ -48,6 +48,10 @@ class User(db.Model): # Avatar field avatar_url = db.Column(db.String(255), nullable=True) # URL to user's avatar image + + # Password reset fields + password_reset_token = db.Column(db.String(100), unique=True, nullable=True) + password_reset_expiry = db.Column(db.DateTime, nullable=True) # Relationships time_entries = db.relationship('TimeEntry', backref='user', lazy=True) @@ -139,6 +143,28 @@ class User(db.Model): elif self.username: return self.username[:2].upper() return "??" + + def generate_password_reset_token(self): + """Generate a password reset token""" + token = secrets.token_urlsafe(32) + self.password_reset_token = token + self.password_reset_expiry = datetime.utcnow() + timedelta(hours=1) + db.session.commit() + return token + + def verify_password_reset_token(self, token): + """Verify if the password reset token is valid""" + if not self.password_reset_token or self.password_reset_token != token: + return False + if not self.password_reset_expiry or datetime.utcnow() > self.password_reset_expiry: + return False + return True + + def clear_password_reset_token(self): + """Clear the password reset token after use""" + self.password_reset_token = None + self.password_reset_expiry = None + db.session.commit() def __repr__(self): return f'' diff --git a/security_headers.py b/security_headers.py index 8e977ea..1307f52 100644 --- a/security_headers.py +++ b/security_headers.py @@ -3,13 +3,13 @@ Security headers middleware for Flask. Add this to ensure secure form submission and prevent security warnings. """ -from flask import request +from flask import request, current_app def add_security_headers(response): """Add security headers to all responses.""" # Force HTTPS for all resources - if request.is_secure or not request.app.debug: + if request.is_secure or not current_app.debug: # Strict Transport Security - force HTTPS for 1 year response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' diff --git a/templates/forgot_password.html b/templates/forgot_password.html new file mode 100644 index 0000000..1da324f --- /dev/null +++ b/templates/forgot_password.html @@ -0,0 +1,135 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+
+

Forgot Password

+ +

+ Enter your username or email address and we'll send you a link to reset your password. +

+ +
+
+ + +
+ + + + +
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index 4365500..da99bc7 100644 --- a/templates/login.html +++ b/templates/login.html @@ -47,6 +47,12 @@ + + diff --git a/templates/reset_password.html b/templates/reset_password.html new file mode 100644 index 0000000..91d71b5 --- /dev/null +++ b/templates/reset_password.html @@ -0,0 +1,291 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+
+

Reset Password

+ +

+ Enter your new password below. +

+ +
+
+ + +
+
+ +
+ + +
+ + +
+

Password must contain:

+
    +
  • At least 8 characters
  • +
  • One uppercase letter
  • +
  • One lowercase letter
  • +
  • One number
  • +
  • One special character
  • +
+
+ + + + +
+
+
+
+ + + + +{% endblock %} \ No newline at end of file