diff --git a/app/Services/PowerShellService.php b/app/Services/PowerShellService.php new file mode 100644 index 0000000..7ad83a4 --- /dev/null +++ b/app/Services/PowerShellService.php @@ -0,0 +1,96 @@ +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), + ]; + } +} diff --git a/public/ad_create_user.php b/public/ad_create_user.php new file mode 100644 index 0000000..9b0e529 --- /dev/null +++ b/public/ad_create_user.php @@ -0,0 +1,58 @@ + 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()]); +} diff --git a/public/ad_create_user_form.html b/public/ad_create_user_form.html new file mode 100644 index 0000000..ff4ac86 --- /dev/null +++ b/public/ad_create_user_form.html @@ -0,0 +1,41 @@ + + +
+ + +