feat: implement per-tenant API key management with AES-256-GCM encryption

Add encrypted API key storage for AI providers (Anthropic, OpenAI, Ollama)
with admin-only CRUD endpoints, tenant isolation, and audit logging.

- DB migration: tenant_api_keys table with RLS policy
- AES-256-GCM encryption utility (ENCRYPTION_KEY env var)
- CRUD API: GET/POST /api/settings/api-keys, PATCH/DELETE /api/settings/api-keys/[id]
- Provider integration: getModelForTenant() checks tenant keys before env fallback
- Frontend: API key management section in Einstellungen page
- Audit logging on all key CRUD operations (DSGVO)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
CTO (LegalAI)
2026-04-09 12:08:40 +00:00
parent 34047739cf
commit 362627981d
8 changed files with 603 additions and 10 deletions

View File

@@ -0,0 +1,31 @@
-- Migration: Add tenant_api_keys table for per-tenant encrypted API key storage
CREATE TYPE "api_key_provider" AS ENUM ('anthropic', 'openai', 'ollama');
CREATE TABLE "tenant_api_keys" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"tenant_id" uuid NOT NULL REFERENCES "tenants"("id") ON DELETE CASCADE,
"provider" "api_key_provider" NOT NULL,
"encrypted_key" text NOT NULL,
"key_hint" varchar(8) NOT NULL,
"label" varchar(100),
"is_active" boolean NOT NULL DEFAULT true,
"created_by_user_id" uuid REFERENCES "users"("id"),
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
CREATE UNIQUE INDEX "tenant_api_keys_tenant_provider_label_idx"
ON "tenant_api_keys" ("tenant_id", "provider", "label");
CREATE INDEX "tenant_api_keys_tenant_idx"
ON "tenant_api_keys" ("tenant_id");
CREATE INDEX "tenant_api_keys_provider_idx"
ON "tenant_api_keys" ("tenant_id", "provider", "is_active");
-- RLS policy: tenants can only see their own API keys
ALTER TABLE "tenant_api_keys" ENABLE ROW LEVEL SECURITY;
CREATE POLICY "tenant_api_keys_tenant_isolation" ON "tenant_api_keys"
USING ("tenant_id" = current_setting('app.tenant_id', true)::uuid);