Rectesystem eingebaut
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# Rechtesystem
|
||||
|
||||
Diese Anleitung beschreibt das aktuelle Rechtesystem fuer die Seiten unter `public/`.
|
||||
|
||||
## Grundidee
|
||||
|
||||
Aktuell gilt:
|
||||
|
||||
- `admin` und `master` haben Vollzugriff auf alle Seiten unter `public/`
|
||||
- normale Benutzer sehen standardmaessig keine geschuetzten Seiten
|
||||
- einzelne Seiten koennen pro Benutzer durch einen `master` freigegeben werden
|
||||
- das Header-Menue passt sich an die hinterlegten Seitenrechte an
|
||||
|
||||
## Rollen
|
||||
|
||||
Es gibt zurzeit diese Rollen:
|
||||
|
||||
- `user`
|
||||
Normale Benutzerrolle ohne automatischen Zugriff auf die geschuetzten Seiten
|
||||
- `admin`
|
||||
Vollzugriff auf alle Seiten unter `public/`
|
||||
- `master`
|
||||
Vollzugriff auf alle Seiten unter `public/` plus Verwaltung der Seitenfreigaben anderer Benutzer
|
||||
|
||||
Die Rollen-Helfer liegen in [auth_helpers.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/auth_helpers.php).
|
||||
|
||||
## Zentrale Logik
|
||||
|
||||
Die zentrale Rechtebasis besteht aus drei Bausteinen:
|
||||
|
||||
- [auth_helpers.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/auth_helpers.php)
|
||||
Enthält Rollen-Helfer, Allowlist und die Liste der freigebbaren Seiten
|
||||
- [header.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/header.php)
|
||||
Prueft beim Aufruf einer Seite den Zugriff und baut das Menue passend zu den Rechten
|
||||
- [admin_users.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/admin_users.php)
|
||||
Enthält die Benutzerverwaltung und die Seitenmatrix fuer `master`
|
||||
|
||||
## Datenbank
|
||||
|
||||
Fuer die Seitenfreigaben wird diese Tabelle verwendet:
|
||||
|
||||
- `app_user_page_permissions`
|
||||
|
||||
Wichtige Spalten:
|
||||
|
||||
- `user_id`
|
||||
- `page_key`
|
||||
|
||||
`page_key` entspricht dem relativen Seitenpfad unter `public/`, zum Beispiel:
|
||||
|
||||
- `moonphase.php`
|
||||
- `tcrb_lightcurve.php`
|
||||
- `geocron/index.php`
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Nicht nur der Dateiname zaehlt.
|
||||
- Unterordner-Seiten werden mit vollem relativen Pfad gespeichert.
|
||||
|
||||
## Oeffentliche Ausnahmen
|
||||
|
||||
Einige Seiten bleiben ohne Seitenfreigabe erreichbar. Diese stehen in der Allowlist in [auth_helpers.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/auth_helpers.php):
|
||||
|
||||
- `login.php`
|
||||
- `register.php`
|
||||
- `logout.php`
|
||||
|
||||
## Wie der Zugriff geprueft wird
|
||||
|
||||
Beim Laden einer Seite mit gemeinsamem Header passiert Folgendes:
|
||||
|
||||
1. `header.php` ermittelt den aktuellen `page_key`
|
||||
2. Wenn der Benutzer `admin` oder `master` ist, ist der Zugriff erlaubt
|
||||
3. Wenn die Seite in der Allowlist steht, ist der Zugriff erlaubt
|
||||
4. Sonst wird in `app_user_page_permissions` geprueft, ob der Benutzer genau diese Seite sehen darf
|
||||
5. Wenn nein, kommt `403 Zugriff verweigert`
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Eine einzelne Seitenfreigabe macht den Benutzer nicht zu `admin`
|
||||
- dadurch bleibt das Menue auf seine wirklich erlaubten Seiten beschraenkt
|
||||
|
||||
## Wie das Menue funktioniert
|
||||
|
||||
Das Header-Menue wird ebenfalls in [header.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/header.php) aufgebaut.
|
||||
|
||||
Dabei gilt:
|
||||
|
||||
- `admin` und `master` sehen das volle Menue
|
||||
- normale Benutzer sehen nur die Menuepunkte, deren `href` in ihren Seitenfreigaben vorhanden ist
|
||||
- auch der Kontobereich unten im Menue wird entsprechend gefiltert
|
||||
|
||||
Damit das funktioniert, muessen Menuelink und `page_key` exakt zusammenpassen.
|
||||
|
||||
Beispiel:
|
||||
|
||||
- Menuelink: `geocron/index.php`
|
||||
- Datenbankeintrag: `geocron/index.php`
|
||||
|
||||
## Wie ein Master Seiten freigibt
|
||||
|
||||
Die Freigaben werden in [admin_users.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/admin_users.php) verwaltet.
|
||||
|
||||
Ablauf:
|
||||
|
||||
1. Als `master` anmelden
|
||||
2. `Benutzerverwaltung` oeffnen
|
||||
3. Im Bereich `Seitenfreigaben` den gewuenschten Benutzer waehlen
|
||||
4. Haken bei den erlaubten Seiten setzen
|
||||
5. Speichern
|
||||
|
||||
Besonderheiten:
|
||||
|
||||
- `admin` und `master` brauchen keine Einzelhaken, weil ihre Rolle bereits Vollzugriff gibt
|
||||
- nur ein `master` darf Seitenfreigaben bearbeiten
|
||||
|
||||
## Wo die freigebaren Seiten definiert sind
|
||||
|
||||
Die Liste der freigebaren Seiten steht zentral in [auth_helpers.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/auth_helpers.php) in:
|
||||
|
||||
- `manageablePublicPages()`
|
||||
|
||||
Nur Seiten, die dort eingetragen sind, koennen:
|
||||
|
||||
- in der Matrix erscheinen
|
||||
- einzeln fuer Benutzer freigegeben werden
|
||||
- sauber im Menue gefiltert werden
|
||||
|
||||
## Neue Seite ins Rechtesystem aufnehmen
|
||||
|
||||
Wenn eine neue Seite unter `public/` dazukommt, sind in der Regel diese Schritte noetig:
|
||||
|
||||
1. Seite anlegen
|
||||
2. Seite so bauen, dass sie den gemeinsamen Header nutzt
|
||||
3. In `manageablePublicPages()` eintragen
|
||||
4. Falls noetig als Menuepunkt in [header.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/header.php) eintragen
|
||||
5. Darauf achten, dass `href` und `page_key` exakt gleich sind
|
||||
|
||||
Beispiel:
|
||||
|
||||
```php
|
||||
'beispielseite.php' => 'Beispielseite',
|
||||
```
|
||||
|
||||
Oder fuer Unterordner:
|
||||
|
||||
```php
|
||||
'tools/beispiel.php' => 'Beispielseite',
|
||||
```
|
||||
|
||||
## Wichtige Regel fuer Unterordner
|
||||
|
||||
Bei Unterordnern niemals nur `index.php` verwenden.
|
||||
|
||||
Richtig:
|
||||
|
||||
- `geocron/index.php`
|
||||
|
||||
Falsch:
|
||||
|
||||
- `index.php`
|
||||
|
||||
Die Seitenerkennung dafuer macht `currentPublicPageKey()` in [auth_helpers.php](/c:/Users/ich/source/repos/skyview.astronomiemuseum.de/public/auth_helpers.php).
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
Der aktuelle Zustand ist bewusst streng:
|
||||
|
||||
- standardmaessig nur `admin` oder `master`
|
||||
- normale Benutzer nur ueber explizite Seitenfreigaben
|
||||
|
||||
Spaeter kann entschieden werden, welche Seiten wieder standardmaessig fuer normale Benutzer sichtbar sein sollen.
|
||||
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"version": 1,
|
||||
"scope": "public",
|
||||
"roles": {
|
||||
"user": {
|
||||
"label": "Normale Benutzerrolle",
|
||||
"full_public_access": false,
|
||||
"can_manage_page_permissions": false
|
||||
},
|
||||
"admin": {
|
||||
"label": "Administrator",
|
||||
"full_public_access": true,
|
||||
"can_manage_page_permissions": false
|
||||
},
|
||||
"master": {
|
||||
"label": "Master",
|
||||
"full_public_access": true,
|
||||
"can_manage_page_permissions": true
|
||||
}
|
||||
},
|
||||
"public_access_allowlist": [
|
||||
"login.php",
|
||||
"register.php",
|
||||
"logout.php"
|
||||
],
|
||||
"manageable_pages": {
|
||||
"index.php": {
|
||||
"label": "Startseite",
|
||||
"menu_group": null
|
||||
},
|
||||
"settings.php": {
|
||||
"label": "Einstellungen",
|
||||
"menu_group": "Konto"
|
||||
},
|
||||
"my_favorites.php": {
|
||||
"label": "Favoriten",
|
||||
"menu_group": "Konto"
|
||||
},
|
||||
"moonphase.php": {
|
||||
"label": "Mondphase",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"planetensichtbarkeit.php": {
|
||||
"label": "Planetensichtbarkeit",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"tcrb_lightcurve.php": {
|
||||
"label": "T CrB Lichtkurve",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"solarsystem.php": {
|
||||
"label": "Sonnensystem",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"jupitersystem.php": {
|
||||
"label": "Jupitersystem",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"tagbogen.php": {
|
||||
"label": "Tagbogen",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"sternenhimmel.php": {
|
||||
"label": "Sternenhimmel",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"meteorshowers.php": {
|
||||
"label": "Meteorstroeme",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"geocron/index.php": {
|
||||
"label": "Geocron",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"geocron3d.php": {
|
||||
"label": "Geocron 3D",
|
||||
"menu_group": "Himmel & Planeten"
|
||||
},
|
||||
"mondfinsternis.php": {
|
||||
"label": "Mondfinsternis",
|
||||
"menu_group": "Finsternisse & Bedeckungen"
|
||||
},
|
||||
"sonnenfinsternis.php": {
|
||||
"label": "Sonnenfinsternis",
|
||||
"menu_group": "Finsternisse & Bedeckungen"
|
||||
},
|
||||
"bedeckung.php": {
|
||||
"label": "Bedeckungsphaenomene",
|
||||
"menu_group": "Finsternisse & Bedeckungen"
|
||||
},
|
||||
"sternbedeckungen.php": {
|
||||
"label": "Mond-Stern-Bedeckungen",
|
||||
"menu_group": "Finsternisse & Bedeckungen"
|
||||
},
|
||||
"monatsvorhersage.php": {
|
||||
"label": "Monatsvorhersage",
|
||||
"menu_group": "Administration"
|
||||
},
|
||||
"wochenvorhersage.php": {
|
||||
"label": "Wochenvorhersage",
|
||||
"menu_group": null
|
||||
},
|
||||
"satellitenhimmel.php": {
|
||||
"label": "Satellitenhimmel",
|
||||
"menu_group": "Konto"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"main_groups": [
|
||||
{
|
||||
"title": "Himmel & Planeten",
|
||||
"links": [
|
||||
"moonphase.php",
|
||||
"planetensichtbarkeit.php",
|
||||
"tcrb_lightcurve.php",
|
||||
"solarsystem.php",
|
||||
"jupitersystem.php",
|
||||
"tagbogen.php",
|
||||
"sternenhimmel.php",
|
||||
"meteorshowers.php",
|
||||
"geocron/index.php",
|
||||
"geocron3d.php"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Finsternisse & Bedeckungen",
|
||||
"links": [
|
||||
"mondfinsternis.php",
|
||||
"sonnenfinsternis.php",
|
||||
"bedeckung.php",
|
||||
"sternbedeckungen.php"
|
||||
]
|
||||
}
|
||||
],
|
||||
"account_links": [
|
||||
"settings.php",
|
||||
"my_favorites.php",
|
||||
"satellitenhimmel.php"
|
||||
],
|
||||
"admin_links": [
|
||||
"admin_users.php",
|
||||
"monatsvorhersage.php",
|
||||
"telegram/broadcast_test.php"
|
||||
]
|
||||
},
|
||||
"database": {
|
||||
"page_permissions_table": "app_user_page_permissions",
|
||||
"page_permissions_columns": [
|
||||
"user_id",
|
||||
"page_key"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"default_policy": "deny",
|
||||
"single_page_permission_does_not_grant_admin": true,
|
||||
"menu_visibility_follows_page_permissions": true,
|
||||
"subfolder_pages_use_relative_path_keys": true
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,21 @@ CREATE TABLE IF NOT EXISTS `app_users` (
|
||||
|
||||
-- Daten-Export vom Benutzer nicht ausgewählt
|
||||
|
||||
-- Exportiere Struktur von Tabelle d0467f49.app_user_page_permissions
|
||||
CREATE TABLE IF NOT EXISTS `app_user_page_permissions` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`page_key` varchar(100) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_app_user_page_permissions_user_page` (`user_id`,`page_key`),
|
||||
KEY `idx_app_user_page_permissions_page_key` (`page_key`),
|
||||
CONSTRAINT `fk_app_user_page_permissions_user_id` FOREIGN KEY (`user_id`) REFERENCES `app_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Daten-Export vom Benutzer nicht ausgewählt
|
||||
|
||||
-- Exportiere Struktur von Tabelle d0467f49.app_user_locations
|
||||
CREATE TABLE IF NOT EXISTS `app_user_locations` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
|
||||
@@ -528,3 +528,274 @@
|
||||
[Sun Apr 5 10:15:53 2026] 127.0.0.1:61416 Accepted
|
||||
[Sun Apr 5 10:18:37 2026] 127.0.0.1:61416 Closed without sending a request; it was probably just an unused speculative preconnection
|
||||
[Sun Apr 5 10:18:37 2026] 127.0.0.1:61416 Closing
|
||||
[Sun Apr 5 10:42:17 2026] 127.0.0.1:64278 Accepted
|
||||
[Sun Apr 5 10:42:17 2026] 127.0.0.1:64278 [200]: GET /moonphase.php
|
||||
[Sun Apr 5 10:42:17 2026] 127.0.0.1:64278 Closing
|
||||
[Sun Apr 5 10:42:17 2026] 127.0.0.1:54179 Accepted
|
||||
[Sun Apr 5 10:42:17 2026] 127.0.0.1:54179 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:42:17 2026] 127.0.0.1:54179 Closing
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:56188 Accepted
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:54704 Accepted
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:56188 [200]: GET /js/three.module.min.js
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:54704 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:54704 Closing
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:56188 Closing
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:54315 Accepted
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:54315 [200]: GET /js/three.core.min.js
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:54315 Closing
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:64368 Accepted
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:64368 [200]: POST /moonphase.php
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:64368 Closing
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:53158 Accepted
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:49519 Accepted
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:53158 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:53158 Closing
|
||||
[Sun Apr 5 10:42:18 2026] 127.0.0.1:49519 [200]: GET /images/moons/moon_8192.jpg
|
||||
[Sun Apr 5 10:42:19 2026] 127.0.0.1:49910 Accepted
|
||||
[Sun Apr 5 10:42:19 2026] 127.0.0.1:49910 [200]: GET /images/moons/moon_displacement_16bit.jpg
|
||||
[Sun Apr 5 10:42:19 2026] 127.0.0.1:49910 Closing
|
||||
[Sun Apr 5 10:42:19 2026] 127.0.0.1:49519 Closing
|
||||
[Sun Apr 5 10:42:20 2026] 127.0.0.1:54809 Accepted
|
||||
[Sun Apr 5 10:42:21 2026] 127.0.0.1:54809 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 10:42:21 2026] 127.0.0.1:54809 Closing
|
||||
[Sun Apr 5 10:42:21 2026] 127.0.0.1:61361 Accepted
|
||||
[Sun Apr 5 10:44:00 2026] 127.0.0.1:61361 Closed without sending a request; it was probably just an unused speculative preconnection
|
||||
[Sun Apr 5 10:44:00 2026] 127.0.0.1:61361 Closing
|
||||
[Sun Apr 5 10:51:22 2026] 127.0.0.1:51086 Accepted
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:51086 [200]: GET /tcrb_lightcurve.php
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:51086 Closing
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:53270 Accepted
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:53270 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:53270 Closing
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:49254 Accepted
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:54706 Accepted
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:49254 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:54706 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:49254 Closing
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:54706 Closing
|
||||
[Sun Apr 5 10:51:23 2026] 127.0.0.1:54380 Accepted
|
||||
[Sun Apr 5 10:51:24 2026] 127.0.0.1:54380 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 10:51:24 2026] 127.0.0.1:54380 Closing
|
||||
[Sun Apr 5 10:51:24 2026] 127.0.0.1:58138 Accepted
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:58138 [302]: GET /logout.php
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:58138 Closing
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:52689 Accepted
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:52689 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:52689 Closing
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:49930 Accepted
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:49930 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:49930 Closing
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:53553 Accepted
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:53553 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:53553 Closing
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:59673 Accepted
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:61110 Accepted
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:59673 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:59673 Closing
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:61110 [302]: GET /favicon.ico
|
||||
[Sun Apr 5 10:51:34 2026] 127.0.0.1:61110 Closing
|
||||
[Sun Apr 5 10:51:35 2026] 127.0.0.1:49921 Accepted
|
||||
[Sun Apr 5 10:51:35 2026] 127.0.0.1:49921 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:35 2026] 127.0.0.1:49921 Closing
|
||||
[Sun Apr 5 10:51:35 2026] 127.0.0.1:54437 Accepted
|
||||
[Sun Apr 5 10:51:40 2026] 127.0.0.1:54437 [302]: GET /bedeckung.php
|
||||
[Sun Apr 5 10:51:40 2026] 127.0.0.1:54437 Closing
|
||||
[Sun Apr 5 10:51:40 2026] 127.0.0.1:51986 Accepted
|
||||
[Sun Apr 5 10:51:40 2026] 127.0.0.1:51986 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:40 2026] 127.0.0.1:51986 Closing
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:61653 Accepted
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:61653 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:61653 Closing
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:53964 Accepted
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:53964 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:53964 Closing
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:65320 Accepted
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:51650 Accepted
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:65320 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:65320 Closing
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:51650 [302]: GET /favicon.ico
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:51650 Closing
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:59582 Accepted
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:59582 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:59582 Closing
|
||||
[Sun Apr 5 10:51:41 2026] 127.0.0.1:57266 Accepted
|
||||
[Sun Apr 5 10:51:43 2026] 127.0.0.1:57266 [302]: GET /tcrb_lightcurve.php
|
||||
[Sun Apr 5 10:51:43 2026] 127.0.0.1:57266 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:63119 Accepted
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:63119 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:63119 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:52558 Accepted
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:52558 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:52558 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:60412 Accepted
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:60412 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:60412 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:64866 Accepted
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:54717 Accepted
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:64866 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:64866 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:54717 [302]: GET /favicon.ico
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:54717 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:53140 Accepted
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:53140 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:53140 Closing
|
||||
[Sun Apr 5 10:51:44 2026] 127.0.0.1:59107 Accepted
|
||||
[Sun Apr 5 10:51:50 2026] 127.0.0.1:59107 [200]: GET /register.php
|
||||
[Sun Apr 5 10:51:50 2026] 127.0.0.1:59107 Closing
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:49407 Accepted
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:49407 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:49407 Closing
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:55522 Accepted
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:55522 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:55522 Closing
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:55160 Accepted
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:62368 Accepted
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:55160 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:55160 Closing
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:62368 [302]: GET /favicon.ico
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:62368 Closing
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:57638 Accepted
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:57638 [200]: GET /login.php
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:57638 Closing
|
||||
[Sun Apr 5 10:51:51 2026] 127.0.0.1:59720 Accepted
|
||||
[Sun Apr 5 10:52:27 2026] PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen() in C:\Users\ich\source\repos\skyview.astronomiemuseum.de\public\register.php:80
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in C:\Users\ich\source\repos\skyview.astronomiemuseum.de\public\register.php on line 80
|
||||
[Sun Apr 5 10:52:27 2026] 127.0.0.1:59720 [200]: POST /register.php - Uncaught Error: Call to undefined function mb_strlen() in C:\Users\ich\source\repos\skyview.astronomiemuseum.de\public\register.php:80
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in C:\Users\ich\source\repos\skyview.astronomiemuseum.de\public\register.php on line 80
|
||||
[Sun Apr 5 10:52:27 2026] 127.0.0.1:59720 Closing
|
||||
[Sun Apr 5 10:52:27 2026] 127.0.0.1:57967 Accepted
|
||||
[Sun Apr 5 10:52:27 2026] 127.0.0.1:57967 [302]: GET /favicon.ico
|
||||
[Sun Apr 5 10:52:27 2026] 127.0.0.1:57967 Closing
|
||||
[Sun Apr 5 10:52:28 2026] 127.0.0.1:59506 Accepted
|
||||
[Sun Apr 5 10:52:28 2026] 127.0.0.1:59506 [200]: GET /login.php
|
||||
[Sun Apr 5 10:52:28 2026] 127.0.0.1:59506 Closing
|
||||
[Sun Apr 5 10:52:28 2026] 127.0.0.1:52354 Accepted
|
||||
[Sun Apr 5 10:53:34 2026] 127.0.0.1:52354 Closed without sending a request; it was probably just an unused speculative preconnection
|
||||
[Sun Apr 5 10:53:34 2026] 127.0.0.1:52354 Closing
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:49712 Accepted
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:49712 [302]: GET /
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:49712 Closing
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:60156 Accepted
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:60156 [200]: GET /login.php
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:60156 Closing
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:60139 Accepted
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:60139 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:59:42 2026] 127.0.0.1:60139 Closing
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:50258 Accepted
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:55895 Accepted
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:50258 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:55895 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:50258 Closing
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:55895 Closing
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:61134 Accepted
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:61134 [302]: GET /favicon.ico
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:61134 Closing
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:64912 Accepted
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:64912 [200]: GET /login.php
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:64912 Closing
|
||||
[Sun Apr 5 10:59:43 2026] 127.0.0.1:50335 Accepted
|
||||
[Sun Apr 5 10:59:52 2026] 127.0.0.1:50335 [302]: POST /login.php
|
||||
[Sun Apr 5 10:59:52 2026] 127.0.0.1:50335 Closing
|
||||
[Sun Apr 5 10:59:52 2026] 127.0.0.1:64873 Accepted
|
||||
[Sun Apr 5 10:59:53 2026] 127.0.0.1:64873 [200]: GET /index.php
|
||||
[Sun Apr 5 10:59:53 2026] 127.0.0.1:64873 Closing
|
||||
[Sun Apr 5 10:59:53 2026] 127.0.0.1:50230 Accepted
|
||||
[Sun Apr 5 10:59:53 2026] 127.0.0.1:50230 [200]: GET /css/style.css
|
||||
[Sun Apr 5 10:59:53 2026] 127.0.0.1:50230 Closing
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:52984 Accepted
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:53538 Accepted
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:52984 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:53538 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:52984 Closing
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:53538 Closing
|
||||
[Sun Apr 5 10:59:54 2026] 127.0.0.1:55187 Accepted
|
||||
[Sun Apr 5 10:59:55 2026] 127.0.0.1:55187 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 10:59:55 2026] 127.0.0.1:55187 Closing
|
||||
[Sun Apr 5 10:59:55 2026] 127.0.0.1:52923 Accepted
|
||||
[Sun Apr 5 11:00:00 2026] 127.0.0.1:52923 [200]: GET /admin_users.php
|
||||
[Sun Apr 5 11:00:00 2026] 127.0.0.1:52923 Closing
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:51769 Accepted
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:51769 [200]: GET /css/style.css
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:51769 Closing
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:61907 Accepted
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:61907 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:61907 Closing
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:54942 Accepted
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:56778 Accepted
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:54942 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 11:00:01 2026] 127.0.0.1:54942 Closing
|
||||
[Sun Apr 5 11:00:02 2026] 127.0.0.1:56778 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 11:00:02 2026] 127.0.0.1:56778 Closing
|
||||
[Sun Apr 5 11:03:03 2026] 127.0.0.1:62953 Accepted
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:62953 [200]: GET /admin_users.php
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:62953 Closing
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:51691 Accepted
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:51691 [200]: GET /css/style.css
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:51691 Closing
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:52469 Accepted
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:57404 Accepted
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:52469 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:57404 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:52469 Closing
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:57404 Closing
|
||||
[Sun Apr 5 11:03:04 2026] 127.0.0.1:49325 Accepted
|
||||
[Sun Apr 5 11:03:05 2026] 127.0.0.1:49325 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 11:03:05 2026] 127.0.0.1:49325 Closing
|
||||
[Sun Apr 5 11:03:05 2026] 127.0.0.1:51057 Accepted
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:51057 Closed without sending a request; it was probably just an unused speculative preconnection
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:51057 Closing
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:60059 Accepted
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:60059 [200]: GET /admin_users.php
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:60059 Closing
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:63636 Accepted
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:63636 [200]: GET /css/style.css
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:63636 Closing
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:60668 Accepted
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:60668 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:60668 Closing
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:57182 Accepted
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:52683 Accepted
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:57182 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 11:05:18 2026] 127.0.0.1:57182 Closing
|
||||
[Sun Apr 5 11:05:19 2026] 127.0.0.1:52683 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 11:05:19 2026] 127.0.0.1:52683 Closing
|
||||
[Sun Apr 5 11:05:37 2026] 127.0.0.1:52585 Accepted
|
||||
[Sun Apr 5 11:05:37 2026] 127.0.0.1:52585 [302]: POST /admin_users.php
|
||||
[Sun Apr 5 11:05:37 2026] 127.0.0.1:52585 Closing
|
||||
[Sun Apr 5 11:05:37 2026] 127.0.0.1:61429 Accepted
|
||||
[Sun Apr 5 11:05:37 2026] 127.0.0.1:61429 [200]: GET /admin_users.php
|
||||
[Sun Apr 5 11:05:37 2026] 127.0.0.1:61429 Closing
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:64587 Accepted
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:64587 [200]: GET /css/style.css
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:64587 Closing
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:57024 Accepted
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:57024 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:57024 Closing
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:56396 Accepted
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:60199 Accepted
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:56396 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 11:05:38 2026] 127.0.0.1:56396 Closing
|
||||
[Sun Apr 5 11:05:39 2026] 127.0.0.1:60199 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 11:05:39 2026] 127.0.0.1:60199 Closing
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:55123 Accepted
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:55123 [302]: POST /admin_users.php
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:55123 Closing
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:63224 Accepted
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:63224 [200]: GET /admin_users.php
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:63224 Closing
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:56558 Accepted
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:56558 [200]: GET /css/style.css
|
||||
[Sun Apr 5 11:12:43 2026] 127.0.0.1:56558 Closing
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:64536 Accepted
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:64536 [200]: GET /images/star16x16.png
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:64536 Closing
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:54192 Accepted
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:55784 Accepted
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:54192 [200]: GET /images/star16x16_rays.png
|
||||
[Sun Apr 5 11:12:44 2026] 127.0.0.1:54192 Closing
|
||||
[Sun Apr 5 11:12:45 2026] 127.0.0.1:55784 [200]: GET /favicon.ico
|
||||
[Sun Apr 5 11:12:45 2026] 127.0.0.1:55784 Closing
|
||||
|
||||
+221
-14
@@ -10,6 +10,8 @@ if (!isset($_SESSION['user_id'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/auth_helpers.php';
|
||||
|
||||
$config = require __DIR__ . '/../config/database.php';
|
||||
|
||||
$dsn = sprintf(
|
||||
@@ -49,6 +51,28 @@ function redirectToAdminUsers(): void
|
||||
exit;
|
||||
}
|
||||
|
||||
function isPrivilegedRole(?string $role): bool
|
||||
{
|
||||
return isAdminLikeRole($role);
|
||||
}
|
||||
|
||||
function ensureUserPagePermissionsTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE IF NOT EXISTS `app_user_page_permissions` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`page_key` varchar(100) NOT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_app_user_page_permissions_user_page` (`user_id`, `page_key`),
|
||||
KEY `idx_app_user_page_permissions_page_key` (`page_key`),
|
||||
CONSTRAINT `fk_app_user_page_permissions_user_id` FOREIGN KEY (`user_id`) REFERENCES `app_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
");
|
||||
}
|
||||
|
||||
function sendActivationNotification(string $username, string $email, ?string $displayName): void
|
||||
{
|
||||
$subject = 'Dein SkyView-Konto wurde freigeschaltet';
|
||||
@@ -99,7 +123,7 @@ $currentUser = $stmtCurrentUser->fetch();
|
||||
|
||||
if (
|
||||
!$currentUser ||
|
||||
$currentUser['role'] !== 'admin' ||
|
||||
!isAdminLikeRole((string) $currentUser['role']) ||
|
||||
(int)$currentUser['is_active'] !== 1
|
||||
) {
|
||||
$_SESSION = [];
|
||||
@@ -108,6 +132,13 @@ if (
|
||||
exit;
|
||||
}
|
||||
|
||||
$currentUserIsMaster = isMasterRole((string) ($currentUser['role'] ?? ''));
|
||||
$manageablePages = manageablePublicPages();
|
||||
|
||||
if ($currentUserIsMaster) {
|
||||
ensureUserPagePermissionsTable($pdo);
|
||||
}
|
||||
|
||||
if (isset($_SESSION['admin_users_flash_success'])) {
|
||||
$successMessage = (string)$_SESSION['admin_users_flash_success'];
|
||||
unset($_SESSION['admin_users_flash_success']);
|
||||
@@ -140,6 +171,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$targetUser = $stmtTargetUser->fetch();
|
||||
}
|
||||
|
||||
if ($action === 'update_page_permissions' && !$currentUserIsMaster) {
|
||||
$errors[] = 'Nur ein Master-Benutzer darf Seitenfreigaben verwalten.';
|
||||
}
|
||||
|
||||
if (!$targetUser) {
|
||||
$errors[] = 'Das ausgewählte Benutzerkonto wurde nicht gefunden.';
|
||||
}
|
||||
@@ -148,40 +183,66 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$errors[] = 'Das eigene Administrationskonto kann hier nicht deaktiviert, geloescht oder umgestellt werden.';
|
||||
}
|
||||
|
||||
$currentUserIsMaster = isMasterRole((string) ($currentUser['role'] ?? ''));
|
||||
$targetUserIsMaster = $targetUser ? isMasterRole((string) ($targetUser['role'] ?? '')) : false;
|
||||
$requestedRole = (string) ($_POST['role'] ?? 'user');
|
||||
|
||||
if (empty($errors) && $targetUser) {
|
||||
$stmtActiveAdminCount = $pdo->query("
|
||||
SELECT COUNT(*)
|
||||
FROM `app_users`
|
||||
WHERE `role` = 'admin' AND `is_active` = 1
|
||||
WHERE `role` IN ('admin', 'master') AND `is_active` = 1
|
||||
");
|
||||
$activeAdminCount = (int)$stmtActiveAdminCount->fetchColumn();
|
||||
|
||||
$stmtMasterCount = $pdo->query("
|
||||
SELECT COUNT(*)
|
||||
FROM `app_users`
|
||||
WHERE `role` = 'master'
|
||||
");
|
||||
$masterCount = (int)$stmtMasterCount->fetchColumn();
|
||||
|
||||
if (
|
||||
!$currentUserIsMaster &&
|
||||
$targetUserIsMaster
|
||||
) {
|
||||
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder aendern.';
|
||||
}
|
||||
|
||||
if (
|
||||
!$currentUserIsMaster &&
|
||||
$requestedRole === 'master' &&
|
||||
$masterCount > 0
|
||||
) {
|
||||
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder aendern.';
|
||||
}
|
||||
|
||||
if (
|
||||
$action === 'deactivate_user' &&
|
||||
$targetUser['role'] === 'admin' &&
|
||||
isPrivilegedRole((string) $targetUser['role']) &&
|
||||
(int)$targetUser['is_active'] === 1 &&
|
||||
$activeAdminCount <= 1
|
||||
) {
|
||||
$errors[] = 'Der letzte aktive Admin kann nicht deaktiviert werden.';
|
||||
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht deaktiviert werden.';
|
||||
}
|
||||
|
||||
if (
|
||||
$action === 'change_role' &&
|
||||
$targetUser['role'] === 'admin' &&
|
||||
($_POST['role'] ?? '') !== 'admin' &&
|
||||
isPrivilegedRole((string) $targetUser['role']) &&
|
||||
!isPrivilegedRole($requestedRole) &&
|
||||
(int)$targetUser['is_active'] === 1 &&
|
||||
$activeAdminCount <= 1
|
||||
) {
|
||||
$errors[] = 'Der letzte aktive Admin kann nicht zur Benutzerrolle herabgestuft werden.';
|
||||
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht zur Benutzerrolle herabgestuft werden.';
|
||||
}
|
||||
|
||||
if (
|
||||
$action === 'delete_user' &&
|
||||
$targetUser['role'] === 'admin' &&
|
||||
isPrivilegedRole((string) $targetUser['role']) &&
|
||||
(int)$targetUser['is_active'] === 1 &&
|
||||
$activeAdminCount <= 1
|
||||
) {
|
||||
$errors[] = 'Der letzte aktive Admin kann nicht geloescht werden.';
|
||||
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht geloescht werden.';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,9 +282,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if ($action === 'change_role') {
|
||||
$newRole = (string)($_POST['role'] ?? 'user');
|
||||
$newRole = $requestedRole;
|
||||
|
||||
if (!in_array($newRole, ['user', 'admin'], true)) {
|
||||
if (!in_array($newRole, ['user', 'admin', 'master'], true)) {
|
||||
$errors[] = 'Die gewählte Rolle ist ungültig.';
|
||||
} else {
|
||||
$stmtUpdate = $pdo->prepare("
|
||||
@@ -251,6 +312,59 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$_SESSION['admin_users_flash_success'] = 'Benutzerkonto wurde geloescht. Zugehoerige Standorte und Satelliten wurden automatisch entfernt.';
|
||||
redirectToAdminUsers();
|
||||
}
|
||||
|
||||
if ($action === 'update_page_permissions' && $currentUserIsMaster) {
|
||||
$submittedPages = array_values(array_unique(array_filter(array_map(
|
||||
static fn ($value): string => trim((string) $value),
|
||||
is_array($_POST['page_keys'] ?? null) ? $_POST['page_keys'] : []
|
||||
), static fn (string $value): bool => $value !== '')));
|
||||
|
||||
$submittedPages = array_values(array_intersect($submittedPages, array_keys($manageablePages)));
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmtDeletePermissions = $pdo->prepare("
|
||||
DELETE FROM `app_user_page_permissions`
|
||||
WHERE `user_id` = :user_id
|
||||
");
|
||||
$stmtDeletePermissions->execute([
|
||||
':user_id' => $targetUserId,
|
||||
]);
|
||||
|
||||
if ($submittedPages !== []) {
|
||||
$stmtInsertPermission = $pdo->prepare("
|
||||
INSERT INTO `app_user_page_permissions` (
|
||||
`user_id`,
|
||||
`page_key`,
|
||||
`created_at`,
|
||||
`updated_at`
|
||||
) VALUES (
|
||||
:user_id,
|
||||
:page_key,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
");
|
||||
|
||||
foreach ($submittedPages as $pageKey) {
|
||||
$stmtInsertPermission->execute([
|
||||
':user_id' => $targetUserId,
|
||||
':page_key' => $pageKey,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$_SESSION['admin_users_flash_success'] = 'Die Seitenfreigaben wurden aktualisiert.';
|
||||
redirectToAdminUsers();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
@@ -264,6 +378,7 @@ $stats = [
|
||||
'active' => 0,
|
||||
'pending' => 0,
|
||||
'admins' => 0,
|
||||
'masters' => 0,
|
||||
];
|
||||
|
||||
$stmtStats = $pdo->query("
|
||||
@@ -271,7 +386,8 @@ $stmtStats = $pdo->query("
|
||||
COUNT(*) AS total_users,
|
||||
SUM(CASE WHEN `is_active` = 1 THEN 1 ELSE 0 END) AS active_users,
|
||||
SUM(CASE WHEN `is_active` = 0 THEN 1 ELSE 0 END) AS pending_users,
|
||||
SUM(CASE WHEN `role` = 'admin' THEN 1 ELSE 0 END) AS admin_users
|
||||
SUM(CASE WHEN `role` = 'admin' THEN 1 ELSE 0 END) AS admin_users,
|
||||
SUM(CASE WHEN `role` = 'master' THEN 1 ELSE 0 END) AS master_users
|
||||
FROM `app_users`
|
||||
");
|
||||
$statsRow = $stmtStats->fetch();
|
||||
@@ -280,6 +396,7 @@ if ($statsRow) {
|
||||
$stats['active'] = (int)$statsRow['active_users'];
|
||||
$stats['pending'] = (int)$statsRow['pending_users'];
|
||||
$stats['admins'] = (int)$statsRow['admin_users'];
|
||||
$stats['masters'] = (int)$statsRow['master_users'];
|
||||
}
|
||||
|
||||
$stmtUsers = $pdo->query("
|
||||
@@ -297,13 +414,36 @@ $stmtUsers = $pdo->query("
|
||||
ORDER BY `is_active` ASC, `role` DESC, `created_at` DESC, `username` ASC
|
||||
");
|
||||
$users = $stmtUsers->fetchAll();
|
||||
$pagePermissionsByUser = [];
|
||||
|
||||
if ($currentUserIsMaster) {
|
||||
$stmtPagePermissions = $pdo->query("
|
||||
SELECT `user_id`, `page_key`
|
||||
FROM `app_user_page_permissions`
|
||||
ORDER BY `user_id` ASC, `page_key` ASC
|
||||
");
|
||||
|
||||
foreach ($stmtPagePermissions as $permissionRow) {
|
||||
$permissionUserId = (int) ($permissionRow['user_id'] ?? 0);
|
||||
$permissionPageKey = (string) ($permissionRow['page_key'] ?? '');
|
||||
if ($permissionUserId < 1 || $permissionPageKey === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($pagePermissionsByUser[$permissionUserId])) {
|
||||
$pagePermissionsByUser[$permissionUserId] = [];
|
||||
}
|
||||
|
||||
$pagePermissionsByUser[$permissionUserId][$permissionPageKey] = true;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php require __DIR__ . '/header.php'; ?>
|
||||
|
||||
<div class="admin-users-layout">
|
||||
<section class="card admin-users-summary-card">
|
||||
<h2>Uebersicht</h2>
|
||||
<p class="hint">Hier kannst du Benutzerkonten freischalten, deaktivieren, loeschen und zwischen <strong>user</strong> und <strong>admin</strong> wechseln.</p>
|
||||
<p class="hint">Hier kannst du Benutzerkonten freischalten, deaktivieren, loeschen und zwischen <strong>user</strong>, <strong>admin</strong> und <strong>master</strong> wechseln.</p>
|
||||
|
||||
<?php if ($successMessage !== ''): ?>
|
||||
<p class="msg-success"><?= h($successMessage) ?></p>
|
||||
@@ -334,6 +474,10 @@ $users = $stmtUsers->fetchAll();
|
||||
<span class="admin-stat-label">Admins</span>
|
||||
<strong><?= $stats['admins'] ?></strong>
|
||||
</div>
|
||||
<div class="admin-stat-box">
|
||||
<span class="admin-stat-label">Master</span>
|
||||
<strong><?= $stats['masters'] ?></strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -375,7 +519,7 @@ $users = $stmtUsers->fetchAll();
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="role-badge <?= $userRow['role'] === 'admin' ? 'role-badge-admin' : 'role-badge-user' ?>">
|
||||
<span class="role-badge <?= in_array($userRow['role'], ['admin', 'master'], true) ? 'role-badge-admin' : 'role-badge-user' ?>">
|
||||
<?= h($userRow['role']) ?>
|
||||
</span>
|
||||
</td>
|
||||
@@ -403,6 +547,9 @@ $users = $stmtUsers->fetchAll();
|
||||
<select name="role"<?= $isSelf ? ' disabled' : '' ?>>
|
||||
<option value="user"<?= $userRow['role'] === 'user' ? ' selected' : '' ?>>user</option>
|
||||
<option value="admin"<?= $userRow['role'] === 'admin' ? ' selected' : '' ?>>admin</option>
|
||||
<?php if ($currentUserIsMaster || $userRow['role'] === 'master'): ?>
|
||||
<option value="master"<?= $userRow['role'] === 'master' ? ' selected' : '' ?>>master</option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-secondary btn-sm"<?= $isSelf ? ' disabled' : '' ?>>Rolle speichern</button>
|
||||
</form>
|
||||
@@ -421,6 +568,66 @@ $users = $stmtUsers->fetchAll();
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if ($currentUserIsMaster): ?>
|
||||
<section class="card admin-users-table-card">
|
||||
<h2>Seitenfreigaben</h2>
|
||||
<p class="hint">Nur Master-Benutzer duerfen hier festlegen, welche Seiten normale Nutzer im Menue sehen und direkt aufrufen duerfen.</p>
|
||||
<div class="admin-users-table-wrap">
|
||||
<table class="data-table admin-users-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Benutzer</th>
|
||||
<?php foreach ($manageablePages as $pageKey => $pageLabel): ?>
|
||||
<th><?= h($pageLabel) ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th>Speichern</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $userRow): ?>
|
||||
<?php
|
||||
$permissionUserId = (int) $userRow['id'];
|
||||
$userPermissions = $pagePermissionsByUser[$permissionUserId] ?? [];
|
||||
$isPrivilegedUser = isPrivilegedRole((string) ($userRow['role'] ?? ''));
|
||||
$permissionFormId = 'page-permissions-user-' . $permissionUserId;
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<form id="<?= h($permissionFormId) ?>" method="post" action="">
|
||||
<input type="hidden" name="csrf_token" value="<?= h($_SESSION['admin_users_csrf']) ?>">
|
||||
<input type="hidden" name="action" value="update_page_permissions">
|
||||
<input type="hidden" name="user_id" value="<?= $permissionUserId ?>">
|
||||
</form>
|
||||
<strong><?= h($userRow['display_name'] ?: $userRow['username']) ?></strong><br>
|
||||
<small><?= h($userRow['username']) ?> (<?= h($userRow['role']) ?>)</small>
|
||||
</td>
|
||||
<?php foreach ($manageablePages as $pageKey => $pageLabel): ?>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="page_keys[]"
|
||||
value="<?= h($pageKey) ?>"
|
||||
form="<?= h($permissionFormId) ?>"
|
||||
<?= isset($userPermissions[$pageKey]) ? 'checked' : '' ?>
|
||||
<?= $isPrivilegedUser ? 'disabled' : '' ?>
|
||||
>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
<td>
|
||||
<?php if ($isPrivilegedUser): ?>
|
||||
<span class="hint">Vollzugriff ueber Rolle</span>
|
||||
<?php else: ?>
|
||||
<button type="submit" class="btn btn-secondary btn-sm" form="<?= h($permissionFormId) ?>">Freigaben speichern</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<dialog class="admin-dialog" id="activateNotifyDialog">
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
function currentUserRole(): string
|
||||
{
|
||||
return trim((string) ($_SESSION['role'] ?? ''));
|
||||
}
|
||||
|
||||
function isMasterRole(?string $role): bool
|
||||
{
|
||||
return trim((string) $role) === 'master';
|
||||
}
|
||||
|
||||
function isAdminLikeRole(?string $role): bool
|
||||
{
|
||||
$normalizedRole = trim((string) $role);
|
||||
|
||||
return $normalizedRole === 'admin' || $normalizedRole === 'master';
|
||||
}
|
||||
|
||||
function currentUserIsAdminLike(): bool
|
||||
{
|
||||
return isAdminLikeRole(currentUserRole());
|
||||
}
|
||||
|
||||
function publicAccessAllowlist(): array
|
||||
{
|
||||
return [
|
||||
'login.php',
|
||||
'register.php',
|
||||
'logout.php',
|
||||
];
|
||||
}
|
||||
|
||||
function currentPublicPageKey(): string
|
||||
{
|
||||
$scriptName = str_replace('\\', '/', (string) ($_SERVER['SCRIPT_NAME'] ?? ''));
|
||||
$scriptName = ltrim($scriptName, '/');
|
||||
|
||||
if ($scriptName === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$publicMarker = 'public/';
|
||||
$publicPosition = strpos($scriptName, $publicMarker);
|
||||
if ($publicPosition !== false) {
|
||||
return substr($scriptName, $publicPosition + strlen($publicMarker));
|
||||
}
|
||||
|
||||
return $scriptName;
|
||||
}
|
||||
|
||||
function manageablePublicPages(): array
|
||||
{
|
||||
return [
|
||||
'index.php' => 'Startseite',
|
||||
'settings.php' => 'Einstellungen',
|
||||
'my_favorites.php' => 'Favoriten',
|
||||
'moonphase.php' => 'Mondphase',
|
||||
'planetensichtbarkeit.php' => 'Planetensichtbarkeit',
|
||||
'tcrb_lightcurve.php' => 'T CrB Lichtkurve',
|
||||
'solarsystem.php' => 'Sonnensystem',
|
||||
'jupitersystem.php' => 'Jupitersystem',
|
||||
'tagbogen.php' => 'Tagbogen',
|
||||
'sternenhimmel.php' => 'Sternenhimmel',
|
||||
'meteorshowers.php' => 'Meteorstroeme',
|
||||
'geocron/index.php' => 'Geocron',
|
||||
'geocron3d.php' => 'Geocron 3D',
|
||||
'mondfinsternis.php' => 'Mondfinsternis',
|
||||
'sonnenfinsternis.php' => 'Sonnenfinsternis',
|
||||
'bedeckung.php' => 'Bedeckungsphaenomene',
|
||||
'sternbedeckungen.php' => 'Mond-Stern-Bedeckungen',
|
||||
'monatsvorhersage.php' => 'Monatsvorhersage',
|
||||
'wochenvorhersage.php' => 'Wochenvorhersage',
|
||||
'satellitenhimmel.php' => 'Satellitenhimmel',
|
||||
];
|
||||
}
|
||||
+125
-9
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/auth_helpers.php';
|
||||
$title = isset($pageTitle) && is_string($pageTitle) && $pageTitle !== ''
|
||||
? $pageTitle
|
||||
: 'Skyview & AstroTools — Astronomiemuseum der Sternwarte Sonneberg';
|
||||
@@ -14,10 +15,14 @@ $headerIntroSub = isset($headerIntroSub) && is_string($headerIntroSub) ? trim($h
|
||||
$showHeaderIntro = $headerIntroPre !== '' || $headerIntroTitle !== '' || $headerIntroSub !== '';
|
||||
$publicBasePath = isset($publicBasePath) && is_string($publicBasePath) ? rtrim($publicBasePath, '/') . '/' : '';
|
||||
$pdoHeader = isset($pdo) ? $pdo : null;
|
||||
$isAdmin = $loggedIn && (($_SESSION['role'] ?? '') === 'admin');
|
||||
$hasFullAdminAccess = $loggedIn && currentUserIsAdminLike();
|
||||
$canAccessCurrentPage = $hasFullAdminAccess;
|
||||
$pendingApprovals = 0;
|
||||
$publicScriptName = currentPublicPageKey();
|
||||
$publicAccessAllowlist = publicAccessAllowlist();
|
||||
$manageablePublicPages = manageablePublicPages();
|
||||
|
||||
if (($loggedIn || $isAdmin) && $pdoHeader === null) {
|
||||
if ($loggedIn && $pdoHeader === null) {
|
||||
$cfgHeader = require __DIR__ . '/../config/database.php';
|
||||
$pdoHeader = new PDO(
|
||||
sprintf('mysql:host=%s;dbname=%s;charset=%s', $cfgHeader['host'], $cfgHeader['dbname'], $cfgHeader['charset'] ?? 'utf8mb4'),
|
||||
@@ -26,6 +31,67 @@ if (($loggedIn || $isAdmin) && $pdoHeader === null) {
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
|
||||
);
|
||||
}
|
||||
|
||||
if (!in_array($publicScriptName, $publicAccessAllowlist, true) && !$canAccessCurrentPage) {
|
||||
if (!$loggedIn) {
|
||||
header('Location: ' . $publicBasePath . 'login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$userHasPagePermission = false;
|
||||
if ($pdoHeader !== null && isset($manageablePublicPages[$publicScriptName], $_SESSION['user_id'])) {
|
||||
try {
|
||||
$stmtPagePermission = $pdoHeader->prepare("
|
||||
SELECT 1
|
||||
FROM `app_user_page_permissions`
|
||||
WHERE `user_id` = :user_id
|
||||
AND `page_key` = :page_key
|
||||
LIMIT 1
|
||||
");
|
||||
$stmtPagePermission->execute([
|
||||
':user_id' => (int) $_SESSION['user_id'],
|
||||
':page_key' => $publicScriptName,
|
||||
]);
|
||||
$userHasPagePermission = (bool) $stmtPagePermission->fetchColumn();
|
||||
} catch (Throwable $e) {
|
||||
$userHasPagePermission = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($userHasPagePermission) {
|
||||
$canAccessCurrentPage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_array($publicScriptName, $publicAccessAllowlist, true) && !$canAccessCurrentPage) {
|
||||
if (!$loggedIn) {
|
||||
header('Location: ' . $publicBasePath . 'login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
http_response_code(403);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zugriff verweigert</title>
|
||||
<link rel="stylesheet" href="<?= htmlspecialchars($publicBasePath . 'css/style.css', ENT_QUOTES, 'UTF-8') ?>">
|
||||
</head>
|
||||
<body>
|
||||
<main class="container" style="padding-top: 4rem; padding-bottom: 4rem;">
|
||||
<section class="card">
|
||||
<h1>Zugriff verweigert</h1>
|
||||
<p>Diese Seite ist aktuell nur fuer Benutzer mit der Rolle <strong>admin</strong> oder <strong>master</strong> freigegeben.</p>
|
||||
<p><a class="btn btn-primary" href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>">Abmelden</a></p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
// Standard-Standort des eingeloggten Benutzers laden
|
||||
$currentLocation = null;
|
||||
if (!empty($defaultLocation)) {
|
||||
@@ -43,7 +109,7 @@ if (!empty($defaultLocation)) {
|
||||
$currentLocation = $stmtLoc->fetch() ?: null;
|
||||
}
|
||||
|
||||
if ($isAdmin && $pdoHeader !== null) {
|
||||
if ($hasFullAdminAccess && $pdoHeader !== null) {
|
||||
$stmtPendingUsers = $pdoHeader->query('SELECT COUNT(*) FROM app_users WHERE is_active = 0');
|
||||
$pendingApprovals = (int)$stmtPendingUsers->fetchColumn();
|
||||
}
|
||||
@@ -53,7 +119,6 @@ $menuGroups = [
|
||||
'title' => 'Himmel & Planeten',
|
||||
'links' => [
|
||||
['href' => 'moonphase.php', 'label' => 'Mondphase'],
|
||||
['href' => 'index_py.php', 'label' => 'Sonne & Mond'],
|
||||
['href' => 'planetensichtbarkeit.php', 'label' => 'Planetensichtbarkeit'],
|
||||
['href' => 'tcrb_lightcurve.php', 'label' => 'T CrB Lichtkurve'],
|
||||
['href' => 'solarsystem.php', 'label' => 'Sonnensystem'],
|
||||
@@ -81,6 +146,57 @@ $adminLinks = [
|
||||
['href' => 'monatsvorhersage.php', 'label' => 'Monatsvorhersage'],
|
||||
['href' => 'telegram/broadcast_test.php', 'label' => 'Telegram-Test'],
|
||||
];
|
||||
$accountLinks = [
|
||||
['href' => 'settings.php', 'label' => 'Einstellungen'],
|
||||
['href' => 'my_favorites.php', 'label' => 'Favoriten'],
|
||||
['href' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
|
||||
];
|
||||
|
||||
if ($loggedIn && !$hasFullAdminAccess && $pdoHeader !== null) {
|
||||
$allowedPageKeys = [];
|
||||
try {
|
||||
$stmtAllowedPages = $pdoHeader->prepare("
|
||||
SELECT `page_key`
|
||||
FROM `app_user_page_permissions`
|
||||
WHERE `user_id` = :user_id
|
||||
");
|
||||
$stmtAllowedPages->execute([
|
||||
':user_id' => (int) ($_SESSION['user_id'] ?? 0),
|
||||
]);
|
||||
foreach ($stmtAllowedPages as $allowedPageRow) {
|
||||
$pageKey = trim((string) ($allowedPageRow['page_key'] ?? ''));
|
||||
if ($pageKey !== '') {
|
||||
$allowedPageKeys[$pageKey] = true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$allowedPageKeys = [];
|
||||
}
|
||||
|
||||
foreach ($menuGroups as &$group) {
|
||||
$group['links'] = array_values(array_filter(
|
||||
$group['links'],
|
||||
static function (array $link) use ($allowedPageKeys): bool {
|
||||
$href = trim((string) ($link['href'] ?? ''));
|
||||
return $href !== '' && isset($allowedPageKeys[$href]);
|
||||
}
|
||||
));
|
||||
}
|
||||
unset($group);
|
||||
|
||||
$menuGroups = array_values(array_filter(
|
||||
$menuGroups,
|
||||
static fn (array $group): bool => !empty($group['links'])
|
||||
));
|
||||
|
||||
$accountLinks = array_values(array_filter(
|
||||
$accountLinks,
|
||||
static function (array $link) use ($allowedPageKeys): bool {
|
||||
$href = trim((string) ($link['href'] ?? ''));
|
||||
return $href !== '' && isset($allowedPageKeys[$href]);
|
||||
}
|
||||
));
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
@@ -165,7 +281,7 @@ $adminLinks = [
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'index.php', ENT_QUOTES, 'UTF-8') ?>">Skyview & AstroTools</a>
|
||||
<span class="header-sub"><a href="https://www.astronomiemuseum.de" target="_blank" rel="noopener">Astronomiemuseum der Sternwarte Sonneberg</a></span>
|
||||
</div>
|
||||
<?php if ($isAdmin && $pendingApprovals > 0): ?>
|
||||
<?php if ($hasFullAdminAccess && $pendingApprovals > 0): ?>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'admin_users.php', ENT_QUOTES, 'UTF-8') ?>" class="admin-alert" aria-live="polite">
|
||||
<span class="admin-alert-icon" aria-hidden="true">!</span>
|
||||
<span class="admin-alert-text">Achtung: <?= $pendingApprovals ?> <?= $pendingApprovals === 1 ? 'Benutzer wartet' : 'Benutzer warten' ?> auf Freischaltung</span>
|
||||
@@ -195,16 +311,16 @@ $adminLinks = [
|
||||
<div class="menu-group">
|
||||
<div class="menu-title">Konto</div>
|
||||
<?php if ($loggedIn): ?>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'settings.php', ENT_QUOTES, 'UTF-8') ?>">Einstellungen</a>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'my_favorites.php', ENT_QUOTES, 'UTF-8') ?>">Favoriten</a>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'satellitenhimmel.php', ENT_QUOTES, 'UTF-8') ?>">Satellitenhimmel</a>
|
||||
<?php foreach ($accountLinks as $accountLink): ?>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . $accountLink['href'], ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($accountLink['label'], ENT_QUOTES, 'UTF-8') ?></a>
|
||||
<?php endforeach; ?>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>" class="nav-logout">Abmelden</a>
|
||||
<?php else: ?>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'login.php', ENT_QUOTES, 'UTF-8') ?>">Anmelden</a>
|
||||
<a href="<?= htmlspecialchars($publicBasePath . 'register.php', ENT_QUOTES, 'UTF-8') ?>">Registrieren</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($isAdmin): ?>
|
||||
<?php if ($hasFullAdminAccess): ?>
|
||||
<div class="menu-group">
|
||||
<div class="menu-title">Administration</div>
|
||||
<?php foreach ($adminLinks as $adminLink): ?>
|
||||
|
||||
@@ -5,8 +5,10 @@ ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/auth_helpers.php';
|
||||
|
||||
$loggedIn = isset($_SESSION['user_id']);
|
||||
$isAdmin = $loggedIn && (($_SESSION['role'] ?? '') === 'admin');
|
||||
$isAdmin = $loggedIn && currentUserIsAdminLike();
|
||||
|
||||
// AJAX-Handler: GRS-Kalibrierung lesen/speichern
|
||||
if (isset($_GET['grs_api'])) {
|
||||
|
||||
@@ -10,6 +10,8 @@ if (!isset($_SESSION['user_id'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../auth_helpers.php';
|
||||
|
||||
$config = require __DIR__ . '/../../config/database.php';
|
||||
$telegramConfig = require __DIR__ . '/../../config/telegram.php';
|
||||
|
||||
@@ -255,7 +257,7 @@ $currentUser = $stmtCurrentUser->fetch();
|
||||
|
||||
if (
|
||||
!$currentUser ||
|
||||
$currentUser['role'] !== 'admin' ||
|
||||
!isAdminLikeRole((string) $currentUser['role']) ||
|
||||
(int) $currentUser['is_active'] !== 1
|
||||
) {
|
||||
$_SESSION = [];
|
||||
|
||||
Reference in New Issue
Block a user