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,9 +1,10 @@
#!/usr/bin/env python3
"""
SME Server Utilities Module - Python 3.6.8 Compatible
Enhanced SME Server Utilities Module - Python 3.6.8 Compatible
This module provides utilities for interfacing with SME Server's
configuration database and system commands.
configuration database and system commands with enhanced password
strength validation.
Compatible with Python 3.6.8
"""
@@ -12,6 +13,7 @@ import subprocess
import os
import logging
import re
import hashlib
# Set up logging
logging.basicConfig(level=logging.INFO)
@@ -78,12 +80,168 @@ class SMEConfigDB:
"""Check if the account is a user account (not system account)"""
account_type = self.get_account_type(username)
return account_type == 'user'
def get_password_strength_setting(self):
"""Get the password strength setting from configuration database"""
success, output = self._run_db_command(['configuration', 'getprop', 'passwordstrength', 'Passwordstrength'])
if success and output:
strength = output.strip().lower()
if strength in ['none', 'normal', 'strong']:
return strength
# Default to 'normal' if not set or invalid
return 'normal'
def set_password_strength_setting(self, strength):
"""Set the password strength setting in configuration database"""
if strength.lower() not in ['none', 'normal', 'strong']:
return False, "Invalid strength level. Must be 'none', 'normal', or 'strong'"
success, output = self._run_db_command(['configuration', 'setprop', 'passwordstrength', 'Passwordstrength', strength.lower()])
return success, output
class PasswordStrengthValidator:
"""Enhanced password strength validation with configurable levels"""
def __init__(self):
# Common weak passwords and patterns for strong validation
self.common_passwords = {
'password', 'password123', '123456', '123456789', 'qwerty', 'abc123',
'password1', 'admin', 'administrator', 'root', 'user', 'guest',
'welcome', 'login', 'pass', 'secret', 'default', 'changeme',
'letmein', 'monkey', 'dragon', 'master', 'shadow', 'superman',
'michael', 'jennifer', 'jordan', 'michelle', 'daniel', 'andrew'
}
self.common_patterns = [
r'^(.)\1+$', # All same character
r'^\d+$', # All numbers
r'^[a-z]+$', # All lowercase
r'^[A-Z]+$', # All uppercase
r'^(abc|123|qwe|asd|zxc)', # Common sequences
r'(password|admin|user|guest|login)', # Common words
]
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 = bool(re.search(r'[A-Z]', password))
has_lower = bool(re.search(r'[a-z]', password))
has_numeric = bool(re.search(r'\d', password))
has_non_alpha = bool(re.search(r'[^a-zA-Z0-9]', 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 common patterns
for pattern in self.common_patterns:
if re.search(pattern, password.lower()):
errors.append("Password contains common patterns that are easily guessable")
break
# Check for keyboard patterns
keyboard_patterns = [
'qwertyuiop', 'asdfghjkl', 'zxcvbnm',
'1234567890', '0987654321',
'qwerty', 'asdfgh', 'zxcvbn'
]
password_lower = password.lower()
for pattern in keyboard_patterns:
if pattern in password_lower or pattern[::-1] in password_lower:
errors.append("Password contains keyboard patterns that are easily guessable")
break
# Check for repeated sequences
if self._has_repeated_sequences(password):
errors.append("Password contains repeated sequences that reduce security")
# Check for dictionary words (basic check)
if self._contains_dictionary_words(password):
errors.append("Password contains common dictionary words")
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
def _contains_dictionary_words(self, password):
"""Basic check for common dictionary words"""
common_words = [
'password', 'admin', 'user', 'login', 'welcome', 'secret',
'computer', 'internet', 'security', 'system', 'network',
'server', 'database', 'application', 'software', 'hardware'
]
password_lower = password.lower()
for word in common_words:
if len(word) >= 4 and word in password_lower:
return True
return False
class SMEPasswordManager:
"""Handle password operations for SME Server"""
"""Handle password operations for SME Server with enhanced validation"""
def __init__(self):
self.config_db = SMEConfigDB()
self.strength_validator = PasswordStrengthValidator()
def validate_username(self, username):
"""Validate username format and existence"""
@@ -105,31 +263,35 @@ class SMEPasswordManager:
return True, "Username is valid"
def validate_password_strength(self, password):
"""Validate password meets SME Server requirements"""
errors = []
"""Validate password meets configured SME Server requirements"""
# Get current password strength setting
strength_level = self.config_db.get_password_strength_setting()
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
# Use the enhanced validator
errors = self.strength_validator.validate_password_strength(password, strength_level)
return errors
def get_password_strength_info(self):
"""Get current password strength setting and requirements"""
strength_level = self.config_db.get_password_strength_setting()
requirements = {
'level': strength_level,
'description': self._get_strength_description(strength_level)
}
return requirements
def _get_strength_description(self, strength_level):
"""Get human-readable description of strength requirements"""
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 descriptions.get(strength_level, 'Unknown strength level')
def verify_current_password(self, username, password):
"""Verify the current password for a user using system authentication"""
try: