NOW() LIMIT 1"); // Failed if (mysqli_num_rows($sql) !== 1) { // Invalid Key $url_path = escapeSql(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH)); mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'API', log_action = 'Failed', log_description = 'Incorrect or expired key (endpoint: $url_path)', log_ip = '$ip', log_user_agent = '$user_agent'"); $return_arr['success'] = "False"; $return_arr['message'] = "Authentication failed. API key is invalid or has expired."; header(WORDING_UNAUTHORIZED); echo json_encode($return_arr); exit(); } else { // SUCCESS // Set client ID, company ID & key name $row = mysqli_fetch_assoc($sql); $api_key_name = htmlentities($row['api_key_name']); $api_key_decrypt_hash = $row['api_key_decrypt_hash']; // No sanitization $api_key_user_id = intval($row['api_key_user_id']); // When it was last used, for Admin > API Keys - at most once a minute, not every call. In // ITFlow's own timezone, as the pages show it: the API doesn't set the clocks to it. $api_tz = mysqli_fetch_row(mysqli_query($mysqli, "SELECT config_timezone FROM settings WHERE company_id = 1"))[0] ?? ''; try { $api_now = (new DateTime('now', new DateTimeZone($api_tz ?: 'UTC')))->format('Y-m-d H:i:s'); } catch (Exception $e) { $api_now = date('Y-m-d H:i:s'); } mysqli_query($mysqli, "UPDATE api_keys SET api_key_last_used_at = '$api_now' WHERE api_key_id = " . intval($row['api_key_id']) . " AND (api_key_last_used_at IS NULL OR api_key_last_used_at < '$api_now' - INTERVAL 1 MINUTE)"); // Set limit & offset for queries if (isset($_GET['limit'])) { $limit = intval($_GET['limit']); } elseif (isset($_POST['limit'])) { $limit = intval($_POST['limit']); } else { $limit = 50; } if (isset($_GET['offset'])) { $offset = intval($_GET['offset']); } elseif (isset($_POST['offset'])) { $offset = intval($_POST['offset']); } else { $offset = 0; } // When the key is tied to a user, enforce that user's RBAC (module + operation + client scope) require __DIR__ . '/enforce_api_rbac.php'; } }