Files
ms1inscription-v5/php/chronotrack_api/fx_chronotrack_oauth.php
stephan 338f5ed6f6 Refine ChronoTrack API configuration and enhance error handling
This commit updates the ChronoTrack API configuration by introducing a variable for the base URL, allowing for easier management of test and production environments. It also improves the admin interface to display the API endpoint status, indicating whether the test or production API is in use. Additionally, error handling in the OAuth token fetching process is enhanced to provide clearer messages when access tokens are absent or when errors occur, improving overall user feedback and robustness of the API.
2026-07-03 20:58:43 -04:00

222 lines
7.5 KiB
PHP

<?php
/**
* MSIN API ChronoTrack — OAuth2 Password Flow + cache token.
*/
function fxChronotrackApiOAuthGetTokenRow() {
global $objDatabase;
$sql = "SELECT * FROM api_chronotrack_token WHERE token_key = '"
. $objDatabase->fxEscape(MSIN_API_CHRONOTRACK_TOKEN_KEY) . "' LIMIT 1";
return $objDatabase->fxGetRow($sql);
}
function fxChronotrackApiOAuthSaveToken($strAccessToken, $strRefreshToken, $intExpiresEpoch) {
global $objDatabase;
$strExpires = '';
if ($intExpiresEpoch > 0) {
$strExpires = date('Y-m-d H:i:s', $intExpiresEpoch);
}
$sqlExists = "SELECT token_key FROM api_chronotrack_token WHERE token_key = '"
. $objDatabase->fxEscape(MSIN_API_CHRONOTRACK_TOKEN_KEY) . "' LIMIT 1";
if ($objDatabase->fxGetRow($sqlExists) !== null) {
$sql = "UPDATE api_chronotrack_token SET access_token = '"
. $objDatabase->fxEscape($strAccessToken) . "', refresh_token = '"
. $objDatabase->fxEscape($strRefreshToken) . "', expires_at = "
. ($strExpires !== '' ? "'" . $objDatabase->fxEscape($strExpires) . "'" : 'NULL')
. ", updated_at = '" . fxGetDateTime() . "' WHERE token_key = '"
. $objDatabase->fxEscape(MSIN_API_CHRONOTRACK_TOKEN_KEY) . "'";
} else {
$sql = "INSERT INTO api_chronotrack_token SET token_key = '"
. $objDatabase->fxEscape(MSIN_API_CHRONOTRACK_TOKEN_KEY) . "', access_token = '"
. $objDatabase->fxEscape($strAccessToken) . "', refresh_token = '"
. $objDatabase->fxEscape($strRefreshToken) . "', expires_at = "
. ($strExpires !== '' ? "'" . $objDatabase->fxEscape($strExpires) . "'" : 'NULL')
. ", updated_at = '" . fxGetDateTime() . "'";
}
$objDatabase->fxQuery($sql);
}
function fxChronotrackApiOAuthTokenValid($arrRow) {
if ($arrRow === null || trim((string)($arrRow['access_token'] ?? '')) === '') {
return false;
}
if (empty($arrRow['expires_at'])) {
return true;
}
$intExpires = strtotime($arrRow['expires_at']);
if ($intExpires === false) {
return true;
}
return ($intExpires > (time() + 120));
}
function fxChronotrackApiOAuthFetchToken($blnForceRefresh = false) {
if (!fxChronotrackApiSettingsConfigured()) {
return array('state' => 'error', 'message' => fxChronotrackApiSettingsErrorMessage());
}
if (!$blnForceRefresh) {
$arrCached = fxChronotrackApiOAuthGetTokenRow();
if (fxChronotrackApiOAuthTokenValid($arrCached)) {
return array(
'state' => 'ok',
'access_token' => $arrCached['access_token'],
'from_cache' => true,
);
}
}
$strBase = fxChronotrackApiGetBaseUrl();
$strQuery = http_build_query(array(
'grant_type' => 'password',
'username' => fxChronotrackApiGetServiceUser(),
'password' => fxChronotrackApiGetServicePass(),
));
$strUrl = $strBase . '/oauth2/token?' . $strQuery;
$arrHttp = fxChronotrackApiHttpRequest('GET', $strUrl, array(
'basic_user' => fxChronotrackApiGetClientId(),
'basic_pass' => fxChronotrackApiGetClientSecret(),
));
if ($arrHttp['state'] !== 'ok' || !is_array($arrHttp['json'])) {
return array(
'state' => 'error',
'message' => fxChronotrackApiHttpGetJsonErrorMessage($arrHttp),
);
}
$arrJson = $arrHttp['json'];
if (!empty($arrJson['errorCode']) || !empty($arrJson['error'])) {
return array(
'state' => 'error',
'message' => fxChronotrackApiHttpGetJsonErrorMessage($arrHttp),
);
}
$strToken = trim((string)($arrJson['access_token'] ?? ''));
if ($strToken === '') {
return array(
'state' => 'error',
'message' => 'access_token absent — ' . fxChronotrackApiHttpGetJsonErrorMessage($arrHttp),
);
}
$strRefresh = trim((string)($arrJson['refresh_token'] ?? ''));
$intExpires = intval($arrJson['expires'] ?? 0);
if ($intExpires <= 0 && !empty($arrJson['expires_in'])) {
$intExpires = time() + intval($arrJson['expires_in']);
}
fxChronotrackApiOAuthSaveToken($strToken, $strRefresh, $intExpires);
return array(
'state' => 'ok',
'access_token' => $strToken,
'from_cache' => false,
);
}
function fxChronotrackApiOAuthApiGet($strPath, $arrQuery = array()) {
$arrToken = fxChronotrackApiOAuthFetchToken(false);
if ($arrToken['state'] !== 'ok') {
return $arrToken;
}
$arrQuery['client_id'] = fxChronotrackApiGetClientId();
$strUrl = fxChronotrackApiGetApiBaseUrl() . '/' . ltrim($strPath, '/');
if (!empty($arrQuery)) {
$strUrl .= '?' . http_build_query($arrQuery);
}
$arrHttp = fxChronotrackApiHttpRequest('GET', $strUrl, array(
'headers' => array('Authorization: Bearer ' . $arrToken['access_token']),
));
if ($arrHttp['state'] === 'ok') {
return array('state' => 'ok', 'json' => $arrHttp['json'], 'http' => $arrHttp);
}
return array(
'state' => 'error',
'message' => fxChronotrackApiHttpGetJsonErrorMessage($arrHttp),
'http' => $arrHttp,
);
}
function fxChronotrackApiOAuthApiPost($strPath, $mixBody, $arrQuery = array()) {
$arrToken = fxChronotrackApiOAuthFetchToken(false);
if ($arrToken['state'] !== 'ok') {
return $arrToken;
}
$arrQuery['client_id'] = fxChronotrackApiGetClientId();
$strUrl = fxChronotrackApiGetApiBaseUrl() . '/' . ltrim($strPath, '/');
if (!empty($arrQuery)) {
$strUrl .= '?' . http_build_query($arrQuery);
}
$strBody = json_encode($mixBody, JSON_UNESCAPED_UNICODE);
if (!is_string($strBody)) {
return array('state' => 'error', 'message' => 'json_encode a échoué');
}
$arrHttp = fxChronotrackApiHttpRequest('POST', $strUrl, array(
'headers' => array(
'Authorization: Bearer ' . $arrToken['access_token'],
'Content-Type: application/json',
'Accept: application/json',
),
'body' => $strBody,
));
if ($arrHttp['state'] === 'ok') {
return array('state' => 'ok', 'json' => $arrHttp['json'], 'http' => $arrHttp);
}
return array(
'state' => 'error',
'message' => fxChronotrackApiHttpGetJsonErrorMessage($arrHttp),
'http' => $arrHttp,
);
}
function fxChronotrackApiOAuthApiPut($strPath, $mixBody, $arrQuery = array()) {
$arrToken = fxChronotrackApiOAuthFetchToken(false);
if ($arrToken['state'] !== 'ok') {
return $arrToken;
}
$arrQuery['client_id'] = fxChronotrackApiGetClientId();
$strUrl = fxChronotrackApiGetApiBaseUrl() . '/' . ltrim($strPath, '/');
if (!empty($arrQuery)) {
$strUrl .= '?' . http_build_query($arrQuery);
}
$strBody = json_encode($mixBody, JSON_UNESCAPED_UNICODE);
if (!is_string($strBody)) {
return array('state' => 'error', 'message' => 'json_encode a échoué');
}
$arrHttp = fxChronotrackApiHttpRequest('PUT', $strUrl, array(
'headers' => array(
'Authorization: Bearer ' . $arrToken['access_token'],
'Content-Type: application/json',
'Accept: application/json',
),
'body' => $strBody,
));
if ($arrHttp['state'] === 'ok') {
return array('state' => 'ok', 'json' => $arrHttp['json'], 'http' => $arrHttp);
}
return array(
'state' => 'error',
'message' => fxChronotrackApiHttpGetJsonErrorMessage($arrHttp),
'http' => $arrHttp,
);
}