Files
StandalonePasswordChange/python-flask/smeserver-password-app/app.py

262 lines
9.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Enhanced SME Server Password Change Application - Python 3.6.8 Compatible
A Flask web application for changing user passwords on SME Server,
with configurable password strength validation and password visibility toggles.
Features:
- Configurable password strength (None/Normal/Strong)
- Password visibility toggles
- Enhanced validation with crypto testing
- Real-time password strength feedback
Compatible with Python 3.6.8 and Flask 2.0.3
"""
import os
from flask import Flask, render_template, request, flash, redirect, url_for, jsonify
from flask_cors import CORS
from smeserver_utils import SMEPasswordManager, SMESystemInfo, SMEConfigDB
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'sme-server-password-change-enhanced-key')
CORS(app)
# Initialize SME Server utilities
password_manager = SMEPasswordManager()
system_info = SMESystemInfo()
config_db = SMEConfigDB()
@app.route('/', methods=['GET', 'POST'])
def password_change():
"""Main password change form handler with enhanced validation"""
# Get current password requirements for display
password_requirements = password_manager.get_password_strength_info()
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")
# Enhanced password strength validation
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,
password_requirements=password_requirements)
@app.route('/api/password-strength', methods=['POST'])
def check_password_strength():
"""API endpoint for real-time password strength checking"""
try:
data = request.get_json()
password = data.get('password', '')
if not password:
return jsonify({'errors': ['Password cannot be empty']})
# Get validation errors
errors = password_manager.validate_password_strength(password)
# Calculate strength score
strength_score = 0
max_score = 5
if len(password) >= 12:
strength_score += 1
if any(c.isupper() for c in password):
strength_score += 1
if any(c.islower() for c in password):
strength_score += 1
if any(c.isdigit() for c in password):
strength_score += 1
if any(not c.isalnum() for c in password):
strength_score += 1
strength_levels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong']
strength_level = strength_levels[min(strength_score, len(strength_levels) - 1)]
return jsonify({
'errors': errors,
'strength_score': strength_score,
'max_score': max_score,
'strength_level': strength_level,
'is_valid': len(errors) == 0
})
except Exception as e:
return jsonify({'error': 'Failed to check password strength'}), 500
@app.route('/api/password-config', methods=['GET', 'POST'])
def password_config():
"""API endpoint for managing password strength configuration"""
if request.method == 'GET':
# Get current configuration
try:
strength_setting = config_db.get_password_strength_setting()
requirements = password_manager.get_password_strength_info()
return jsonify({
'current_setting': strength_setting,
'requirements': requirements,
'available_levels': {
'none': 'No specific password requirements',
'normal': 'Minimum 12 characters with complexity requirements',
'strong': 'Normal requirements plus protection against common passwords'
}
})
except Exception as e:
return jsonify({'error': 'Failed to get password configuration'}), 500
elif request.method == 'POST':
# Update configuration (admin only)
try:
data = request.get_json()
new_strength = data.get('strength', '').lower()
if new_strength not in ['none', 'normal', 'strong']:
return jsonify({'error': 'Invalid strength level'}), 400
success, message = config_db.set_password_strength_setting(new_strength)
if success:
return jsonify({
'success': True,
'message': 'Password strength setting updated to: {}'.format(new_strength),
'new_setting': new_strength
})
else:
return jsonify({'error': message}), 500
except Exception as e:
return jsonify({'error': 'Failed to update password configuration'}), 500
@app.route('/admin')
def admin_panel():
"""Simple admin panel for password strength configuration"""
try:
current_setting = config_db.get_password_strength_setting()
requirements = password_manager.get_password_strength_info()
return render_template('admin_panel.html',
current_setting=current_setting,
requirements=requirements,
version=system_info.get_version())
except Exception as e:
flash('Error loading admin panel: {}'.format(str(e)), 'error')
return redirect(url_for('password_change'))
@app.route('/health')
def health_check():
"""Health check endpoint"""
try:
# Test database connectivity
strength_setting = config_db.get_password_strength_setting()
return jsonify({
'status': 'healthy',
'service': 'sme-server-password-change-enhanced',
'password_strength_setting': strength_setting,
'features': ['configurable_strength', 'password_visibility', 'crypto_validation']
})
except Exception as e:
return jsonify({
'status': 'unhealthy',
'error': str(e)
}), 500
@app.errorhandler(404)
def not_found(error):
"""Handle 404 errors"""
password_requirements = password_manager.get_password_strength_info()
return render_template('password_change.html',
error="Page not found",
password_requirements=password_requirements), 404
@app.errorhandler(500)
def internal_error(error):
"""Handle 500 errors"""
password_requirements = password_manager.get_password_strength_info()
return render_template('password_change.html',
error="Internal server error",
password_requirements=password_requirements), 500
if __name__ == '__main__':
print("Starting Enhanced SME Server Password Change Application")
print("Features:")
print(" - Configurable password strength validation")
print(" - Password visibility toggles")
print(" - Real-time strength checking")
print(" - Crypto validation for strong passwords")
print("")
try:
current_strength = config_db.get_password_strength_setting()
print("Current password strength setting: {}".format(current_strength.upper()))
except:
print("Password strength setting: NORMAL (default)")
print("")
print("Access the application at: http://localhost:5000")
print("Admin panel at: http://localhost:5000/admin")
print("API endpoints:")
print(" - GET/POST /api/password-config")
print(" - POST /api/password-strength")
# Run the application
app.run(host='0.0.0.0', port=5000, debug=True)