Files
itflow/api/v1/validate_api_key.php
johnnyq be002b6f29 Roles stage 4: the money on invoices goes by Financial with Sales at Read through billingMoneyCan and enforceBillingMoney, so taking payments one at a time, by saved card or as a batch, changing them and emailing receipts, reminders and statements need write access to Financial, and deleting or refunding a payment needs full access, with the old Sales rules for reminders, statements and receipts still accepted; invoice, invoice list, Income and payment quick looks offer only what the role can do, the reminder window offers only the reminder to a role that cannot send the invoice, a reminder for a draft or with nothing owed still needs Sales, and Income bulk Set Category on payments needs Sales as it changes the invoice; database 3.1.8 gives the built-in Accountant full access to Financial when it still has its shipped levels, and new installs seed it that way; calendar events for a client follow the role's Clients level through calendarEventCan, Read to see, Modify to add, change, move, skip or send, Full to delete or Modify for the agent who added it, events an agent is on always show, events with no client stay open to everyone, and the calendar, dashboard and calendar links leave out other agents' client events for roles without Clients, with New event, the client picker and dragging following the same rule; the API now sets PHP and MySQL to ITFlow's timezone so what it creates, API ticket SLA times and key expiry go by local time, and inc_set_timezone.php only writes the session when one is open and no longer leaves its result and row variables behind; changelog updated
2026-10-17 21:52:10 -04:00

129 lines
4.1 KiB
PHP

<?php
/*
* API - validate_api_key.php
* Called by API endpoint to validate API key is valid
* Allows execution to continue or exits returning errors to the user
*/
// Includes
require_once __DIR__ . '../../../functions.php';
require_once __DIR__ . "../../../config.php";
// ITFlow's timezone on both clocks, as the pages and cron have it - what the API
// stamps, the SLA times it works out and when a key expires all go by it
require_once __DIR__ . "/../../includes/inc_set_timezone.php";
// JSON header
header('Content-Type: application/json');
// POST data
$_POST = json_decode(file_get_contents('php://input'), true);
// Get IP & UA
$ip = escapeSql(getIP());
$user_agent = escapeSql($_SERVER['HTTP_USER_AGENT']);
// Temp Added this to work with the new logAction function
$session_ip = $ip;
$session_user_agent = $user_agent;
// Setup return array
$return_arr = array();
// Unauthorised wording
DEFINE("WORDING_UNAUTHORIZED", "HTTP/1.1 401 Unauthorized");
/*
* API Notes:
*
* To avoid over-complicating the app by using PUT and DELETE methods, only going to allow the use of GET and POST methods.
* GET - Retrieving (READ) data
* POST - Inserting (CREATE), Updating (UPDATE) or Deleting (DELETE) data
*
* Data returned as json encoded $return_arr:-
* Success - True/False
* Message - Brief info about a request / failure
* Count - Count of rows affected/returned
* Data - Requested data
*
*/
// Decline methods other than GET/POST
if ($_SERVER['REQUEST_METHOD'] !== "GET" && $_SERVER['REQUEST_METHOD'] !== "POST") {
header("HTTP/1.1 405 Method Not Allowed");
var_dump($_SERVER['REQUEST_METHOD']);
exit();
}
// Check API key is provided
if (!isset($_GET['api_key']) && !isset($_POST['api_key'])) {
header(WORDING_UNAUTHORIZED);
exit();
}
// Set API key variable
if (isset($_GET['api_key'])) {
$api_key = escapeSql($_GET['api_key']);
}
if (isset($_POST['api_key'])) {
$api_key = escapeSql($_POST['api_key']);
}
// Validate API key
if (isset($api_key)) {
$api_key = escapeSql($api_key);
$sql = mysqli_query($mysqli, "SELECT api_key_id, api_key_decrypt_hash, api_key_name, api_key_user_id FROM api_keys WHERE api_key_secret = '$api_key' AND api_key_expire > 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
mysqli_query($mysqli, "UPDATE api_keys SET api_key_last_used_at = NOW() WHERE api_key_id = " . intval($row['api_key_id']) . "
AND (api_key_last_used_at IS NULL OR api_key_last_used_at < 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';
}
}