Add in proper passord check lib, show results in form
This commit is contained in:
@@ -7,32 +7,46 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<style>
|
||||
.admin-panel {
|
||||
max-width: 600px;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.config-option {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
.account-type-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
}
|
||||
.account-type-title {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
.config-option {
|
||||
margin: 10px 0;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.config-option.active {
|
||||
background-color: #e8f4f8;
|
||||
border-color: #007bff;
|
||||
}
|
||||
.config-option label {
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
.config-description {
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
color: #666;
|
||||
margin-top: 5px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.update-button {
|
||||
background-color: #007bff;
|
||||
@@ -42,6 +56,7 @@
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 15px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.update-button:hover {
|
||||
background-color: #0056b3;
|
||||
@@ -51,10 +66,26 @@
|
||||
margin-bottom: 20px;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.zxcvbn-info {
|
||||
background-color: #e8f5e8;
|
||||
border: 1px solid #4caf50;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.current-settings {
|
||||
background-color: #f0f8ff;
|
||||
border: 1px solid #87ceeb;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -64,96 +95,198 @@
|
||||
<h1>Password Strength Configuration</h1>
|
||||
|
||||
<div class="admin-panel">
|
||||
<p><strong>Current Setting:</strong> {{ current_setting|title }}</p>
|
||||
<p><strong>Description:</strong> {{ requirements.description }}</p>
|
||||
{% if using_zxcvbn %}
|
||||
<div class="zxcvbn-info">
|
||||
✓ Using zxcvbn library for advanced password validation
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="zxcvbn-info" style="background-color: #fff3cd; border-color: #ffc107;">
|
||||
⚠ zxcvbn library not available - using basic validation
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form id="config-form">
|
||||
<div class="config-option {{ 'active' if current_setting == 'none' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="none" {{ 'checked' if current_setting == 'none' else '' }}>
|
||||
None
|
||||
</label>
|
||||
<div class="config-description">
|
||||
No specific password requirements. Only basic validation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_setting == 'normal' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="normal" {{ 'checked' if current_setting == 'normal' else '' }}>
|
||||
Normal
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Minimum 12 characters with at least one uppercase letter, lowercase letter, number, and special character.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_setting == 'strong' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="strong" {{ 'checked' if current_setting == 'strong' else '' }}>
|
||||
Strong
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Normal requirements plus protection against common passwords, keyboard patterns, and dictionary words.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="update-button">Update Password Strength Setting</button>
|
||||
</form>
|
||||
<div class="current-settings">
|
||||
<strong>Current Settings:</strong><br>
|
||||
Users: {{ current_settings.Users|title }}<br>
|
||||
Admin: {{ current_settings.Admin|title }}<br>
|
||||
Ibays: {{ current_settings.Ibays|title }}
|
||||
</div>
|
||||
|
||||
<div id="status-message" style="margin-top: 15px;"></div>
|
||||
<!-- Users Configuration -->
|
||||
<div class="account-type-section">
|
||||
<div class="account-type-title">👤 Users Password Strength</div>
|
||||
<form class="config-form" data-account-type="Users">
|
||||
<div class="config-option {{ 'active' if current_settings.Users == 'none' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="none" {{ 'checked' if current_settings.Users == 'none' else '' }}>
|
||||
None
|
||||
</label>
|
||||
<div class="config-description">
|
||||
No specific password requirements. Only basic validation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_settings.Users == 'normal' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="normal" {{ 'checked' if current_settings.Users == 'normal' else '' }}>
|
||||
Normal
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Minimum 12 characters with at least one uppercase letter, lowercase letter, number, and special character.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_settings.Users == 'strong' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="strong" {{ 'checked' if current_settings.Users == 'strong' else '' }}>
|
||||
Strong
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Normal requirements plus {{ 'zxcvbn advanced validation against common passwords, patterns, and dictionary attacks' if using_zxcvbn else 'basic protection against common passwords and keyboard patterns' }}.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="update-button">Update Users Password Strength</button>
|
||||
<div class="status-message"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Admin Configuration -->
|
||||
<div class="account-type-section">
|
||||
<div class="account-type-title">👑 Admin Password Strength</div>
|
||||
<form class="config-form" data-account-type="Admin">
|
||||
<div class="config-option {{ 'active' if current_settings.Admin == 'none' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="none" {{ 'checked' if current_settings.Admin == 'none' else '' }}>
|
||||
None
|
||||
</label>
|
||||
<div class="config-description">
|
||||
No specific password requirements. Only basic validation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_settings.Admin == 'normal' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="normal" {{ 'checked' if current_settings.Admin == 'normal' else '' }}>
|
||||
Normal
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Minimum 12 characters with complexity requirements.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_settings.Admin == 'strong' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="strong" {{ 'checked' if current_settings.Admin == 'strong' else '' }}>
|
||||
Strong
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Normal requirements plus advanced validation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="update-button">Update Admin Password Strength</button>
|
||||
<div class="status-message"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Ibays Configuration -->
|
||||
<div class="account-type-section">
|
||||
<div class="account-type-title">📁 Ibays Password Strength</div>
|
||||
<form class="config-form" data-account-type="Ibays">
|
||||
<div class="config-option {{ 'active' if current_settings.Ibays == 'none' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="none" {{ 'checked' if current_settings.Ibays == 'none' else '' }}>
|
||||
None
|
||||
</label>
|
||||
<div class="config-description">
|
||||
No specific password requirements. Only basic validation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_settings.Ibays == 'normal' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="normal" {{ 'checked' if current_settings.Ibays == 'normal' else '' }}>
|
||||
Normal
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Minimum 12 characters with complexity requirements.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-option {{ 'active' if current_settings.Ibays == 'strong' else '' }}">
|
||||
<label>
|
||||
<input type="radio" name="strength" value="strong" {{ 'checked' if current_settings.Ibays == 'strong' else '' }}>
|
||||
Strong
|
||||
</label>
|
||||
<div class="config-description">
|
||||
Normal requirements plus advanced validation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="update-button">Update Ibays Password Strength</button>
|
||||
<div class="status-message"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>{{ version if version else 'SME Server 11 (beta1)' }} - Admin Panel</p>
|
||||
<p>{{ version if version else 'SME Server 11 (beta1)' }} - Admin Panel (Corrected DB Structure)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Handle form submission
|
||||
document.getElementById('config-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const strength = formData.get('strength');
|
||||
const statusMessage = document.getElementById('status-message');
|
||||
|
||||
// Update visual selection
|
||||
document.querySelectorAll('.config-option').forEach(option => {
|
||||
option.classList.remove('active');
|
||||
});
|
||||
|
||||
const selectedOption = document.querySelector('input[name="strength"]:checked').closest('.config-option');
|
||||
selectedOption.classList.add('active');
|
||||
|
||||
// Send update request
|
||||
fetch('/api/password-config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ strength: strength })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
statusMessage.innerHTML = '<div style="color: green; font-weight: bold;">✓ ' + data.message + '</div>';
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
statusMessage.innerHTML = '<div style="color: red; font-weight: bold;">✗ Error: ' + (data.error || 'Unknown error') + '</div>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
statusMessage.innerHTML = '<div style="color: red; font-weight: bold;">✗ Network error: ' + error.message + '</div>';
|
||||
// Handle form submissions for all account types
|
||||
document.querySelectorAll('.config-form').forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const strength = formData.get('strength');
|
||||
const accountType = this.dataset.accountType;
|
||||
const statusMessage = this.querySelector('.status-message');
|
||||
|
||||
// Update visual selection
|
||||
this.querySelectorAll('.config-option').forEach(option => {
|
||||
option.classList.remove('active');
|
||||
});
|
||||
|
||||
const selectedOption = this.querySelector('input[name="strength"]:checked').closest('.config-option');
|
||||
selectedOption.classList.add('active');
|
||||
|
||||
// Send update request
|
||||
fetch('/api/password-config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
strength: strength,
|
||||
account_type: accountType
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
statusMessage.innerHTML = '<div style="color: green; font-weight: bold; font-size: 10px;">✓ ' + data.message + '</div>';
|
||||
setTimeout(() => {
|
||||
statusMessage.innerHTML = '';
|
||||
}, 3000);
|
||||
} else {
|
||||
statusMessage.innerHTML = '<div style="color: red; font-weight: bold; font-size: 10px;">✗ Error: ' + (data.error || 'Unknown error') + '</div>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
statusMessage.innerHTML = '<div style="color: red; font-weight: bold; font-size: 10px;">✗ Network error: ' + error.message + '</div>';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Handle radio button changes for visual feedback
|
||||
document.querySelectorAll('input[name="strength"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
document.querySelectorAll('.config-option').forEach(option => {
|
||||
const form = this.closest('.config-form');
|
||||
form.querySelectorAll('.config-option').forEach(option => {
|
||||
option.classList.remove('active');
|
||||
});
|
||||
this.closest('.config-option').classList.add('active');
|
||||
|
@@ -134,38 +134,49 @@
|
||||
});
|
||||
|
||||
function updatePasswordStrengthIndicator(password) {
|
||||
// Simple client-side strength indicator
|
||||
let strength = 0;
|
||||
let feedback = [];
|
||||
|
||||
if (password.length >= 12) strength++;
|
||||
else feedback.push('At least 12 characters');
|
||||
|
||||
if (/[A-Z]/.test(password)) strength++;
|
||||
else feedback.push('Uppercase letter');
|
||||
|
||||
if (/[a-z]/.test(password)) strength++;
|
||||
else feedback.push('Lowercase letter');
|
||||
|
||||
if (/\d/.test(password)) strength++;
|
||||
else feedback.push('Number');
|
||||
|
||||
if (/[^a-zA-Z0-9]/.test(password)) strength++;
|
||||
else feedback.push('Special character');
|
||||
|
||||
// Update visual indicator if it exists
|
||||
const indicator = document.getElementById('password-strength-indicator');
|
||||
if (indicator) {
|
||||
if (!indicator) return;
|
||||
|
||||
if (password.length === 0) {
|
||||
indicator.textContent = 'Enter password to see strength';
|
||||
indicator.style.color = '#888';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/password-strength', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ password: password, username: document.getElementById('username').value }),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
indicator.textContent = 'Error checking strength';
|
||||
indicator.style.color = '#ff4444';
|
||||
return;
|
||||
}
|
||||
|
||||
const strengthLevels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong'];
|
||||
const strengthColors = ['#ff4444', '#ff8800', '#ffaa00', '#88aa00', '#44aa44'];
|
||||
|
||||
indicator.textContent = strengthLevels[strength] || 'Very Weak';
|
||||
indicator.style.color = strengthColors[strength] || '#ff4444';
|
||||
|
||||
if (feedback.length > 0) {
|
||||
indicator.textContent += ' (Missing: ' + feedback.join(', ') + ')';
|
||||
|
||||
let displayLevel = data.strength_level;
|
||||
let displayColor = strengthColors[strengthLevels.indexOf(data.strength_level)];
|
||||
|
||||
if (data.errors && data.errors.length > 0) {
|
||||
displayLevel += ' (Missing: ' + data.errors.join(', ') + ')';
|
||||
displayColor = '#ff4444'; // Indicate error with red color
|
||||
}
|
||||
}
|
||||
|
||||
indicator.textContent = displayLevel;
|
||||
indicator.style.color = displayColor;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
indicator.textContent = 'Network Error';
|
||||
indicator.style.color = '#ff4444';
|
||||
});
|
||||
}
|
||||
|
||||
// Clear password fields on page load for security
|
||||
|
Reference in New Issue
Block a user