197 lines
8.7 KiB
SQL
197 lines
8.7 KiB
SQL
-- Umsetzungsauftrag_Sonnet5.md Teil C, Phase 2, Schritt 12: Mandantenfaehigkeit
|
|
-- vollstaendig aus dem Schema entfernen.
|
|
--
|
|
-- Betreiberentscheidungen (Phase 0, per AskUserQuestion eingeholt):
|
|
-- * tenant_admins: Benutzer wurden bereits VOR dieser Migration per
|
|
-- scripts/promote_tenant_admins.py auf is_admin=1
|
|
-- befoerdert (Phase 1, mit eigenem Audit-Event je
|
|
-- Benutzer) -- diese Migration setzt das nur noch
|
|
-- schematisch um (Tabelle faellt weg).
|
|
-- * users.home_tenant_id: VOLLSTAENDIGER Rebuild (nicht als tote Spalte
|
|
-- belassen) -- daher ist 'users' hier mit dabei,
|
|
-- zusaetzlich zu den fuenf primaer betroffenen
|
|
-- Tabellen.
|
|
-- * api_tokens: bestehende Tokens bleiben gueltig (KEIN
|
|
-- Massenwiderruf) -- diese Migration entfernt nur
|
|
-- die tenant_id-Spalte, ruehrt token_hash/
|
|
-- revoked_at/expires_at nicht an.
|
|
--
|
|
-- ALTER TABLE ... DROP COLUMN scheidet fuer alle sechs Spalten aus (SQLite
|
|
-- verweigert das Droppen indizierter/fremdschluesselbehafteter Spalten) --
|
|
-- deshalb voller Tabellen-Rebuild (12-Schritte-Verfahren) fuer host_groups,
|
|
-- user_groups, ssh_keys, api_tokens, rdp_credentials und users.
|
|
--
|
|
-- Reihenfolge wichtig: erst die fuenf idx_*_tenant-Indizes droppen, dann
|
|
-- rebuilden, DANN tenant_admins/tenants droppen -- erst wenn keine
|
|
-- REFERENCES tenants(id)-Klausel mehr im Schema steht, verschwindet die
|
|
-- letzte Abhaengigkeit, die DROP TABLE tenants verhindern wuerde.
|
|
--
|
|
-- Kein Tabellen-Rename der ALTEN Tabelle (z.B. "host_groups RENAME TO
|
|
-- host_groups_old") -- das wuerde seit SQLite 3.25 (legacy_alter_table=OFF)
|
|
-- automatisch alle REFERENCES host_groups(...)-Klauseln in ANDEREN Tabellen
|
|
-- (z.B. hosts.host_group_id) auf den neuen Namen umschreiben, was hier
|
|
-- explizit unerwuenscht ist. Stattdessen: neue Tabelle unter Platzhalter-
|
|
-- namen anlegen, Daten kopieren, ALTE Tabelle direkt droppen (kein Rename),
|
|
-- NEUE Tabelle auf den Zielnamen umbenennen -- der Platzhaltername taucht in
|
|
-- keiner REFERENCES-Klausel auf, dieser Rename loest also keinen
|
|
-- Seiteneffekt aus.
|
|
--
|
|
-- Fremdschluesselgeprueft wird NICHT hier inline per PRAGMA foreign_key_check
|
|
-- (das PRAGMA liefert nur eine Ergebnismenge zurueck, die ein per
|
|
-- executescript() ausgefuehrtes Skript nicht auswertet) -- app/db.py::
|
|
-- _apply_migrations() fuehrt diese Pruefung nach JEDER Migration
|
|
-- programmatisch aus und bricht den Start mit RuntimeError ab, falls
|
|
-- Verletzungen gefunden werden.
|
|
--
|
|
-- Audit-Log: historische tenant_created/tenant_updated/tenant_deleted/
|
|
-- tenant_admin_granted/tenant_admin_revoked/tenant_admin_promoted_to_admin-
|
|
-- Eintraege werden NICHT angefasst (audit_log hat ohnehin keine
|
|
-- Tenant-Spalte) -- die Trigger no_audit_update/no_audit_delete
|
|
-- (0001_initial.sql) wuerden das ohnehin verweigern. Sie bleiben dauerhaft
|
|
-- im (manipulationssicheren) Log erhalten.
|
|
--
|
|
-- Verifiziert (Testharness gegen synthetischen Zwei-Mandanten-Datensatz,
|
|
-- ausserhalb dieses Repos, da hier keine echte Produktiv-DB verfuegbar
|
|
-- ist): Zeilenzahlen, Datenwerte, foreign_key_check, integrity_check,
|
|
-- Audit-Hash-Kette und die Phase-1-Beforderung ueberstehen die Migration
|
|
-- unveraendert.
|
|
|
|
PRAGMA foreign_keys = OFF;
|
|
|
|
DROP INDEX IF EXISTS idx_host_groups_tenant;
|
|
DROP INDEX IF EXISTS idx_user_groups_tenant;
|
|
DROP INDEX IF EXISTS idx_ssh_keys_tenant;
|
|
DROP INDEX IF EXISTS idx_api_tokens_tenant;
|
|
DROP INDEX IF EXISTS idx_rdp_credentials_tenant;
|
|
DROP INDEX IF EXISTS idx_users_home_tenant;
|
|
|
|
BEGIN IMMEDIATE;
|
|
|
|
-- --- host_groups ------------------------------------------------------------
|
|
CREATE TABLE host_groups_new (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT UNIQUE NOT NULL,
|
|
description TEXT
|
|
);
|
|
INSERT INTO host_groups_new (id, name, description)
|
|
SELECT id, name, description FROM host_groups;
|
|
DROP TABLE host_groups;
|
|
ALTER TABLE host_groups_new RENAME TO host_groups;
|
|
|
|
-- --- user_groups --------------------------------------------------------
|
|
CREATE TABLE user_groups_new (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT UNIQUE NOT NULL,
|
|
description TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
);
|
|
INSERT INTO user_groups_new (id, name, description, created_at)
|
|
SELECT id, name, description, created_at FROM user_groups;
|
|
DROP TABLE user_groups;
|
|
ALTER TABLE user_groups_new RENAME TO user_groups;
|
|
|
|
-- --- ssh_keys -------------------------------------------------------------
|
|
CREATE TABLE ssh_keys_new (
|
|
id INTEGER PRIMARY KEY,
|
|
label TEXT NOT NULL,
|
|
owner_user_id INTEGER REFERENCES users(id),
|
|
private_key_enc BLOB NOT NULL,
|
|
public_key TEXT NOT NULL,
|
|
key_type TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
rotated_at TEXT,
|
|
expires_at TEXT,
|
|
passphrase_enc BLOB,
|
|
username TEXT
|
|
);
|
|
INSERT INTO ssh_keys_new (
|
|
id, label, owner_user_id, private_key_enc, public_key, key_type,
|
|
created_at, rotated_at, expires_at, passphrase_enc, username
|
|
)
|
|
SELECT id, label, owner_user_id, private_key_enc, public_key, key_type,
|
|
created_at, rotated_at, expires_at, passphrase_enc, username
|
|
FROM ssh_keys;
|
|
DROP TABLE ssh_keys;
|
|
ALTER TABLE ssh_keys_new RENAME TO ssh_keys;
|
|
|
|
-- --- api_tokens -----------------------------------------------------------
|
|
CREATE TABLE api_tokens_new (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
label TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
token_prefix TEXT NOT NULL,
|
|
scopes_json TEXT NOT NULL,
|
|
created_by INTEGER REFERENCES users(id),
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
expires_at TEXT,
|
|
last_used_at TEXT,
|
|
revoked_at TEXT
|
|
);
|
|
INSERT INTO api_tokens_new (
|
|
id, user_id, label, token_hash, token_prefix, scopes_json,
|
|
created_by, created_at, expires_at, last_used_at, revoked_at
|
|
)
|
|
SELECT id, user_id, label, token_hash, token_prefix, scopes_json,
|
|
created_by, created_at, expires_at, last_used_at, revoked_at
|
|
FROM api_tokens;
|
|
DROP TABLE api_tokens;
|
|
ALTER TABLE api_tokens_new RENAME TO api_tokens;
|
|
CREATE INDEX IF NOT EXISTS idx_api_tokens_user ON api_tokens(user_id);
|
|
|
|
-- --- rdp_credentials --------------------------------------------------------
|
|
CREATE TABLE rdp_credentials_new (
|
|
id INTEGER PRIMARY KEY,
|
|
label TEXT NOT NULL,
|
|
username TEXT NOT NULL,
|
|
domain TEXT,
|
|
password_enc BLOB NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
rotated_at TEXT
|
|
);
|
|
INSERT INTO rdp_credentials_new (id, label, username, domain, password_enc, created_at, rotated_at)
|
|
SELECT id, label, username, domain, password_enc, created_at, rotated_at
|
|
FROM rdp_credentials;
|
|
DROP TABLE rdp_credentials;
|
|
ALTER TABLE rdp_credentials_new RENAME TO rdp_credentials;
|
|
|
|
-- --- users (Betreiberentscheidung: voller Rebuild statt tote Spalte) -----
|
|
CREATE TABLE users_new (
|
|
id INTEGER PRIMARY KEY,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
totp_secret_enc BLOB,
|
|
totp_enrolled INTEGER NOT NULL DEFAULT 0,
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
is_admin INTEGER NOT NULL DEFAULT 0,
|
|
failed_logins INTEGER NOT NULL DEFAULT 0,
|
|
locked_until TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
password_changed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
must_change_password INTEGER NOT NULL DEFAULT 1,
|
|
session_version INTEGER NOT NULL DEFAULT 1,
|
|
deleted_at TEXT
|
|
);
|
|
INSERT INTO users_new (
|
|
id, username, password_hash, totp_secret_enc, totp_enrolled, is_active,
|
|
is_admin, failed_logins, locked_until, created_at, password_changed_at,
|
|
must_change_password, session_version, deleted_at
|
|
)
|
|
SELECT id, username, password_hash, totp_secret_enc, totp_enrolled, is_active,
|
|
is_admin, failed_logins, locked_until, created_at, password_changed_at,
|
|
must_change_password, session_version, deleted_at
|
|
FROM users;
|
|
DROP TABLE users;
|
|
ALTER TABLE users_new RENAME TO users;
|
|
|
|
-- --- tenant_admins/tenants zuletzt -- erst jetzt existiert keine
|
|
-- REFERENCES tenants(id)-Klausel mehr im Schema (alle sechs Tabellen oben
|
|
-- wurden bereits ohne tenant_id/home_tenant_id neu angelegt).
|
|
DROP INDEX IF EXISTS idx_tenant_admins_user;
|
|
DROP TABLE tenant_admins;
|
|
DROP TABLE tenants;
|
|
|
|
COMMIT;
|
|
|
|
PRAGMA foreign_keys = ON;
|