Files
MikrotikManager/backend/src/services/certificate-installation.ts
T

34 lines
2.5 KiB
TypeScript

type Row = Record<string, unknown>
function rows(value: unknown): Row[] {
if (!Array.isArray(value) || value.some(row => !row || typeof row !== "object" || Array.isArray(row))) throw new Error("Некорректный ответ проверки сертификата")
return value as Row[]
}
function fingerprint(value: unknown): string | null {
if (typeof value !== "string") return null
const normalized = value.replaceAll(":", "").toLowerCase()
return /^[a-f0-9]{64}$/.test(normalized) ? normalized : null
}
export function assertImportedCertificate(value: unknown, name: string, expectedFingerprint: string): void {
const matches = rows(value).filter(row => row.name === name)
if (matches.length !== 1) throw new Error("Импорт не подтверждён: имя сертификата отсутствует или неоднозначно. Назначение сервисам не подтверждено.")
const row = matches[0]!
const expected = fingerprint(expectedFingerprint)
if (!expected || fingerprint(row.fingerprint) !== expected) throw new Error("Импорт не подтверждён: отпечаток сертификата отличается от выданного")
const privateKey = row["private-key"] === "true" || row["private-key"] === "yes" || row["private-key"] === undefined && typeof row.flags === "string" && row.flags.toUpperCase().includes("K")
if (!privateKey) throw new Error("Импорт не подтверждён: закрытый ключ сертификата не обнаружен")
}
export async function applyVerifiedCertificateToServices(deps: {
certificates: () => Promise<unknown>
apply: () => Promise<void>
services: () => Promise<unknown>
}, name: string, expectedFingerprint: string): Promise<void> {
assertImportedCertificate(await deps.certificates(), name, expectedFingerprint)
await deps.apply()
assertImportedCertificate(await deps.certificates(), name, expectedFingerprint)
const services = rows(await deps.services()).filter(row => row.dynamic !== "true" && row.dynamic !== "yes")
for (const service of ["www-ssl", "api-ssl"]) {
const matching = services.filter(row => row.name === service)
if (matching.length !== 1 || matching[0]!.certificate !== name || !["false", "no"].includes(String(matching[0]!.disabled))) throw new Error(`Назначение сертификата сервису ${service} не подтверждено; проверьте фактические настройки устройства`)
}
}