Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.37% covered (success)
88.37%
38 / 43
0.00% covered (danger)
0.00%
0 / 3
CRAP
n/a
0 / 0
ticketbai_derived_env
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
4.25
ticketbai_derived_env_int
80.00% covered (success)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
ticketbai_derived_validate_session
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
6.02
1<?php
2
3declare(strict_types=1);
4
5/**
6 * @file
7 * TicketBAI action: return a per-session PIN derived from a server-only secret.
8 * The PIN is never stored; it is recomputed on every request from the caller's
9 * session token, so it becomes invalid as soon as that session is invalidated.
10 */
11
12require_once __DIR__.'/SessionPin.php';
13
14/**
15 * Resolve env value with fallback.
16 *
17 * @param string $key Environment key.
18 * @param string $default Fallback.
19 * @return string
20 */
21function ticketbai_derived_env(string $key, string $default = ''): string
22{
23    $value = $_ENV[$key] ?? getenv($key);
24    if ($value === false || $value === null || $value === '') {
25        return $default;
26    }
27    return (string) $value;
28}
29
30/**
31 * Resolve numeric env value with bounds.
32 *
33 * @param string $key Environment key.
34 * @param int $default Fallback.
35 * @param int $min Minimum accepted.
36 * @param int $max Maximum accepted.
37 * @return int
38 */
39function ticketbai_derived_env_int(string $key, int $default, int $min, int $max): int
40{
41    $raw = ticketbai_derived_env($key, (string) $default);
42    $value = (int) $raw;
43    if ($value < $min || $value > $max) {
44        return $default;
45    }
46    return $value;
47}
48
49/**
50 * Validate mandatory query params and session.
51 *
52 * @param array<string, mixed> $query Query params.
53 * @param \ConxHelper $conx Connection helper.
54 * @param \UppServices\SessionService $sessionService Session service.
55 * @param \Psr\Log\LoggerInterface $logger Logger.
56 * @return int ApplicationError code.
57 */
58function ticketbai_derived_validate_session(array $query, \ConxHelper $conx, \UppServices\SessionService $sessionService, \Psr\Log\LoggerInterface $logger): int
59{
60    $retval = \ApplicationError::Success;
61
62    $device = (string) ($query['device'] ?? '');
63    $session = (string) ($query['session'] ?? '');
64    $place = (string) ($query['place'] ?? '');
65
66    if ($device === '' || $session === '' || $place === '') {
67        $retval = \ApplicationError::Parameters;
68        $logger->error("Missing mandatory query params for ticketbai/derived (device, session, place).");
69    }
70
71    if (success($retval)) {
72        $userid = null;
73        $retval = $sessionService->checkSession($conx, $session, $device, $userid);
74        if (!success($retval)) {
75            $logger->error("Session check failed for ticketbai/derived.");
76        }
77    }
78
79    return $retval;
80}
81
82/**
83 * Action callable: return the per-session PIN for TicketBAI key derivation.
84 *
85 * @param array<string, mixed> $query Query params.
86 * @param \ConxHelper $conx Connection helper.
87 * @param \Psr\Log\LoggerInterface $logger Logger.
88 * @param \UppServices\SessionService $sessionService Session service.
89 * @return array{output: string, contentType: string}
90 */
91return function (array $query, \ConxHelper $conx, \Psr\Log\LoggerInterface $logger, \UppServices\SessionService $sessionService): array {
92    $retval = ticketbai_derived_validate_session($query, $conx, $sessionService, $logger);
93
94    $secret = session_pin_secret($logger);
95    if (success($retval) && $secret === '') {
96        $retval = \ApplicationError::Parameters;
97        $logger->error("BUILD_DERIVED_SECRET is required for ticketbai/derived.");
98    }
99
100    $session = (string) ($query['session'] ?? '');
101    $pin = success($retval) ? session_pin($secret, $session) : '';
102
103    $iterations = ticketbai_derived_env_int('BUILD_DERIVED_ITERATIONS', 210000, 10000, 1000000);
104    $result = [
105        'errorcode' => $retval,
106        'pin' => success($retval) ? $pin : null,
107        'kdf' => success($retval) ? [
108            'algorithm' => 'PBKDF2-SHA256',
109            'iterations' => $iterations,
110            'keyLength' => 32,
111            'cipher' => 'AES-256-GCM',
112        ] : null,
113    ];
114
115    return ['output' => json_encode($result), 'contentType' => 'application/json; charset=utf-8'];
116};