added password validation and a hint to also use the correct password and format
This commit is contained in:
parent
59e9975b22
commit
d350130dba
@ -68,6 +68,8 @@ Der komplette Ablauf ist im [Gitea-Workflow](https://git.eckertplayground.de/taa
|
|||||||
| Powershell Script für einzelne Benutzer und CSV Import | Alle Fisis |
|
| Powershell Script für einzelne Benutzer und CSV Import | Alle Fisis |
|
||||||
| UI/UX anpassen | Yasin B (@Muchentuchen), Alexander M (@Alexander), Torsten J (@tojacobs) |
|
| UI/UX anpassen | Yasin B (@Muchentuchen), Alexander M (@Alexander), Torsten J (@tojacobs) |
|
||||||
|
|
||||||
|
**Hinweis:** Die Passwortanforderungen (Mindestlänge, Kategorien, keine Teile des Benutzernamens) werden beim Erstellen validiert. Die Validierung ist in `scripts/powershell/create_users_csv.ps1` implementiert und die Mockup-UI (`docs/Mockup/index.html`) zeigt die Anforderungen und prüft sie clientseitig.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Dokumentation
|
## Dokumentation
|
||||||
|
|||||||
@ -262,6 +262,10 @@
|
|||||||
|
|
||||||
background-color: var(--color-secondary);
|
background-color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Password hint and invalid input styles */
|
||||||
|
.password-hint { display:block; margin-top: 6px; font-size: 0.9rem; color: #6c757d; }
|
||||||
|
.invalid { border: 1px solid #c0152f; background-color: rgba(192,21,47,0.04); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -280,6 +284,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password">Passwort</label>
|
<label for="password">Passwort</label>
|
||||||
<input type="password" id="password" name="password" required>
|
<input type="password" id="password" name="password" required>
|
||||||
|
<small class="text-muted password-hint">Das Passwort muss mindestens 7 Zeichen lang sein, darf keine größeren Teile des Benutzernamens enthalten und muss Zeichen aus mindestens 3 von 4 Kategorien enthalten: Großbuchstaben, Kleinbuchstaben, Ziffern, Sonderzeichen.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -377,14 +382,60 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validatePasswordJS(password, sam) {
|
||||||
|
const errors = [];
|
||||||
|
if (!password || password.length < 7) {
|
||||||
|
errors.push('Passwort muss mindestens 7 Zeichen lang sein.');
|
||||||
|
}
|
||||||
|
let categories = 0;
|
||||||
|
if (/[A-Z]/.test(password)) categories++;
|
||||||
|
if (/[a-z]/.test(password)) categories++;
|
||||||
|
if (/\d/.test(password)) categories++;
|
||||||
|
if (/[^A-Za-z0-9]/.test(password)) categories++;
|
||||||
|
if (categories < 3) {
|
||||||
|
errors.push('Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten.');
|
||||||
|
}
|
||||||
|
if (sam) {
|
||||||
|
const pwLower = password.toLowerCase();
|
||||||
|
const samLower = sam.toLowerCase();
|
||||||
|
if (pwLower.includes(samLower)) {
|
||||||
|
errors.push('Passwort darf den Benutzernamen nicht enthalten.');
|
||||||
|
} else {
|
||||||
|
const minLen = 4;
|
||||||
|
if (samLower.length >= minLen) {
|
||||||
|
outer: for (let len = minLen; len <= samLower.length; len++) {
|
||||||
|
for (let s = 0; s <= samLower.length - len; s++) {
|
||||||
|
const sub = samLower.substr(s, len);
|
||||||
|
if (pwLower.includes(sub)) {
|
||||||
|
errors.push('Passwort darf keine größeren Teile des Benutzernamens enthalten.');
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('adForm').addEventListener('submit', (e) => {
|
document.getElementById('adForm').addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
const firstname = document.getElementById('firstname').value.trim();
|
||||||
|
const lastname = document.getElementById('lastname').value.trim();
|
||||||
|
const samGuess = (firstname + lastname).replace(/\s+/g, '');
|
||||||
|
const password = document.getElementById('password').value;
|
||||||
|
const pwErrors = validatePasswordJS(password, samGuess);
|
||||||
|
if (pwErrors.length > 0) {
|
||||||
|
alert('Passwort ungültig:\n' + pwErrors.join('\n'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
firstname: document.getElementById('firstname').value,
|
firstname: firstname,
|
||||||
lastname: document.getElementById('lastname').value,
|
lastname: lastname,
|
||||||
group: document.getElementById('group').value,
|
group: document.getElementById('group').value,
|
||||||
password: document.getElementById('password').value,
|
password: password,
|
||||||
customFields: {}
|
customFields: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -442,6 +493,9 @@
|
|||||||
|
|
||||||
// Datenzeilen erstellen (mit editierbaren Inputs)
|
// Datenzeilen erstellen (mit editierbaren Inputs)
|
||||||
tableBody.innerHTML = '';
|
tableBody.innerHTML = '';
|
||||||
|
const pwdHeaderIndex = headers.findIndex(h => /pass(word)?/i.test(h));
|
||||||
|
const samHeaderIndex = headers.findIndex(h => /(sam(accountname)?)|samaccountname/i.test(h));
|
||||||
|
let foundInvalid = false;
|
||||||
for (let i = 1; i < csvData.length; i++) {
|
for (let i = 1; i < csvData.length; i++) {
|
||||||
const row = csvData[i];
|
const row = csvData[i];
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
@ -454,6 +508,10 @@
|
|||||||
input.dataset.col = index;
|
input.dataset.col = index;
|
||||||
input.addEventListener('input', (e) => {
|
input.addEventListener('input', (e) => {
|
||||||
csvData[i][index] = e.target.value;
|
csvData[i][index] = e.target.value;
|
||||||
|
// live re-validate if this is password or sam column
|
||||||
|
if (index === pwdHeaderIndex || index === samHeaderIndex) {
|
||||||
|
validateCsvPasswords(headers);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
td.appendChild(input);
|
td.appendChild(input);
|
||||||
tr.appendChild(td);
|
tr.appendChild(td);
|
||||||
@ -461,10 +519,53 @@
|
|||||||
tableBody.appendChild(tr);
|
tableBody.appendChild(tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate password column if present
|
||||||
|
function validateCsvPasswords(headersLocal) {
|
||||||
|
const pwdIdx = pwdHeaderIndex;
|
||||||
|
const samIdx = samHeaderIndex;
|
||||||
|
foundInvalid = false;
|
||||||
|
// Clear previous highlights/messages
|
||||||
|
const rows = tableBody.querySelectorAll('tr');
|
||||||
|
rows.forEach((r, idx) => {
|
||||||
|
r.querySelectorAll('input').forEach(inp => inp.classList.remove('invalid'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pwdIdx >= 0) {
|
||||||
|
for (let r = 0; r < rows.length; r++) {
|
||||||
|
const inputs = rows[r].querySelectorAll('input');
|
||||||
|
const pwdVal = inputs[pwdIdx] ? inputs[pwdIdx].value : '';
|
||||||
|
const samVal = (samIdx >= 0 && inputs[samIdx]) ? inputs[samIdx].value : '';
|
||||||
|
const errs = validatePasswordJS(pwdVal, samVal);
|
||||||
|
if (errs.length > 0) {
|
||||||
|
foundInvalid = true;
|
||||||
|
if (inputs[pwdIdx]) inputs[pwdIdx].classList.add('invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const previewInfo = previewDiv.querySelector('.preview-info');
|
||||||
|
if (!previewInfo) return;
|
||||||
|
if (foundInvalid) {
|
||||||
|
previewInfo.innerHTML = '<strong>Hinweis:</strong> Einige Passwörter entsprechen nicht den Anforderungen. Bitte korrigieren Sie diese in der Tabelle bevor Sie importieren.';
|
||||||
|
} else {
|
||||||
|
previewInfo.innerHTML = '<strong>Hinweis:</strong> Sie können die Werte direkt in der Tabelle bearbeiten, bevor Sie importieren.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial validation run
|
||||||
|
validateCsvPasswords(headers);
|
||||||
|
|
||||||
previewDiv.style.display = 'block';
|
previewDiv.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
function importCSVData() {
|
function importCSVData() {
|
||||||
|
// Prevent import if any password entries are invalid (highlighted)
|
||||||
|
const invalids = document.querySelectorAll('#csvTableBody input.invalid');
|
||||||
|
if (invalids.length > 0) {
|
||||||
|
alert('Import abgebrochen: Es gibt ungültige Passwörter in der CSV-Vorschau. Bitte korrigieren Sie diese zuerst.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Importierte CSV-Daten:', csvData);
|
console.log('Importierte CSV-Daten:', csvData);
|
||||||
alert(`${csvData.length - 1} Benutzer erfolgreich importiert!\n\nDaten in der Konsole (F12) einsehen.`);
|
alert(`${csvData.length - 1} Benutzer erfolgreich importiert!\n\nDaten in der Konsole (F12) einsehen.`);
|
||||||
cancelCSVPreview();
|
cancelCSVPreview();
|
||||||
|
|||||||
@ -33,6 +33,50 @@ if ($sam === '' || $pass === '') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server-side password validation (same rules as CSV script)
|
||||||
|
$passwordHint = "Das Passwort muss mindestens 7 Zeichen lang sein, darf keine größeren Teile des Benutzernamens enthalten und muss Zeichen aus mindestens 3 von 4 Kategorien enthalten: Großbuchstaben, Kleinbuchstaben, Ziffern, Sonderzeichen.";
|
||||||
|
function validate_password_php(string $password, string $sam): array {
|
||||||
|
$errors = [];
|
||||||
|
if ($password === '' || mb_strlen($password) < 7) {
|
||||||
|
$errors[] = 'Passwort muss mindestens 7 Zeichen lang sein.';
|
||||||
|
}
|
||||||
|
$categories = 0;
|
||||||
|
if (preg_match('/[A-Z]/u', $password)) $categories++;
|
||||||
|
if (preg_match('/[a-z]/u', $password)) $categories++;
|
||||||
|
if (preg_match('/\d/', $password)) $categories++;
|
||||||
|
if (preg_match('/[^A-Za-z0-9]/u', $password)) $categories++;
|
||||||
|
if ($categories < 3) {
|
||||||
|
$errors[] = 'Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten.';
|
||||||
|
}
|
||||||
|
$samLower = mb_strtolower($sam);
|
||||||
|
$pwLower = mb_strtolower($password);
|
||||||
|
if ($samLower !== '' && mb_strpos($pwLower, $samLower) !== false) {
|
||||||
|
$errors[] = 'Passwort darf den Benutzernamen nicht enthalten.';
|
||||||
|
} else {
|
||||||
|
$minLen = 4;
|
||||||
|
if (mb_strlen($samLower) >= $minLen) {
|
||||||
|
$samLen = mb_strlen($samLower);
|
||||||
|
for ($len = $minLen; $len <= $samLen; $len++) {
|
||||||
|
for ($start = 0; $start <= $samLen - $len; $start++) {
|
||||||
|
$sub = mb_substr($samLower, $start, $len);
|
||||||
|
if (mb_strpos($pwLower, $sub) !== false) {
|
||||||
|
$errors[] = 'Passwort darf keine größeren Teile des Benutzernamens enthalten.';
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pwErrors = validate_password_php($pass, $sam);
|
||||||
|
if (count($pwErrors) > 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Ungültiges Passwort: ' . implode(' | ', $pwErrors) . "\n\nHinweis: $passwordHint";
|
||||||
|
header('Location: ../index.php?route=createuser');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if ($ou === '') {
|
if ($ou === '') {
|
||||||
$defaultOu = (string)($config['powershell']['default_ou'] ?? '');
|
$defaultOu = (string)($config['powershell']['default_ou'] ?? '');
|
||||||
if ($defaultOu !== '') {
|
if ($defaultOu !== '') {
|
||||||
|
|||||||
@ -72,6 +72,7 @@ declare(strict_types=1);
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password">Passwort</label>
|
<label for="password">Passwort</label>
|
||||||
<input type="password" class="form-control" id="password" name="password" required>
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
<small class="form-text text-muted">Das Passwort muss mindestens 7 Zeichen lang sein, darf keine größeren Teile des Benutzernamens enthalten und muss Zeichen aus mindestens 3 von 4 Kategorien enthalten: Großbuchstaben, Kleinbuchstaben, Ziffern, Sonderzeichen.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -81,6 +82,51 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Benutzer erstellen</button>
|
<button type="submit" class="btn btn-primary">Benutzer erstellen</button>
|
||||||
</form>
|
</form>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const form = document.querySelector('form[action="/api/create_user.php"]');
|
||||||
|
if (!form) return;
|
||||||
|
function validatePassword(password, sam) {
|
||||||
|
const errors = [];
|
||||||
|
if (!password || password.length < 7) errors.push('Passwort muss mindestens 7 Zeichen lang sein.');
|
||||||
|
let categories = 0;
|
||||||
|
if (/[A-Z]/.test(password)) categories++;
|
||||||
|
if (/[a-z]/.test(password)) categories++;
|
||||||
|
if (/\d/.test(password)) categories++;
|
||||||
|
if (/[^A-Za-z0-9]/.test(password)) categories++;
|
||||||
|
if (categories < 3) errors.push('Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten.');
|
||||||
|
if (sam) {
|
||||||
|
const pwLower = password.toLowerCase();
|
||||||
|
const samLower = sam.toLowerCase();
|
||||||
|
if (pwLower.includes(samLower)) errors.push('Passwort darf den Benutzernamen nicht enthalten.');
|
||||||
|
else {
|
||||||
|
const minLen = 4;
|
||||||
|
if (samLower.length >= minLen) {
|
||||||
|
outer: for (let len = minLen; len <= samLower.length; len++) {
|
||||||
|
for (let s = 0; s <= samLower.length - len; s++) {
|
||||||
|
const sub = samLower.substr(s, len);
|
||||||
|
if (pwLower.includes(sub)) { errors.push('Passwort darf keine größeren Teile des Benutzernamens enthalten.'); break outer; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
const sam = document.getElementById('samaccountname').value.trim();
|
||||||
|
const password = document.getElementById('password').value;
|
||||||
|
const errs = validatePassword(password, sam);
|
||||||
|
if (errs.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
alert('Passwort ungültig:\n' + errs.join('\n'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -32,7 +32,64 @@ $pass = [string]$payload.password
|
|||||||
$ou = [string]($payload.ou)
|
$ou = [string]($payload.ou)
|
||||||
$groups = [string]($payload.groups)
|
$groups = [string]($payload.groups)
|
||||||
$dryRun = [bool]($payload.dry_run -as [bool])
|
$dryRun = [bool]($payload.dry_run -as [bool])
|
||||||
|
# Password hint and validation helper (German)
|
||||||
|
$passwordHint = @"
|
||||||
|
Das sollten die Anforderungen an das Passwort sein:
|
||||||
|
|
||||||
|
- mindestens 7 Zeichen
|
||||||
|
- darf den Benutzer-/Accountnamen nicht enthalten (bzw. keine zu großen Teile davon)
|
||||||
|
- muss Zeichen aus mindestens 3 von 4 Kategorien enthalten:
|
||||||
|
1) Großbuchstaben (A–Z)
|
||||||
|
2) Kleinbuchstaben (a–z)
|
||||||
|
3) Ziffern (0–9)
|
||||||
|
4) Sonderzeichen (alles, was kein Buchstabe/Zahl ist, z. B. ! ? # _ - . , usw.)
|
||||||
|
"@
|
||||||
|
|
||||||
|
function Test-PasswordRequirements {
|
||||||
|
param(
|
||||||
|
[string]$Password,
|
||||||
|
[string]$SamAccountName
|
||||||
|
)
|
||||||
|
|
||||||
|
$errors = @()
|
||||||
|
|
||||||
|
if ([string]::IsNullOrEmpty($Password) -or $Password.Length -lt 7) {
|
||||||
|
$errors += 'Passwort muss mindestens 7 Zeichen lang sein.'
|
||||||
|
}
|
||||||
|
|
||||||
|
$categories = 0
|
||||||
|
if ($Password -match '[A-Z]') { $categories++ }
|
||||||
|
if ($Password -match '[a-z]') { $categories++ }
|
||||||
|
if ($Password -match '\d') { $categories++ }
|
||||||
|
if ($Password -match '[^A-Za-z0-9]') { $categories++ }
|
||||||
|
if ($categories -lt 3) {
|
||||||
|
$errors += 'Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten (Groß, Klein, Ziffern, Sonderzeichen).'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not [string]::IsNullOrEmpty($SamAccountName)) {
|
||||||
|
$pwLower = $Password.ToLowerInvariant()
|
||||||
|
$samLower = $SamAccountName.ToLowerInvariant()
|
||||||
|
|
||||||
|
if ($pwLower -like "*${samLower}*") {
|
||||||
|
$errors += 'Passwort darf den Benutzernamen nicht enthalten.'
|
||||||
|
} else {
|
||||||
|
$minLen = 4
|
||||||
|
if ($samLower.Length -ge $minLen) {
|
||||||
|
for ($len = $minLen; $len -le $samLower.Length; $len++) {
|
||||||
|
for ($start = 0; $start -le $samLower.Length - $len; $start++) {
|
||||||
|
$sub = $samLower.Substring($start, $len)
|
||||||
|
if ($pwLower -like "*${sub}*") {
|
||||||
|
$errors += 'Passwort darf keine größeren Teile des Benutzernamens enthalten.'
|
||||||
|
break 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors
|
||||||
|
}
|
||||||
# Ensure ActiveDirectory module available
|
# Ensure ActiveDirectory module available
|
||||||
try {
|
try {
|
||||||
Import-Module ActiveDirectory -ErrorAction Stop
|
Import-Module ActiveDirectory -ErrorAction Stop
|
||||||
@ -52,6 +109,15 @@ $props = @{
|
|||||||
if ($mail -and $mail -ne '') { $props['EmailAddress'] = $mail }
|
if ($mail -and $mail -ne '') { $props['EmailAddress'] = $mail }
|
||||||
if ($ou -and $ou -ne '') { $props['Path'] = $ou }
|
if ($ou -and $ou -ne '') { $props['Path'] = $ou }
|
||||||
|
|
||||||
|
# Validate password before continuing
|
||||||
|
$pwErrors = Test-PasswordRequirements -Password $pass -SamAccountName $sam
|
||||||
|
if ($pwErrors.Count -gt 0) {
|
||||||
|
$result.message = 'Invalid password: ' + ($pwErrors -join ' | ')
|
||||||
|
$result.hint = $passwordHint
|
||||||
|
Write-Output ($result | ConvertTo-Json -Compress)
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
# Build secure password
|
# Build secure password
|
||||||
$securePass = ConvertTo-SecureString $pass -AsPlainText -Force
|
$securePass = ConvertTo-SecureString $pass -AsPlainText -Force
|
||||||
$props['AccountPassword'] = $securePass
|
$props['AccountPassword'] = $securePass
|
||||||
|
|||||||
@ -65,6 +65,67 @@ foreach ($row in $items) {
|
|||||||
$ou = $defaultOu
|
$ou = $defaultOu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Password hint text (German)
|
||||||
|
$passwordHint = @"
|
||||||
|
Das sollten die Anforderungen an das Passwort sein:
|
||||||
|
|
||||||
|
- mindestens 7 Zeichen
|
||||||
|
- darf den Benutzer-/Accountnamen nicht enthalten (bzw. keine zu großen Teile davon)
|
||||||
|
- muss Zeichen aus mindestens 3 von 4 Kategorien enthalten:
|
||||||
|
1) Großbuchstaben (A–Z)
|
||||||
|
2) Kleinbuchstaben (a–z)
|
||||||
|
3) Ziffern (0–9)
|
||||||
|
4) Sonderzeichen (alles, was kein Buchstabe/Zahl ist, z. B. ! ? # _ - . , usw.)
|
||||||
|
"@
|
||||||
|
|
||||||
|
function Test-PasswordRequirements {
|
||||||
|
param(
|
||||||
|
[string]$Password,
|
||||||
|
[string]$SamAccountName
|
||||||
|
)
|
||||||
|
|
||||||
|
$errors = @()
|
||||||
|
|
||||||
|
if ([string]::IsNullOrEmpty($Password) -or $Password.Length -lt 7) {
|
||||||
|
$errors += 'Passwort muss mindestens 7 Zeichen lang sein.'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Categories: uppercase, lowercase, digit, special
|
||||||
|
$categories = 0
|
||||||
|
if ($Password -match '[A-Z]') { $categories++ }
|
||||||
|
if ($Password -match '[a-z]') { $categories++ }
|
||||||
|
if ($Password -match '\d') { $categories++ }
|
||||||
|
if ($Password -match '[^A-Za-z0-9]') { $categories++ }
|
||||||
|
if ($categories -lt 3) {
|
||||||
|
$errors += 'Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten (Groß, Klein, Ziffern, Sonderzeichen).'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for username inclusion or large parts of it (case-insensitive)
|
||||||
|
if (-not [string]::IsNullOrEmpty($SamAccountName)) {
|
||||||
|
$pwLower = $Password.ToLowerInvariant()
|
||||||
|
$samLower = $SamAccountName.ToLowerInvariant()
|
||||||
|
|
||||||
|
if ($pwLower -like "*${samLower}*") {
|
||||||
|
$errors += 'Passwort darf den Benutzernamen nicht enthalten.'
|
||||||
|
} else {
|
||||||
|
# Check for substrings of username of length >= 4
|
||||||
|
$minLen = 4
|
||||||
|
if ($samLower.Length -ge $minLen) {
|
||||||
|
for ($len = $minLen; $len -le $samLower.Length; $len++) {
|
||||||
|
for ($start = 0; $start -le $samLower.Length - $len; $start++) {
|
||||||
|
$sub = $samLower.Substring($start, $len)
|
||||||
|
if ($pwLower -like "*${sub}*") {
|
||||||
|
$errors += 'Passwort darf keine größeren Teile des Benutzernamens enthalten.'
|
||||||
|
break 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors
|
||||||
|
}
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($sam) -or [string]::IsNullOrWhiteSpace($pass)) {
|
if ([string]::IsNullOrWhiteSpace($sam) -or [string]::IsNullOrWhiteSpace($pass)) {
|
||||||
$results += @{ sam = $sam; success = $false; message = 'Missing samaccountname or password' }
|
$results += @{ sam = $sam; success = $false; message = 'Missing samaccountname or password' }
|
||||||
@ -72,8 +133,16 @@ foreach ($row in $items) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Validate password according to requirements, also during dry run so issues are visible early
|
||||||
|
$pwErrors = Test-PasswordRequirements -Password $pass -SamAccountName $sam
|
||||||
|
if ($pwErrors.Count -gt 0) {
|
||||||
|
$results += @{ sam = $sam; success = $false; message = ('Invalid password: ' + ($pwErrors -join ' | ')); hint = $passwordHint }
|
||||||
|
$failCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if ($dryRun) {
|
if ($dryRun) {
|
||||||
$results += @{ sam = $sam; success = $true; message = 'DRY RUN: would create' }
|
$results += @{ sam = $sam; success = $true; message = 'DRY RUN: would create (password validated)' }
|
||||||
$successCount++
|
$successCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user