Add in 12 character threshold and crypto checking

This commit is contained in:
2025-07-20 10:13:38 +01:00
parent a6cc1b40ee
commit 0127326b77
11 changed files with 1269 additions and 180 deletions

View File

@@ -1,19 +1,19 @@
#!/usr/bin/env python3
"""
Demo mode for SME Server Password Change Application - Python 3.6.8 Compatible
Enhanced 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.
This version simulates SME Server functionality with enhanced password validation
and configurable strength levels for testing purposes.
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 import Flask, render_template, request, flash, redirect, url_for, jsonify
from flask_cors import CORS
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'sme-server-password-change-demo-key')
app.secret_key = os.environ.get('SECRET_KEY', 'sme-server-password-change-enhanced-demo-key')
CORS(app)
# Demo users for testing
@@ -23,11 +23,110 @@ DEMO_USERS = {
'john': 'johnpass789'
}
class DemoPasswordManager:
"""Demo password manager for testing"""
# Demo password strength setting (can be changed via admin panel)
DEMO_PASSWORD_STRENGTH = 'normal'
class DemoPasswordStrengthValidator:
"""Demo password strength validator with configurable levels"""
@staticmethod
def validate_username(username):
def __init__(self):
# Common weak passwords for strong validation
self.common_passwords = {
'password', 'password123', '123456', '123456789', 'qwerty', 'abc123',
'password1', 'admin', 'administrator', 'root', 'user', 'guest'
}
def validate_password_strength(self, password, strength_level='normal'):
"""Validate password based on configured strength level"""
errors = []
if strength_level == 'none':
# No validation - only basic length check
if len(password) < 1:
errors.append("Password cannot be empty")
return errors
elif strength_level == 'normal':
# Normal validation: 12+ chars, complexity requirements
errors.extend(self._validate_normal_strength(password))
elif strength_level == 'strong':
# Strong validation: Normal + crypto testing
errors.extend(self._validate_normal_strength(password))
errors.extend(self._validate_strong_crypto(password))
return errors
def _validate_normal_strength(self, password):
"""Validate normal strength requirements"""
errors = []
# Minimum 12 characters
if len(password) < 12:
errors.append("Password must be at least 12 characters long")
# Maximum length check
if len(password) > 127:
errors.append("Password must be no more than 127 characters long")
# Character type requirements
has_upper = any(c.isupper() for c in password)
has_lower = any(c.islower() for c in password)
has_numeric = any(c.isdigit() for c in password)
has_non_alpha = any(not c.isalnum() for c in password)
missing_types = []
if not has_upper:
missing_types.append("uppercase letter")
if not has_lower:
missing_types.append("lowercase letter")
if not has_numeric:
missing_types.append("number")
if not has_non_alpha:
missing_types.append("special character")
if missing_types:
errors.append("Password must contain at least one: {}".format(", ".join(missing_types)))
return errors
def _validate_strong_crypto(self, password):
"""Validate strong crypto requirements"""
errors = []
# Check against common passwords
if password.lower() in self.common_passwords:
errors.append("Password is too common and easily guessable")
# Check for repeated sequences
if self._has_repeated_sequences(password):
errors.append("Password contains repeated sequences that reduce security")
# Check for keyboard patterns
keyboard_patterns = ['qwerty', 'asdfgh', 'zxcvbn', '123456']
password_lower = password.lower()
for pattern in keyboard_patterns:
if pattern in password_lower:
errors.append("Password contains keyboard patterns that are easily guessable")
break
return errors
def _has_repeated_sequences(self, password):
"""Check for repeated character sequences"""
for i in range(len(password) - 2):
sequence = password[i:i+3]
if password.count(sequence) > 1:
return True
return False
class DemoPasswordManager:
"""Demo password manager with enhanced validation"""
def __init__(self):
self.strength_validator = DemoPasswordStrengthValidator()
def validate_username(self, username):
"""Validate username in demo mode"""
if not username:
return False, "Username cannot be empty"
@@ -37,33 +136,31 @@ class DemoPasswordManager:
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
def validate_password_strength(self, password):
"""Validate password strength using current setting"""
global DEMO_PASSWORD_STRENGTH
return self.strength_validator.validate_password_strength(password, DEMO_PASSWORD_STRENGTH)
@staticmethod
def verify_current_password(username, password):
def get_password_strength_info(self):
"""Get current password strength setting and requirements"""
global DEMO_PASSWORD_STRENGTH
descriptions = {
'none': 'No specific password requirements',
'normal': 'Minimum 12 characters with uppercase, lowercase, number, and special character',
'strong': 'Normal requirements plus protection against common passwords and patterns'
}
return {
'level': DEMO_PASSWORD_STRENGTH,
'description': descriptions.get(DEMO_PASSWORD_STRENGTH, 'Unknown strength level')
}
def verify_current_password(self, username, password):
"""Verify current password in demo mode"""
return DEMO_USERS.get(username) == password
@staticmethod
def change_password(username, new_password):
def change_password(self, username, new_password):
"""Change password in demo mode"""
try:
# Simulate password change
@@ -72,12 +169,29 @@ class DemoPasswordManager:
except Exception as e:
return False, "Error changing password: {}".format(str(e))
class DemoConfigDB:
"""Demo configuration database"""
def get_password_strength_setting(self):
"""Get password strength setting"""
global DEMO_PASSWORD_STRENGTH
return DEMO_PASSWORD_STRENGTH
def set_password_strength_setting(self, strength):
"""Set password strength setting"""
global DEMO_PASSWORD_STRENGTH
if strength.lower() not in ['none', 'normal', 'strong']:
return False, "Invalid strength level"
DEMO_PASSWORD_STRENGTH = strength.lower()
return True, "Password strength setting updated"
class DemoSystemInfo:
"""Demo system info for testing"""
@staticmethod
def get_version():
return "SME Server 11 (beta1) - Demo Mode"
return "SME Server 11 (beta1) - Enhanced Demo Mode"
@staticmethod
def get_copyright_info():
@@ -90,10 +204,14 @@ class DemoSystemInfo:
# Initialize demo utilities
password_manager = DemoPasswordManager()
system_info = DemoSystemInfo()
config_db = DemoConfigDB()
@app.route('/', methods=['GET', 'POST'])
def password_change():
"""Main password change form handler"""
"""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
@@ -131,7 +249,7 @@ def password_change():
if not password_manager.verify_current_password(username, old_password):
errors.append("Current password is incorrect")
# Validate new password strength
# Enhanced password strength validation
if new_password:
strength_errors = password_manager.validate_password_strength(new_password)
errors.extend(strength_errors)
@@ -155,29 +273,144 @@ def password_change():
return render_template('password_change.html',
version=version,
copyright_info=copyright_info)
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
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': 5,
'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
try:
data = request.get_json()
new_strength = data.get('strength', '').lower()
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('/demo-info')
def demo_info():
"""Demo information page"""
return {
'mode': 'demo',
global DEMO_PASSWORD_STRENGTH
return jsonify({
'mode': 'enhanced_demo',
'demo_users': list(DEMO_USERS.keys()),
'current_password_strength': DEMO_PASSWORD_STRENGTH,
'features': ['configurable_strength', 'password_visibility', 'crypto_validation'],
'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'}
return jsonify({
'status': 'healthy',
'service': 'sme-server-password-change-enhanced-demo',
'password_strength_setting': DEMO_PASSWORD_STRENGTH,
'features': ['configurable_strength', 'password_visibility', 'crypto_validation']
})
if __name__ == '__main__':
print("Starting SME Server Password Change Application in Demo Mode")
print("Starting Enhanced SME Server Password Change Application in Demo Mode")
print("Features:")
print(" - Configurable password strength validation")
print(" - Password visibility toggles")
print(" - Real-time strength checking")
print(" - Crypto validation for strong passwords")
print("")
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")
print("")
print("Current password strength setting: {}".format(DEMO_PASSWORD_STRENGTH.upper()))
print("")
print("Access the application at: http://localhost:5002")
print("Admin panel at: http://localhost:5002/admin")
print("Demo info at: http://localhost:5002/demo-info")
app.run(host='0.0.0.0', port=5001, debug=False)
app.run(host='0.0.0.0', port=5002, debug=False)