Files
itflow/api/v1/credentials/credential_model.php
johnnyq 359851c876 Security sweep of the 26.11 work: API contact lookup by phone or mobile kept to the key's clients; API credentials refuse a wrong decrypt password instead of saving unreadable usernames and passwords, link credential_vendor_id and credential_software_id, and drop another client's contact or asset; Products Receive window lists expenses only with Financial access and only for clients the agent can open; account ledger, reconcile page and ledger export show lines for clients the agent can't open as Another client with only date, kind and amount, with search and the client filter kept to reachable clients; Tax Collected folds unreachable exempt clients into one line; client-limited agents no longer see other clients' names in the export window client filters, Transfer to Client, company vendor expense lists, My Activity or the New Client duplicate check; Stripe switched off stops guest pay links and saving cards, and the Stripe return never books a payment twice; Admin > Cron job window is admins only; records per page needs the CSRF token and takes only the listed sizes; uploads/canned_responses closed to direct access; Admin > Debug no longer stops at the footer; report export audit log no longer double-escapes names; bulk interface network refuses a deleted network; API assets/update keeps the primary interface IP, MAC and network, tickets/resolve reports success, software/read by name works, clients/create takes the company currency, certificates/create returns its error, clients/archive logs as Client, and the 405 response no longer echoes the method; changelog database step count corrected to sixty
2026-10-23 12:09:07 -04:00

163 lines
6.5 KiB
PHP

<?php
// Variable assignment from POST (or: blank/from DB is updating)
/*
* There is no form behind the API, so nothing has capped these before they arrive.
* An overlong value is a hard MySQL error, not a truncation, so it would surface as a
* generic "insert query failed" - say what actually went wrong instead.
* Only the fields present are checked, which keeps partial updates working.
*/
$credential_field_too_long = checkCredentialLengths([
'name' => $_POST['credential_name'] ?? null,
'description' => $_POST['credential_description'] ?? null,
'uri' => $_POST['credential_uri'] ?? null,
'uri_2' => $_POST['credential_uri_2'] ?? null,
'username' => $_POST['credential_username'] ?? null,
'password' => $_POST['credential_password'] ?? null,
'otp_secret' => $_POST['credential_otp_secret'] ?? null,
]);
if ($credential_field_too_long) {
$return_arr['success'] = "False";
$return_arr['message'] = "credential_$credential_field_too_long is too long to store.";
echo json_encode($return_arr);
exit();
}
$api_key_decrypt_password = '';
if (isset($_POST['api_key_decrypt_password'])) {
$api_key_decrypt_password = $_POST['api_key_decrypt_password']; // No sanitization
}
// A wrong decrypt password would save the username and password where nothing can read them back
if ($api_key_decrypt_password !== '' && (isset($_POST['credential_username']) || isset($_POST['credential_password'])) && !apiDecryptPasswordOpens($api_key_decrypt_hash, $api_key_decrypt_password)) {
$return_arr['success'] = "False";
$return_arr['message'] = "api_key_decrypt_password is not the one set for this API key - nothing was saved.";
echo json_encode($return_arr);
exit();
}
if (isset($_POST['credential_name'])) {
$name = escapeSql($_POST['credential_name']);
} elseif (isset($credential_row) && isset($credential_row['credential_name'])) {
$name = mysqli_real_escape_string($mysqli, $credential_row['credential_name']);
} else {
$name = '';
}
if (isset($_POST['credential_description'])) {
$description = escapeSql($_POST['credential_description']);
} elseif (isset($credential_row) && isset($credential_row['credential_description'])) {
$description = mysqli_real_escape_string($mysqli, $credential_row['credential_description']);
} else {
$description = '';
}
if (isset($_POST['credential_uri'])) {
$uri = escapeSql($_POST['credential_uri']);
} elseif (isset($credential_row) && isset($credential_row['credential_uri'])) {
$uri = mysqli_real_escape_string($mysqli, $credential_row['credential_uri']);
} else {
$uri = '';
}
if (isset($_POST['credential_uri_2'])) {
$uri_2 = escapeSql($_POST['credential_uri_2']);
} elseif (isset($credential_row) && isset($credential_row['credential_uri_2'])) {
$uri_2 = mysqli_real_escape_string($mysqli, $credential_row['credential_uri_2']);
} else {
$uri_2 = '';
}
if (isset($_POST['credential_username'])) {
$username = $_POST['credential_username'];
$username = apiEncryptCredentialEntry($username, $api_key_decrypt_hash, $api_key_decrypt_password);
} elseif (isset($credential_row) && isset($credential_row['credential_username'])) {
$username = $credential_row['credential_username'];
} else {
$username = '';
}
if (isset($_POST['credential_password'])) {
$password = $_POST['credential_password'];
$password = apiEncryptCredentialEntry($password, $api_key_decrypt_hash, $api_key_decrypt_password);
$password_changed = true;
} elseif (isset($credential_row) && isset($credential_row['credential_password'])) {
$password = $credential_row['credential_password'];
$password_changed = false;
} else {
$password = '';
$password_changed = false;
}
if (isset($_POST['credential_otp_secret'])) {
// Stored the way the agent side stores it - spaces and dashes out, an otpauth:// link down to its secret
$otp_seed = credentialOtpSeed($_POST['credential_otp_secret']);
$otp_secret = escapeSql($otp_seed === false ? trim((string) $_POST['credential_otp_secret']) : $otp_seed);
} elseif (isset($credential_row) && isset($credential_row['credential_otp_secret'])) {
$otp_secret = mysqli_real_escape_string($mysqli, $credential_row['credential_otp_secret']);
} else {
$otp_secret = '';
}
if (isset($_POST['credential_note'])) {
$note = escapeSql($_POST['credential_note']);
} elseif (isset($credential_row) && isset($credential_row['credential_note'])) {
$note = mysqli_real_escape_string($mysqli, $credential_row['credential_note']);
} else {
$note = '';
}
if (isset($_POST['credential_favorite'])) {
$favorite = intval($_POST['credential_favorite']);
} elseif (isset($credential_row) && isset($credential_row['credential_favorite'])) {
$favorite = $credential_row['credential_favorite'];
} else {
$favorite = 0;
}
if (isset($_POST['credential_contact_id'])) {
$contact_id = intval($_POST['credential_contact_id']);
} elseif (isset($credential_row) && isset($credential_row['credential_contact_id'])) {
$contact_id = $credential_row['credential_contact_id'];
} else {
$contact_id = 0;
}
if (isset($_POST['credential_vendor_id'])) {
$vendor_id = intval($_POST['credential_vendor_id']);
} elseif (isset($credential_row) && isset($credential_row['credential_vendor_id'])) {
$vendor_id = $credential_row['credential_vendor_id'];
} else {
$vendor_id = 0;
}
if (isset($_POST['credential_asset_id'])) {
$asset_id = intval($_POST['credential_asset_id']);
} elseif (isset($credential_row) && isset($credential_row['credential_asset_id'])) {
$asset_id = $credential_row['credential_asset_id'];
} else {
$asset_id = 0;
}
if (isset($_POST['credential_software_id'])) {
$software_id = intval($_POST['credential_software_id']);
} elseif (isset($credential_row) && isset($credential_row['credential_software_id'])) {
$software_id = $credential_row['credential_software_id'];
} else {
$software_id = 0;
}
// The contact, asset, vendor and license have to be the credential's client's, as on the
// form - another client's is dropped. The vendor and license are links (vendor_credentials,
// software_credentials) that create.php and update.php add.
$contact_id = assetClientOwnedId('contact', $contact_id, $client_id);
$asset_id = assetClientOwnedId('asset', $asset_id, $client_id);
$vendor_id = assetClientOwnedId('vendor', $vendor_id, $client_id);
$software_id = intval($software_id);
if ($software_id && !mysqli_num_rows(mysqli_query($mysqli, "SELECT 1 FROM software WHERE software_id = $software_id AND software_client_id = " . intval($client_id) . " LIMIT 1"))) {
$software_id = 0;
}