Oids hinzugefügt #2
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,4 @@
|
||||
/.idea/
|
||||
/.vscode/
|
||||
/storage/
|
||||
/config.php
|
||||
/config.php
|
||||
94
README.md
94
README.md
@ -1,19 +1,65 @@
|
||||
# PHP_AdminTool_Projekt
|
||||
Admin Tool Projekt für das Fach PHP
|
||||
|
||||
---
|
||||
Ich (@blaerf) habe mich mal hingesetzt und einen Windows Server 2025 als VM aufgesetzt. Darauf habe ich die Active Directory und IIS Rolle installiert.
|
||||
Zudem habe ich alles so konfiguriert, dass wir PHP mit IIS nutzen können und per LDAPS auf das AD per PHP zugreifen können.
|
||||
Dieses Repository enthält das gemeinsame PHP AdminTool, das auf IIS unter Windows betrieben wird und über LDAP/AD, SNMP und PowerShell administrative Aufgaben ermöglicht. Das Projekt wird von mehreren Entwicklern gepflegt und folgt einem klar definierten Workflow, um Qualität, Stabilität und Nachvollziehbarkeit sicherzustellen.
|
||||
|
||||
Ich habe mir auch noch Gedanken zum Git-Workflow gemacht und es nieder geschrieben. So können wir sauber und sicher arbeiten.
|
||||
Eine Ordner Struktur habe ich mir auch überlegt und schon mal angelegt. Alle Infos habe ich soweit im Wiki zusammen getragen.
|
||||
Meine Tests habe ich, wie im Git-workflow beschrieben, in ein eigenes branch (structure/first-structure) gepackt.
|
||||
Ein zentraler Bestandteil der Arbeit mit diesem Repository ist der verbindliche Gitea-Workflow im Wiki.
|
||||
|
||||
Bitte auch das Wiki beachten und durchlesen, falls nötig!
|
||||
|
||||
**Hinweis:** Der [Gitea-Workflow](https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/wiki) ist zwingend zu lesen und einzuhalten. Er legt fest, wie Branches erstellt, gemerged und getestet werden.
|
||||
|
||||
---
|
||||
|
||||
## Aufgaben: ##
|
||||
## Überblick
|
||||
|
||||
Das Projekt dient als webbasiertes Administrationswerkzeug und umfasst unter anderem:
|
||||
|
||||
- LDAP/AD‑Anbindung (LDAPS)
|
||||
- Benutzer- und Gruppenverwaltung
|
||||
- SNMP‑Auswertungen
|
||||
- PowerShell‑Integration
|
||||
- Weboberfläche zur zentralen Administration
|
||||
|
||||
Die Anwendung läuft in zwei Umgebungen:
|
||||
|
||||
- Produktion (`main`): https://itfa.schraubenfuzzi.de
|
||||
- Test (`develop`): https://test.itfa.schraubenfuzzi.de
|
||||
|
||||
---
|
||||
|
||||
## Ordnerstruktur (Auszug)
|
||||
|
||||
- `app/` – Anwendungscode
|
||||
- `public/` – Webroot
|
||||
- `config/` – Konfigurationen für LDAP, Systemvariablen usw.
|
||||
- `scripts/` – Hilfs- und Bereitstellungsskripte
|
||||
- `docs/` – interne Dokumente
|
||||
- `.gitignore` – ausgeschlossene Dateien
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
Die Entwicklung folgt einem strengen, verbindlichen Ablauf:
|
||||
|
||||
1. **Feature‑Branch erstellen** (`feature/name`)
|
||||
2. Feature entwickeln, committen, pushen
|
||||
3. **PR nach `develop`** für Tests auf der Testinstanz
|
||||
- wird vom Autor selbst gemerged
|
||||
- Branch bleibt bestehen
|
||||
4. Nach bestandenem Test: **PR nach `main`**
|
||||
- mit Review
|
||||
- Merge per Squash
|
||||
- danach wird der Feature‑Branch gelöscht
|
||||
|
||||
Details stehen im Wiki.
|
||||
|
||||
**Wichtiger Hinweis:**
|
||||
Der komplette Ablauf ist im [Gitea-Workflow](https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/wiki) beschrieben und muss befolgt werden.
|
||||
|
||||
---
|
||||
|
||||
## Aufgaben
|
||||
|
||||
| Aufgabe | Benutzer |
|
||||
| :---- | :---- |
|
||||
| Benutzer und Gruppen über LDAP anzeigen | Jens E (@blaerf), Stefan W (@viperion) |
|
||||
@ -23,4 +69,32 @@ Bitte auch das Wiki beachten und durchlesen, falls nötig!
|
||||
| UI/UX anpassen | Yasin B (@Muchentuchen), Alexander M (@Alexander), Torsten J (@tojacobs) |
|
||||
|
||||
---
|
||||
## Infos gibt es im [Wiki](https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/wiki)
|
||||
|
||||
## Dokumentation
|
||||
|
||||
Alle weiteren Informationen wie Regeln, technische Details, Konzepte und Anleitungen befinden sich im Wiki:
|
||||
|
||||
`Wiki → Gitea‑Workflow`
|
||||
`Wiki → Implementierung / LDAP / IIS / Setup`
|
||||
|
||||
Dieser Bereich muss von allen Entwicklern gelesen werden, bevor am Projekt gearbeitet wird.
|
||||
|
||||
---
|
||||
|
||||
## Mitwirken
|
||||
|
||||
Wer etwas ändern oder erweitern möchte:
|
||||
|
||||
- Branch vom aktuellen Stand erstellen
|
||||
- Entwickeln, Committen, Pushen
|
||||
- PR zuerst nach `develop`, später nach bestandenen Tests nach `main`
|
||||
- Reviewer zuweisen (für `main`)
|
||||
|
||||
Nur tested und reviewed Code gelangt in die Produktion.
|
||||
|
||||
---
|
||||
|
||||
## Ziel
|
||||
|
||||
Das AdminTool soll eine wartbare, erweiterbare und zuverlässige Verwaltungsoberfläche bieten, die zentrale Aufgaben des AD‑Umfelds über eine moderne Weboberfläche ermöglicht.
|
||||
|
||||
|
||||
153
app/Controllers/UserManagementController.php
Normal file
153
app/Controllers/UserManagementController.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Services\Ldap\LdapDirectoryService;
|
||||
|
||||
/**
|
||||
* Controller für die Benutzer- und Gruppenanzeige.
|
||||
*
|
||||
* Aufgaben:
|
||||
* - holt über den LdapDirectoryService die Listen von Benutzern und Gruppen
|
||||
* - behandelt technische Fehler und bereitet eine Fehlermeldung für die View auf
|
||||
* - gibt die Daten an eine View-Datei (public/views/users.php) weiter
|
||||
*
|
||||
* WICHTIG:
|
||||
* - Es werden aktuell nur Daten angezeigt (Read-only).
|
||||
* - Es findet keine Änderung im Active Directory statt.
|
||||
*/
|
||||
class UserManagementController
|
||||
{
|
||||
/** @var array<string, mixed> Vollständige Anwendungskonfiguration (aus config.php) */
|
||||
private array $config;
|
||||
|
||||
/** @var LdapDirectoryService Service für das Lesen von Benutzern und Gruppen aus dem LDAP/AD */
|
||||
private LdapDirectoryService $directoryService;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $config Vollständige Konfiguration aus config.php
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
// Komplette Konfiguration speichern (falls später weitere Werte benötigt werden).
|
||||
$this->config = $config;
|
||||
|
||||
// LDAP-Konfiguration aus der Gesamt-Konfiguration herausziehen.
|
||||
$ldapConfig = $config['ldap'] ?? [];
|
||||
|
||||
// Directory-Service initialisieren, der die eigentliche LDAP-Arbeit übernimmt.
|
||||
$this->directoryService = new LdapDirectoryService($ldapConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt Benutzer- und Gruppenliste an.
|
||||
* Wird typischerweise über die Route "users" (index.php?route=users) aufgerufen.
|
||||
*/
|
||||
public function show(): void
|
||||
{
|
||||
// Standardwerte für die View-Variablen vorbereiten.
|
||||
$error = null;
|
||||
$users = [];
|
||||
$groups = [];
|
||||
|
||||
try {
|
||||
// Benutzer- und Gruppenlisten aus dem AD laden.
|
||||
$users = $this->directoryService->getUsers();
|
||||
$groups = $this->directoryService->getGroups();
|
||||
} catch (\Throwable $exception) {
|
||||
// Sämtliche technischen Fehler (z. B. Verbindungs- oder Konfigurationsprobleme)
|
||||
// werden hier in eine für den Benutzer lesbare Fehlermeldung übersetzt.
|
||||
$error = 'Fehler beim Laden von Benutzern/Gruppen: ' . $exception->getMessage();
|
||||
}
|
||||
|
||||
// Pfad zur eigentlichen View-Datei bestimmen.
|
||||
$viewPath = __DIR__ . '/../../public/views/users.php';
|
||||
|
||||
// Falls die View-Datei (noch) nicht existiert, Fallback-Ausgabe verwenden.
|
||||
if (file_exists($viewPath) === false) {
|
||||
$this->renderInline($users, $groups, $error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Variablen $users, $groups, $error stehen in der View zur Verfügung,
|
||||
// weil sie im aktuellen Scope definiert sind.
|
||||
require $viewPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback-Ausgabe, falls noch keine View-Datei existiert.
|
||||
*
|
||||
* @param array<int, array<string, string>> $users Liste der Benutzer-Datensätze
|
||||
* @param array<int, array<string, string>> $groups Liste der Gruppen-Datensätze
|
||||
* @param string|null $error Fehlermeldung (falls vorhanden)
|
||||
*/
|
||||
private function renderInline(array $users, array $groups, ?string $error): void
|
||||
{
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AD Admin Tool – Benutzer & Gruppen</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Benutzer & Gruppen</h1>
|
||||
|
||||
<?php if ($error !== null): ?>
|
||||
<!-- Fehlermeldung ausgeben, HTML-sicher maskiert -->
|
||||
<p style="color: red;"><?php echo htmlspecialchars($error, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>Benutzer</h2>
|
||||
<table border="1" cellpadding="4" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Benutzername (sAMAccountName)</th>
|
||||
<th>Anzeigename</th>
|
||||
<th>E-Mail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($user['samaccountname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['displayname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['mail'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Gruppen</h2>
|
||||
<table border="1" cellpadding="4" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppenname (sAMAccountName)</th>
|
||||
<th>CN</th>
|
||||
<th>Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($group['samaccountname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($group['cn'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($group['description'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="index.php?route=dashboard">Zurück zum Dashboard</a> |
|
||||
<a href="index.php?route=logout">Logout</a>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,37 @@
|
||||
<?php
|
||||
|
||||
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Ldap;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Hilfsklasse zum Aufbau einer LDAP/LDAPS-Verbindung.
|
||||
*
|
||||
* Aufgaben:
|
||||
* - liest Server, Port und Timeout aus der LDAP-Konfiguration
|
||||
* - erstellt eine LDAP-Verbindung
|
||||
* - setzt die notwendigen Optionen (Protokollversion, Netzwerk-Timeout)
|
||||
*
|
||||
* Wichtig:
|
||||
* - Diese Klasse führt KEIN ldap_bind durch.
|
||||
* - Das Bind (mit Benutzer- oder Service-Konto) erfolgt in den Fach-Services
|
||||
* wie LdapAuthService oder LdapDirectoryService.
|
||||
*/
|
||||
class LdapConnectionHelper
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
/** @var array<string, mixed> LDAP-spezifische Konfiguration (server, port, timeout, etc.) */
|
||||
private array $config;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $ldapConfig
|
||||
* @param array<string, mixed> $ldapConfig Teilbereich "ldap" aus der config.php
|
||||
*/
|
||||
public function __construct(array $ldapConfig)
|
||||
{
|
||||
// LDAP-Konfiguration in der Instanz speichern.
|
||||
// Dadurch steht sie in allen Methoden dieser Klasse zur Verfügung.
|
||||
$this->config = $ldapConfig;
|
||||
}
|
||||
|
||||
@ -24,26 +40,43 @@ class LdapConnectionHelper
|
||||
* aber ohne Bind. Den Bind führen die aufrufenden Services durch.
|
||||
*
|
||||
* @return resource LDAP-Verbindungs-Handle
|
||||
*
|
||||
* @throws RuntimeException wenn der Server nicht konfiguriert ist oder die Verbindung scheitert
|
||||
*/
|
||||
public function createConnection()
|
||||
{
|
||||
// Server, Port und Timeout aus der Konfiguration lesen.
|
||||
// Die Null-Koaleszenz-Operatoren (??) setzen Standardwerte, falls Keys fehlen.
|
||||
$server = (string)($this->config['server'] ?? '');
|
||||
$port = (int)($this->config['port'] ?? 636);
|
||||
$timeout = (int)($this->config['timeout'] ?? 5);
|
||||
|
||||
// Ohne Server-Adresse kann keine Verbindung aufgebaut werden.
|
||||
if ($server === '') {
|
||||
throw new RuntimeException('LDAP-Konfiguration ist unvollständig (server fehlt).');
|
||||
}
|
||||
|
||||
// Verbindung zum LDAP/AD-Server herstellen.
|
||||
// ldap_connect liefert entweder ein Verbindungs-Handle (Resource) oder false.
|
||||
$connection = ldap_connect($server, $port);
|
||||
|
||||
// Wenn keine Verbindung aufgebaut werden konnte, Exception werfen.
|
||||
if ($connection === false) {
|
||||
throw new RuntimeException('LDAP-Verbindung konnte nicht aufgebaut werden.');
|
||||
}
|
||||
|
||||
// LDAP-Version auf 3 setzen, dies ist die gängige Standardversion.
|
||||
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
|
||||
// Netzwerk-Timeout setzen, damit Anfragen nicht ewig hängen bleiben.
|
||||
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, $timeout);
|
||||
|
||||
// Referral-Following deaktivieren (0 = aus).
|
||||
// Das verhindert, dass der Client automatisch zu anderen LDAP-Servern weitergeleitet wird.
|
||||
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
// Verbindungs-Handle an aufrufende Services zurückgeben.
|
||||
// Dort wird dann der eigentliche ldap_bind durchgeführt.
|
||||
return $connection;
|
||||
}
|
||||
}
|
||||
|
||||
203
app/Services/Ldap/LdapDirectoryService.php
Normal file
203
app/Services/Ldap/LdapDirectoryService.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Ldap;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Service zum Lesen von Objekten aus dem Active Directory.
|
||||
*
|
||||
* Aktueller Umfang:
|
||||
* - Liste von Benutzern (sAMAccountName, displayName, mail)
|
||||
* - Liste von Gruppen (sAMAccountName, cn, description)
|
||||
*
|
||||
* Technische Details:
|
||||
* - Verwendet ein technisches Konto (bind_dn + bind_password) für Lesezugriffe
|
||||
* - Nutzt LdapConnectionHelper zum Aufbau der Verbindung
|
||||
*/
|
||||
class LdapDirectoryService
|
||||
{
|
||||
/** @var array<string, mixed> LDAP-Konfiguration (inkl. base_dn, bind_dn, bind_password) */
|
||||
private array $config;
|
||||
|
||||
/** @var LdapConnectionHelper Zentrale Hilfsklasse für den Aufbau von LDAP-Verbindungen */
|
||||
private LdapConnectionHelper $connectionHelper;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $ldapConfig Teilbereich "ldap" aus der config.php
|
||||
*/
|
||||
public function __construct(array $ldapConfig)
|
||||
{
|
||||
// Vollständige LDAP-Konfiguration speichern.
|
||||
$this->config = $ldapConfig;
|
||||
|
||||
// Gemeinsamen Verbindungs-Helper initialisieren.
|
||||
// Dieser kümmert sich um ldap_connect und die grundlegenden Optionen.
|
||||
$this->connectionHelper = new LdapConnectionHelper($ldapConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stellt eine LDAP-Verbindung her und bindet sich mit dem technischen Konto.
|
||||
*
|
||||
* @return resource LDAP-Verbindungs-Handle
|
||||
*
|
||||
* @throws RuntimeException wenn Bind-Daten fehlen oder der Bind fehlschlägt
|
||||
*/
|
||||
private function connect()
|
||||
{
|
||||
// Technischen Bind-DN (Service-Account) aus der Konfiguration lesen.
|
||||
$bindDn = (string)($this->config['bind_dn'] ?? '');
|
||||
$bindPassword = (string)($this->config['bind_password'] ?? '');
|
||||
|
||||
// Ohne Bind-DN oder Passwort kann kein technischer Bind durchgeführt werden.
|
||||
if ($bindDn === '' || $bindPassword === '') {
|
||||
throw new RuntimeException('LDAP-Binddaten für technisches Konto sind nicht konfiguriert.');
|
||||
}
|
||||
|
||||
// Verbindung über den zentralen Helper erstellen (Server, Port, Optionen).
|
||||
$connection = $this->connectionHelper->createConnection();
|
||||
|
||||
// Mit dem technischen Konto binden, um Lesezugriffe durchführen zu können.
|
||||
// Das @-Zeichen unterdrückt PHP-Warnings bei Fehlversuchen.
|
||||
if (@ldap_bind($connection, $bindDn, $bindPassword) !== true) {
|
||||
// Bei fehlgeschlagenem Bind Verbindung wieder schließen.
|
||||
ldap_unbind($connection);
|
||||
throw new RuntimeException('LDAP-Bind mit technischem Konto ist fehlgeschlagen.');
|
||||
}
|
||||
|
||||
// Erfolgreich gebundene Verbindung zurückgeben.
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert eine Liste von Benutzern aus dem AD.
|
||||
*
|
||||
* @return array<int, array<string, string>> Liste von Benutzer-Datensätzen
|
||||
* [
|
||||
* [
|
||||
* 'samaccountname' => 'user1',
|
||||
* 'displayname' => 'User Eins',
|
||||
* 'mail' => 'user1@example.local',
|
||||
* ],
|
||||
* ...
|
||||
* ]
|
||||
*
|
||||
* @throws RuntimeException bei Konfigurations- oder Verbindungsproblemen
|
||||
*/
|
||||
public function getUsers(): array
|
||||
{
|
||||
// Base-DN ist der Startpunkt der Suche im Verzeichnisbaum (z. B. DC=...,DC=...).
|
||||
$baseDn = (string)($this->config['base_dn'] ?? '');
|
||||
if ($baseDn === '') {
|
||||
throw new RuntimeException('LDAP base_dn für Benutzersuche ist nicht konfiguriert.');
|
||||
}
|
||||
|
||||
// Verbindung mit technischem Bind herstellen.
|
||||
$connection = $this->connect();
|
||||
|
||||
// Standard-Filter für Benutzer in AD:
|
||||
// - objectClass=user
|
||||
// - objectCategory=person
|
||||
$filter = '(&(objectClass=user)(objectCategory=person))';
|
||||
|
||||
// Nur relevante Attribute auslesen, um Datenmenge klein zu halten.
|
||||
$attributes = ['sAMAccountName', 'displayName', 'mail'];
|
||||
|
||||
// LDAP-Suche unterhalb des Base-DN durchführen.
|
||||
$search = @ldap_search($connection, $baseDn, $filter, $attributes);
|
||||
if ($search === false) {
|
||||
// Bei einem Fehler die Verbindung schließen und Exception werfen.
|
||||
ldap_unbind($connection);
|
||||
throw new RuntimeException('LDAP-Suche nach Benutzern ist fehlgeschlagen.');
|
||||
}
|
||||
|
||||
// Suchergebnisse in ein PHP-Array umwandeln.
|
||||
$entries = ldap_get_entries($connection, $search);
|
||||
|
||||
// Verbindung wieder schließen, da sie nicht mehr benötigt wird.
|
||||
ldap_unbind($connection);
|
||||
|
||||
// Ergebnisliste für die View vorbereiten.
|
||||
$users = [];
|
||||
|
||||
// Wenn die Struktur unerwartet ist, leere Liste zurückgeben.
|
||||
if (!is_array($entries) || !isset($entries['count'])) {
|
||||
return $users;
|
||||
}
|
||||
|
||||
// Jeden einzelnen Eintrag aus dem Ergebnis verarbeiten.
|
||||
for ($i = 0; $i < $entries['count']; $i++) {
|
||||
$entry = $entries[$i];
|
||||
|
||||
// Attribute sind im Ergebnis verschachtelt, daher Zugriff über [...][0].
|
||||
$users[] = [
|
||||
'samaccountname' => isset($entry['samaccountname'][0]) ? (string)$entry['samaccountname'][0] : '',
|
||||
'displayname' => isset($entry['displayname'][0]) ? (string)$entry['displayname'][0] : '',
|
||||
'mail' => isset($entry['mail'][0]) ? (string)$entry['mail'][0] : '',
|
||||
];
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert eine Liste von Gruppen aus dem AD.
|
||||
*
|
||||
* @return array<int, array<string, string>> Liste von Gruppen-Datensätzen
|
||||
*
|
||||
* @throws RuntimeException bei Konfigurations- oder Verbindungsproblemen
|
||||
*/
|
||||
public function getGroups(): array
|
||||
{
|
||||
// Base-DN für die Gruppensuche aus der Konfiguration lesen.
|
||||
$baseDn = (string)($this->config['base_dn'] ?? '');
|
||||
if ($baseDn === '') {
|
||||
throw new RuntimeException('LDAP base_dn für Gruppensuche ist nicht konfiguriert.');
|
||||
}
|
||||
|
||||
// Verbindung mit technischem Bind herstellen.
|
||||
$connection = $this->connect();
|
||||
|
||||
// Filter für Gruppenobjekte im AD.
|
||||
$filter = '(objectClass=group)';
|
||||
|
||||
// Attribute, die für die Darstellung relevant sind.
|
||||
$attributes = ['sAMAccountName', 'cn', 'description'];
|
||||
|
||||
// LDAP-Suche ausführen.
|
||||
$search = @ldap_search($connection, $baseDn, $filter, $attributes);
|
||||
if ($search === false) {
|
||||
ldap_unbind($connection);
|
||||
throw new RuntimeException('LDAP-Suche nach Gruppen ist fehlgeschlagen.');
|
||||
}
|
||||
|
||||
// Ergebnisse holen.
|
||||
$entries = ldap_get_entries($connection, $search);
|
||||
|
||||
// Verbindung schließen.
|
||||
ldap_unbind($connection);
|
||||
|
||||
$groups = [];
|
||||
|
||||
// Struktur prüfen, leere Liste zurückgeben, falls unerwartet.
|
||||
if (!is_array($entries) || !isset($entries['count'])) {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
// Alle Einträge in ein flacheres Array-Format transformieren.
|
||||
for ($i = 0; $i < $entries['count']; $i++) {
|
||||
$entry = $entries[$i];
|
||||
|
||||
$groups[] = [
|
||||
'samaccountname' => isset($entry['samaccountname'][0]) ? (string)$entry['samaccountname'][0] : '',
|
||||
'cn' => isset($entry['cn'][0]) ? (string)$entry['cn'][0] : '',
|
||||
'description' => isset($entry['description'][0]) ? (string)$entry['description'][0] : '',
|
||||
];
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,9 @@ return [
|
||||
|
||||
// Optional: Timeout in Sekunden
|
||||
'timeout' => 5,
|
||||
|
||||
'bind_dn' => 'CN=Service IIS,OU=WebAppUsers,DC=ITFA-PROJ-DOM,DC=local',
|
||||
'bind_password' => '$7aE!R$l$D!p1Q9l458K8@O6&',
|
||||
],
|
||||
|
||||
'security' => [
|
||||
|
||||
@ -47,6 +47,7 @@ $config = require $configPath;
|
||||
|
||||
use App\Controllers\AuthController;
|
||||
use App\Controllers\DashboardController;
|
||||
use App\Controllers\UserManagementController;
|
||||
|
||||
// Hilfsfunktion für geschützte Routen
|
||||
function requireLogin(array $config): void
|
||||
@ -71,6 +72,9 @@ $authController = new AuthController($config);
|
||||
// Neue Instanz der Klasse DashboardController erstellen (wird bei Bedarf über den Autoloader geladen).
|
||||
$dashboardController = new DashboardController($config);
|
||||
|
||||
// Neue Instanz der Klasse UserManagmentController erstellen (wird bei Bedarf über den Autoloader geladen).
|
||||
$userManagementController = new UserManagementController($config);
|
||||
|
||||
// Anhand des Routing-Ziels (route) entscheiden, welcher Code ausgeführt wird.
|
||||
switch ($route) {
|
||||
case 'login':
|
||||
@ -94,6 +98,11 @@ switch ($route) {
|
||||
$dashboardController->show();
|
||||
break;
|
||||
|
||||
case 'users':
|
||||
requireLogin($config);
|
||||
$userManagementController->show();
|
||||
break;
|
||||
|
||||
default:
|
||||
http_response_code(404);
|
||||
echo 'Route nicht gefunden.';
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
// Call the dataTables jQuery plugin
|
||||
$(document).ready(function() {
|
||||
$('#dataTable').DataTable();
|
||||
$('#dataTable').DataTable();
|
||||
$('#usersTable').DataTable();
|
||||
$('#groupsTable').DataTable();
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@ declare(strict_types=1);
|
||||
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
|
||||
|
||||
<!-- Sidebar - Brand -->
|
||||
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="dashboard.php">
|
||||
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="../index.php?route=dashboard">
|
||||
<div class="sidebar-brand-icon rotate-n-15">
|
||||
<i class="fas fa-laugh-wink"></i>
|
||||
</div>
|
||||
@ -48,11 +48,17 @@ declare(strict_types=1);
|
||||
|
||||
<!-- Nav Item - Dashboard -->
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="dashboard.php">
|
||||
<a class="nav-link" href="../index.php?route=dashboard">
|
||||
<i class="fas fa-fw fa-tachometer-alt"></i>
|
||||
<span>Dashboard</span></a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../index.php?route=users">
|
||||
<i class="fas fa-fw fa-users"></i>
|
||||
<span>Users</span></a>
|
||||
</li>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider">
|
||||
|
||||
|
||||
520
public/views/users.php
Normal file
520
public/views/users.php
Normal file
@ -0,0 +1,520 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/** @var array<int, array<string, string>> $users */
|
||||
/** @var array<int, array<string, string>> $groups */
|
||||
/** @var string|null $error */
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>SB Admin 2 - Tables</title>
|
||||
|
||||
<!-- Custom fonts for this template -->
|
||||
<link href="../vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"
|
||||
rel="stylesheet">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="../css/sb-admin-2.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom styles for this page -->
|
||||
<link href="../vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Page Wrapper -->
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
|
||||
|
||||
<!-- Sidebar - Brand -->
|
||||
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="../views/dashboard.php">
|
||||
<div class="sidebar-brand-icon rotate-n-15">
|
||||
<i class="fas fa-laugh-wink"></i>
|
||||
</div>
|
||||
<div class="sidebar-brand-text mx-3">SB Admin <sup>2</sup></div>
|
||||
</a>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider my-0">
|
||||
|
||||
<!-- Nav Item - Dashboard -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../views/dashboard.php">
|
||||
<i class="fas fa-fw fa-tachometer-alt"></i>
|
||||
<span>Dashboard</span></a>
|
||||
</li>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider">
|
||||
|
||||
<!-- Heading -->
|
||||
<div class="sidebar-heading">
|
||||
Interface
|
||||
</div>
|
||||
|
||||
<!-- Nav Item - Pages Collapse Menu -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseTwo"
|
||||
aria-expanded="true" aria-controls="collapseTwo">
|
||||
<i class="fas fa-fw fa-cog"></i>
|
||||
<span>Components</span>
|
||||
</a>
|
||||
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordionSidebar">
|
||||
<div class="bg-white py-2 collapse-inner rounded">
|
||||
<h6 class="collapse-header">Custom Components:</h6>
|
||||
<a class="collapse-item" href="../buttons.php">Buttons</a>
|
||||
<a class="collapse-item" href="../cards.php">Cards</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Nav Item - Utilities Collapse Menu -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseUtilities"
|
||||
aria-expanded="true" aria-controls="collapseUtilities">
|
||||
<i class="fas fa-fw fa-wrench"></i>
|
||||
<span>Utilities</span>
|
||||
</a>
|
||||
<div id="collapseUtilities" class="collapse" aria-labelledby="headingUtilities"
|
||||
data-parent="#accordionSidebar">
|
||||
<div class="bg-white py-2 collapse-inner rounded">
|
||||
<h6 class="collapse-header">Custom Utilities:</h6>
|
||||
<a class="collapse-item" href="../utilities-color.php">Colors</a>
|
||||
<a class="collapse-item" href="../utilities-border.php">Borders</a>
|
||||
<a class="collapse-item" href="../utilities-animation.php">Animations</a>
|
||||
<a class="collapse-item" href="../utilities-other.php">Other</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider">
|
||||
|
||||
<!-- Heading -->
|
||||
<div class="sidebar-heading">
|
||||
Addons
|
||||
</div>
|
||||
|
||||
<!-- Nav Item - Pages Collapse Menu -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapsePages"
|
||||
aria-expanded="true" aria-controls="collapsePages">
|
||||
<i class="fas fa-fw fa-folder"></i>
|
||||
<span>Pages</span>
|
||||
</a>
|
||||
<div id="collapsePages" class="collapse" aria-labelledby="headingPages" data-parent="#accordionSidebar">
|
||||
<div class="bg-white py-2 collapse-inner rounded">
|
||||
<h6 class="collapse-header">Login Screens:</h6>
|
||||
<a class="collapse-item" href="login.php">Login</a>
|
||||
<a class="collapse-item" href="../register.php">Register</a>
|
||||
<a class="collapse-item" href="../forgot-password.php">Forgot Password</a>
|
||||
<div class="collapse-divider"></div>
|
||||
<h6 class="collapse-header">Other Pages:</h6>
|
||||
<a class="collapse-item" href="../404.php">404 Page</a>
|
||||
<a class="collapse-item" href="../blank.php">Blank Page</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Nav Item - Charts -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../charts.php">
|
||||
<i class="fas fa-fw fa-chart-area"></i>
|
||||
<span>Charts</span></a>
|
||||
</li>
|
||||
|
||||
<!-- Nav Item - Tables -->
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../tables.html">
|
||||
<i class="fas fa-fw fa-table"></i>
|
||||
<span>Tables</span></a>
|
||||
</li>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider d-none d-md-block">
|
||||
|
||||
<!-- Sidebar Toggler (Sidebar) -->
|
||||
<div class="text-center d-none d-md-inline">
|
||||
<button class="rounded-circle border-0" id="sidebarToggle"></button>
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
<!-- End of Sidebar -->
|
||||
|
||||
<!-- Content Wrapper -->
|
||||
<div id="content-wrapper" class="d-flex flex-column">
|
||||
|
||||
<!-- Main Content -->
|
||||
<div id="content">
|
||||
|
||||
<!-- Topbar -->
|
||||
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
|
||||
|
||||
<!-- Sidebar Toggle (Topbar) -->
|
||||
<form class="form-inline">
|
||||
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- Topbar Search -->
|
||||
<form
|
||||
class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..."
|
||||
aria-label="Search" aria-describedby="basic-addon2">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" type="button">
|
||||
<i class="fas fa-search fa-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Topbar Navbar -->
|
||||
<ul class="navbar-nav ml-auto">
|
||||
|
||||
<!-- Nav Item - Search Dropdown (Visible Only XS) -->
|
||||
<li class="nav-item dropdown no-arrow d-sm-none">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-search fa-fw"></i>
|
||||
</a>
|
||||
<!-- Dropdown - Messages -->
|
||||
<div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in"
|
||||
aria-labelledby="searchDropdown">
|
||||
<form class="form-inline mr-auto w-100 navbar-search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control bg-light border-0 small"
|
||||
placeholder="Search for..." aria-label="Search"
|
||||
aria-describedby="basic-addon2">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" type="button">
|
||||
<i class="fas fa-search fa-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Nav Item - Alerts -->
|
||||
<li class="nav-item dropdown no-arrow mx-1">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="alertsDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-bell fa-fw"></i>
|
||||
<!-- Counter - Alerts -->
|
||||
<span class="badge badge-danger badge-counter">3+</span>
|
||||
</a>
|
||||
<!-- Dropdown - Alerts -->
|
||||
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
||||
aria-labelledby="alertsDropdown">
|
||||
<h6 class="dropdown-header">
|
||||
Alerts Center
|
||||
</h6>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="mr-3">
|
||||
<div class="icon-circle bg-primary">
|
||||
<i class="fas fa-file-alt text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="small text-gray-500">December 12, 2019</div>
|
||||
<span class="font-weight-bold">A new monthly report is ready to download!</span>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="mr-3">
|
||||
<div class="icon-circle bg-success">
|
||||
<i class="fas fa-donate text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="small text-gray-500">December 7, 2019</div>
|
||||
$290.29 has been deposited into your account!
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="mr-3">
|
||||
<div class="icon-circle bg-warning">
|
||||
<i class="fas fa-exclamation-triangle text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="small text-gray-500">December 2, 2019</div>
|
||||
Spending Alert: We've noticed unusually high spending for your account.
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item text-center small text-gray-500" href="#">Show All Alerts</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Nav Item - Messages -->
|
||||
<li class="nav-item dropdown no-arrow mx-1">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="messagesDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-envelope fa-fw"></i>
|
||||
<!-- Counter - Messages -->
|
||||
<span class="badge badge-danger badge-counter">7</span>
|
||||
</a>
|
||||
<!-- Dropdown - Messages -->
|
||||
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
||||
aria-labelledby="messagesDropdown">
|
||||
<h6 class="dropdown-header">
|
||||
Message Center
|
||||
</h6>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="../images/undraw_profile_1.svg"
|
||||
alt="...">
|
||||
<div class="status-indicator bg-success"></div>
|
||||
</div>
|
||||
<div class="font-weight-bold">
|
||||
<div class="text-truncate">Hi there! I am wondering if you can help me with a
|
||||
problem I've been having.</div>
|
||||
<div class="small text-gray-500">Emily Fowler · 58m</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="../images/undraw_profile_2.svg"
|
||||
alt="...">
|
||||
<div class="status-indicator"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-truncate">I have the photos that you ordered last month, how
|
||||
would you like them sent to you?</div>
|
||||
<div class="small text-gray-500">Jae Chun · 1d</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="../images/undraw_profile_3.svg"
|
||||
alt="...">
|
||||
<div class="status-indicator bg-warning"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-truncate">Last month's report looks great, I am very happy with
|
||||
the progress so far, keep up the good work!</div>
|
||||
<div class="small text-gray-500">Morgan Alvarez · 2d</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="https://source.unsplash.com/Mv9hjnEUHR4/60x60"
|
||||
alt="...">
|
||||
<div class="status-indicator bg-success"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-truncate">Am I a good boy? The reason I ask is because someone
|
||||
told me that people say this to all dogs, even if they aren't good...</div>
|
||||
<div class="small text-gray-500">Chicken the Dog · 2w</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item text-center small text-gray-500" href="#">Read More Messages</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div class="topbar-divider d-none d-sm-block"></div>
|
||||
|
||||
<!-- Nav Item - User Information -->
|
||||
<li class="nav-item dropdown no-arrow">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="mr-2 d-none d-lg-inline text-gray-600 small">Douglas McGee</span>
|
||||
<img class="img-profile rounded-circle"
|
||||
src="../images/undraw_profile.svg">
|
||||
</a>
|
||||
<!-- Dropdown - User Information -->
|
||||
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
||||
aria-labelledby="userDropdown">
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
|
||||
Profile
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="fas fa-cogs fa-sm fa-fw mr-2 text-gray-400"></i>
|
||||
Settings
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="fas fa-list fa-sm fa-fw mr-2 text-gray-400"></i>
|
||||
Activity Log
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
|
||||
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
|
||||
Logout
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
<!-- End of Topbar -->
|
||||
|
||||
<!-- Begin Page Content -->
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Page Heading -->
|
||||
<h1 class="h3 mb-2 text-gray-800">Benutzer & Gruppen</h1>
|
||||
<?php if ($error !== null): ?>
|
||||
<p class="error">
|
||||
<?php echo htmlspecialchars($error, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<p class="mb-4">DataTables is a third party plugin that is used to generate the demo table below.
|
||||
For more information about DataTables, please visit the <a target="_blank"
|
||||
href="https://datatables.net">official DataTables documentation</a>.</p>
|
||||
|
||||
<!-- DataTales Example -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Benutzer</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="usersTable" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Anmeldename</th>
|
||||
<th>Anzeigename</th>
|
||||
<th>E-Mail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Anmeldename</th>
|
||||
<th>Anzeigename</th>
|
||||
<th>E-Mail</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($user['samaccountname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['displayname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['mail'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Gruppen</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="groupsTable" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppenname</th>
|
||||
<th>CN</th>
|
||||
<th>Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Gruppenname</th>
|
||||
<th>CN</th>
|
||||
<th>Beschreibung</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($group['samaccountname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($group['cn'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($group['description'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- /.container-fluid -->
|
||||
|
||||
</div>
|
||||
<!-- End of Main Content -->
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="sticky-footer bg-white">
|
||||
<div class="container my-auto">
|
||||
<div class="copyright text-center my-auto">
|
||||
<span>Copyright © Your Website 2020</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- End of Footer -->
|
||||
|
||||
</div>
|
||||
<!-- End of Content Wrapper -->
|
||||
|
||||
</div>
|
||||
<!-- End of Page Wrapper -->
|
||||
|
||||
<!-- Scroll to Top Button-->
|
||||
<a class="scroll-to-top rounded" href="#page-top">
|
||||
<i class="fas fa-angle-up"></i>
|
||||
</a>
|
||||
|
||||
<!-- Logout Modal-->
|
||||
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
|
||||
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
|
||||
<a class="btn btn-primary" href="login.php">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap core JavaScript-->
|
||||
<script src="../vendor/jquery/jquery.min.js"></script>
|
||||
<script src="../vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Core plugin JavaScript-->
|
||||
<script src="../vendor/jquery-easing/jquery.easing.min.js"></script>
|
||||
|
||||
<!-- Custom scripts for all pages-->
|
||||
<script src="../js/sb-admin-2.min.js"></script>
|
||||
|
||||
<!-- Page level plugins -->
|
||||
<script src="../vendor/datatables/jquery.dataTables.min.js"></script>
|
||||
<script src="../vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
||||
|
||||
<!-- Page level custom scripts -->
|
||||
<script src="../js/demo/datatables-demo.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user