Buchungskalender: Abschluss – S7 Buchungslink-Gueltigkeit (90 Tage nach Abreise, 30 Tage nach Absage), tokenHash beim Anonymisieren leeren

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 15:26:12 +00:00
1 parent 17400cac1f
commit 4f9fb6b109
6 files changed
+57 -12

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@ import * as mails from './mails.js'
import { berechnePreis, preiseVollstaendig, pruefePosten, summiere } from './preis.js'
import { stelleAus } from './rechnungen.js'
import { aktualisiereAnfrage, ladeAnfrage, findeRechnung, ladeEinstellungen, ladePreise, ladeRechnung, ladeUnterkunft, legeAnfrageDokAn, listeAnfragen, listeAnfragenAlle } from './speicher.js'
import { neuesToken, tokenPasst } from './token.js'
import { linkGueltig, neuesToken, tokenPasst } from './token.js'
import * as zahlung from './zahlung.js'
export const UEBERGAENGE = {
@@ -194,7 +194,7 @@ export async function bucheVerbindlich(ctx, anfrageId, token, { rechnungsadresse
if (!pre || pre.featureId !== ctx.featureId) throw new UkFehler(404, 'nicht_gefunden')
const r = await withLocks(withLocksKey(pre.unterkunftId), async () => {
const a = await ladeAnfrage(anfrageId)
if (!a || a.featureId !== ctx.featureId || !tokenPasst(a.tokenHash, token)) throw new UkFehler(404, 'nicht_gefunden')
if (!a || a.featureId !== ctx.featureId || !tokenPasst(a.tokenHash, token) || !linkGueltig(a, now)) throw new UkFehler(404, 'nicht_gefunden')
if (a.status === 'gebucht' || a.status === 'bezahlt') {
return { anfrage: a, rechnung: a.rechnungId ? await ladeRechnung(a.rechnungId) : null, neu: false }
}
+1 -1
View File
@@ -27,7 +27,7 @@ const ANONYM_JAHRE = () => config.unterkunft.anonymisierenJahre ?? 3
const RECHNUNG_JAHRE = () => config.unterkunft.rechnungJahre ?? 8
const MARKER = 'Buchungsdaten gelöscht am '
const MARKER_RE = /Buchungsdaten gelöscht am (\d{4}-\d{2}-\d{2})/
const ANFRAGE_ANONYM = { gast: {}, nachrichtVermieter: '', ipHash: '' }
const ANFRAGE_ANONYM = { gast: {}, nachrichtVermieter: '', ipHash: '', tokenHash: '' } // tokenHash leer: der Buchungslink ist mit der Anonymisierung tot
const zeitMs = (v) => {
const t = Date.parse(v)
+24
View File
@@ -1,6 +1,8 @@
import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto'
import { config } from '../../config.js'
import { ipSchluessel } from '../termine/limits.js'
import { heute } from '../termine/zeit.js'
import { plusTage } from './datum.js'
/**
* Tokens des Buchungskalenders. Buchungslink-Token: 32 Byte zufaellig (base64url), gespeichert wird nur der SHA-256-Hash.
@@ -28,6 +30,28 @@ export function tokenPasst(hash, token) {
return erwartet.length === gegeben.length && timingSafeEqual(erwartet, gegeben)
}
const LINK_TAGE_NACH_ABREISE = 90
const LINK_TAGE_NACH_ABSAGE = 30
const TAG_RE = /^\d{4}-\d{2}-\d{2}/
/**
* Gilt der Buchungslink noch? Bis 90 Tage nach der Abreise (bis, einschliesslich dieses Tages, Berliner Tag); bei abgelehnten und abgelaufenen
* Anfragen (nicht zustande gekommen) nur 30 Tage nach dem Statuswechsel. Danach antwortet die Buchungsseite wie bei einem unbekannten Link (404).
* Ohne lesbares Datum gilt der Link (kein Grund, ihn zu sperren).
*/
export function linkGueltig(anfrage, now = new Date()) {
const heuteTag = heute(now instanceof Date ? now : new Date(Number(now)))
const status = anfrage?.status
if (status === 'abgelehnt' || status === 'abgelaufen') {
const seit = String((status === 'abgelehnt' ? anfrage.abgelehntAt : anfrage.abgelaufenAt) || anfrage.$updatedAt || '')
if (!TAG_RE.test(seit)) return true
return heuteTag <= plusTage(heute(new Date(seit)), LINK_TAGE_NACH_ABSAGE)
}
const bis = String(anfrage?.bis ?? '')
if (!TAG_RE.test(bis)) return true
return heuteTag <= plusTage(bis.slice(0, 10), LINK_TAGE_NACH_ABREISE)
}
export const formMinSekunden = () => {
const wert = config.unterkunft?.formMinSekunden
return Number.isFinite(wert) && wert >= 0 ? wert : MIN_STANDARD_SEKUNDEN