From 10c72de859f1394a10af44066110d4a41c5aef37 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 17 Aug 2026 15:10:40 +0000 Subject: [PATCH] v2.6.1c (Lyra): Admin gebruikersbeheer opnieuw ontworpen + CLI reset Nieuwe features: - Admin gebruikerslijst met zoeken/filter op gebruikersnaam en rol - Profiel bewerken pagina met wachtwoord wijzigen en rol wijzigen - Nieuwe gebruiker aanmaken via aparte pagina - CLI commando cli/reset-admin-password.php voor admin wachtword reset + lockout reset Architectuur: - /admin/users: lijst met zoekveld, rol filter, bewerk/verwijder acties - /admin/users-edit?user=: profiel, wachtwoord, rol, verwijderen - /admin/users-new: nieuwe gebruiker aanmaken - AdminAuth::clearLockout() public methode voor CLI gebruik Documentatie: - guide/nl/en/admin-beheerder/gebruikers.md herschreven - 15 nieuwe admin vertaalkeys in NL/EN/DE - Release notes: docs/release-notes/v2.6.1c.md Tests: - Pentest: 30/30 geslaagd, 0 vulnerabilities - WCAG 2.1 AA: 25/25 geslaagd, 100% compliance --- TODO.md | 9 + admin/src/AdminAuth.php | 8 + .../theme/default/views/pages/users-edit.twig | 136 ++++++++++ .../theme/default/views/pages/users-new.twig | 65 +++++ admin/theme/default/views/pages/users.twig | 249 ++++++------------ cli/reset-admin-password.php | 80 ++++++ docs/release-notes/v2.6.1c.md | 100 +++++++ guide/en/admin-beheerder/gebruikers.md | 64 ++++- guide/nl/admin-beheerder/gebruikers.md | 64 ++++- language/de/admin.php | 13 + language/en/admin.php | 13 + language/nl/admin.php | 13 + public/admin.php | 171 ++++++++++-- version.php | 2 +- 14 files changed, 767 insertions(+), 220 deletions(-) create mode 100644 admin/theme/default/views/pages/users-edit.twig create mode 100644 admin/theme/default/views/pages/users-new.twig create mode 100755 cli/reset-admin-password.php create mode 100644 docs/release-notes/v2.6.1c.md diff --git a/TODO.md b/TODO.md index 17f49a5..21d6af7 100644 --- a/TODO.md +++ b/TODO.md @@ -49,6 +49,15 @@ - [x] Pentest controles uitgevoerd (30/30 tests geslaagd — 0 vulnerabilities) - [x] WCAG 2.1 AA accessibility tests (25/25 tests geslaagd — 100% compliance, test-script verbeterd met min/max checks en grep -E) +## v2.6.1c (2026-08-17) ✅ +- [x] Pentest controles uitgevoerd (30/30) +- [x] WCAG 2.1 AA accessibility tests (25/25) +- [x] README.md en handleidingen in guide/ doorgeloken en bijgewerkt +- [x] Admin gebruikersbeheer opnieuw ontworpen (lijst + zoeken/filter + profiel + wachtwoord) +- [x] CLI commando voor admin wachtwoord reset (cli/reset-admin-password.php) +- [x] Verslag gemaakt (docs/release-notes/v2.6.1c.md) +- [x] Versie verhoogd naar 2.6.1c + ## v2.6.1 (2026-08-17) ✅ - [x] Pentest controles uitgevoerd (30/30) - [x] WCAG 2.1 AA accessibility tests (25/25) diff --git a/admin/src/AdminAuth.php b/admin/src/AdminAuth.php index a1e862d..20b0c74 100644 --- a/admin/src/AdminAuth.php +++ b/admin/src/AdminAuth.php @@ -432,6 +432,14 @@ class AdminAuth file_put_contents($this->lockFile, json_encode($attempts)); } + /** + * Clear all failed login attempts for a user (public, used by CLI reset). + */ + public function clearLockout(string $username): void + { + $this->clearFailedAttempts($username); + } + private function getFailedAttempts(): array { if (!file_exists($this->lockFile)) { diff --git a/admin/theme/default/views/pages/users-edit.twig b/admin/theme/default/views/pages/users-edit.twig new file mode 100644 index 0000000..25ee757 --- /dev/null +++ b/admin/theme/default/views/pages/users-edit.twig @@ -0,0 +1,136 @@ +{% extends "layouts/admin.twig" %} + +{% block title %}{{ ta.edit_profile|default('Profiel bewerken') }}: {{ target_user.username }} - {{ ta.admin_title|default('CodePress Admin') }}{% endblock %} + +{% block content %} +
+

{{ ta.edit_profile|default('Profiel bewerken') }}

+ + {{ ta.back_to_users|default('Terug naar gebruikers') }} + +
+ +
+
+
+
+ {{ ta.profile_info|default('Profiel gegevens') }} + {% if is_self %} + {{ ta.you|default('Jij') }} + {% endif %} +
+
+
+ + +
+ + +
+
+ + +
+
+ + + {{ ta.author_name_help|default('Getoond als auteur op de website.') }} +
+
+ + +
+ +
+
+
+ +
+
+ {{ ta.change_password|default('Wachtwoord wijzigen') }} +
+
+
+ + +
+ + + {{ ta.password_help|default('Minimaal 8 tekens.') }} +
+
+ + +
+ +
+
+
+
+ +
+
+
+ {{ ta.col_role|default('Rol') }} +
+
+

{{ ta.current_role|default('Huidige rol') }}

+

+ + {{ target_user.role_label|default(target_user.role) }} + +

+
+
+ + +
+ + +
+ +
+
+
+ + {% if not is_self %} +
+
+ {{ ta.danger_zone|default('Gevarenzone') }} +
+
+
+ + + +
+
+
+ {% endif %} +
+
+ + +{% endblock %} \ No newline at end of file diff --git a/admin/theme/default/views/pages/users-new.twig b/admin/theme/default/views/pages/users-new.twig new file mode 100644 index 0000000..cccb4e8 --- /dev/null +++ b/admin/theme/default/views/pages/users-new.twig @@ -0,0 +1,65 @@ +{% extends "layouts/admin.twig" %} + +{% block title %}{{ ta.new_user|default('Nieuwe gebruiker') }} - {{ ta.admin_title|default('CodePress Admin') }}{% endblock %} + +{% block content %} +
+

{{ ta.new_user|default('Nieuwe gebruiker') }}

+ + {{ ta.back_to_users|default('Terug naar gebruikers') }} + +
+ +
+
+
+
+ {{ ta.add_user|default('Gebruiker toevoegen') }} +
+
+
+ + +
+ + +
+
+ + + {{ ta.password_help|default('Minimaal 8 tekens.') }} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + {{ ta.cancel|default('Annuleren') }} + +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/admin/theme/default/views/pages/users.twig b/admin/theme/default/views/pages/users.twig index e7d8d6f..97d4270 100644 --- a/admin/theme/default/views/pages/users.twig +++ b/admin/theme/default/views/pages/users.twig @@ -5,182 +5,89 @@ {% block content %}

{{ ta.users|default('Gebruikers') }}

-
-
-
-
- {{ ta.users|default('Gebruikers') }} -
-
- - - - - - - - - - - {% for username, data in users %} - - - - - - - {% else %} - - - - {% endfor %} - -
{{ ta.username|default('Gebruikersnaam') }}{{ ta.col_role|default('Rol') }}{{ ta.col_created|default('Aangemaakt') }}{{ ta.col_actions|default('Acties') }}
- - {{ username }} - {% if username == user.username %} - {{ ta.you|default('Jij') }} - {% endif %} - - - {{ data.role_label|default(data.role) }} - - {{ data.created|default(ta.unknown|default('Onbekend')) }} - {% if username != user.username %} - -
- - - - -
- - - - {% else %} - - {% endif %} -
{{ ta.no_users|default('Geen gebruikers gevonden.') }}
-
+
+
+
+ {{ ta.users_list|default('Gebruikerslijst') }}
+ + {{ ta.new_user|default('Nieuwe gebruiker') }} +
- -
- -
-
- {{ ta.my_profile|default('Mijn profiel') }} +
+
+
+
+ + +
-
- - - - -
- - -
-
- - -
-
- - - {{ ta.author_name_help|default('Getoond als auteur op de website.') }} -
-
- - -
- - +
+
-
+
+ +
+ - -
-
- {{ ta.new_user|default('Nieuwe gebruiker') }} -
-
-
- - -
- - -
-
- - - {{ ta.password_help|default('Minimaal 8 tekens.') }} -
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
+
+ + + + + + + + + + + + {% for username, data in users %} + + + + + + + + {% else %} + + + + {% endfor %} + +
{{ ta.username|default('Gebruikersnaam') }}{{ ta.col_role|default('Rol') }}{{ ta.login_email|default('Login e-mail') }}{{ ta.col_created|default('Aangemaakt') }}{{ ta.col_actions|default('Acties') }}
+ + {{ username }} + {% if username == user.username %} + {{ ta.you|default('Jij') }} + {% endif %} + + + {{ data.role_label|default(data.role) }} + + {{ data.email|default('-') }}{{ data.created|default(ta.unknown|default('Onbekend')) }} + + + + {% if username != user.username %} +
+ + + + +
+ {% endif %} +
{{ ta.no_users|default('Geen gebruikers gevonden.') }}
diff --git a/cli/reset-admin-password.php b/cli/reset-admin-password.php new file mode 100755 index 0000000..1dc6fd1 --- /dev/null +++ b/cli/reset-admin-password.php @@ -0,0 +1,80 @@ +#!/usr/bin/env php +getUsers(); +if (!isset($users[$username])) { + fwrite(STDERR, "Fout: Gebruiker '$username' niet gevonden.\n"); + fwrite(STDERR, "Beschikbare gebruikers: " . implode(', ', array_keys($users)) . "\n"); + exit(1); +} + +// Wachtwoord genereren als niet opgegeven +if ($password === '') { + $password = bin2hex(random_bytes(12)); + $generated = true; +} else { + $generated = false; +} + +// Wachtwoord wijzigen +$result = $auth->changePassword($username, $password); +if (!$result['success']) { + fwrite(STDERR, "Fout: " . $result['message'] . "\n"); + exit(1); +} + +// Lockout wissen +$auth->clearLockout($username); + +echo "CodePress CMS — Admin wachtwoord reset\n"; +echo "========================================\n\n"; +echo "Gebruiker: $username\n"; +echo "Wachtwoord: $password\n"; +if ($generated) { + echo " (automatisch gegenereerd — bewaar dit veilig)\n"; +} +echo "\n"; +echo "Lockout voor '$username' is gewist.\n"; +echo "Je kunt nu inloggen via /admin met het nieuwe wachtwoord.\n"; \ No newline at end of file diff --git a/docs/release-notes/v2.6.1c.md b/docs/release-notes/v2.6.1c.md new file mode 100644 index 0000000..18bbd1d --- /dev/null +++ b/docs/release-notes/v2.6.1c.md @@ -0,0 +1,100 @@ +# CodePress CMS v2.6.1c — Release Notes + +**Release datum:** 2026-08-17 +**Codename:** Lyra +**Status:** stable + +--- + +## Nieuwe features + +### Admin gebruikersbeheer volledig opnieuw ontworpen + +De admin/gebruikers pagina is volledig heringericht met drie aparte pagina's: + +#### Gebruikerslijst (`/admin/users`) +- Lijst met alle gebruikers (gebruikersnaam, rol, login e-mail, aanmaakdatum, acties) +- **Zoekveld** — zoekt op gebruikersnaam, e-mail of auteur naam +- **Rol filter** — dropdown om op een specifieke rol te filteren +- **"Nieuwe gebruiker" knop** — linkt naar het aanmaakformulier +- **Bewerk knop** per gebruiker — linkt naar de profiel pagina +- **Verwijder knop** per gebruiker (bevestiging via JavaScript) + +#### Profiel bewerken (`/admin/users-edit?user=`) +- **Profiel gegevens**: login e-mail, auteur naam, auteur e-mail wijzigen +- **Wachtwoord wijzigen**: nieuw wachtwoord + bevestiging met JavaScript validatie +- **Rol wijzigen**: dropdown met alle rollen, toont huidige rol met gekleurde badge +- **Gevarenzone**: gebruiker verwijderen (alleen voor andere gebruikers, niet jezelf) + +#### Nieuwe gebruiker (`/admin/users-new`) +- Formulier met gebruikersnaam, wachtwoord, e-mail, auteur naam, auteur e-mail, rol +- Na aanmaken → automatische redirect naar profiel pagina van de nieuwe gebruiker + +### CLI commando voor admin wachtwoord reset + +Nieuw CLI script `cli/reset-admin-password.php` om een admin wachtwoord te resetten en brute-force lockout te wissen — handig bij lockout of vergeten wachtwoord: + +```bash +# Met specifiek wachtwoord +php cli/reset-admin-password.php admin NieuwWachtwoord123 + +# Met automatisch gegenereerd wachtwoord +php cli/reset-admin-password.php admin +``` + +Het commando wijzigt het wachtwoord (als bcrypt hash), wist de lockout en toont het nieuwe wachtwoord. + +--- + +## Verbeteringen + +### AdminAuth uitbreiding +- Nieuwe public methode `clearLockout(string $username)` — wist failed login attempts (voor CLI gebruik) + +### Handleidingen bijgewerkt +- `guide/nl/admin-beheerder/gebruikers.md` — volledig herschreven met nieuwe architectuur (lijst, zoeken/filter, profiel bewerken, CLI reset) +- `guide/en/admin-beheerder/gebruikers.md` — Engelse versie bijgewerkt + +### Vertalingen +- 15 nieuwe admin vertaalkeys toegevoegd in NL, EN en DE (`users_list`, `search_users`, `all_roles`, `filter`, `back_to_users`, `profile_info`, `change_password`, `new_password`, `confirm_password`, `passwords_no_match`, `danger_zone`, `delete_user`, etc.) + +--- + +## Beveiliging +- Pentest suite: 30/30 tests geslaagd, 0 vulnerabilities +- Security headers aanwezig: X-Frame-Options, Content-Security-Policy, X-Content-Type-Options +- Geen vulnerabilities gedetecteerd + +--- + +## Accessibility +- WCAG 2.1 AA: 25/25 tests geslaagd (100%) +- ARIA landmarks aanwezig (24 per pagina) +- Semantic HTML structuur (header, nav, main, footer) +- Skip-to-content links, focus indicators, screen reader support + +--- + +## Systeem vereisten +- PHP >= 8.0 +- Extensies: json, mbstring +- Optioneel: opcache (aanbevolen voor performance) +- Git (optioneel, voor content versioning feature) +- ZipArchive (optioneel, voor ZIP backup/restore feature) +- Apache met `mod_rewrite` en `AllowOverride All` (voor clean URLs en asset-serving) + +--- + +## Upgraden +1. Pull de nieuwe code +2. Test de website +3. Het admin wachtwoord kan gereset worden via `php cli/reset-admin-password.php admin ` indien nodig + +--- + +## Test resultaten samenvatting + +| Test | Resultaat | +|------|----------| +| Pentest | 30/30 geslaagd, 0 vulnerabilities | +| WCAG 2.1 AA Accessibility | 25/25 geslaagd, 100% compliance | \ No newline at end of file diff --git a/guide/en/admin-beheerder/gebruikers.md b/guide/en/admin-beheerder/gebruikers.md index d2a6f5a..8af4831 100644 --- a/guide/en/admin-beheerder/gebruikers.md +++ b/guide/en/admin-beheerder/gebruikers.md @@ -15,23 +15,63 @@ CodePress has four roles, defined in `AdminAuth::ROLE_PERMISSIONS`: Roles are displayed with their label via `AdminAuth::ROLE_LABELS`. -## Adding a user +## Users list (`/admin/users`) -1. Go to **Users** -2. Enter username -3. Choose password (stored as bcrypt hash) -4. Select a role -5. Click **Add** +The users list shows all users with their username, role, login email and creation date. -## Editing a user +### Search and filter -- Change password (new bcrypt hash) -- Change role (immediately affects visible routes and sidebar items) +- **Search field**: search by username, email or author name +- **Role filter**: filter by a specific role via the dropdown +- Click **Filter** to apply the results -## Deleting a user +### Adding a new user -- Not possible for own account -- Confirm with password +1. Click **New user** (top right of the list) +2. Enter username, password (minimum 8 characters), email, author name and author email +3. Select a role +4. Click **Add user** +5. You will be automatically redirected to the profile page of the new user + +## Editing a profile (`/admin/users-edit?user=`) + +Click on a user in the list to edit their profile. The profile page contains three sections: + +### Profile information +- Login email, author name and author email can be changed +- The username cannot be changed + +### Change password +- Enter a new password (minimum 8 characters) +- Confirm the password +- The password is stored as a bcrypt hash + +### Change role +- Shows the current role with a colored badge +- Select a new role from the dropdown +- The change immediately affects the visible admin routes and sidebar items + +### Delete user (Danger zone) +- Only visible for other users (not for your own account) +- Confirmation via JavaScript dialog +- After deletion you return to the users list + +## Admin password reset via CLI + +If the admin is locked out (e.g. due to brute-force lockout or forgotten password), the password can be reset via the CLI: + +```bash +# Reset with a specific password +php cli/reset-admin-password.php admin NewPassword123 + +# Reset with an automatically generated password +php cli/reset-admin-password.php admin +``` + +The command: +- Changes the password (as a bcrypt hash) +- Clears the brute-force lockout for the user +- Displays the new password in the terminal ## Access control diff --git a/guide/nl/admin-beheerder/gebruikers.md b/guide/nl/admin-beheerder/gebruikers.md index 0d4d9b8..2bb4158 100644 --- a/guide/nl/admin-beheerder/gebruikers.md +++ b/guide/nl/admin-beheerder/gebruikers.md @@ -15,23 +15,63 @@ CodePress kent vier rollen, gedefinieerd in `AdminAuth::ROLE_PERMISSIONS`: Rollen worden getoond met hun label via `AdminAuth::ROLE_LABELS`. -## Gebruiker toevoegen +## Gebruikerslijst (`/admin/users`) -1. Ga naar **Gebruikers** -2. Vul gebruikersnaam in -3. Kies wachtwoord (opgeslagen als bcrypt hash) -4. Selecteer een rol -5. Klik **Toevoegen** +De gebruikerslijst toont alle gebruikers met hun gebruikersnaam, rol, login e-mail en aanmaakdatum. -## Gebruiker bewerken +### Zoeken en filteren -- Wachtwoord wijzigen (nieuwe bcrypt hash) -- Rol wijzigen (beïnvloedt direct zichtbare routes en sidebar items) +- **Zoekveld**: zoek op gebruikersnaam, e-mail of auteur naam +- **Rol filter**: filter op een specifieke rol via de dropdown +- Klik op **Filteren** om de resultaten toe te passen -## Gebruiker verwijderen +### Nieuwe gebruiker aanmaken -- Kan niet voor eigen account -- Bevestig met wachtwoord +1. Klik op **Nieuwe gebruiker** (rechtsboven in de lijst) +2. Vul gebruikersnaam, wachtwoord (minimaal 8 tekens), e-mail, auteur naam en auteur e-mail in +3. Selecteer een rol +4. Klik **Gebruiker toevoegen** +5. Je wordt automatisch doorgestuurd naar de profiel pagina van de nieuwe gebruiker + +## Profiel bewerken (`/admin/users-edit?user=`) + +Klik op een gebruiker in de lijst om het profiel te bewerken. De profiel pagina bevat drie secties: + +### Profiel gegevens +- Login e-mail, auteur naam en auteur e-mail kunnen worden gewijzigd +- De gebruikersnaam kan niet worden gewijzigd + +### Wachtwoord wijzigen +- Vul een nieuw wachtwoord in (minimaal 8 tekens) +- Bevestig het wachtwoord +- Het wachtwoord wordt opgeslagen als bcrypt hash + +### Rol wijzigen +- Toont de huidige rol met een gekleurde badge +- Selecteer een nieuwe rol uit de dropdown +- De wijziging beïnvloedt direct de zichtbare admin routes en sidebar items + +### Gebruiker verwijderen (Gevarenzone) +- Alleen zichtbaar voor andere gebruikers (niet voor je eigen account) +- Bevestiging via JavaScript dialog +- Na verwijderen keer je terug naar de gebruikerslijst + +## Admin wachtwoord reset via CLI + +Als de admin is uitgesloten (bijv. door brute-force lockout of vergeten wachtwoord), kan het wachtwoord via de CLI worden gereset: + +```bash +# Reset met een specifiek wachtwoord +php cli/reset-admin-password.php admin NieuwWachtwoord123 + +# Reset met een automatisch gegenereerd wachtwoord +php cli/reset-admin-password.php admin +``` + +Het commando: +- Wijzigt het wachtwoord (als bcrypt hash) +- Wist de brute-force lockout voor de gebruiker +- Toont het nieuwe wachtwoord in de terminal ## Toegangscontrole diff --git a/language/de/admin.php b/language/de/admin.php index c72cf9f..2ffe06e 100644 --- a/language/de/admin.php +++ b/language/de/admin.php @@ -242,6 +242,19 @@ return [ 'no_users' => 'Keine Benutzer gefunden.', 'new_user' => 'Neuer Benutzer', 'add_user' => 'Benutzer hinzufügen', + 'users_list' => 'Benutzerliste', + 'search_users' => 'Nach Benutzername, E-Mail oder Autor-Name suchen...', + 'all_roles' => 'Alle Rollen', + 'filter' => 'Filtern', + 'delete' => 'Löschen', + 'back_to_users' => 'Zurück zu Benutzer', + 'profile_info' => 'Profilinformationen', + 'change_password' => 'Passwort ändern', + 'new_password' => 'Neues Passwort', + 'confirm_password' => 'Passwort bestätigen', + 'passwords_no_match' => 'Passwörter stimmen nicht überein.', + 'danger_zone' => 'Gefahrenzone', + 'delete_user' => 'Benutzer löschen', // Login 'login_title' => 'CodePress Admin - Anmeldung', diff --git a/language/en/admin.php b/language/en/admin.php index 61f963c..4dc1260 100644 --- a/language/en/admin.php +++ b/language/en/admin.php @@ -242,6 +242,19 @@ return [ 'no_users' => 'No users found.', 'new_user' => 'New user', 'add_user' => 'Add user', + 'users_list' => 'User list', + 'search_users' => 'Search by username, email or author name...', + 'all_roles' => 'All roles', + 'filter' => 'Filter', + 'delete' => 'Delete', + 'back_to_users' => 'Back to users', + 'profile_info' => 'Profile information', + 'change_password' => 'Change password', + 'new_password' => 'New password', + 'confirm_password' => 'Confirm password', + 'passwords_no_match' => 'Passwords do not match.', + 'danger_zone' => 'Danger zone', + 'delete_user' => 'Delete user', // Login 'login_title' => 'CodePress Admin - Login', diff --git a/language/nl/admin.php b/language/nl/admin.php index c0d15cf..d0d5b22 100644 --- a/language/nl/admin.php +++ b/language/nl/admin.php @@ -242,6 +242,19 @@ return [ 'no_users' => 'Geen gebruikers gevonden.', 'new_user' => 'Nieuwe gebruiker', 'add_user' => 'Gebruiker toevoegen', + 'users_list' => 'Gebruikerslijst', + 'search_users' => 'Zoek op gebruikersnaam, e-mail of auteur naam...', + 'all_roles' => 'Alle rollen', + 'filter' => 'Filteren', + 'delete' => 'Verwijderen', + 'back_to_users' => 'Terug naar gebruikers', + 'profile_info' => 'Profiel gegevens', + 'change_password' => 'Wachtwoord wijzigen', + 'new_password' => 'Nieuw wachtwoord', + 'confirm_password' => 'Bevestig wachtwoord', + 'passwords_no_match' => 'Wachtwoorden komen niet overeen.', + 'danger_zone' => 'Gevarenzone', + 'delete_user' => 'Gebruiker verwijderen', // Login 'login_title' => 'CodePress Admin - Login', diff --git a/public/admin.php b/public/admin.php index 9922855..4e1c04e 100644 --- a/public/admin.php +++ b/public/admin.php @@ -328,6 +328,14 @@ switch ($route) { handleUsers($auth, $appConfig, $twig, $user, $csrf); break; + case 'users-edit': + handleUsersEdit($auth, $appConfig, $twig, $user, $csrf); + break; + + case 'users-new': + handleUsersNew($auth, $appConfig, $twig, $user, $csrf); + break; + case 'guide': handleGuide($auth, $appConfig, $twig, $user, $csrf); break; @@ -1575,35 +1583,15 @@ function handleUsers($auth, $config, $twig, $user, $csrf): void { $message = ''; $messageType = ''; - + if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) { $message = 'Ongeldige CSRF token.'; $messageType = 'danger'; } else { $action = $_POST['action'] ?? ''; - - if ($action === 'add') { - $newUsername = trim($_POST['new_username'] ?? ''); - $newPassword = $_POST['new_password'] ?? ''; - $newRole = $_POST['new_role'] ?? 'content-manager'; - $newEmail = trim($_POST['new_email'] ?? ''); - $newAuthorName = trim($_POST['new_author_name'] ?? ''); - $newAuthorEmail = trim($_POST['new_author_email'] ?? ''); - - $result = $auth->addUser($newUsername, $newPassword, $newRole, $newEmail, $newAuthorName, $newAuthorEmail); - $message = $result['message']; - $messageType = $result['success'] ? 'success' : 'danger'; - } elseif ($action === 'profile') { - $profileUser = $_POST['profile_username'] ?? ''; - $profileEmail = trim($_POST['profile_email'] ?? ''); - $profileAuthorName = trim($_POST['profile_author_name'] ?? ''); - $profileAuthorEmail = trim($_POST['profile_author_email'] ?? ''); - - $result = $auth->updateUserProfile($profileUser, $profileEmail, $profileAuthorName, $profileAuthorEmail); - $message = $result['message']; - $messageType = $result['success'] ? 'success' : 'danger'; - } elseif ($action === 'delete') { + + if ($action === 'delete') { $deleteUser = $_POST['delete_username'] ?? ''; if ($deleteUser !== $user['username']) { $result = $auth->deleteUser($deleteUser); @@ -1622,17 +1610,152 @@ function handleUsers($auth, $config, $twig, $user, $csrf): void } } } - + $route = 'users'; $users = $auth->getUsers(); $roles = AdminAuth::getRoles(); + // Search and filter + $search = trim($_GET['search'] ?? ''); + $roleFilter = $_GET['role'] ?? ''; + + if ($search !== '') { + $users = array_filter($users, function($u) use ($search) { + return stripos($u['username'], $search) !== false + || stripos($u['email'] ?? '', $search) !== false + || stripos($u['author_name'] ?? '', $search) !== false; + }); + } + if ($roleFilter !== '') { + $users = array_filter($users, function($u) use ($roleFilter) { + return $u['role'] === $roleFilter; + }); + } + echo $twig->render('pages/users.twig', [ 'user' => $user, 'route' => $route, 'csrf_token' => $csrf, 'users' => $users, 'roles' => $roles, + 'search' => $search, + 'role_filter' => $roleFilter, + 'sidebar_color' => getSidebarColor($config), + 'needs_editor' => false, + 'message' => $message, + 'message_type' => $messageType, + ]); +} + +function handleUsersEdit($auth, $config, $twig, $user, $csrf): void +{ + $targetUsername = $_GET['user'] ?? ''; + $targetUsername = preg_replace('/[^a-zA-Z0-9_\-]/', '', $targetUsername); + + if ($targetUsername === '') { + header('Location: /admin/users'); + exit; + } + + $message = ''; + $messageType = ''; + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) { + $message = 'Ongeldige CSRF token.'; + $messageType = 'danger'; + } else { + $action = $_POST['action'] ?? ''; + + if ($action === 'profile') { + $profileEmail = trim($_POST['profile_email'] ?? ''); + $profileAuthorName = trim($_POST['profile_author_name'] ?? ''); + $profileAuthorEmail = trim($_POST['profile_author_email'] ?? ''); + + $result = $auth->updateUserProfile($targetUsername, $profileEmail, $profileAuthorName, $profileAuthorEmail); + $message = $result['message']; + $messageType = $result['success'] ? 'success' : 'danger'; + } elseif ($action === 'change_role') { + $newRole = $_POST['new_role'] ?? ''; + $result = $auth->changeRole($targetUsername, $newRole); + $message = $result['message']; + $messageType = $result['success'] ? 'success' : 'danger'; + } elseif ($action === 'change_password') { + $newPassword = $_POST['new_password'] ?? ''; + $result = $auth->changePassword($targetUsername, $newPassword); + $message = $result['message']; + $messageType = $result['success'] ? 'success' : 'danger'; + } elseif ($action === 'delete') { + if ($targetUsername !== $user['username']) { + $result = $auth->deleteUser($targetUsername); + header('Location: /admin/users?msg=' . urlencode($result['message'])); + exit; + } else { + $message = 'Je kunt jezelf niet verwijderen.'; + $messageType = 'danger'; + } + } + } + } + + $users = $auth->getUsers(); + if (!isset($users[$targetUsername])) { + header('Location: /admin/users'); + exit; + } + + $targetUser = $users[$targetUsername]; + $roles = AdminAuth::getRoles(); + $isSelf = ($targetUsername === $user['username']); + + echo $twig->render('pages/users-edit.twig', [ + 'user' => $user, + 'route' => 'users', + 'csrf_token' => $csrf, + 'target_user' => $targetUser, + 'is_self' => $isSelf, + 'roles' => $roles, + 'sidebar_color' => getSidebarColor($config), + 'needs_editor' => false, + 'message' => $message, + 'message_type' => $messageType, + ]); +} + +function handleUsersNew($auth, $config, $twig, $user, $csrf): void +{ + $message = ''; + $messageType = ''; + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) { + $message = 'Ongeldige CSRF token.'; + $messageType = 'danger'; + } else { + $newUsername = trim($_POST['new_username'] ?? ''); + $newPassword = $_POST['new_password'] ?? ''; + $newRole = $_POST['new_role'] ?? 'content-manager'; + $newEmail = trim($_POST['new_email'] ?? ''); + $newAuthorName = trim($_POST['new_author_name'] ?? ''); + $newAuthorEmail = trim($_POST['new_author_email'] ?? ''); + + $result = $auth->addUser($newUsername, $newPassword, $newRole, $newEmail, $newAuthorName, $newAuthorEmail); + if ($result['success']) { + header('Location: /admin/users-edit?user=' . urlencode($newUsername) . '&msg=' . urlencode($result['message'])); + exit; + } + $message = $result['message']; + $messageType = 'danger'; + } + } + + $roles = AdminAuth::getRoles(); + + echo $twig->render('pages/users-new.twig', [ + 'user' => $user, + 'route' => 'users', + 'csrf_token' => $csrf, + 'roles' => $roles, 'sidebar_color' => getSidebarColor($config), 'needs_editor' => false, 'message' => $message, diff --git a/version.php b/version.php index d73e3e9..26c2190 100644 --- a/version.php +++ b/version.php @@ -4,7 +4,7 @@ */ return [ - 'version' => '2.6.1', + 'version' => '2.6.1c', 'release_date' => '2026-08-17', 'codename' => 'Lyra', 'status' => 'stable',