All checks were successful
Deploy bericht / deploy (push) Successful in 6s
- Neuer API-Endpoint api/shipments.php: Liste Lieferungen zu Auftrag, PDF-Stream, confirm (Unterschrift stempeln)
- ODT-Hook actions_bericht.class.php: ersetzt {signature} Platzhalter via odfphp->setImage, setzt {signer_name}/{signed_at}/{gps}
- Backup-Roundtrip: generateDocument-Backup → signed.pdf erzeugen → Original wiederherstellen
- JWT-Fallback in _jwt.php: ?jwt= Query-Param für <img>/<object> ohne Authorization-Header
- Admin: BERICHT_SIGNATURE_IMAGE_RATIO Feld, Toggle BERICHT_TAB_ON_SHIPMENT, Signature-Box-Editor
- DB: llx_bericht_signature_box für pro-Template mm-Box-Geometrie
- element_type='shipment' in modBericht + lib/bericht.lib.php
- element_element Richtung: commande=source, shipping=target (fk_target=expedition_id)
- DOL_DATA_ROOT-Auflösung für EXPEDITION_ADDON_PDF_ODT_PATH
- Sprachen: de_DE + en_US mit neuen Schlüsseln für Signatur-Workflow
[deploy]
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
74 lines
2.5 KiB
PHP
74 lines
2.5 KiB
PHP
<?php
|
|
/* Mini JWT Helper für die Bericht-API.
|
|
* HS256 only — kein externes Lib nötig.
|
|
*
|
|
* Secret kommt aus $dolibarr_main_instance_unique_id (siehe conf.php),
|
|
* salt'ed mit "bericht-api-v1".
|
|
*/
|
|
|
|
if (!defined('BERICHT_JWT_TTL')) define('BERICHT_JWT_TTL', 30 * 86400); // 30 Tage
|
|
|
|
function bericht_jwt_secret()
|
|
{
|
|
global $dolibarr_main_instance_unique_id;
|
|
$base = $dolibarr_main_instance_unique_id ?? 'fallback-secret-do-not-use';
|
|
return hash('sha256', $base.'|bericht-api-v1');
|
|
}
|
|
|
|
function bericht_b64url_encode($data)
|
|
{
|
|
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
|
}
|
|
|
|
function bericht_b64url_decode($data)
|
|
{
|
|
return base64_decode(strtr($data, '-_', '+/').str_repeat('=', (4 - strlen($data) % 4) % 4));
|
|
}
|
|
|
|
function bericht_jwt_encode(array $payload)
|
|
{
|
|
$header = array('alg' => 'HS256', 'typ' => 'JWT');
|
|
$h = bericht_b64url_encode(json_encode($header));
|
|
$p = bericht_b64url_encode(json_encode($payload));
|
|
$sig = hash_hmac('sha256', $h.'.'.$p, bericht_jwt_secret(), true);
|
|
return $h.'.'.$p.'.'.bericht_b64url_encode($sig);
|
|
}
|
|
|
|
function bericht_jwt_decode($token)
|
|
{
|
|
$parts = explode('.', $token);
|
|
if (count($parts) !== 3) return null;
|
|
list($h, $p, $s) = $parts;
|
|
$expected = bericht_b64url_encode(hash_hmac('sha256', $h.'.'.$p, bericht_jwt_secret(), true));
|
|
if (!hash_equals($expected, $s)) return null;
|
|
$payload = json_decode(bericht_b64url_decode($p), true);
|
|
if (!is_array($payload)) return null;
|
|
if (isset($payload['exp']) && $payload['exp'] < time()) return null;
|
|
return $payload;
|
|
}
|
|
|
|
/**
|
|
* Liest und validiert das Authorization: Bearer <jwt> Header.
|
|
* @return array|null decoded payload
|
|
*/
|
|
function bericht_jwt_from_request()
|
|
{
|
|
$hdr = '';
|
|
if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
|
$hdr = $_SERVER['HTTP_AUTHORIZATION'];
|
|
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
|
|
$hdr = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
|
} elseif (function_exists('apache_request_headers')) {
|
|
$h = apache_request_headers();
|
|
if (isset($h['Authorization'])) $hdr = $h['Authorization'];
|
|
}
|
|
$token = '';
|
|
if ($hdr && stripos($hdr, 'bearer ') === 0) {
|
|
$token = trim(substr($hdr, 7));
|
|
} elseif (!empty($_GET['jwt'])) {
|
|
// Fallback: JWT als Query-Param (fuer <img>, <object>, <iframe> die keinen Authorization-Header schicken koennen)
|
|
$token = (string) $_GET['jwt'];
|
|
}
|
|
if (!$token) return null;
|
|
return bericht_jwt_decode($token);
|
|
}
|