M0 Einrichtung

This commit is contained in:
2026-07-11 22:24:22 +02:00
parent 6f99e7246d
commit 2e6d79e896
21 changed files with 1246 additions and 22 deletions
+58
View File
@@ -0,0 +1,58 @@
# Lokaler MySQL-Dev-Start
Die Legacy-App kann fuer Entwicklung gegen MySQL laufen. Dafuer gibt es eine
Kompatibilitaetsschicht, die die verwendeten `sqlsrv_*`-Funktionen im Dev-Modus
auf PDO/MySQL abbildet.
## Voraussetzungen
- PHP CLI mit `pdo_mysql`.
- MySQL-Testdatenbank.
- Keine Produktivdaten und keine Produktivpasswoerter im Repository.
In dieser VS-Code-Server-Umgebung wurde PHP lokal unter `.local/` entpackt,
weil `sudo` nicht passwordless verfuegbar war. `.local/` wird von Git ignoriert.
## Umgebungsvariablen
```bash
export APP_DB_DRIVER=mysql
export DB_HOST="example.mysql.host"
export DB_PORT="3306"
export DB_NAME="example_database"
export DB_USER="example_user"
export DB_PASS="example_password"
export DEV_AUTH_EMAIL="admin@test.local"
export DEV_AUTH_NAME="Test Admin"
```
## Testdatenbank initialisieren
```bash
php scripts/init-mysql-dev.php
```
In der aktuellen lokalen Umgebung:
```bash
LD_LIBRARY_PATH="$PWD/.local/php/usr/lib/x86_64-linux-gnu" \
"$PWD/.local/php/usr/bin/php8.3" \
-c "$PWD/.local/php-dev.ini" \
scripts/init-mysql-dev.php
```
## Server starten
```bash
HOST=127.0.0.1 PORT=8080 scripts/run-dev-server.sh
```
Danach ist die App unter `http://127.0.0.1:8080/` erreichbar. In VS Code kann
der Port ueber die Ports-Ansicht weitergeleitet werden.
## Hinweise
- Der Dev-Login umgeht AD/LDAP und nutzt `DEV_AUTH_EMAIL`.
- Die Tabellen werden aus `database/mysql-dev-schema.sql` angelegt.
- Der Dev-Modus ist nur fuer lokale Tests gedacht, nicht fuer Produktion.
+68
View File
@@ -0,0 +1,68 @@
# M0 Baseline
Stand: 2026-07-11
M0 dokumentiert den aktuellen Legacy-Bestand, bevor die SaaS-Umstrukturierung
beginnt. Ziel ist eine belastbare Ausgangsbasis fuer Migration, Sicherheit,
Design-Erhalt und fachliche Vergleichstests.
## Status
| Bereich | Status | Ergebnis |
| --- | --- | --- |
| Code-Inventar | begonnen | Siehe `code-inventory.md` |
| Prozesslandkarte | begonnen | In `code-inventory.md` enthalten |
| Sicherheitsbaseline | begonnen | Siehe `security-baseline.md` |
| DB-Schema-Export | vorbereitet | Siehe `schema-export.sql` |
| Golden-Master-Auswertungen | vorbereitet | Siehe `golden-master-queries.sql` |
| Design-/Screenshot-Referenz | vorbereitet | Siehe `screenshot-checklist.md` |
## Lokal abgeschlossen
- Legacy-Einstiegspunkte und Kernseiten identifiziert.
- Fachliche Tabellen aus SQL-Strings abgeleitet.
- Schreibende Aktionen und kritische Datenfluesse markiert.
- Sicherheitsrisiken fuer M0 inventarisiert.
- SQL-Vorlagen fuer Schema- und Golden-Master-Export angelegt.
- Screenshot-Checkliste fuer Design-Erhalt angelegt.
## Lokale Einschraenkungen
- `php` ist in dieser Umgebung nicht im PATH, daher kein lokaler Syntaxcheck und
kein lokaler App-Start.
- `sqlcmd` ist in dieser Umgebung nicht im PATH, daher kein direkter
SQL-Server-Export aus der Shell.
- Die aktuelle M0-Auswertung ist deshalb statisch aus dem Code abgeleitet.
## Blockiert bis Input vorliegt
Diese Punkte kann ich ohne externe Informationen nicht abschliessen:
1. Live- oder Staging-DB-Schema exportieren.
2. Tabellen, Spalten, Indizes, Constraints und Rowcounts verifizieren.
3. Golden-Master-Ergebnisse gegen echte Daten berechnen.
4. Screenshots der echten App-Zustaende aufnehmen, falls die App lokal nicht
lauffaehig ist.
5. Produktivumgebung dokumentieren: PHP-Version, Webserver, SQL-Server-Version,
Auth-Setup, Cron/Job-Ausfuehrung.
## Was ich von dir brauche
Bitte stelle eines der folgenden Pakete bereit:
- Idealerweise: einen anonymisierten SQL-Server-Schemaexport plus Rowcounts.
- Alternativ: Zugriffsdaten zu einer Test-/Staging-DB, nicht zu Produktion.
- Alternativ: die Ergebnisse aus `schema-export.sql` als CSV/Excel/Markdown.
Zusaetzlich hilfreich:
- Ein erreichbarer Test-Link zur bestehenden App oder Screenshots der Seiten aus
`screenshot-checklist.md`.
- Beispiel-Accounts oder Rollen fuer Admin und normales Mitglied, nur fuer eine
Testumgebung.
- Info, ob hart codierte Zugangsdaten aus Legacy-Skripten bereits rotiert wurden.
- Entscheidung, ob M1/M2 nativ in PHP oder mit Framework vorbereitet werden
sollen.
Bitte keine Produktivpasswoerter oder echten personenbezogenen Exportdaten in
das Repository legen.
+192
View File
@@ -0,0 +1,192 @@
# M0 Code- und Prozessinventar
Stand: 2026-07-11
Dieses Inventar basiert auf statischer Sichtung des Legacy-Codes im Repository.
Das echte Datenbankschema muss noch mit `schema-export.sql` verifiziert werden.
## App-Struktur
| Datei | Rolle | Wichtige Beobachtung |
| --- | --- | --- |
| `config.php` | AD-/DB-Konfiguration | Erstellt globale SQL-Server-Verbindung `$conn` |
| `functions.php` | Auth-/User-Helper | Liest `$_SERVER['AUTH_USER']`, inkludiert LDAP, prueft `kl_Mitarbeiter` |
| `functionsLDAP.php` | LDAP-Mailauflösung | Ermittelt `$mailadress` aus AD |
| `header.php` | HTML-Head und Hinweisbanner | Laedt `assets/css/main.css`, liest `kl_hinweise` |
| `footer.php` | App-Sidebar und JS | Enthaelt die sichtbare Navigation und Admin-Menues |
| `nav.php` | Navigation | Aktuell praktisch leer |
| `headerline.php` | Headerfragment | Enthaelt NUL-Zeichen, fachlich unklar |
| `assets/css/main.css` | Hauptdesign | HTML5-UP Editorial, gruener Akzent, Tabellen, Buttons, Sidebar |
| `DataTables/` | Tabellen-Assets | Wird teilweise eingebunden, aber nicht konsequent initialisiert |
| `PHPMailer/` | Mailversand | Fuer Rundmails/Jahresauswertung genutzt |
| `TCPDF/` | PDF-Export | Fuer Kaffeelisten-Export genutzt |
## Fachliche Kernseiten
| Datei | Prozess | Tabellen |
| --- | --- | --- |
| `index.php` | Persoenliches Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal-Links | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_config` |
| `kaffeeliste.php` | Admin-Gesamtuebersicht mit Salden | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
| `stricheintragen.php` | Sammelerfassung von Strichen | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_config` |
| `einzahlung.php` | Sammelerfassung von Einzahlungen | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
| `mitarbeiterverwalten.php` | Mitglieder anlegen, bearbeiten, aktivieren, deaktivieren | `kl_Mitarbeiter` |
| `namenanpassen.php` | Anzeigenamen aendern | `kl_Mitarbeiter` |
| `letzteneintraege.php` | Letzte Buchungen anzeigen und loeschen | `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_Mitarbeiter` |
| `csvupload.php` | Zahlungs-CSV importieren | `kl_Mitarbeiter`, `kl_Einzahlungen` |
| `exportKaffeeliste.php` | PDF-/Listenexport | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_Einzahlungen`, `kl_config` |
| `mailausgebe.php` | Mailausgabe/Adressliste | `kl_Mitarbeiter` |
| `mailversenden.php` | Massenmail mit aktuellem Stand | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
| `hinweise.php` | Hinweise pflegen | `kl_hinweise` |
| `faq.php` | FAQ | Keine fachlichen Schreibzugriffe |
| `jahresauswertung.php` | Jahresgutschriften und Mailversand | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_Einzahlungen` |
| `umfrage.php` | Umfrage | `CoffeeSurveyVotedEmails`, `CoffeeSurveyResponses` |
| `umfrageergebnisse.php` | Umfrageauswertung | `CoffeeSurveyVotedEmails`, `CoffeeSurveyResponses` |
## Abgeleitete Tabellen und Spalten
Diese Spalten sind aus dem Code abgeleitet und muessen mit dem echten DB-Schema
abgeglichen werden.
| Tabelle | Abgeleitete Spalten |
| --- | --- |
| `kl_Mitarbeiter` | `MitarbeiterID`, `Name`, `Email`, `aktiv`, `admin`, `paypalname` |
| `kl_Einzahlungen` | `EinzahlungsID`, `MitarbeiterID`, `Betrag`, `Datum` |
| `kl_Kaffeeverbrauch` | `VerbrauchID`, `MitarbeiterID`, `AnzahlStriche`, `Kosten`, `KostenproStrich`, `Datum`, `Eintragsart` |
| `kl_config` | `KostenproStrich`, `paypaluse`, `paypallink`, `strichperweb` |
| `kl_hinweise` | `id`, `nachricht`, `gueltig_bis` |
| `CoffeeSurveyVotedEmails` | `EmailNorm` |
| `CoffeeSurveyResponses` | diverse `Q*_`-Antwortspalten, siehe Umfragecode |
## Prozesslandkarte
### Authentifizierung
1. Seite inkludiert `functions.php`.
2. `functions.php` liest `$_SERVER['AUTH_USER']`.
3. `functionsLDAP.php` ermittelt Mailadresse aus LDAP.
4. Zugriff wird ueber `kl_Mitarbeiter.Email`, `aktiv` und `admin` geprueft.
M0-Feststellung:
Login-Konto, Kaffee-Teilnehmer und Rolle sind im Legacy-Modell gekoppelt. Fuer
SaaS muessen `users`, `participants` und `tenant_memberships` getrennt werden.
### Persoenliches Dashboard
Quelle: `index.php`.
Berechnungen:
- Mitarbeiter per E-Mail suchen.
- Gesamteinzahlung: Summe `kl_Einzahlungen.Betrag`.
- Gesamtausgabe: Summe `kl_Kaffeeverbrauch.Kosten`.
- Gesamtstriche: Summe `kl_Kaffeeverbrauch.AnzahlStriche`.
- Aktueller Stand: Einzahlungen minus Ausgaben.
- Jahreswerte: dieselben Summen fuer aktuelles Jahr.
- Bei aktivem `strichperweb`: eigener Strich-Eintrag.
- Bei aktivem `paypaluse`: PayPal-Links anzeigen.
### Sammelerfassung Striche
Quelle: `stricheintragen.php`.
Berechnungen:
- Preis pro Strich aus `kl_config.KostenproStrich`.
- Pro Mitarbeiter eine Anzahl erfassen.
- Kosten = Anzahl Striche mal Kosten pro Strich.
- Neue Zeile in `kl_Kaffeeverbrauch`.
- Filter `vorderseite`/`rueckseite` anhand der letzten 100 Tage und mindestens
10 Strichen.
### Sammelerfassung Einzahlungen
Quelle: `einzahlung.php`.
Berechnungen:
- Pro Mitarbeiter Betrag erfassen.
- Neue Zeile in `kl_Einzahlungen`.
- Filter `vorderseite`/`rueckseite` anhand der letzten 100 Tage und mindestens
10 Strichen.
### Mitgliederverwaltung
Quelle: `mitarbeiterverwalten.php`.
Aktionen:
- Mitglied anlegen.
- Name/E-Mail/aktiv/admin bearbeiten.
- Mitglied aktivieren/deaktivieren.
M0-Feststellung:
Das `admin`-Flag ist fachlich eine Rolleninformation und sollte in der SaaS-App
in `tenant_memberships.role` wandern.
### Korrektur letzter Eintraege
Quelle: `letzteneintraege.php`.
Aktionen:
- Letzte 100 Einzahlungen anzeigen.
- Einzahlung hart loeschen.
- Letzte 100 Strich-Eintraege anzeigen.
- Strich-Eintrag hart loeschen.
M0-Feststellung:
Finanznahe Korrekturen sollten im Zielmodell nicht hart loeschen, sondern als
Storno/Reversal mit Audit-Trail gespeichert werden.
### CSV-Import
Quelle: `csvupload.php`.
Ablauf:
- Datei hochladen.
- CSV mit Komma-Trennung lesen.
- Name aus Spalte 3, Betrag aus Spalte 7, Datum aus Spalte 0.
- Mitarbeiter per Name oder `paypalname` finden.
- Dublette anhand Mitarbeiter, Betrag und Datumstag pruefen.
- Einzahlung einfuegen.
- Ergebnis in Erfolg/Fehler klassifizieren.
### Jahresauswertung
Quelle: `jahresauswertung.php`.
Ablauf:
- Gesamtstriche des aktuellen Jahres je aktivem Mitarbeiter berechnen.
- Konstante Anzahl neuer Striche/Gutschrift proportional verteilen.
- Einzahlung je Mitarbeiter einfuegen.
- Mail an Mitarbeiter senden.
M0-Feststellung:
Das ist ein Job/Skript mit direktem Schreibzugriff und Mailversand. Fuer SaaS
braucht es Dry-Run, Audit, Tenant-Scope und expliziten Operator.
## Offene Schemafragen
- Gibt es Primary Keys, Foreign Keys und Indizes fuer die `kl_`-Tabellen?
- Sind Datentypen fuer Geldwerte `decimal`, `money`, `float` oder etwas anderes?
- Gibt es historische Spalten, die im Code nicht sichtbar sind?
- Gibt es Trigger oder Stored Procedures?
- Gibt es weitere Jobs ausser `jahresauswertung.php`?
- Sind Umfragetabellen produktiv relevant oder Archiv/Randmodul?
## Technische Legacy-Inkonsistenzen
- `csvupload.php` sucht in `getMitarbeiterID` nach `Name = ? or paypalname = ?`,
uebergibt aber nur einen Parameter. Das sollte vor der Uebernahme des
CSV-Flows verifiziert werden.
- `einzahlung.php` prueft auf `$_GET["action"]`, die Buttons erzeugen aber
URLs mit `aktion=...`. Der Vorderseite-/Rueckseite-Filter kann dadurch
abweichend vom erwarteten Verhalten laufen.
- `exportKaffeeliste.php` referenziert `tcpdf/tcpdf.php`, waehrend der Ordner im
Repository `TCPDF/` heisst. Auf case-sensitiven Dateisystemen kann das
brechen.
- `headerline.php` ist fachlich leer beziehungsweise mit NUL-Zeichen gefuellt.
Vor der neuen App-Shell sollte geklaert werden, ob die Datei entfernt werden
kann.
+185
View File
@@ -0,0 +1,185 @@
/*
M0 Golden-Master Queries
Ziel:
- Fachliche Legacy-Ergebnisse exportieren, damit Migration und neue SaaS-App
gegen denselben Stand verglichen werden koennen.
Ausfuehrung:
- In der Legacy-Datenbank ausfuehren.
- Ergebnisse je Query als CSV speichern.
- Keine personenbezogenen Exporte ins Repo committen.
*/
DECLARE @CurrentYear int = YEAR(GETDATE());
-- 1. Teilnehmer-Basisdaten
SELECT
m.MitarbeiterID,
m.Name,
m.Email,
m.aktiv,
m.admin
FROM kl_Mitarbeiter m
ORDER BY m.Name;
-- 2. Saldo je Teilnehmer
WITH payments AS (
SELECT
MitarbeiterID,
SUM(Betrag) AS total_payments
FROM kl_Einzahlungen
GROUP BY MitarbeiterID
),
consumption AS (
SELECT
MitarbeiterID,
SUM(Kosten) AS total_costs,
SUM(AnzahlStriche) AS total_marks
FROM kl_Kaffeeverbrauch
GROUP BY MitarbeiterID
)
SELECT
m.MitarbeiterID,
m.Name,
m.Email,
COALESCE(p.total_payments, 0) AS total_payments,
COALESCE(c.total_costs, 0) AS total_costs,
COALESCE(c.total_marks, 0) AS total_marks,
COALESCE(p.total_payments, 0) - COALESCE(c.total_costs, 0) AS balance
FROM kl_Mitarbeiter m
LEFT JOIN payments p ON p.MitarbeiterID = m.MitarbeiterID
LEFT JOIN consumption c ON c.MitarbeiterID = m.MitarbeiterID
ORDER BY m.Name;
-- 3. Jahreswerte je Teilnehmer
WITH year_payments AS (
SELECT
MitarbeiterID,
SUM(Betrag) AS year_payments
FROM kl_Einzahlungen
WHERE YEAR(Datum) = @CurrentYear
GROUP BY MitarbeiterID
),
year_consumption AS (
SELECT
MitarbeiterID,
SUM(Kosten) AS year_costs,
SUM(AnzahlStriche) AS year_marks
FROM kl_Kaffeeverbrauch
WHERE YEAR(Datum) = @CurrentYear
GROUP BY MitarbeiterID
)
SELECT
m.MitarbeiterID,
m.Name,
m.Email,
COALESCE(yp.year_payments, 0) AS year_payments,
COALESCE(yc.year_costs, 0) AS year_costs,
COALESCE(yc.year_marks, 0) AS year_marks
FROM kl_Mitarbeiter m
LEFT JOIN year_payments yp ON yp.MitarbeiterID = m.MitarbeiterID
LEFT JOIN year_consumption yc ON yc.MitarbeiterID = m.MitarbeiterID
ORDER BY m.Name;
-- 4. Letzte Einzahlungen je Teilnehmer
SELECT
e.EinzahlungsID,
e.MitarbeiterID,
m.Name,
m.Email,
e.Betrag,
e.Datum
FROM kl_Einzahlungen e
JOIN kl_Mitarbeiter m ON m.MitarbeiterID = e.MitarbeiterID
ORDER BY e.Datum DESC, e.EinzahlungsID DESC;
-- 5. Letzte Strich-/Verbrauchseintraege je Teilnehmer
SELECT
v.VerbrauchID,
v.MitarbeiterID,
m.Name,
m.Email,
v.AnzahlStriche,
v.Kosten,
v.KostenproStrich,
v.Datum,
v.Eintragsart
FROM kl_Kaffeeverbrauch v
JOIN kl_Mitarbeiter m ON m.MitarbeiterID = v.MitarbeiterID
ORDER BY v.Datum DESC, v.VerbrauchID DESC;
-- 6. Aktuelle Konfiguration
SELECT
*
FROM kl_config;
-- 7. Aktuelle und historische Hinweise
SELECT
id,
nachricht,
gueltig_bis
FROM kl_hinweise
ORDER BY gueltig_bis DESC;
-- 8. 100-Tage-Vorderseite analog Legacy-Stricherfassung
DECLARE @ReferenceDate date = (
SELECT MAX(CAST(Datum AS date))
FROM kl_Kaffeeverbrauch
WHERE Datum < CAST(GETDATE() AS date)
);
SELECT
m.MitarbeiterID,
m.Name,
m.Email,
SUM(v.AnzahlStriche) AS marks_in_window
FROM kl_Mitarbeiter m
JOIN kl_Kaffeeverbrauch v ON m.MitarbeiterID = v.MitarbeiterID
WHERE v.Datum >= DATEADD(DAY, -100, @ReferenceDate)
AND m.aktiv = 1
GROUP BY m.MitarbeiterID, m.Name, m.Email
HAVING SUM(v.AnzahlStriche) >= 10
ORDER BY m.Name;
-- 9. 100-Tage-Rueckseite analog Legacy-Stricherfassung
SELECT
m.MitarbeiterID,
m.Name,
m.Email,
COALESCE(SUM(v.AnzahlStriche), 0) AS marks_in_window
FROM kl_Mitarbeiter m
LEFT JOIN kl_Kaffeeverbrauch v
ON m.MitarbeiterID = v.MitarbeiterID
AND v.Datum >= DATEADD(DAY, -100, @ReferenceDate)
WHERE m.aktiv = 1
GROUP BY m.MitarbeiterID, m.Name, m.Email
HAVING COALESCE(SUM(v.AnzahlStriche), 0) < 10
ORDER BY m.Name;
-- 10. CSV-Import-Dublettenbasis
SELECT
e.MitarbeiterID,
m.Name,
e.Betrag,
CONVERT(varchar, e.Datum, 23) AS booking_day,
COUNT(*) AS duplicates_per_day
FROM kl_Einzahlungen e
JOIN kl_Mitarbeiter m ON m.MitarbeiterID = e.MitarbeiterID
GROUP BY e.MitarbeiterID, m.Name, e.Betrag, CONVERT(varchar, e.Datum, 23)
HAVING COUNT(*) > 1
ORDER BY booking_day DESC, m.Name;
-- 11. Jahresauswertung: Gesamtstriche aktuelles Jahr
SELECT
m.MitarbeiterID,
m.Name,
m.Email,
SUM(v.AnzahlStriche) AS GesamtStriche
FROM kl_Kaffeeverbrauch v
JOIN kl_Mitarbeiter m ON v.MitarbeiterID = m.MitarbeiterID
WHERE YEAR(v.Datum) = @CurrentYear
AND m.aktiv = 1
GROUP BY m.MitarbeiterID, m.Name, m.Email
ORDER BY GesamtStriche DESC, m.Name;
+152
View File
@@ -0,0 +1,152 @@
/*
M0 SQL Server Schema Export
Ziel:
- Tabellen, Spalten, Defaults, Primary Keys, Foreign Keys, Indizes und Rowcounts
aus der Legacy-Datenbank exportieren.
Hinweise:
- In der Ziel-Datenbank ausfuehren.
- Ergebnisse als CSV/Excel speichern und nicht mit Produktiv-Secrets ins Repo
legen.
- Falls nur Kaffeelisten-Tabellen exportiert werden sollen, den WHERE-Block bei
Bedarf auf kl_% und CoffeeSurvey% begrenzen.
*/
-- 1. Tabellen und Spalten
SELECT
s.name AS schema_name,
t.name AS table_name,
c.column_id,
c.name AS column_name,
ty.name AS data_type,
c.max_length,
c.precision,
c.scale,
c.is_nullable,
c.is_identity,
dc.definition AS default_definition,
ep.value AS column_description
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types ty ON ty.user_type_id = c.user_type_id
LEFT JOIN sys.default_constraints dc
ON dc.parent_object_id = t.object_id
AND dc.parent_column_id = c.column_id
LEFT JOIN sys.extended_properties ep
ON ep.major_id = t.object_id
AND ep.minor_id = c.column_id
AND ep.name = 'MS_Description'
WHERE t.is_ms_shipped = 0
ORDER BY s.name, t.name, c.column_id;
-- 2. Primary Keys und Unique Constraints
SELECT
s.name AS schema_name,
t.name AS table_name,
kc.name AS constraint_name,
kc.type_desc,
ic.key_ordinal,
c.name AS column_name
FROM sys.key_constraints kc
JOIN sys.tables t ON t.object_id = kc.parent_object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.index_columns ic
ON ic.object_id = kc.parent_object_id
AND ic.index_id = kc.unique_index_id
JOIN sys.columns c
ON c.object_id = ic.object_id
AND c.column_id = ic.column_id
ORDER BY s.name, t.name, kc.name, ic.key_ordinal;
-- 3. Foreign Keys
SELECT
sch_parent.name AS parent_schema,
parent_t.name AS parent_table,
parent_c.name AS parent_column,
fk.name AS foreign_key_name,
sch_ref.name AS referenced_schema,
ref_t.name AS referenced_table,
ref_c.name AS referenced_column,
fk.delete_referential_action_desc,
fk.update_referential_action_desc
FROM sys.foreign_keys fk
JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
JOIN sys.tables parent_t ON parent_t.object_id = fkc.parent_object_id
JOIN sys.schemas sch_parent ON sch_parent.schema_id = parent_t.schema_id
JOIN sys.columns parent_c
ON parent_c.object_id = fkc.parent_object_id
AND parent_c.column_id = fkc.parent_column_id
JOIN sys.tables ref_t ON ref_t.object_id = fkc.referenced_object_id
JOIN sys.schemas sch_ref ON sch_ref.schema_id = ref_t.schema_id
JOIN sys.columns ref_c
ON ref_c.object_id = fkc.referenced_object_id
AND ref_c.column_id = fkc.referenced_column_id
ORDER BY parent_schema, parent_table, foreign_key_name;
-- 4. Indizes
SELECT
s.name AS schema_name,
t.name AS table_name,
i.name AS index_name,
i.type_desc,
i.is_unique,
i.is_primary_key,
i.is_unique_constraint,
ic.key_ordinal,
ic.is_included_column,
c.name AS column_name
FROM sys.indexes i
JOIN sys.tables t ON t.object_id = i.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.index_columns ic
ON ic.object_id = i.object_id
AND ic.index_id = i.index_id
JOIN sys.columns c
ON c.object_id = ic.object_id
AND c.column_id = ic.column_id
WHERE t.is_ms_shipped = 0
AND i.name IS NOT NULL
ORDER BY s.name, t.name, i.name, ic.key_ordinal, ic.index_column_id;
-- 5. Rowcounts
SELECT
s.name AS schema_name,
t.name AS table_name,
SUM(p.row_count) AS row_count
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.dm_db_partition_stats p ON p.object_id = t.object_id
WHERE t.is_ms_shipped = 0
AND p.index_id IN (0, 1)
GROUP BY s.name, t.name
ORDER BY s.name, t.name;
-- 6. Trigger
SELECT
s.name AS schema_name,
t.name AS table_name,
tr.name AS trigger_name,
tr.is_disabled,
OBJECT_DEFINITION(tr.object_id) AS trigger_definition
FROM sys.triggers tr
JOIN sys.tables t ON t.object_id = tr.parent_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
ORDER BY s.name, t.name, tr.name;
-- 7. Stored Procedures und Funktionen mit Kaffeelisten-Bezug
SELECT
s.name AS schema_name,
o.name AS object_name,
o.type_desc,
OBJECT_DEFINITION(o.object_id) AS object_definition
FROM sys.objects o
JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE o.type IN ('P', 'FN', 'IF', 'TF')
AND (
OBJECT_DEFINITION(o.object_id) LIKE '%kl_%'
OR OBJECT_DEFINITION(o.object_id) LIKE '%CoffeeSurvey%'
)
ORDER BY s.name, o.name;
+74
View File
@@ -0,0 +1,74 @@
# M0 Screenshot- und Designreferenz
Stand: 2026-07-11
Diese Checkliste friert die bestehende Optik ein, damit die SaaS-App vertraut
bleibt. Wenn die Legacy-App lokal nicht laeuft, koennen Screenshots aus der
aktuellen Umgebung geliefert werden.
## Allgemeine Vorgaben
- Je Seite Desktop mit ca. 1440 px Breite aufnehmen.
- Je Kernseite mobile Ansicht mit ca. 390 px Breite aufnehmen.
- Je relevanter Rolle aufnehmen: normales Mitglied und Admin.
- Keine echten personenbezogenen Daten veroeffentlichen.
- Dateinamen nach Muster `m0-<rolle>-<seite>-<viewport>.png`.
## Kernseiten
| Prioritaet | Seite | Rolle | Zweck |
| --- | --- | --- | --- |
| Hoch | `index.php` | Mitglied | Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal |
| Hoch | `index.php` | Admin | Dashboard mit Admin-Sidebar |
| Hoch | `kaffeeliste.php` | Admin | Gesamtuebersicht und Aktionsleiste |
| Hoch | `stricheintragen.php` | Admin | Sammelerfassung Striche |
| Hoch | `einzahlung.php` | Admin | Sammelerfassung Einzahlungen |
| Hoch | `mitarbeiterverwalten.php` | Admin | Mitgliederliste und Formular |
| Hoch | `letzteneintraege.php` | Admin | Korrekturansicht mit Loeschaktionen |
| Mittel | `csvupload.php` | Admin | CSV-Upload-Formular und Ergebniszustand |
| Mittel | `hinweise.php` | Admin | Hinweise anlegen und Liste |
| Mittel | `faq.php` | Mitglied | FAQ-Inhalte und Typografie |
| Mittel | `namenanpassen.php` | Mitglied | Formular fuer Anzeigenamen |
| Niedrig | `teilnehmerauswertung.php` | Admin | Detailauswertung pro Teilnehmer |
| Niedrig | `mailausgebe.php` | Admin | Mail-/Adressausgabe |
| Niedrig | `umfrage.php` | Mitglied | Randmodul, falls SaaS-relevant |
| Niedrig | `umfrageergebnisse.php` | Admin | Randmodul, falls SaaS-relevant |
## Zustaende
Diese UI-Zustaende sollten, wenn moeglich, separat gesichert werden:
- Positiver Kontostand.
- Negativer Kontostand.
- Nullsaldo.
- Nutzer ohne Zugriff.
- Erfolgreiche Stricherfassung.
- Erfolgreiche Einzahlung.
- CSV-Import mit Erfolg, Dublette und unbekanntem Nutzer.
- Hinweisbanner aktiv.
- Leere Tabellen oder keine Daten.
- Mobile Sidebar geoeffnet und geschlossen.
## Designmerkmale, die erhalten bleiben sollen
- Gruener Akzent fuer Links, Buttons und Linien.
- Roboto-Slab-Ueberschriften.
- Open-Sans-Fliesstext.
- Weisse Hauptflaeche.
- Graue Sidebar in der App.
- Tabellen als zentrale Datenansicht.
- Schlichte Formularfelder und Buttons.
- Ruhige, arbeitsorientierte Darstellung.
## Ablage
Empfohlene lokale Ablage, falls Screenshots ins Repo sollen:
```text
docs/m0/screenshots/
```
Vor dem Commit sollten Screenshots anonymisiert sein. Wenn echte Namen,
E-Mails, PayPal-Daten oder Salden sichtbar sind, besser ausserhalb des Repos
ablegen und nur als Referenz verwenden.
+67
View File
@@ -0,0 +1,67 @@
# M0 Sicherheitsbaseline
Stand: 2026-07-11
Diese Baseline ist eine statische Erstbewertung. Sie ersetzt kein Penetration
Testing, markiert aber die wichtigsten Risiken fuer die SaaS-Umstrukturierung.
## Kritische Sofortthemen
| Risiko | Quelle | Auswirkung | Empfehlung |
| --- | --- | --- | --- |
| Hart codierte DB-Zugangsdaten | `jahresauswertung.php` Zeilen 4-8 | Secret-Leak, direkte Produktiv-DB-Gefahr | Zugangsdaten rotieren, Skript deaktivieren oder auf Env-Konfiguration umstellen |
| Schreibende Seiten ohne eigene Rollenpruefung | `stricheintragen.php`, `einzahlung.php`, `mailversenden.php`, `exportKaffeeliste.php` | Direkter URL-Aufruf kann Aktionen erlauben | Jede Seite serverseitig mit `requireRole` absichern |
| Keine CSRF-Token | nahezu alle POST-/Delete-Formulare | Ungewollte Buchungen, Loeschungen, Imports | CSRF fuer alle schreibenden Aktionen |
| Harte Deletes fuer Buchungen | `letzteneintraege.php` | Audit-Historie und Revisionsfaehigkeit gehen verloren | Storno-/Reversal-Modell statt Delete |
| Upload in Webroot | `csvupload.php` Zeilen 131-138 | Dateiablage kann missbraucht werden | Upload ausserhalb Webroot, Dateityp/MIME/Name pruefen |
| Kein Tenant-Scope | alle fachlichen Queries | Zentrales SaaS-Leak-Risiko | `tenant_id` verpflichtend und Query-Schicht testen |
## Weitere Befunde
| Befund | Quelle | Empfehlung |
| --- | --- | --- |
| Direkte SQL-String-Verkettung mit Mailadresse | `functions.php` Zeilen 14, 25, 39, 55 | Parameterisierte Queries |
| LDAP-Filter ohne Escaping | `functionsLDAP.php` LDAP-Suchen nach `samaccountname` | LDAP-Filter escapen |
| Datumsformat `Y-d-m H:i:s` | `index.php`, `stricheintragen.php`, `einzahlung.php`, `hinweise.php` | ISO/DB-kompatibel `Y-m-d H:i:s` oder DB-Zeit verwenden |
| Ausgabe von Namen/E-Mails teils unescaped | mehrere Tabellen, z.B. Mitgliederverwaltung | `htmlspecialchars` zentral erzwingen |
| Fehlerausgabe mit `sqlsrv_errors()` an Nutzer | mehrere Dateien | Logging intern, neutrale Fehlermeldung extern |
| CSV-Mitarbeitersuche mit mutmasslich falscher Parameteranzahl | `csvupload.php` `getMitarbeiterID` | Query pruefen und mit Tests abdecken |
| Basis-Auth-Beispiel mit Platzhalter-Passwort | `umfrageergebnisse.php` Kommentarblock | Entfernen oder echte Auth-Middleware nutzen |
| App-Navigation ist in `footer.php` | Layoutstruktur | Trennung in App-Shell und Public-Shell |
| `headerline.php` enthaelt NUL-Zeichen | `headerline.php` | Datei pruefen/entfernen, wenn ungenutzt |
| Massenmail ohne Versandlog | `mailversenden.php`, `jahresauswertung.php` | Job-Modell mit Dry-Run, Audit, Rate-Limit |
## Rollen- und Zugriffsrisiken
Aktuell gilt:
- Sichtbare Admin-Navigation wird in `footer.php` per `checkKaffeelisteAdmin`
gesteuert.
- Einige Admin-Zielseiten haben eigene Checks.
- Einige schreibende oder sensible Dateien verlassen sich nicht durchgehend auf
eine eigene Rollenpruefung.
Fuer SaaS gilt:
- Menue-Ausblendung ist keine Berechtigungspruefung.
- Jede Route braucht serverseitige Auth- und Rollenpruefung.
- Jede fachliche Route braucht Tenant-Kontext.
- IDs aus Requests muessen zum aktuellen Tenant gehoeren.
## Empfohlene Reihenfolge fuer Sicherheitsarbeit
1. Secrets rotieren und aus dem Code entfernen.
2. Legacy-Schreibseiten bis zum Umbau hinter explizite Admin-Pruefung setzen.
3. CSRF-Schutz in der neuen App-Basis einplanen.
4. Finanzdaten im Zielmodell nur noch stornieren, nicht loeschen.
5. Uploads ausserhalb des Webroots modellieren.
6. Einheitliche Escape-/View-Helfer einfuehren.
7. Tenant-Isolation mit Tests gegen zwei Tenants absichern.
## Nicht im Repo speichern
- Produktivpasswoerter.
- Echte Nutzerlisten.
- Bank-/PayPal-Exportdaten mit Personenbezug.
- AD-/LDAP-Service-Account-Daten.
- Vollstaendige Produktiv-Dumps.
+9
View File
@@ -296,6 +296,15 @@ Uebersicht:
Ziel:
Den aktuellen Zustand verlaesslich dokumentieren, bevor umgebaut wird.
Arbeitsartefakte:
- `docs/m0/README.md`
- `docs/m0/code-inventory.md`
- `docs/m0/security-baseline.md`
- `docs/m0/schema-export.sql`
- `docs/m0/golden-master-queries.sql`
- `docs/m0/screenshot-checklist.md`
Schritte:
- Live-DB-Schema exportieren.