#!/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)