parent
d6baaad963
commit
6060a5134b
@ -1,96 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
final class PowerShellService
|
||||
{
|
||||
private string $psExe;
|
||||
|
||||
public function __construct(?string $psExe = null)
|
||||
{
|
||||
// Windows PowerShell 5.1
|
||||
$this->psExe = $psExe ?? 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
||||
// Alternative PowerShell 7:
|
||||
// $this->psExe = $psExe ?? 'C:\\Program Files\\PowerShell\\7\\pwsh.exe';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{exitCode:int, stdout:string, stderr:string}
|
||||
*/
|
||||
public function runScript(string $scriptPath, array $namedArgs, int $timeoutSeconds = 30): array
|
||||
{
|
||||
$scriptPath = realpath($scriptPath) ?: $scriptPath;
|
||||
if (!is_file($scriptPath)) {
|
||||
throw new RuntimeException("PowerShell script not found: {$scriptPath}");
|
||||
}
|
||||
|
||||
$args = [];
|
||||
foreach ($namedArgs as $name => $value) {
|
||||
if (!preg_match('/^[A-Za-z][A-Za-z0-9]*$/', (string)$name)) {
|
||||
throw new InvalidArgumentException("Invalid argument name: {$name}");
|
||||
}
|
||||
$args[] = '-' . $name;
|
||||
$args[] = (string)$value; // wird als eigenes Argument übergeben (kein Command-String-Basteln)
|
||||
}
|
||||
|
||||
$cmd = array_merge([
|
||||
$this->psExe,
|
||||
'-NoLogo',
|
||||
'-NoProfile',
|
||||
'-NonInteractive',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-File', $scriptPath,
|
||||
], $args);
|
||||
|
||||
$descriptors = [
|
||||
0 => ["pipe", "r"],
|
||||
1 => ["pipe", "w"],
|
||||
2 => ["pipe", "w"],
|
||||
];
|
||||
|
||||
$proc = proc_open($cmd, $descriptors, $pipes, null, null, [
|
||||
'bypass_shell' => true,
|
||||
'suppress_errors' => true,
|
||||
]);
|
||||
|
||||
if (!is_resource($proc)) {
|
||||
throw new RuntimeException("Failed to start PowerShell process.");
|
||||
}
|
||||
|
||||
fclose($pipes[0]);
|
||||
stream_set_blocking($pipes[1], false);
|
||||
stream_set_blocking($pipes[2], false);
|
||||
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$start = time();
|
||||
|
||||
while (true) {
|
||||
$stdout .= stream_get_contents($pipes[1]);
|
||||
$stderr .= stream_get_contents($pipes[2]);
|
||||
|
||||
$status = proc_get_status($proc);
|
||||
if (!$status['running']) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((time() - $start) > $timeoutSeconds) {
|
||||
proc_terminate($proc, 9);
|
||||
$stderr .= "\n[PHP] Timeout after {$timeoutSeconds}s";
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(50_000);
|
||||
}
|
||||
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
$exitCode = proc_close($proc);
|
||||
|
||||
return [
|
||||
'exitCode' => (int)$exitCode,
|
||||
'stdout' => trim($stdout),
|
||||
'stderr' => trim($stderr),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
require_once __DIR__ . '/../app/Services/PowerShellService.php';
|
||||
|
||||
// Minimal-Validation (zusätzlich zur PS-Validation)
|
||||
$username = (string)($_POST['username'] ?? '');
|
||||
$vorname = (string)($_POST['vorname'] ?? '');
|
||||
$nachname = (string)($_POST['nachname'] ?? '');
|
||||
$passwort = (string)($_POST['passwort'] ?? '');
|
||||
$gruppe = (string)($_POST['gruppe'] ?? '');
|
||||
|
||||
if ($username === '' || $vorname === '' || $nachname === '' || $passwort === '' || $gruppe === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['ok' => false, 'error' => 'Bitte alle Felder ausfüllen.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$ps = new PowerShellService();
|
||||
|
||||
$script = __DIR__ . '/../scripts/powershell/New-AdUserFromPhp.ps1';
|
||||
|
||||
$res = $ps->runScript($script, [
|
||||
'Username' => $username,
|
||||
'Vorname' => $vorname,
|
||||
'Nachname' => $nachname,
|
||||
'Passwort' => $passwort,
|
||||
'Benutzergruppe' => $gruppe,
|
||||
], 60);
|
||||
|
||||
// PowerShell gibt JSON zurück
|
||||
$json = $res['stdout'];
|
||||
$data = json_decode($json, true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'error' => 'Ungültige Antwort von PowerShell.',
|
||||
'stderr' => $res['stderr'],
|
||||
'raw' => $json,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Wenn PS exitCode != 0, trotzdem JSON ausgeben (enthält error)
|
||||
if ($res['exitCode'] !== 0) {
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AD User anlegen</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>AD User anlegen</h1>
|
||||
|
||||
<form method="post" action="ad_create_user.php">
|
||||
<p>
|
||||
<label>Username (sAMAccountName)<br>
|
||||
<input name="username" required maxlength="32">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>Vorname<br>
|
||||
<input name="vorname" required maxlength="64">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>Nachname<br>
|
||||
<input name="nachname" required maxlength="64">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>Passwort<br>
|
||||
<input name="passwort" type="password" required minlength="8" maxlength="128">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>Benutzergruppe (Gruppenname / DN)<br>
|
||||
<input name="gruppe" required maxlength="128" placeholder="z.B. 'IT-Users'">
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<button type="submit">Anlegen</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,72 +0,0 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidatePattern('^[a-zA-Z0-9._-]{1,32}$')]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateLength(1, 64)]
|
||||
[string]$Vorname,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateLength(1, 64)]
|
||||
[string]$Nachname,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateLength(8, 128)]
|
||||
[string]$Passwort,
|
||||
|
||||
# Gruppe als Name / DN / SamAccountName der Gruppe
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateLength(1, 128)]
|
||||
[string]$Benutzergruppe
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
try {
|
||||
Import-Module ActiveDirectory
|
||||
|
||||
# Existiert User schon?
|
||||
$existing = Get-ADUser -Filter "SamAccountName -eq '$Username'" -ErrorAction SilentlyContinue
|
||||
if ($null -ne $existing) {
|
||||
throw "Benutzer existiert bereits: $Username"
|
||||
}
|
||||
|
||||
# Existiert Gruppe?
|
||||
$grp = Get-ADGroup -Identity $Benutzergruppe -ErrorAction SilentlyContinue
|
||||
if ($null -eq $grp) {
|
||||
throw "Gruppe nicht gefunden: $Benutzergruppe"
|
||||
}
|
||||
|
||||
$displayName = "$Vorname $Nachname"
|
||||
$securePw = ConvertTo-SecureString -AsPlainText $Passwort -Force
|
||||
|
||||
New-ADUser `
|
||||
-SamAccountName $Username `
|
||||
-Name $displayName `
|
||||
-GivenName $Vorname `
|
||||
-Surname $Nachname `
|
||||
-DisplayName $displayName `
|
||||
-AccountPassword $securePw `
|
||||
-Enabled $true `
|
||||
-ChangePasswordAtLogon $true
|
||||
|
||||
Add-ADGroupMember -Identity $Benutzergruppe -Members $Username
|
||||
|
||||
[pscustomobject]@{
|
||||
ok = $true
|
||||
username = $Username
|
||||
displayName = $displayName
|
||||
group = $Benutzergruppe
|
||||
} | ConvertTo-Json -Depth 4
|
||||
|
||||
exit 0
|
||||
}
|
||||
catch {
|
||||
[pscustomobject]@{
|
||||
ok = $false
|
||||
error = $_.Exception.Message
|
||||
} | ConvertTo-Json -Depth 4
|
||||
|
||||
exit 1
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user