feat(nextcloud-files): Modul Dateien mit Nextcloud-Adresse, Verbindungsprüfung und gesicherter Verbindungsschicht
- Migration 20261008180000: NextcloudFilesConfig (Organisation) und NextcloudFilesAccount (Mandant UND Benutzer per Zeilenschutz, nur verschlüsseltes App-Passwort) - Transportschicht ncRequest: feste Pfadanfänge, Segmentcodierung, keine Weiterleitungen, keine Cookies, Zeitgrenzen; Aufrufsperre pausiert den Ursprung nach jedem 429 und sperrt Zugangsschlüssel nach dem ersten 401 - Einstellungen: Adresse speichern/prüfen, Adresswechsel läuft alle Konten ab (mit Bestätigung) - Controller mit Verwalten nur auf Einstellungen, Seed, Modul, Registrierung in Web (Ladefunktion, Symbol folder, Navigation, Layout) und Reiter Einstellungen - RLS-Inventar fortgeschrieben, Testaufbau (tessera-nc-test) und E2E-Skripte Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
-- 261008-mzu — Modul "Nextcloud-Dateien" (Etappe 1).
|
||||
--
|
||||
-- Zweck: zwei Tabellen und zwei Aufzaehlungen.
|
||||
-- * "NextcloudFilesConfig": Einstellungen der Organisation (Singleton), genau
|
||||
-- eine Nextcloud-Adresse (`baseUrl`, Normalform von normalizeCloudUrl).
|
||||
-- * "NextcloudFilesAccount": das persoenliche Konto eines Benutzers in dieser
|
||||
-- Nextcloud. Gespeichert wird NUR das App-Passwort, AES-256-GCM-verschluesselt
|
||||
-- ueber den CryptoService (`encryptedAppPassword`); das echte Passwort des
|
||||
-- Benutzers wird nie abgelegt. `ncUserId` ist die Kennung in der Nextcloud
|
||||
-- (nicht der eingegebene Anmeldename), `connectedVia` merkt sich den Weg
|
||||
-- (Passwort oder Login Flow v2 fuer Zwei-Faktor-Konten).
|
||||
--
|
||||
-- Warum das Konto die Adresse mitfuehrt ("baseUrl"): ein App-Passwort gilt nur
|
||||
-- fuer die Nextcloud, die es ausgestellt hat. Es wird nie an einen anderen Host
|
||||
-- geschickt; wechselt die Adresse der Organisation, laufen alle Konten ab
|
||||
-- (status = EXPIRED, oder Konto-baseUrl weicht von der Einstellung ab) und die
|
||||
-- Benutzer verbinden neu.
|
||||
--
|
||||
-- Der DDL-Teil stammt aus `prisma migrate diff`, damit der Stand ohne Abweichung
|
||||
-- zum Schema passt; die Zeilenschutz-Regeln sind von Hand ergaenzt.
|
||||
--
|
||||
-- Zeilenschutz (Pflicht — sonst schlaegt rls-coverage.spec.ts fehl):
|
||||
-- "NextcloudFilesConfig" — tenant_isolation_policy nur ueber den Mandanten
|
||||
-- (Verwaltungsdaten der Organisation, wie "DomainsConfig").
|
||||
-- "NextcloudFilesAccount" — tenant_isolation_policy ueber Mandant UND Benutzer
|
||||
-- (Form aus 20260929140000_reminder): ohne gesetzten Benutzer gilt nur der
|
||||
-- Mandant, mit Benutzer zusaetzlich "userId". Genau das haelt einen Benutzer
|
||||
-- davon ab, je das Konto eines anderen zu lesen. KEINE system_read_policy:
|
||||
-- es gibt keinen Hintergrunddienst, der ueber alle Mandanten liest.
|
||||
--
|
||||
-- Faellt ein Benutzer weg, faellt sein Konto mit (ON DELETE CASCADE). Das
|
||||
-- App-Passwort bleibt dann in der Nextcloud bestehen (Geraeteliste dort) — das
|
||||
-- ist dokumentiert und fuer Etappe 1 bewusst nicht aufgeraeumt.
|
||||
--
|
||||
-- Rechte fuer die Anwendungsrolle tessera_app: kommen ueber ALTER DEFAULT
|
||||
-- PRIVILEGES aus 20260909130000_rls_app_role automatisch — hier nichts zu tun.
|
||||
--
|
||||
-- WICHTIG: wie alle RLS-Regeln dieses Schemas wirken diese erst, wenn die
|
||||
-- Anwendung als Rolle ohne Umgehungsrecht verbindet (Schalter heute AUS, siehe
|
||||
-- docs/mandantentrennung-datenbankrolle.md). Bis dahin tragen die
|
||||
-- Anwendungspruefungen im Dienst den Schutz allein.
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "NextcloudFilesAccountStatus" AS ENUM ('ACTIVE', 'EXPIRED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "NextcloudFilesConnectMethod" AS ENUM ('PASSWORD', 'LOGIN_FLOW');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "NextcloudFilesConfig" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"baseUrl" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "NextcloudFilesConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "NextcloudFilesAccount" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"baseUrl" TEXT NOT NULL,
|
||||
"ncUserId" TEXT NOT NULL,
|
||||
"ncDisplayName" TEXT,
|
||||
"encryptedAppPassword" TEXT NOT NULL,
|
||||
"status" "NextcloudFilesAccountStatus" NOT NULL DEFAULT 'ACTIVE',
|
||||
"connectedVia" "NextcloudFilesConnectMethod" NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "NextcloudFilesAccount_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "NextcloudFilesConfig_tenantId_key" ON "NextcloudFilesConfig"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "NextcloudFilesConfig_tenantId_idx" ON "NextcloudFilesConfig"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "NextcloudFilesAccount_tenantId_idx" ON "NextcloudFilesAccount"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "NextcloudFilesAccount_tenantId_userId_key" ON "NextcloudFilesAccount"("tenantId", "userId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "NextcloudFilesAccount" ADD CONSTRAINT "NextcloudFilesAccount_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- Zeilenschutz: nur Mandant (Organisations-Einstellung)
|
||||
ALTER TABLE "NextcloudFilesConfig" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "NextcloudFilesConfig" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "NextcloudFilesConfig"
|
||||
USING ("tenantId" = current_tenant_id());
|
||||
|
||||
-- Zeilenschutz: Mandant UND Benutzer (Muster 20260929140000_reminder)
|
||||
ALTER TABLE "NextcloudFilesAccount" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "NextcloudFilesAccount" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "NextcloudFilesAccount"
|
||||
USING (
|
||||
"tenantId" = current_tenant_id()
|
||||
AND (current_user_id() IS NULL OR "userId" = current_user_id())
|
||||
);
|
||||
Reference in New Issue
Block a user