Lieferantenmodul neu: Asset-basiert + Anforderungs-Engine (Phase 1)
Referenz: docs/ISMS-Lieferantenmanagement-GEFIM.html Architektur: Lieferanten (und IT-Services) sind jetzt Assets. - AssetType += IT_SERVICE; SupplierProfile/ITServiceProfile als 1:1- Erweiterung eines Asset; alle Kind-Entitäten (Contract, Nda, SupplierEvidence, SupplierAssessment, ManagementDecision, Subcontractor, ServiceControlResponsibility, MaturityAssessment) hängen am Asset (keine parallele Datenhaltung). Migration + RLS; Demo neu geseedet. - Lieferanten erscheinen im Asset-Inventar (Filter „Lieferant"/„IT-Service"), tragen C/I/A, nutzbar in Graph/BIA/Risiko. Anforderungs-Engine (Herzstück, src/lib/supplier.ts): - Schutzbedarf aus max C/I/A der verknüpften Assets → Stufe normal/hoch/ sehr hoch; schaltet Anforderungen gestuft scharf (must/should/high/veryhigh) - Flache Anforderungsliste (nach Themen gruppiert) mit Status-Ableitung aus den hinterlegten Objekten, Referenz + Herkunfts-Control (6.1.1–6.1.3) - Gate-Logik: sehr hoch ohne Audit/Label → Kompensation (Management- entscheidung UND verknüpftes Risiko); „Risiko anlegen" erzeugt echten Risk - Konformität (Konform/Teilweise/Lücken/Handlung nötig) + berechneter Reifegrad; ISB-Freigabe (Abweichung nur mit Begründung → Audit-Log) - Cockpit-Detail mit Schutzbedarf-Banner + Live-Simulation der Stufe Verifiziert: Register mit abgeleiteter Stufe/Reifegrad/Konformität, Cockpit-Simulation (Tiers werden inaktiv), Gate bei sehr hoch ohne Audit, „Risiko anlegen" erzeugt R-005 im Risikomodul. Offen (Phase 2): Fragebogen-Builder, Self-Service-Portal, IT-Service- Detail mit RACI-Matrix, Klick im Asset-Inventar öffnet Cockpit, Scheduler-Automatisierung. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
32ab91efbf
commit
dc884062b2
@@ -0,0 +1,239 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SupplierLifecycle" AS ENUM ('ACTIVE', 'ONBOARDING', 'UNDER_REVIEW', 'OFFBOARDED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RaciResponsibility" AS ENUM ('PROVIDER', 'US', 'SHARED');
|
||||
|
||||
-- AlterEnum
|
||||
ALTER TYPE "AssetType" ADD VALUE 'IT_SERVICE';
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "contracts" DROP CONSTRAINT "contracts_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "management_decisions" DROP CONSTRAINT "management_decisions_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "ndas" DROP CONSTRAINT "ndas_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "service_responsibilities" DROP CONSTRAINT "service_responsibilities_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "subcontractors" DROP CONSTRAINT "subcontractors_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "supplier_assessments" DROP CONSTRAINT "supplier_assessments_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "supplier_assets" DROP CONSTRAINT "supplier_assets_asset_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "supplier_assets" DROP CONSTRAINT "supplier_assets_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "supplier_control_maturity" DROP CONSTRAINT "supplier_control_maturity_control_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "supplier_control_maturity" DROP CONSTRAINT "supplier_control_maturity_supplier_id_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "supplier_evidence" DROP CONSTRAINT "supplier_evidence_supplier_id_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "contracts" DROP COLUMN "customer_transparency",
|
||||
DROP COLUMN "supplier_id",
|
||||
ADD COLUMN "asset_id" TEXT NOT NULL,
|
||||
ADD COLUMN "customer_requirements_passed" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "management_decisions" DROP COLUMN "supplier_id",
|
||||
ADD COLUMN "asset_id" TEXT NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ndas" DROP COLUMN "supplier_id",
|
||||
ADD COLUMN "asset_id" TEXT NOT NULL,
|
||||
ADD COLUMN "beyond_term" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "subcontractors" DROP COLUMN "supplier_id",
|
||||
ADD COLUMN "asset_id" TEXT NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "supplier_assessments" DROP COLUMN "status",
|
||||
DROP COLUMN "supplier_id",
|
||||
ADD COLUMN "asset_id" TEXT NOT NULL,
|
||||
ADD COLUMN "self_score" INTEGER,
|
||||
ADD COLUMN "verified_score" INTEGER;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "supplier_evidence" DROP COLUMN "supplier_id",
|
||||
ADD COLUMN "asset_id" TEXT NOT NULL;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "service_responsibilities";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "supplier_assets";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "supplier_control_maturity";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "supplier_controls";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "suppliers";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "AssessmentStatus";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "ResponsibleParty";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "SupplierStatus";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "supplier_profiles" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenant_id" TEXT NOT NULL,
|
||||
"asset_id" TEXT NOT NULL,
|
||||
"ref_no" INTEGER NOT NULL,
|
||||
"sector" TEXT,
|
||||
"service_desc" TEXT,
|
||||
"criticality" INTEGER NOT NULL DEFAULT 1,
|
||||
"data_categories" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"nis2_relevant" BOOLEAN NOT NULL DEFAULT false,
|
||||
"lifecycle" "SupplierLifecycle" NOT NULL DEFAULT 'ACTIVE',
|
||||
"contact" TEXT,
|
||||
"tisax_label" TEXT,
|
||||
"tisax_valid_to" TIMESTAMP(3),
|
||||
"next_review" TIMESTAMP(3),
|
||||
"notes" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_by" TEXT,
|
||||
|
||||
CONSTRAINT "supplier_profiles_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "it_service_profiles" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenant_id" TEXT NOT NULL,
|
||||
"asset_id" TEXT NOT NULL,
|
||||
"ref_no" INTEGER NOT NULL,
|
||||
"provider_asset_id" TEXT,
|
||||
"criticality" INTEGER NOT NULL DEFAULT 1,
|
||||
"internal" BOOLEAN NOT NULL DEFAULT false,
|
||||
"notes" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_by" TEXT,
|
||||
|
||||
CONSTRAINT "it_service_profiles_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "service_control_responsibilities" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenant_id" TEXT NOT NULL,
|
||||
"asset_id" TEXT NOT NULL,
|
||||
"control_ref" TEXT NOT NULL,
|
||||
"title" TEXT,
|
||||
"applicable" BOOLEAN NOT NULL DEFAULT true,
|
||||
"responsibility" "RaciResponsibility" NOT NULL DEFAULT 'SHARED',
|
||||
"evidence_ref" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "service_control_responsibilities_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "maturity_assessments" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenant_id" TEXT NOT NULL,
|
||||
"asset_id" TEXT NOT NULL,
|
||||
"self_value" DOUBLE PRECISION,
|
||||
"verified_value" DOUBLE PRECISION,
|
||||
"computed_value" DOUBLE PRECISION,
|
||||
"isb_value" DOUBLE PRECISION,
|
||||
"isb_justification" TEXT,
|
||||
"approved_by" TEXT,
|
||||
"approved_at" TIMESTAMP(3),
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "maturity_assessments_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "supplier_profiles_asset_id_key" ON "supplier_profiles"("asset_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "supplier_profiles_tenant_id_idx" ON "supplier_profiles"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "supplier_profiles_tenant_id_ref_no_key" ON "supplier_profiles"("tenant_id", "ref_no");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "it_service_profiles_asset_id_key" ON "it_service_profiles"("asset_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "it_service_profiles_tenant_id_idx" ON "it_service_profiles"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "it_service_profiles_tenant_id_ref_no_key" ON "it_service_profiles"("tenant_id", "ref_no");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "service_control_responsibilities_tenant_id_idx" ON "service_control_responsibilities"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "maturity_assessments_asset_id_key" ON "maturity_assessments"("asset_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "maturity_assessments_tenant_id_idx" ON "maturity_assessments"("tenant_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "supplier_profiles" ADD CONSTRAINT "supplier_profiles_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "it_service_profiles" ADD CONSTRAINT "it_service_profiles_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "it_service_profiles" ADD CONSTRAINT "it_service_profiles_provider_asset_id_fkey" FOREIGN KEY ("provider_asset_id") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "supplier_assessments" ADD CONSTRAINT "supplier_assessments_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "supplier_evidence" ADD CONSTRAINT "supplier_evidence_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "management_decisions" ADD CONSTRAINT "management_decisions_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "contracts" ADD CONSTRAINT "contracts_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ndas" ADD CONSTRAINT "ndas_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "subcontractors" ADD CONSTRAINT "subcontractors_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "service_control_responsibilities" ADD CONSTRAINT "service_control_responsibilities_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "maturity_assessments" ADD CONSTRAINT "maturity_assessments_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- RLS für neue/umgestellte Lieferanten-Tabellen
|
||||
DO $$
|
||||
DECLARE t text;
|
||||
BEGIN
|
||||
FOREACH t IN ARRAY ARRAY['supplier_profiles','it_service_profiles','maturity_assessments','service_control_responsibilities'] LOOP
|
||||
EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t);
|
||||
EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t);
|
||||
END LOOP;
|
||||
END $$;
|
||||
+144
-150
@@ -122,6 +122,7 @@ enum AssetType {
|
||||
APPLICATION
|
||||
LOCATION
|
||||
SUPPLIER
|
||||
IT_SERVICE
|
||||
PERSON
|
||||
DATA
|
||||
}
|
||||
@@ -166,7 +167,19 @@ model Asset {
|
||||
relationsTo AssetRelation[] @relation("relationTo")
|
||||
processAssets ProcessAsset[]
|
||||
riskAssets RiskAsset[]
|
||||
supplierLinks SupplierAsset[]
|
||||
|
||||
// Lieferanten-/IT-Service-Erweiterungen (1:1) + Kind-Entitäten
|
||||
supplierProfile SupplierProfile?
|
||||
serviceProfile ITServiceProfile? @relation("serviceAsset")
|
||||
serviceProvided ITServiceProfile[] @relation("serviceProvider")
|
||||
assessments SupplierAssessment[]
|
||||
evidence SupplierEvidence[]
|
||||
decisions ManagementDecision[]
|
||||
contracts Contract[]
|
||||
ndas Nda[]
|
||||
subcontractors Subcontractor[]
|
||||
raci ServiceControlResponsibility[]
|
||||
maturity MaturityAssessment?
|
||||
|
||||
@@index([tenantId])
|
||||
@@index([tenantId, type])
|
||||
@@ -397,9 +410,12 @@ model Vulnerability {
|
||||
@@map("vulnerabilities")
|
||||
}
|
||||
|
||||
// ── Lieferantenmanagement (SPEC §4.14, VDA-ISA 2027 Kap. 6, NIS2 Art. 21(2)(d)) ──
|
||||
// ── Lieferanten- & IT-Service-Management (VDA-ISA 2027 Kap. 6, NIS2 Art. 21(2)(d)) ──
|
||||
// Grundsatz: Lieferanten UND IT-Services sind Assets (type SUPPLIER | IT_SERVICE).
|
||||
// SupplierProfile/ITServiceProfile sind 1:1-Erweiterungen eines Asset; alle
|
||||
// Kind-Entitäten hängen am Asset (subject) — keine parallele Datenhaltung.
|
||||
|
||||
enum SupplierStatus {
|
||||
enum SupplierLifecycle {
|
||||
ACTIVE
|
||||
ONBOARDING
|
||||
UNDER_REVIEW
|
||||
@@ -412,13 +428,6 @@ enum AssessmentType {
|
||||
AUDIT
|
||||
}
|
||||
|
||||
enum AssessmentStatus {
|
||||
SENT
|
||||
RECEIVED
|
||||
EVALUATED
|
||||
OVERDUE
|
||||
}
|
||||
|
||||
enum EvidenceKind {
|
||||
CERTIFICATE
|
||||
TISAX_LABEL
|
||||
@@ -427,99 +436,132 @@ enum EvidenceKind {
|
||||
SELF_ASSESSMENT
|
||||
}
|
||||
|
||||
enum ResponsibleParty {
|
||||
CLIENT
|
||||
SUPPLIER
|
||||
enum RaciResponsibility {
|
||||
PROVIDER
|
||||
US
|
||||
SHARED
|
||||
}
|
||||
|
||||
model Supplier {
|
||||
model SupplierProfile {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
refNo Int @map("ref_no") // Anzeige "L-001"
|
||||
assetId String @unique @map("asset_id")
|
||||
refNo Int @map("ref_no")
|
||||
|
||||
name String
|
||||
sector String?
|
||||
services String? // erbrachte Leistung / IT-Services
|
||||
criticality Int @default(1) // 1–4
|
||||
dataCategories String[] @default([]) @map("data_categories")
|
||||
// Schutzbedarf der verarbeiteten Informationen (C/I/A 1–4)
|
||||
confidentiality Int @default(1)
|
||||
integrity Int @default(1)
|
||||
availability Int @default(1)
|
||||
nis2Relevant Boolean @default(false) @map("nis2_relevant") // Teil der Lieferkette
|
||||
status SupplierStatus @default(ACTIVE)
|
||||
serviceDesc String? @map("service_desc")
|
||||
criticality Int @default(1)
|
||||
dataCategories String[] @default([]) @map("data_categories")
|
||||
nis2Relevant Boolean @default(false) @map("nis2_relevant")
|
||||
lifecycle SupplierLifecycle @default(ACTIVE)
|
||||
contact String?
|
||||
nextReview DateTime? @map("next_review")
|
||||
tisaxLabel String? @map("tisax_label")
|
||||
tisaxValidTo DateTime? @map("tisax_valid_to")
|
||||
nextReview DateTime? @map("next_review")
|
||||
notes String?
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
createdBy String? @map("created_by")
|
||||
|
||||
assessments SupplierAssessment[]
|
||||
contracts Contract[]
|
||||
ndas Nda[]
|
||||
evidence SupplierEvidence[]
|
||||
responsibilities ServiceResponsibility[]
|
||||
subcontractors Subcontractor[]
|
||||
decisions ManagementDecision[]
|
||||
controlMaturity SupplierControlMaturity[]
|
||||
assetLinks SupplierAsset[]
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([tenantId, refNo])
|
||||
@@index([tenantId])
|
||||
@@map("suppliers")
|
||||
@@map("supplier_profiles")
|
||||
}
|
||||
|
||||
// Lieferant ↔ Asset (welcher Dienstleister betrifft welche Assets) — speist den Graph
|
||||
model SupplierAsset {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
assetId String @map("asset_id")
|
||||
model ITServiceProfile {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @unique @map("asset_id")
|
||||
refNo Int @map("ref_no")
|
||||
providerAssetId String? @map("provider_asset_id") // Asset(type SUPPLIER)
|
||||
criticality Int @default(1)
|
||||
internal Boolean @default(false)
|
||||
notes String?
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
createdBy String? @map("created_by")
|
||||
|
||||
@@unique([supplierId, assetId])
|
||||
asset Asset @relation("serviceAsset", fields: [assetId], references: [id], onDelete: Cascade)
|
||||
provider Asset? @relation("serviceProvider", fields: [providerAssetId], references: [id])
|
||||
|
||||
@@unique([tenantId, refNo])
|
||||
@@index([tenantId])
|
||||
@@map("supplier_assets")
|
||||
@@map("it_service_profiles")
|
||||
}
|
||||
|
||||
// Bewertung & Nachweise (6.1.1)
|
||||
model SupplierAssessment {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @map("asset_id")
|
||||
type AssessmentType
|
||||
status AssessmentStatus @default(SENT)
|
||||
score Int? // 0–100 Scoring
|
||||
score Int?
|
||||
selfScore Int? @map("self_score")
|
||||
verifiedScore Int? @map("verified_score")
|
||||
date DateTime?
|
||||
nextReview DateTime? @map("next_review")
|
||||
nextReview DateTime? @map("next_review")
|
||||
result String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("supplier_assessments")
|
||||
}
|
||||
|
||||
model Contract {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
type String @default("service") // service | av_dpa | nda | sla …
|
||||
avDpa Boolean @default(false) @map("av_dpa") // AV/DPA (DSGVO Art. 28)
|
||||
securityClauses Boolean @default(false) @map("security_clauses")
|
||||
flowdown Boolean @default(false) // Weitergabe an Subunternehmer
|
||||
customerTransparency Boolean @default(false) @map("customer_transparency")
|
||||
validFrom DateTime? @map("valid_from")
|
||||
validTo DateTime? @map("valid_to")
|
||||
reference String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
model SupplierEvidence {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @map("asset_id") // Subjekt (Lieferant ODER Service)
|
||||
kind EvidenceKind
|
||||
name String?
|
||||
protectsCia String? @map("protects_cia")
|
||||
validTo DateTime? @map("valid_to")
|
||||
adequacyChecked Boolean @default(false) @map("adequacy_checked")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("supplier_evidence")
|
||||
}
|
||||
|
||||
model ManagementDecision {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @map("asset_id")
|
||||
reasonNoAudit String @map("reason_no_audit")
|
||||
decision String
|
||||
decidedBy String? @map("decided_by")
|
||||
date DateTime @default(now())
|
||||
recordRef String? @map("record_ref")
|
||||
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("management_decisions")
|
||||
}
|
||||
|
||||
// Vertrag & Geheimhaltung (6.1.1 / 6.1.2)
|
||||
model Contract {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @map("asset_id")
|
||||
type String @default("service")
|
||||
avDpa Boolean @default(false) @map("av_dpa")
|
||||
securityClauses Boolean @default(false) @map("security_clauses")
|
||||
flowdown Boolean @default(false)
|
||||
customerRequirementsPassed Boolean @default(false) @map("customer_requirements_passed")
|
||||
validFrom DateTime? @map("valid_from")
|
||||
validTo DateTime? @map("valid_to")
|
||||
reference String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("contracts")
|
||||
@@ -528,120 +570,72 @@ model Contract {
|
||||
model Nda {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
assetId String @map("asset_id")
|
||||
parties String?
|
||||
infoScope String? @map("info_scope")
|
||||
subject String?
|
||||
validFrom DateTime? @map("valid_from")
|
||||
validTo DateTime? @map("valid_to")
|
||||
obligations String?
|
||||
extensionStatus String? @map("extension_status") // z. B. offen | verlängert | ausgelaufen
|
||||
beyondTerm Boolean @default(false) @map("beyond_term")
|
||||
extensionStatus String? @map("extension_status")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("ndas")
|
||||
}
|
||||
|
||||
model SupplierEvidence {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
kind EvidenceKind
|
||||
name String?
|
||||
protectsCia String? @map("protects_cia") // z. B. "C,I,A"
|
||||
validTo DateTime? @map("valid_to")
|
||||
adequacyChecked Boolean @default(false) @map("adequacy_checked")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("supplier_evidence")
|
||||
}
|
||||
|
||||
model ServiceResponsibility {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
itService String @map("it_service")
|
||||
requirement String
|
||||
responsibleParty ResponsibleParty @default(SHARED) @map("responsible_party")
|
||||
isaApplicability String? @map("isa_applicability")
|
||||
evidence String?
|
||||
integratedLocalControls String? @map("integrated_local_controls")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("service_responsibilities")
|
||||
}
|
||||
|
||||
model Subcontractor {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
assetId String @map("asset_id")
|
||||
name String
|
||||
flowdownObligation Boolean @default(false) @map("flowdown_obligation")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("subcontractors")
|
||||
}
|
||||
|
||||
model ManagementDecision {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
reasonNoAudit String @map("reason_no_audit")
|
||||
decision String
|
||||
decidedBy String? @map("decided_by")
|
||||
date DateTime @default(now())
|
||||
recordRef String? @map("record_ref")
|
||||
// Verantwortung je IT-Service (6.1.3) — RACI über den ISA-Control-Katalog
|
||||
model ServiceControlResponsibility {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @map("asset_id") // Asset(type IT_SERVICE)
|
||||
controlRef String @map("control_ref")
|
||||
title String?
|
||||
applicable Boolean @default(true)
|
||||
responsibility RaciResponsibility @default(SHARED)
|
||||
evidenceRef String? @map("evidence_ref")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tenantId])
|
||||
@@map("management_decisions")
|
||||
@@map("service_control_responsibilities")
|
||||
}
|
||||
|
||||
// VDA-ISA 2027 Kapitel 6 — Supplier Relationships (globaler Katalog, per Import versioniert)
|
||||
model SupplierControl {
|
||||
id String @id @default(cuid())
|
||||
ref String @unique // 6.1.1 / 6.1.2 / 6.1.3
|
||||
title String
|
||||
objective String
|
||||
// Reifegrad je Subjekt — Fragebogen-Selbstauskunft, durch Nachweise verifiziert, ISB-Freigabe
|
||||
model MaturityAssessment {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
assetId String @unique @map("asset_id")
|
||||
selfValue Float? @map("self_value")
|
||||
verifiedValue Float? @map("verified_value")
|
||||
computedValue Float? @map("computed_value")
|
||||
isbValue Float? @map("isb_value")
|
||||
isbJustification String? @map("isb_justification")
|
||||
approvedBy String? @map("approved_by")
|
||||
approvedAt DateTime? @map("approved_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
mustReq String? @map("must_req")
|
||||
shouldReq String? @map("should_req")
|
||||
highReq String? @map("high_req") // Additional for high protection
|
||||
veryHighReq String? @map("very_high_req") // Additional for very high protection
|
||||
simplifiedGroupAssessment Boolean @default(false) @map("simplified_group_assessment")
|
||||
targetMaturity Int @default(3) @map("target_maturity")
|
||||
references String? // ISO 27001, NIST CSF, BSI …
|
||||
asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||||
|
||||
maturity SupplierControlMaturity[]
|
||||
|
||||
@@map("supplier_controls")
|
||||
}
|
||||
|
||||
model SupplierControlMaturity {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
supplierId String @map("supplier_id")
|
||||
controlId String @map("control_id")
|
||||
maturity Int @default(0) // 0–5
|
||||
notes String?
|
||||
|
||||
supplier Supplier @relation(fields: [supplierId], references: [id], onDelete: Cascade)
|
||||
control SupplierControl @relation(fields: [controlId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([supplierId, controlId])
|
||||
@@index([tenantId])
|
||||
@@map("supplier_control_maturity")
|
||||
@@map("maturity_assessments")
|
||||
}
|
||||
|
||||
model AuditLog {
|
||||
|
||||
+20
-157
@@ -453,168 +453,31 @@ async function main() {
|
||||
console.log(`✔ ${measures.length} Beispiel-Maßnahmen angelegt, Rest-Risiken berechnet`);
|
||||
}
|
||||
|
||||
// 8. VDA-ISA 2027 Kapitel 6 — Supplier Relationships (globaler Katalog)
|
||||
const controls = [
|
||||
{
|
||||
ref: "6.1.1",
|
||||
title:
|
||||
"To what extent is information security ensured among contractors and cooperation partners?",
|
||||
objective:
|
||||
"Ein angemessenes Informationssicherheitsniveau wird auch bei der Zusammenarbeit mit Partnern und Auftragnehmern aufrechterhalten.",
|
||||
mustReq:
|
||||
"Auftragnehmer/Partner werden einer Sicherheitsrisikobewertung unterzogen; angemessenes Niveau vertraglich sichergestellt; Kundenanforderungen ggf. weitergegeben.",
|
||||
shouldReq:
|
||||
"Vertragliche Verpflichtung zur Weitergabe an Subunternehmer; Prüfung von Service-Reports/Dokumenten.",
|
||||
highReq:
|
||||
"Nachweis eines angemessenen Sicherheitsniveaus (geprüfter Fragebogen/Self-Assessment, Attest, Zertifikat, Lieferantenaudit) (C,I,A); Compliance dokumentiert, regelmäßig & anlassbezogen überwacht.",
|
||||
veryHighReq:
|
||||
"Nachweis durch Third-Party-Audit (adäquates TISAX-Label o. Ä.) oder Lieferantenaudit; ohne Audit: risikobasierte Managemententscheidung mit Protokoll (C,I,A); Transparenzpflichten gegenüber Kunden erfüllt.",
|
||||
targetMaturity: 3,
|
||||
simplifiedGroupAssessment: true,
|
||||
references:
|
||||
"ISO 27001:2022 A.5.19–A.5.22 · NIST CSF 2.0 GV.SC-02..06 · BSI OPS.2.1/OPS.2.2/OPS.3.1/ORP.2 · NIST SP800-53r5 MA-4, CA-3, CA-6, PM-16, PM-30, SR-2, SR-3, SR-6, SR-7, SR-8",
|
||||
},
|
||||
{
|
||||
ref: "6.1.2",
|
||||
title:
|
||||
"To what extent is non-disclosure regarding the exchange of information contractually agreed?",
|
||||
objective:
|
||||
"Geheimhaltungsvereinbarungen schützen den Informationsaustausch über Organisationsgrenzen hinweg rechtlich.",
|
||||
mustReq:
|
||||
"Geheimhaltungsanforderungen bestimmt und erfüllt; allen Beteiligten bekannt; gültige NDAs vor Weitergabe sensibler Infos; regelmäßige Überprüfung.",
|
||||
shouldReq:
|
||||
"Geprüfte NDA-Vorlagen; NDAs mit Parteien, Informationsart, Gegenstand, Gültigkeit, Pflichten; Nachweis-/Auditrechte; Prozess zur Fristenüberwachung und rechtzeitigen Verlängerung.",
|
||||
highReq: null,
|
||||
veryHighReq: null,
|
||||
targetMaturity: 3,
|
||||
simplifiedGroupAssessment: true,
|
||||
references: "ISO 27001:2022 A.5.14, A.6.6 · BSI OPS.2.1/OPS.2.2/OPS.3.1/ORP.5/CON.2",
|
||||
},
|
||||
{
|
||||
ref: "6.1.3",
|
||||
title:
|
||||
"To what extent are the responsibilities between external IT service providers and the own organization defined?",
|
||||
objective:
|
||||
"Gemeinsames Verständnis der Verantwortungsteilung; alle Sicherheitsanforderungen umgesetzt und nachweislich dokumentiert.",
|
||||
mustReq:
|
||||
"Betroffene IT-Services identifiziert; Anforderungen bestimmt; verantwortliche Organisation je Anforderung definiert; Mechanismen für geteilte Verantwortung umgesetzt.",
|
||||
shouldReq:
|
||||
"Konfiguration konzeptioniert/umgesetzt/dokumentiert; zuständiges Personal geschult.",
|
||||
highReq:
|
||||
"Liste der IT-Services und Provider (C,I,A); ISA-Control-Anwendbarkeit bewertet; regelmäßige Security-Assessments; Nachweis der Pflichterfüllung; Integration in lokale Schutzmaßnahmen dokumentiert.",
|
||||
veryHighReq: null,
|
||||
targetMaturity: 3,
|
||||
simplifiedGroupAssessment: false,
|
||||
references:
|
||||
"ISO 27001:2022 A.5.23, A.8.9 · ISO 27017 CLD.6.3.1 · IEC 62443-2-1 6.2.3 · NIST CSF 2.0 GV.OC-05, GV.SC-01/02 · BSI 200-2, OPS.2.1/OPS.2.2/OPS.3.1/ORP.2 · NIST SP800-53r5 MA-4, PT-1, PL-2",
|
||||
},
|
||||
];
|
||||
for (const c of controls) {
|
||||
await prisma.supplierControl.upsert({ where: { ref: c.ref }, update: c, create: c });
|
||||
}
|
||||
console.log(`✔ VDA-ISA 2027 Kap. 6: ${controls.length} Supplier-Controls`);
|
||||
// 8. Demo-Lieferanten als Assets (type SUPPLIER) mit Profil, Nachweisen, Verträgen
|
||||
const supCount = await prisma.supplierProfile.count({ where: { tenantId: tenant.id } });
|
||||
if (supCount === 0) {
|
||||
const hoster = await prisma.asset.findFirst({ where: { tenantId: tenant.id, name: "Cloud-Hoster (IaaS)" } });
|
||||
|
||||
// 9. Demo-Lieferant mit Nachweisen/Verträgen (nur wenn noch keiner existiert)
|
||||
const supplierCount = await prisma.supplier.count({ where: { tenantId: tenant.id } });
|
||||
if (supplierCount === 0) {
|
||||
const hoster = await prisma.asset.findFirst({
|
||||
where: { tenantId: tenant.id, name: "Cloud-Hoster (IaaS)" },
|
||||
const a1 = await prisma.asset.create({
|
||||
data: { tenantId: tenant.id, name: "Cloud-Hoster GmbH", type: "SUPPLIER", confidentiality: 3, integrity: 3, availability: 4 },
|
||||
});
|
||||
const sup = await prisma.supplier.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
refNo: 1,
|
||||
name: "Cloud-Hoster GmbH",
|
||||
sector: "IT-Dienstleistung / IaaS",
|
||||
services: "Rechenzentrum, Virtualisierung, Backup für ERP & CRM",
|
||||
criticality: 4,
|
||||
dataCategories: ["Kundendaten", "Auftragsdaten"],
|
||||
confidentiality: 3,
|
||||
integrity: 3,
|
||||
availability: 4,
|
||||
nis2Relevant: true,
|
||||
status: "ACTIVE",
|
||||
contact: "security@cloud-hoster.example",
|
||||
nextReview: new Date(Date.now() + 90 * 24 * 3600 * 1000),
|
||||
},
|
||||
await prisma.supplierProfile.create({
|
||||
data: { tenantId: tenant.id, assetId: a1.id, refNo: 1, sector: "IT-Dienstleistung / IaaS", serviceDesc: "Rechenzentrum, Virtualisierung, Backup", criticality: 4, dataCategories: ["Kundendaten", "Auftragsdaten"], nis2Relevant: true, lifecycle: "ACTIVE", contact: "security@cloud-hoster.example", nextReview: new Date(Date.now() + 90 * 24 * 3600 * 1000) },
|
||||
});
|
||||
if (hoster) {
|
||||
await prisma.supplierAsset.create({
|
||||
data: { tenantId: tenant.id, supplierId: sup.id, assetId: hoster.id },
|
||||
});
|
||||
}
|
||||
await prisma.supplierEvidence.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
supplierId: sup.id,
|
||||
kind: "TISAX_LABEL",
|
||||
name: "TISAX AL3 (info high)",
|
||||
protectsCia: "C,I,A",
|
||||
validTo: new Date(Date.now() + 200 * 24 * 3600 * 1000),
|
||||
adequacyChecked: true,
|
||||
},
|
||||
if (hoster) await prisma.assetRelation.create({ data: { tenantId: tenant.id, assetId: a1.id, relatedAssetId: hoster.id, type: "provides" } }).catch(() => {});
|
||||
await prisma.supplierEvidence.create({ data: { tenantId: tenant.id, assetId: a1.id, kind: "TISAX_LABEL", name: "TISAX AL3 (info high)", protectsCia: "C,I,A", validTo: new Date(Date.now() + 200 * 24 * 3600 * 1000), adequacyChecked: true } });
|
||||
await prisma.contract.create({ data: { tenantId: tenant.id, assetId: a1.id, type: "av_dpa", avDpa: true, securityClauses: true, flowdown: true, customerRequirementsPassed: true, validTo: new Date(Date.now() + 400 * 24 * 3600 * 1000), reference: "AV-2025-014" } });
|
||||
await prisma.nda.create({ data: { tenantId: tenant.id, assetId: a1.id, subject: "Betrieb der ERP-/CRM-Infrastruktur", parties: "Demo GmbH / Cloud-Hoster GmbH", validTo: new Date(Date.now() + 60 * 24 * 3600 * 1000), extensionStatus: "offen" } });
|
||||
await prisma.supplierAssessment.create({ data: { tenantId: tenant.id, assetId: a1.id, type: "SELF_ASSESSMENT", score: 82, date: new Date(Date.now() - 120 * 24 * 3600 * 1000), nextReview: new Date(Date.now() + 245 * 24 * 3600 * 1000), result: "angemessen" } });
|
||||
|
||||
const a2 = await prisma.asset.create({
|
||||
data: { tenantId: tenant.id, name: "WebAgentur X", type: "SUPPLIER", confidentiality: 2, integrity: 2, availability: 2 },
|
||||
});
|
||||
await prisma.contract.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
supplierId: sup.id,
|
||||
type: "av_dpa",
|
||||
avDpa: true,
|
||||
securityClauses: true,
|
||||
flowdown: true,
|
||||
customerTransparency: false,
|
||||
validFrom: new Date(Date.now() - 300 * 24 * 3600 * 1000),
|
||||
validTo: new Date(Date.now() + 400 * 24 * 3600 * 1000),
|
||||
reference: "AV-2025-014",
|
||||
},
|
||||
await prisma.supplierProfile.create({
|
||||
data: { tenantId: tenant.id, assetId: a2.id, refNo: 2, sector: "Webentwicklung", serviceDesc: "Website-Betrieb", criticality: 2, dataCategories: ["Marketingdaten"], nis2Relevant: false, lifecycle: "UNDER_REVIEW" },
|
||||
});
|
||||
await prisma.nda.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
supplierId: sup.id,
|
||||
parties: "Demo GmbH ↔ Cloud-Hoster GmbH",
|
||||
infoScope: "Betriebs- und Kundendaten",
|
||||
subject: "Betrieb der ERP-/CRM-Infrastruktur",
|
||||
validFrom: new Date(Date.now() - 300 * 24 * 3600 * 1000),
|
||||
validTo: new Date(Date.now() + 60 * 24 * 3600 * 1000),
|
||||
extensionStatus: "offen",
|
||||
},
|
||||
});
|
||||
await prisma.supplierAssessment.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
supplierId: sup.id,
|
||||
type: "SELF_ASSESSMENT",
|
||||
status: "EVALUATED",
|
||||
score: 82,
|
||||
date: new Date(Date.now() - 120 * 24 * 3600 * 1000),
|
||||
nextReview: new Date(Date.now() + 245 * 24 * 3600 * 1000),
|
||||
result: "angemessen",
|
||||
},
|
||||
});
|
||||
await prisma.serviceResponsibility.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
supplierId: sup.id,
|
||||
itService: "IaaS-Plattform",
|
||||
requirement: "Patch-Management der Hypervisor-Ebene",
|
||||
responsibleParty: "SUPPLIER",
|
||||
isaApplicability: "5.x IT/Cyber Security",
|
||||
integratedLocalControls: "Sichere Authentisierung, Monitoring beim Kunden",
|
||||
},
|
||||
});
|
||||
const dbControls = await prisma.supplierControl.findMany();
|
||||
for (const c of dbControls) {
|
||||
await prisma.supplierControlMaturity.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
supplierId: sup.id,
|
||||
controlId: c.id,
|
||||
maturity: c.ref === "6.1.2" ? 2 : 3,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✔ Demo-Lieferant mit Nachweisen/Vertrag/NDA/Assessment angelegt");
|
||||
|
||||
console.log("✔ 2 Demo-Lieferanten als Assets angelegt");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user