From 227b4cd1943c8eefd0c4f8f278431191d4c1d2b4 Mon Sep 17 00:00:00 2001 From: sbp-jm Date: Tue, 28 Jul 2026 06:39:11 +0000 Subject: [PATCH] readme aktualisiert --- README.md | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 559 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 49f2f0c..c9d50b7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,253 @@ # flarum-msteams-webhook -Diese Version enthält: +Flarum-Erweiterung zur Spiegelung ausgewählter Flarum-Benachrichtigungen in den **Microsoft Teams Activity Feed**. -- rudimentäres Admin-UI für Flarum 1.x -- User Settings werden angepasst, so dass man unten MS Teams auswählen kann! -- Test-Command `php flarum teams:test-user ` +Die Erweiterung ergänzt Flarum um einen eigenen Benachrichtigungskanal namens **Microsoft Teams**. Benutzer können diesen Kanal in ihren persönlichen Benachrichtigungseinstellungen aktivieren. Neue Aktivitäten – beispielsweise Antworten in gefolgten Diskussionen oder Erwähnungen – werden anschließend über Microsoft Graph an den Teams Activity Feed des jeweiligen Benutzers gesendet. -## Build des Admin-Frontends +Beim Öffnen einer Teams-Benachrichtigung erscheint eine schlanke Zwischenansicht im persönlichen Teams-Tab. Diese enthält einen Link zur zugehörigen Flarum-Diskussion beziehungsweise direkt zum konkreten Beitrag. Das eigentliche Forum muss dadurch nicht in Microsoft Teams eingebettet werden und kann weiterhin mit restriktiven Frame-Sicherheitsheadern geschützt bleiben. + +> **Projektstatus:** Interne Erweiterung für Flarum 1.8. Vor einem produktiven Einsatz sollten Konfiguration, Berechtigungen, Logging und Fehlerbehandlung an die jeweilige Umgebung angepasst und getestet werden. + +## Funktionsumfang + +- eigener Flarum-Benachrichtigungskanal `teams` +- rudimentäres Admin-UI zur Konfiguration der Microsoft-Graph-Anbindung +- Erweiterung der persönlichen Flarum-Benachrichtigungseinstellungen um Microsoft Teams +- asynchroner Versand über einen dedizierten Queue-Job +- Auflösung des Microsoft-Graph-Zielbenutzers über E-Mail-Adresse, UPN oder eine Benutzerpräferenz +- Abruf eines App-only Access Tokens über Microsoft Entra ID +- Versand von Activity-Feed-Benachrichtigungen über Microsoft Graph +- Vorschautext mit auslösendem Benutzer und Diskussionstitel +- Teams Deep Link zu einem persönlichen Teams-Tab +- Übergabe der konkreten Flarum-Ziel-URL über `context.subEntityId` +- Auslesen des Ziels im Teams-Tab über TeamsJS v2 und `context.page.subPageId` +- sichere Zwischenansicht mit validiertem Link zur Diskussion oder zum Beitrag +- Test-Command für einzelne Flarum-Benutzer + +## Unterstützte Benachrichtigungstypen + +Die Erweiterung enthält Unterstützung beziehungsweise Textvorlagen für folgende Flarum-Typen: + +- `newPost` – neue Antwort in einer gefolgten Diskussion +- `postMentioned` – Erwähnung in einem Beitrag +- `userMentioned` – Benutzererwähnung +- `postLiked` – eigener Beitrag wurde mit „Gefällt mir“ markiert +- `discussionRenamed` – Diskussion wurde umbenannt +- `teamsTest` – interne Testbenachrichtigung des CLI-Commands + +Welche Typen tatsächlich in den persönlichen Einstellungen angeboten oder standardmäßig aktiviert werden, wird durch die registrierten Flarum-Blueprints und die Frontend-Erweiterung bestimmt. + +## Systemvoraussetzungen + +- PHP 8.1 oder neuer +- Flarum 1.8 +- Composer +- Node.js und npm zum Bauen des Flarum-Frontends +- ein funktionierender Flarum-Queue-Worker +- öffentlich beziehungsweise für Microsoft Teams erreichbare HTTPS-URL +- Microsoft-Entra-App-Registrierung mit den erforderlichen Microsoft-Graph-Berechtigungen +- installierte beziehungsweise im Mandanten veröffentlichte Microsoft-Teams-App + +Relevante Composer-Abhängigkeiten: + +```json +{ + "php": "^8.1", + "flarum/core": "^1.8", + "guzzlehttp/guzzle": "^7.8" +} +``` + +## Architektur und Ablauf + +Der Versand läuft vereinfacht über folgende Komponenten: + +```text +Flarum Notification Blueprint + -> TeamsNotificationDriver + -> SendTeamsActivityNotificationJob + -> TeamsActivityNotifier + -> NotificationPayloadFactory + -> Microsoft Graph sendActivityNotification + -> Microsoft Teams Activity Feed +``` + +### Notification Driver + +`TeamsNotificationDriver` ist in `extend.php` als Flarum Notification Driver registriert: + +```php +(new Extend\Notification()) + ->driver('teams', TeamsNotificationDriver::class, []), +``` + +Der Driver prüft die Benachrichtigungseinstellungen der Empfänger und legt den Versand als `SendTeamsActivityNotificationJob` in der Queue `teams` ab. + +### Queue-Job + +`SendTeamsActivityNotificationJob` führt den eigentlichen Versand außerhalb des Web-Requests aus. Der Job besitzt Wiederholungs- und Backoff-Einstellungen, damit vorübergehende Graph- oder Netzwerkfehler nicht sofort zum endgültigen Verlust einer Nachricht führen. + +Nach Änderungen am PHP-Code müssen lang laufende Queue-Worker neu gestartet werden, da sie andernfalls weiterhin bereits geladene Klassen verwenden können. + +### Token Provider + +`GraphTokenProvider` ruft über den OAuth-2.0-Client-Credentials-Flow ein App-only Access Token bei Microsoft Entra ID ab. Tenant-ID, Client-ID und Client Secret werden aus den Flarum-Einstellungen gelesen. + +Secrets dürfen weder in das Git-Repository eingecheckt noch in Logdateien geschrieben werden. + +### Zielauflösung + +`TargetResolver` ordnet einen Flarum-Benutzer einem Microsoft-Graph-Benutzer zu. Unterstützt werden – abhängig von der Admin-Konfiguration – unter anderem: + +- Flarum-E-Mail-Adresse +- Microsoft Entra User Principal Name (UPN) +- benutzerdefinierte Flarum-Präferenz + +Der aufgelöste Wert wird für den Graph-Endpunkt verwendet: + +```text +POST https://graph.microsoft.com/v1.0/users/{target}/teamwork/sendActivityNotification +``` + +### Payload-Erzeugung + +`NotificationPayloadFactory` erstellt den Request-Body für den Microsoft Teams Activity Feed. Der Payload enthält unter anderem: + +- `topic.source` +- `topic.value` +- `topic.webUrl` +- `activityType` +- `previewText` +- `templateParameters` + +Aktuell wird `systemDefault` als Activity Type verwendet. Dadurch ist kein eigener Activity Type im Teams-Manifest erforderlich. + +Für Discussion- und Post-Subjects wird eine passende Ziel-URL erzeugt: + +```text +https://forum.example/d/{discussionId} +https://forum.example/d/{discussionId}/{postNumber} +``` + +Die Ziel-URL wird nicht direkt im Teams-Frame geladen. Stattdessen wird sie im Deep Link als `context.subEntityId` an den persönlichen Teams-Tab übergeben. + +## Zwischenansicht im Teams-Tab + +Die Teams-App lädt als Personal Tab eine statische Seite, beispielsweise: + +```text +https://forum.example/static/flarum-notify-v2.html +``` + +Die Seite bindet TeamsJS ein und liest den Deep-Link-Kontext aus: + +```javascript +await window.microsoftTeams.app.initialize(); +const context = await window.microsoftTeams.app.getContext(); +const target = context.page.subPageId; +``` + +Anschließend wird nur dann ein Link angezeigt, wenn: + +- die Ziel-URL dieselbe Origin wie die Landing Page besitzt und +- der Pfad mit `/d/` beginnt. + +Dadurch kann `subEntityId` nicht als offener Redirect auf fremde Domains missbraucht werden. + +Die Seite unterstützt zusätzlich einen direkten Browser-Test über einen Query-Parameter: + +```text +https://forum.example/static/flarum-notify-v2.html?target=https%3A%2F%2Fforum.example%2Fd%2F123%2F17 +``` + +## Teams-App-Manifest + +Das Teams-Manifest benötigt mindestens einen persönlichen statischen Tab und die Forum-Domain in `validDomains`. + +Beispiel: + +```json +{ + "staticTabs": [ + { + "entityId": "YOUR_TEAMS_ENTITY_ID", + "name": "Flarum Notify", + "contentUrl": "https://forum.example/static/flarum-notify-v2.html", + "websiteUrl": "https://forum.example/static/flarum-notify-v2.html", + "scopes": [ + "personal" + ], + "context": [ + "personalTab" + ] + } + ], + "validDomains": [ + "forum.example" + ] +} +``` + +Die folgenden Werte müssen zwischen Manifest und Erweiterung übereinstimmen: + +- Teams App ID +- Entity ID des Personal Tabs +- öffentliche Forum-Basis-URL + +## Microsoft Entra ID und Graph + +Für den App-only-Versand werden eine Microsoft-Entra-App-Registrierung und geeignete Microsoft-Graph-Anwendungsberechtigungen benötigt. Die konkrete Berechtigungskonfiguration hängt vom verwendeten Graph-Endpunkt, dem Mandanten und den internen Sicherheitsrichtlinien ab. + +Erforderliche Konfigurationswerte sind typischerweise: + +- Tenant ID +- Client ID +- Client Secret +- Teams App ID +- Forum Base URL +- Strategie zur Auflösung des Zielbenutzers + +Nach dem Hinzufügen von Application Permissions ist in der Regel eine Administratorzustimmung im Microsoft-Entra-Mandanten erforderlich. + +## Flarum-Admin-Einstellungen + +Das Admin-Frontend enthält Einstellungen für: + +- Aktivierung der Erweiterung +- Tenant ID +- Client ID +- Client Secret +- Forum Base URL +- User Lookup Strategy +- UPN Preference Key +- User Preference Key +- Activity Type +- Teams App ID +- optionale Icon ID +- HTTP Timeout + +Beispiel für die Forum Base URL: + +```text +https://forum.example +``` + +Keinen abschließenden Pfad wie `/public`, `/api` oder `/static` eintragen. + +## Installation + +### 1. Erweiterung bereitstellen + +Das Repository muss als Composer-Paket beziehungsweise lokales Flarum-Paket verfügbar sein. Danach im Flarum-Root die Abhängigkeiten beziehungsweise den Autoloader aktualisieren: + +```bash +cd /var/www/html/flarum +composer dump-autoload +``` + +Je nach Installationsart kann zuvor ein `composer require` oder ein Composer-Path-Repository erforderlich sein. + +### 2. Admin- und Forum-Frontend bauen ```bash cd js @@ -14,8 +255,320 @@ npm install npm run build ``` -Danach im Flarum-Root: +Für die Entwicklung mit Watch-Modus: + +```bash +cd js +npm install +npm run dev +``` + +### 3. Flarum-Cache leeren + +Im Flarum-Root: ```bash php flarum cache:clear ``` + +### 4. Queue-Worker neu starten + +Bei Supervisor beispielsweise: + +```bash +sudo supervisorctl restart all +sudo supervisorctl status +``` + +In einer produktiven Umgebung sollte möglichst nur die betroffene Worker-Gruppe neu gestartet werden. + +## Statische Teams-Dateien + +Die Landing Page besteht aus: + +```text +flarum-notify-v2.html +flarum-notify-v2.js +``` + +Bei einer Apache-Konfiguration mit folgendem Alias: + +```apache +Alias "/static" "/var/www/html/static" +``` + +liegen die Dateien beispielsweise hier: + +```text +/var/www/html/static/flarum-notify-v2.html +/var/www/html/static/flarum-notify-v2.js +``` + +## Apache-, CSP- und Frame-Konfiguration + +Teams stellt Personal Tabs in einem eingebetteten Browserkontext dar. Ein globales + +```http +X-Frame-Options: DENY +``` + +oder + +```http +Content-Security-Policy: frame-ancestors 'none' +``` + +blockiert deshalb die Landing Page und erzeugt im Teams-Desktop typischerweise eine weiße Fläche. + +Das eigentliche Forum kann weiterhin global gegen Framing geschützt bleiben. Nur die statische Landing Page sollte für Microsoft Teams freigegeben werden. + +Beispiel für Apache: + +```apache + + + Header always unset X-Frame-Options + Header always unset Content-Security-Policy + Header always unset Content-Security-Policy-Report-Only + + Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://res.cdn.office.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'self' https://teams.microsoft.com https://*.teams.microsoft.com https://*.cloud.microsoft; base-uri 'self'; form-action 'self'" + + Header always set Cache-Control "no-cache, must-revalidate, max-age=0" + + +``` + +Anschließend: + +```bash +sudo apache2ctl configtest +sudo systemctl reload apache2 +``` + +Header prüfen: + +```bash +curl -sSI https://forum.example/static/flarum-notify-v2.html +``` + +Für die Landing Page darf kein restriktiver `X-Frame-Options`-Header mehr ausgeliefert werden. Die CSP muss die benötigten Teams-Hosts als `frame-ancestors` und das TeamsJS-CDN unter `script-src` erlauben. + +Das eigentliche Forum – insbesondere `/d/...`, Login und Admin-Bereiche – muss für diesen Ansatz nicht in einem Frame freigegeben werden. + +## Cache-Verhalten in Microsoft Teams + +Microsoft Teams kann persönliche Tabs im Desktop-Client im Speicher halten beziehungsweise suspendieren. Änderungen an HTML oder JavaScript werden deshalb nicht immer bei jedem Klick sofort neu geladen. + +Empfehlungen: + +- Landing-Page-HTML mit `Cache-Control: no-cache, must-revalidate` ausliefern +- JavaScript bei Änderungen versionieren, zum Beispiel: + +```html + +``` + +- nach größeren Änderungen Teams vollständig beenden und neu starten +- ein manuelles Löschen des vollständigen Teams-Caches sollte im Normalbetrieb nicht erforderlich sein + +## Test-Command + +Eine Testbenachrichtigung kann an einen einzelnen Flarum-Benutzer gesendet werden: + +```bash +php flarum teams:test-user +``` + +Beispiel: + +```bash +php flarum teams:test-user 42 +``` + +Nur das aufgelöste Graph-Ziel anzeigen: + +```bash +php flarum teams:test-user 42 --show-target +``` + +Ziel testweise überschreiben: + +```bash +php flarum teams:test-user 42 --target user@example.com +``` + +Der Test-Blueprint besitzt keinen realen Discussion- oder Post-Subject. Deshalb führt eine reine Testbenachrichtigung üblicherweise nicht zu einem konkreten Beitragslink, sondern zur allgemeinen Zwischenansicht. + +## Benutzerkonfiguration + +Die Erweiterung ergänzt die persönlichen Flarum-Benachrichtigungseinstellungen um Microsoft Teams. Benutzer können Teams für die angebotenen Benachrichtigungstypen ein- oder ausschalten. + +Wenn ein Benutzer keine Teams-Benachrichtigungen erhält, sollten folgende Punkte geprüft werden: + +1. Ist Microsoft Teams für den betreffenden Typ in Flarum aktiviert? +2. Kann `TargetResolver` eine E-Mail-Adresse oder einen UPN auflösen? +3. Ist die Teams-App für den Benutzer installiert beziehungsweise verfügbar? +4. Besitzt die Entra-App die benötigten Berechtigungen und Admin Consent? +5. Läuft der Queue-Worker? +6. Enthalten Worker- und Supervisor-Logs Graph-Fehler? + +## Fehlersuche + +### Weiße Fläche im Teams-Desktop + +Response Header prüfen: + +```bash +curl -sSI https://forum.example/static/flarum-notify-v2.html +``` + +Typische Ursachen: + +- `X-Frame-Options: DENY` +- `X-Frame-Options: SAMEORIGIN` +- `frame-ancestors 'none'` +- `frame-ancestors 'self'` ohne Teams-Domains + +### Seite wird ohne CSS dargestellt + +Wenn CSS als `