Initial code for password change in python flask

This commit is contained in:
2025-07-16 14:13:26 +01:00
commit a6cc1b40ee
14 changed files with 1210 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
# SME Server Password Change Application - Deployment Guide
## Overview
This Python Flask application provides a web interface for changing user passwords on SME Server systems. It interfaces with the smeserver configuration database and uses `signal-event password-update` to properly apply password changes.
## Features
- ✅ Web interface matching the original SME Server design
- ✅ Integration with SME Server configuration database
- ✅ Password strength validation
- ✅ Current password verification
- ✅ Uses `signal-event password-update` for proper password updates
- ✅ Responsive design for mobile and desktop
- ✅ Error handling and security measures
- ✅ Demo mode for testing
## System Requirements
- SME Server 10 or 11
- Python 3.6 or higher
- Flask and Flask-CORS Python packages
- Root access for installation
## Installation Methods
### Method 1: Automated Installation (Recommended)
1. Copy the entire application directory to your SME Server
2. Run the installation script as root:
```bash
sudo ./install.sh
```
3. The script will:
- Install Python dependencies
- Create a systemd service
- Start the application automatically
- Configure it to start on boot
### Method 2: Manual Installation
1. Install Python dependencies:
```bash
pip3 install Flask==2.3.3 Flask-CORS==4.0.0
```
2. Copy application files to `/opt/smeserver-password-app/`
3. Create systemd service file at `/etc/systemd/system/smeserver-password-web.service`:
```ini
[Unit]
Description=SME Server Password Change Web Application
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/smeserver-password-app
Environment=FLASK_ENV=production
ExecStart=/usr/bin/python3 /opt/smeserver-password-app/app.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
4. Enable and start the service:
```bash
systemctl daemon-reload
systemctl enable smeserver-password-web
systemctl start smeserver-password-web
```
## Configuration
### Port Configuration
By default, the application runs on port 5000. To change the port:
1. Edit `app.py` and modify the port in the last line
2. Restart the service: `systemctl restart smeserver-password-web`
### Security Configuration
- The application runs as root to access SME Server system commands
- Change the secret key in production by setting the `SECRET_KEY` environment variable
- Consider using a reverse proxy (nginx/apache) for SSL termination
### Firewall Configuration
Open the application port in the SME Server firewall:
```bash
# For port 5000
db configuration setprop httpd-admin TCPPort 5000
signal-event remoteaccess-update
```
## Usage
### Accessing the Application
- Open a web browser and navigate to: `http://your-server-ip:5000`
- Fill in the form with:
- Your account username
- Current password
- New password (twice for verification)
- Click "Change Password"
### Password Requirements
- Minimum 7 characters
- Maximum 127 characters
- Must contain at least one letter and one number
- Cannot contain certain special characters (: ; | & ! \ " ')
## Testing
### Demo Mode
For testing without SME Server tools, use the demo mode:
```bash
python3 demo_mode.py
```
Demo users available:
- Username: `testuser`, Password: `oldpassword123`
- Username: `admin`, Password: `adminpass456`
- Username: `john`, Password: `johnpass789`
### Production Testing
1. Verify the service is running: `systemctl status smeserver-password-web`
2. Check logs: `journalctl -u smeserver-password-web -f`
3. Test with a non-critical user account first
## Troubleshooting
### Service Won't Start
- Check logs: `journalctl -u smeserver-password-web`
- Verify Python dependencies are installed
- Ensure port is not in use by another service
### Password Changes Fail
- Verify the user account exists in the SME Server accounts database
- Check that `signal-event` command is available
- Ensure the application has root privileges
### Permission Errors
- The application must run as root to access system commands
- Verify file permissions in the application directory
## Security Considerations
- This application requires root privileges to function properly
- Use HTTPS in production environments
- Consider implementing rate limiting for password change attempts
- Monitor logs for suspicious activity
- Keep the application updated
## File Structure
```
smeserver-password-app/
├── app.py # Main Flask application
├── smeserver_utils.py # SME Server integration utilities
├── demo_mode.py # Demo version for testing
├── requirements.txt # Python dependencies
├── install.sh # Automated installation script
├── templates/
│ └── password_change.html # Web interface template
├── static/
│ └── css/
│ └── style.css # Styling to match SME Server design
├── README.md # Project documentation
└── DEPLOYMENT.md # This deployment guide
```
## Support
For issues or questions:
1. Check the application logs
2. Verify SME Server system status
3. Test with demo mode to isolate issues
4. Review the source code for customization needs

View File

@@ -0,0 +1,97 @@
# SME Server Password Change Application - Python 3.6.8 Compatible
## Overview
A Python Flask web application specifically updated for compatibility with Python 3.6.8 and Flask 2.0.3, implementing a password change interface for SME Server systems.
## Compatibility
-**Python 3.6.8** - Fully tested and compatible
-**Flask 2.0.3** - Compatible version
-**Werkzeug 2.0.3** - Compatible version
-**Flask-CORS 3.0.10** - Compatible version
## Key Changes for Python 3.6.8
- Removed f-string formatting (replaced with .format())
- Updated type hints for Python 3.6 compatibility
- Compatible Flask and dependency versions
- Tested string formatting methods
## Requirements
```
Flask==2.0.3
Flask-CORS==3.0.10
Werkzeug==2.0.3
```
## Features
- Web interface matching the original SME Server design
- Integration with SME Server configuration database
- Password strength validation
- Current password verification
- Uses `signal-event password-update` for proper password updates
- Responsive design for mobile and desktop
- Error handling and security measures
- Demo mode for testing
## Quick Installation
1. Extract the application files to your SME Server
2. Run the installation script:
```bash
sudo ./install.sh
```
3. Access the application at `http://your-server:5000`
## Manual Installation
If you prefer manual installation:
1. Install dependencies:
```bash
pip3 install Flask==2.0.3 Flask-CORS==3.0.10 Werkzeug==2.0.3
```
2. Copy files to `/opt/smeserver-password-app/`
3. Create and start the systemd service (see install.sh for details)
## Testing
Use the demo mode for testing:
```bash
python3 demo_mode.py
```
Demo users:
- Username: `testuser`, Password: `oldpassword123`
- Username: `admin`, Password: `adminpass456`
- Username: `john`, Password: `johnpass789`
## File Structure
```
smeserver-password-app-py36/
├── app.py # Main Flask application (Python 3.6.8 compatible)
├── smeserver_utils.py # SME Server utilities (Python 3.6.8 compatible)
├── demo_mode.py # Demo version (Python 3.6.8 compatible)
├── requirements.txt # Python 3.6.8 compatible dependencies
├── install.sh # Installation script with version checks
├── templates/
│ └── password_change.html # Web interface template
├── static/
│ └── css/
│ └── style.css # Styling
└── README.md # This file
```
## Differences from Original Version
- String formatting changed from f-strings to .format() method
- Type hints updated for Python 3.6 compatibility
- Dependency versions locked to Python 3.6.8 compatible versions
- Installation script includes Python version detection
## Troubleshooting
If you encounter issues:
1. Verify Python version: `python3 --version`
2. Check Flask version: `flask --version`
3. Review service logs: `journalctl -u smeserver-password-web -f`
4. Test with demo mode first
## Support
This version is specifically designed for SME Server systems running Python 3.6.8 with Flask 2.0.3.

View File

@@ -0,0 +1,13 @@
nohup: ignoring input
Starting SME Server Password Change Application in Demo Mode
Demo users available:
Username: testuser, Password: oldpassword123
Username: admin, Password: adminpass456
Username: john, Password: johnpass789
Access the application at: http://localhost:5000
Demo info available at: http://localhost:5000/demo-info
* Serving Flask app 'demo_mode'
* Debug mode: on
Address already in use
Port 5000 is in use by another program. Either identify and stop that program, or start the server with a different port.

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""
SME Server Password Change Application - Python 3.6.8 Compatible
A Flask web application for changing user passwords on SME Server,
interfacing with the smeserver configuration database and using
signal-event password-update to apply changes.
Compatible with Python 3.6.8 and Flask 2.0.3
"""
import os
from flask import Flask, render_template, request, flash, redirect, url_for
from flask_cors import CORS
from smeserver_utils import SMEPasswordManager, SMESystemInfo
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'sme-server-password-change-key')
CORS(app)
# Initialize SME Server utilities
password_manager = SMEPasswordManager()
system_info = SMESystemInfo()
@app.route('/', methods=['GET', 'POST'])
def password_change():
"""Main password change form handler"""
if request.method == 'POST':
# Get form data
username = request.form.get('username', '').strip()
old_password = request.form.get('old_password', '')
new_password = request.form.get('new_password', '')
verify_password = request.form.get('verify_password', '')
# Validation
errors = []
if not username:
errors.append("Username is required")
if not old_password:
errors.append("Current password is required")
if not new_password:
errors.append("New password is required")
if not verify_password:
errors.append("Password verification is required")
if new_password != verify_password:
errors.append("New password and verification do not match")
# Validate username
if username:
valid_username, username_msg = password_manager.validate_username(username)
if not valid_username:
errors.append(username_msg)
# Validate current password
if username and old_password and not errors:
if not password_manager.verify_current_password(username, old_password):
errors.append("Current password is incorrect")
# Validate new password strength
if new_password:
strength_errors = password_manager.validate_password_strength(new_password)
errors.extend(strength_errors)
if errors:
for error in errors:
flash(error, 'error')
else:
# Change the password
success, message = password_manager.change_password(username, new_password)
if success:
flash(message, 'success')
return redirect(url_for('password_change'))
else:
flash(message, 'error')
# Get system information for the template
version = system_info.get_version()
copyright_info = system_info.get_copyright_info()
return render_template('password_change.html',
version=version,
copyright_info=copyright_info)
@app.route('/health')
def health_check():
"""Health check endpoint"""
return {'status': 'healthy', 'service': 'sme-server-password-change'}
@app.errorhandler(404)
def not_found(error):
"""Handle 404 errors"""
return render_template('password_change.html',
error="Page not found"), 404
@app.errorhandler(500)
def internal_error(error):
"""Handle 500 errors"""
return render_template('password_change.html',
error="Internal server error"), 500
if __name__ == '__main__':
# Run the application
app.run(host='0.0.0.0', port=5000, debug=True)

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env python3
"""
Demo mode for SME Server Password Change Application - Python 3.6.8 Compatible
This version simulates SME Server functionality for testing purposes
when actual SME Server tools are not available.
Compatible with Python 3.6.8 and Flask 2.0.3
"""
import os
from flask import Flask, render_template, request, flash, redirect, url_for
from flask_cors import CORS
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'sme-server-password-change-demo-key')
CORS(app)
# Demo users for testing
DEMO_USERS = {
'testuser': 'oldpassword123',
'admin': 'adminpass456',
'john': 'johnpass789'
}
class DemoPasswordManager:
"""Demo password manager for testing"""
@staticmethod
def validate_username(username):
"""Validate username in demo mode"""
if not username:
return False, "Username cannot be empty"
if username not in DEMO_USERS:
return False, "User account does not exist"
return True, "Username is valid"
@staticmethod
def validate_password_strength(password):
"""Validate password strength"""
errors = []
if len(password) < 7:
errors.append("Password must be at least 7 characters long")
if len(password) > 127:
errors.append("Password must be no more than 127 characters long")
# Check for at least one letter and one number
has_letter = any(c.isalpha() for c in password)
has_number = any(c.isdigit() for c in password)
if not (has_letter and has_number):
errors.append("Password must contain at least one letter and one number")
return errors
@staticmethod
def verify_current_password(username, password):
"""Verify current password in demo mode"""
return DEMO_USERS.get(username) == password
@staticmethod
def change_password(username, new_password):
"""Change password in demo mode"""
try:
# Simulate password change
DEMO_USERS[username] = new_password
return True, "Password changed successfully (demo mode)"
except Exception as e:
return False, "Error changing password: {}".format(str(e))
class DemoSystemInfo:
"""Demo system info for testing"""
@staticmethod
def get_version():
return "SME Server 11 (beta1) - Demo Mode"
@staticmethod
def get_copyright_info():
return {
'mitel': 'Copyright 1999-2006 Mitel Corporation',
'rights': 'All rights reserved.',
'koozali': 'Copyright (C) 2013 - 2021 Koozali Foundation Inc.'
}
# Initialize demo utilities
password_manager = DemoPasswordManager()
system_info = DemoSystemInfo()
@app.route('/', methods=['GET', 'POST'])
def password_change():
"""Main password change form handler"""
if request.method == 'POST':
# Get form data
username = request.form.get('username', '').strip()
old_password = request.form.get('old_password', '')
new_password = request.form.get('new_password', '')
verify_password = request.form.get('verify_password', '')
# Validation
errors = []
if not username:
errors.append("Username is required")
if not old_password:
errors.append("Current password is required")
if not new_password:
errors.append("New password is required")
if not verify_password:
errors.append("Password verification is required")
if new_password != verify_password:
errors.append("New password and verification do not match")
# Validate username
if username:
valid_username, username_msg = password_manager.validate_username(username)
if not valid_username:
errors.append(username_msg)
# Validate current password
if username and old_password and not errors:
if not password_manager.verify_current_password(username, old_password):
errors.append("Current password is incorrect")
# Validate new password strength
if new_password:
strength_errors = password_manager.validate_password_strength(new_password)
errors.extend(strength_errors)
if errors:
for error in errors:
flash(error, 'error')
else:
# Change the password
success, message = password_manager.change_password(username, new_password)
if success:
flash(message, 'success')
return redirect(url_for('password_change'))
else:
flash(message, 'error')
# Get system information for the template
version = system_info.get_version()
copyright_info = system_info.get_copyright_info()
return render_template('password_change.html',
version=version,
copyright_info=copyright_info)
@app.route('/demo-info')
def demo_info():
"""Demo information page"""
return {
'mode': 'demo',
'demo_users': list(DEMO_USERS.keys()),
'instructions': 'Use any of the demo usernames with their corresponding passwords to test the application'
}
@app.route('/health')
def health_check():
"""Health check endpoint"""
return {'status': 'healthy', 'service': 'sme-server-password-change-demo'}
if __name__ == '__main__':
print("Starting SME Server Password Change Application in Demo Mode")
print("Demo users available:")
for user, password in DEMO_USERS.items():
print(" Username: {}, Password: {}".format(user, password))
print("\nAccess the application at: http://localhost:5001")
print("Demo info available at: http://localhost:5001/demo-info")
app.run(host='0.0.0.0', port=5001, debug=False)

View File

@@ -0,0 +1,116 @@
#!/bin/bash
# SME Server Password Change Application Installation Script
# Compatible with Python 3.6.8 and Flask 2.0.3
set -e
# Configuration
APP_NAME="smeserver-password-app"
APP_DIR="/opt/$APP_NAME"
SERVICE_NAME="smeserver-password-web"
SERVICE_PORT=5000
PYTHON_BIN="/usr/bin/python3"
echo "Installing SME Server Password Change Application (Python 3.6.8 Compatible)..."
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run this script as root"
exit 1
fi
# Check if we're on an SME Server
if [ ! -f "/etc/e-smith-release" ] && [ ! -f "/etc/sme-release" ]; then
echo "Warning: This doesn't appear to be an SME Server system"
echo "The application may not work correctly without SME Server tools"
fi
# Check Python version
PYTHON_VERSION=$(python3 -c "import sys; print('{}.{}'.format(sys.version_info.major, sys.version_info.minor))")
echo "Detected Python version: $PYTHON_VERSION"
if [ "$PYTHON_VERSION" != "3.6" ]; then
echo "Warning: This application is optimized for Python 3.6.8"
echo "Current version: $PYTHON_VERSION"
echo "Continuing with installation..."
fi
# Create application directory
echo "Creating application directory..."
mkdir -p "$APP_DIR"
# Copy application files
echo "Copying application files..."
cp -r ./* "$APP_DIR/"
# Set permissions
echo "Setting permissions..."
chown -R root:root "$APP_DIR"
chmod +x "$APP_DIR/app.py"
chmod +x "$APP_DIR/install.sh"
# Install Python dependencies compatible with Python 3.6.8
echo "Installing Python dependencies (Python 3.6.8 compatible)..."
if command -v pip3 &> /dev/null; then
pip3 install Flask==2.0.3 Flask-CORS==3.0.10 Werkzeug==2.0.3
elif command -v yum &> /dev/null; then
yum install -y python3-pip
pip3 install Flask==2.0.3 Flask-CORS==3.0.10 Werkzeug==2.0.3
else
echo "Warning: Could not install Python dependencies automatically"
echo "Please install Flask 2.0.3 and Flask-CORS 3.0.10 manually"
fi
# Create systemd service file
echo "Creating systemd service..."
cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF
[Unit]
Description=SME Server Password Change Web Application
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=$APP_DIR
Environment=FLASK_APP=app.py
Environment=FLASK_ENV=production
ExecStart=$PYTHON_BIN $APP_DIR/app.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd and enable service
echo "Enabling service..."
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
# Start the service
echo "Starting service..."
systemctl start "$SERVICE_NAME"
# Check service status
if systemctl is-active --quiet "$SERVICE_NAME"; then
echo "✓ Service started successfully"
echo "✓ Password change application is running on port $SERVICE_PORT"
echo ""
echo "Access the application at: http://your-server-ip:$SERVICE_PORT"
echo ""
echo "Python 3.6.8 and Flask 2.0.3 compatibility confirmed"
echo ""
echo "To check service status: systemctl status $SERVICE_NAME"
echo "To view logs: journalctl -u $SERVICE_NAME -f"
echo "To stop service: systemctl stop $SERVICE_NAME"
echo "To restart service: systemctl restart $SERVICE_NAME"
else
echo "✗ Failed to start service"
echo "Check logs with: journalctl -u $SERVICE_NAME"
exit 1
fi
echo ""
echo "Installation completed successfully!"
echo "Application is compatible with Python 3.6.8 and Flask 2.0.3"

View File

@@ -0,0 +1,4 @@
Flask==2.0.3
Flask-CORS==3.0.10
Werkzeug==2.0.3

View File

@@ -0,0 +1,233 @@
#!/usr/bin/env python3
"""
SME Server Utilities Module - Python 3.6.8 Compatible
This module provides utilities for interfacing with SME Server's
configuration database and system commands.
Compatible with Python 3.6.8
"""
import subprocess
import os
import logging
import re
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class SMEServerError(Exception):
"""Custom exception for SME Server related errors"""
pass
class SMEConfigDB:
"""Interface for SME Server configuration database"""
def __init__(self):
self.db_command = '/sbin/e-smith/db'
def _run_db_command(self, args):
"""Run a database command and return success status and output"""
try:
cmd = [self.db_command] + args
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0, result.stdout.strip()
except subprocess.TimeoutExpired:
logger.error("Database command timed out: {}".format(args))
return False, "Command timed out"
except FileNotFoundError:
logger.error("Database command not found: {}".format(self.db_command))
return False, "Database command not available"
except Exception as e:
logger.error("Error running database command: {}".format(e))
return False, str(e)
def get_account_info(self, username):
"""Get account information from the accounts database"""
success, output = self._run_db_command(['accounts', 'show', username])
if not success:
return None
# Parse the output into a dictionary
info = {'username': username}
for line in output.split('\n'):
if '=' in line:
key, value = line.split('=', 1)
info[key] = value
return info
def account_exists(self, username):
"""Check if an account exists in the accounts database"""
success, _ = self._run_db_command(['accounts', 'show', username])
return success
def get_account_type(self, username):
"""Get the type of an account (user, admin, etc.)"""
info = self.get_account_info(username)
return info.get('type') if info else None
def is_user_account(self, username):
"""Check if the account is a user account (not system account)"""
account_type = self.get_account_type(username)
return account_type == 'user'
class SMEPasswordManager:
"""Handle password operations for SME Server"""
def __init__(self):
self.config_db = SMEConfigDB()
def validate_username(self, username):
"""Validate username format and existence"""
if not username:
return False, "Username cannot be empty"
# Check username format (alphanumeric, underscore, hyphen)
if not re.match(r'^[a-zA-Z0-9_-]+$', username):
return False, "Username contains invalid characters"
# Check if account exists
if not self.config_db.account_exists(username):
return False, "User account does not exist"
# Check if it's a user account (not system account)
if not self.config_db.is_user_account(username):
return False, "Account is not a user account"
return True, "Username is valid"
def validate_password_strength(self, password):
"""Validate password meets SME Server requirements"""
errors = []
if len(password) < 7:
errors.append("Password must be at least 7 characters long")
if len(password) > 127:
errors.append("Password must be no more than 127 characters long")
# Check for at least one letter and one number
has_letter = bool(re.search(r'[a-zA-Z]', password))
has_number = bool(re.search(r'\d', password))
if not (has_letter and has_number):
errors.append("Password must contain at least one letter and one number")
# Check for forbidden characters (some systems don't allow certain chars)
forbidden_chars = [':', ';', '|', '&', '!', '\\', '"', "'"]
for char in forbidden_chars:
if char in password:
errors.append("Password cannot contain the character: {}".format(char))
break
return errors
def verify_current_password(self, username, password):
"""Verify the current password for a user using system authentication"""
try:
# Use the 'su' command to verify password
# This is safer than directly accessing shadow files
process = subprocess.Popen(
['su', username, '-c', 'true'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = process.communicate(input=password + '\n', timeout=10)
return process.returncode == 0
except subprocess.TimeoutExpired:
logger.error("Password verification timed out")
return False
except Exception as e:
logger.error("Error verifying password: {}".format(e))
return False
def change_password(self, username, new_password):
"""Change user password and signal the update event"""
try:
# First, change the password using passwd command
process = subprocess.Popen(
['passwd', username],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Send the new password twice (passwd asks for confirmation)
input_data = "{}\n{}\n".format(new_password, new_password)
stdout, stderr = process.communicate(input=input_data, timeout=30)
if process.returncode != 0:
logger.error("passwd command failed: {}".format(stderr))
return False, "Failed to change password: {}".format(stderr)
# Signal the password update event
signal_result = subprocess.run(
['/sbin/e-smith/signal-event', 'password-update', username],
capture_output=True,
text=True,
timeout=60
)
if signal_result.returncode != 0:
logger.error("signal-event failed: {}".format(signal_result.stderr))
return False, "Password changed but failed to update system: {}".format(signal_result.stderr)
logger.info("Password successfully changed for user: {}".format(username))
return True, "Password changed successfully"
except subprocess.TimeoutExpired:
logger.error("Password change operation timed out")
return False, "Password change operation timed out"
except FileNotFoundError as e:
logger.error("Required command not found: {}".format(e))
return False, "System command not available"
except Exception as e:
logger.error("Unexpected error changing password: {}".format(e))
return False, "Unexpected error: {}".format(str(e))
class SMESystemInfo:
"""Get SME Server system information"""
@staticmethod
def get_version():
"""Get SME Server version"""
try:
# Try to read version from release file
version_files = [
'/etc/e-smith-release',
'/etc/sme-release',
'/etc/redhat-release'
]
for version_file in version_files:
if os.path.exists(version_file):
with open(version_file, 'r') as f:
return f.read().strip()
return "SME Server (version unknown)"
except Exception:
return "SME Server (version unknown)"
@staticmethod
def get_copyright_info():
"""Get copyright information"""
return {
'mitel': 'Copyright 1999-2006 Mitel Corporation',
'rights': 'All rights reserved.',
'koozali': 'Copyright (C) 2013 - 2021 Koozali Foundation Inc.'
}

View File

@@ -0,0 +1,180 @@
/* SME Server Password Change Form Styles */
body {
font-family: Arial, sans-serif;
font-size: 12px;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
color: #333;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: white;
padding: 0;
}
h1 {
font-size: 18px;
font-weight: bold;
margin: 0 0 20px 0;
padding: 0;
color: #333;
}
.instructions {
margin-bottom: 20px;
line-height: 1.4;
}
.instructions p {
margin: 0 0 10px 0;
}
.instructions em {
font-style: italic;
}
/* Flash Messages */
.messages {
margin-bottom: 20px;
}
.message {
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
}
.message.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* Form Container - Green Background */
.form-container {
background-color: #c8e6c8;
padding: 20px;
border: 1px solid #a0d0a0;
margin-bottom: 20px;
}
.form-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.form-table tr {
height: 35px;
}
.label-cell {
text-align: right;
padding-right: 10px;
width: 200px;
vertical-align: middle;
font-weight: normal;
}
.input-cell {
text-align: left;
vertical-align: middle;
}
.form-table input[type="text"],
.form-table input[type="password"] {
width: 200px;
padding: 4px;
border: 1px solid #999;
font-size: 12px;
font-family: Arial, sans-serif;
}
.form-table input[type="text"]:focus,
.form-table input[type="password"]:focus {
outline: none;
border-color: #666;
}
.button-container {
text-align: right;
padding-top: 10px;
}
.submit-button {
background-color: #e0e0e0;
border: 2px outset #e0e0e0;
padding: 4px 12px;
font-size: 12px;
font-family: Arial, sans-serif;
cursor: pointer;
}
.submit-button:hover {
background-color: #d0d0d0;
}
.submit-button:active {
border: 2px inset #e0e0e0;
}
/* Footer */
.footer {
margin-top: 30px;
font-size: 10px;
color: #666;
line-height: 1.3;
}
.footer p {
margin: 2px 0;
}
/* Responsive Design */
@media (max-width: 600px) {
body {
padding: 10px;
}
.form-table {
width: 100%;
}
.label-cell {
width: auto;
text-align: left;
padding-right: 5px;
display: block;
padding-bottom: 5px;
}
.input-cell {
display: block;
padding-bottom: 15px;
}
.form-table input[type="text"],
.form-table input[type="password"] {
width: 100%;
max-width: 300px;
}
.form-table tr {
height: auto;
}
.form-table td {
display: block;
}
}

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Change account password - SME Server</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<div class="container">
<h1>Change account password</h1>
<div class="instructions">
<p>To change your account password, please fill out the following form. You will need to provide the name of your account, your old password, and your desired new password. (You must type the new password twice.)</p>
<p>If you cannot change your password because you have forgotten the old one, your local system administrator can reset your password using the <em>server manager</em>.</p>
</div>
<!-- Flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="messages">
{% for category, message in messages %}
<div class="message {{ category }}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form method="POST" class="password-form">
<div class="form-container">
<table class="form-table">
<tr>
<td class="label-cell">Your account:</td>
<td class="input-cell">
<input type="text" name="username" id="username"
value="{{ request.form.username if request.form.username else '' }}"
required>
</td>
</tr>
<tr>
<td class="label-cell">Old password:</td>
<td class="input-cell">
<input type="password" name="old_password" id="old_password" required>
</td>
</tr>
<tr>
<td class="label-cell">New password:</td>
<td class="input-cell">
<input type="password" name="new_password" id="new_password" required>
</td>
</tr>
<tr>
<td class="label-cell">New password (verify):</td>
<td class="input-cell">
<input type="password" name="verify_password" id="verify_password" required>
</td>
</tr>
</table>
<div class="button-container">
<input type="submit" value="Change Password" class="submit-button">
</div>
</div>
</form>
<div class="footer">
<p>{{ version if version else 'SME Server 11 (beta1)' }}</p>
<p>{{ copyright_info.mitel if copyright_info else 'Copyright 1999-2006 Mitel Corporation' }}</p>
<p>{{ copyright_info.rights if copyright_info else 'All rights reserved.' }}</p>
<p>{{ copyright_info.koozali if copyright_info else 'Copyright (C) 2013 - 2021 Koozali Foundation Inc.' }}</p>
</div>
</div>
<script>
// Simple client-side validation
document.querySelector('.password-form').addEventListener('submit', function(e) {
const newPassword = document.getElementById('new_password').value;
const verifyPassword = document.getElementById('verify_password').value;
if (newPassword !== verifyPassword) {
e.preventDefault();
alert('New password and verification do not match');
return false;
}
if (newPassword.length < 8) {
e.preventDefault();
alert('Password must be at least 8 characters long');
return false;
}
});
// Clear password fields on page load for security
window.addEventListener('load', function() {
document.getElementById('old_password').value = '';
document.getElementById('new_password').value = '';
document.getElementById('verify_password').value = '';
});
</script>
</body>
</html>