diff --git a/ajax/verify_signature.php b/ajax/verify_signature.php new file mode 100644 index 0000000..e7ae199 --- /dev/null +++ b/ajax/verify_signature.php @@ -0,0 +1,102 @@ +hasRight('bericht', 'read')) bericht_ajax_fail('Permission denied', 403); + +$pageid = (int) ($_POST['pageid'] ?? $_GET['pageid'] ?? 0); +if (!$pageid) bericht_ajax_fail('pageid fehlt'); + +$sqlr = $db->query("SELECT rowid, fk_bericht, source_path FROM ".$db->prefix()."bericht_page WHERE rowid = ".$pageid); +if (!$sqlr || !($row = $db->fetch_object($sqlr))) bericht_ajax_fail('Seite nicht gefunden', 404); + +$full = bericht_resolve_data_path($row->source_path); +if (!$full || !file_exists($full)) bericht_ajax_fail('Datei nicht gefunden', 404); + +$meta_path = $full.'.meta.json'; +if (!file_exists($meta_path)) { + bericht_ajax_ok(array( + 'verified' => false, + 'reason' => 'Keine Metadaten-Datei — diese Seite ist keine aufgewertete Unterschrift', + )); +} + +$meta = json_decode(file_get_contents($meta_path), true); +if (!is_array($meta) || empty($meta['chain_hash_sha256'])) { + bericht_ajax_ok(array( + 'verified' => false, + 'reason' => 'Metadaten ungültig oder kein Hash enthalten', + )); +} + +// Wir brauchen den Bericht +$bericht = new Bericht($db); +if ($bericht->fetch($row->fk_bericht) <= 0) bericht_ajax_fail('Bericht nicht gefunden', 404); + +// Die zum Signaturzeitpunkt existierenden Seiten: alle mit page_order < unserer. +// Sort nach page_order wie beim Signieren. +$prev_pages = BerichtPage::fetchAllForBericht($db, $row->fk_bericht); +// Nur die Seiten die VOR der Signatur waren — wir nehmen die ersten N entsprechend meta.page_count_at_signing +$expected_count = (int) ($meta['page_count_at_signing'] ?? 0); +$to_hash = array_slice($prev_pages, 0, $expected_count); + +$current_count = 0; +foreach ($prev_pages as $pp) { + if ($pp->id == $row->rowid) break; + $current_count++; +} + +// Hash nachrechnen +$hash_ctx = hash_init('sha256'); +hash_update($hash_ctx, 'bericht:'.$bericht->id.'|ref:'.$bericht->ref); +foreach ($to_hash as $pp) { + $full_pp = bericht_resolve_data_path($pp->source_path); + if ($full_pp && file_exists($full_pp)) { + hash_update_file($hash_ctx, $full_pp); + } + hash_update($hash_ctx, '|order:'.$pp->page_order.'|note:'.($pp->note ?? '')); +} +$current_hash = hash_final($hash_ctx); +$stored_hash = $meta['chain_hash_sha256']; + +$verified = hash_equals($stored_hash, $current_hash); + +$result = array( + 'verified' => $verified, + 'stored_hash' => $stored_hash, + 'current_hash' => $current_hash, + 'expected_page_count' => $expected_count, + 'current_page_count' => $current_count, + 'meta' => array( + 'signer_name' => $meta['signer_name'] ?? '', + 'signed_at' => $meta['signed_at'] ?? '', + 'user_login' => $meta['user_login'] ?? '', + 'kunde' => $meta['kunde'] ?? '', + 'parent_ref' => $meta['parent_ref'] ?? '', + 'gps_lat' => $meta['gps_lat'] ?? null, + 'gps_lon' => $meta['gps_lon'] ?? null, + 'remote_ip' => $meta['remote_ip'] ?? '', + ), +); + +if (!$verified) { + if ($current_count !== $expected_count) { + $result['reason'] = "Seitenanzahl hat sich geändert ($expected_count → $current_count)"; + } else { + $result['reason'] = "Hash der Vorseiten stimmt nicht — eine Seite wurde nach der Unterschrift verändert"; + } +} + +bericht_ajax_ok($result); diff --git a/api/pages.php b/api/pages.php index 1bf253f..29ae2c0 100644 --- a/api/pages.php +++ b/api/pages.php @@ -17,6 +17,24 @@ if (!$user->hasRight('bericht', 'write')) api_fail('Permission denied', 403); $method = $_SERVER['REQUEST_METHOD']; $action = $_GET['action'] ?? ''; +/* ---------- Seiten umsortieren ---------- */ +if ($method === 'POST' && $action === 'reorder') { + $in = api_input(); + $ids = $in['order'] ?? null; + if (!is_array($ids) || empty($ids)) api_fail('order-Array fehlt'); + $db->begin(); + foreach ($ids as $pos => $pageid) { + $pid = (int) $pageid; + if ($pid <= 0) continue; + if (!$db->query("UPDATE ".$db->prefix()."bericht_page SET page_order = ".((int) ($pos + 1))." WHERE rowid = ".$pid)) { + $db->rollback(); + api_fail($db->lasterror(), 500); + } + } + $db->commit(); + api_ok(); +} + /* ---------- Unterschrift als neue Seite anhängen ---------- */ if ($method === 'POST' && $action === 'signature') { $bericht_id = (int) ($_GET['bericht_id'] ?? 0); diff --git a/bericht_card.php b/bericht_card.php index a310bf9..1c51a00 100644 --- a/bericht_card.php +++ b/bericht_card.php @@ -267,6 +267,7 @@ if (!$bericht) { 'set_slot_image' => dol_buildpath('/bericht/ajax/set_slot_image.php', 1), 'create_upload_token' => dol_buildpath('/bericht/ajax/create_upload_token.php', 1), 'list_pages' => dol_buildpath('/bericht/ajax/list_pages.php', 1), + 'verify_signature' => dol_buildpath('/bericht/ajax/verify_signature.php', 1), ), 'lang' => array( 'undo' => $langs->trans("BerichtUndo"), @@ -456,12 +457,25 @@ if (!$bericht) { print ''; print '