Merge manually to close #34719. Need to add API_IN_TOKEN_TABLE=1 for the

transition to use several API tokens per user.
This commit is contained in:
Laurent Destailleur
2025-11-20 23:51:37 +01:00
parent 106cc684d8
commit 09da2b2365
8 changed files with 136 additions and 154 deletions

View File

@@ -95,7 +95,6 @@ class DolibarrApiAccess implements iAuthenticate
$login = '';
$stored_key = '';
$use_api = '';
$userClass = Defaults::$userIdentifierClass;
@@ -133,24 +132,33 @@ class DolibarrApiAccess implements iAuthenticate
if ($api_key) {
$userentity = 0;
$token_rowid = 0;
$sql = "SELECT u.login, u.api_key as use_api, u.entity, oat.token as api_key, oat.entity as token_entity";
$sql .= " FROM ".MAIN_DB_PREFIX."oauth_token AS oat";
$sql .= " JOIN ".MAIN_DB_PREFIX."user AS u ON u.rowid = oat.fk_user";
$sql .= " WHERE oat.token = '".$this->db->escape($api_key)."'";
$sql .= " OR oat.token = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."'";
$sql .= " AND oat.service = 'dolibarr_rest_api'";
if (!getDolGlobalString('API_IN_TOKEN_TABLE')) {
$sql = "SELECT u.login, u.datec, u.api_key as use_api, u.entity, u.api_key as api_key, u.entity as token_entity, 0 as token_rowid";
$sql .= " u.tms as date_modification, u.entity";
$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
$sql .= " WHERE u.api_key = '".$this->db->escape($api_key)."' OR u.api_key = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."'";
} else {
$sql = "SELECT u.login, u.datec, u.api_key as use_api, u.entity, oat.tokenstring as api_key, oat.entity as token_entity, rowid as token_rowid";
$sql .= " FROM ".MAIN_DB_PREFIX."oauth_token AS oat";
$sql .= " JOIN ".MAIN_DB_PREFIX."user AS u ON u.rowid = oat.fk_user";
$sql .= " WHERE (oat.tokenstring = '".$this->db->escape($api_key)."'";
$sql .= " OR oat.tokenstring = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."')";
$sql .= " AND oat.service = 'dolibarr_rest_api'";
}
$result = $this->db->query($sql);
if ($result) {
$nbrows = $this->db->num_rows($result);
if ($nbrows == 1) {
$obj = $this->db->fetch_object($result);
$login = $obj->login;
$stored_key = dolDecrypt($obj->api_key);
$userentity = $obj->entity;
$tokenentity = $obj->token_entity;
$use_api = $obj->use_api;
$token_entity = $obj->token_entity;
$token_rowid = $obj->token_rowid;
if (!defined("DOLENTITY") && $conf->entity != ($obj->entity ? $obj->entity : 1)) { // If API was not forced with HTTP_DOLENTITY, and user is on another entity, so we reset entity to entity of user
$conf->entity = ($obj->entity ? $obj->entity : 1);
@@ -199,7 +207,7 @@ class DolibarrApiAccess implements iAuthenticate
$mysoc = $fmysoc;
// Reload langs
$langcode = (!getDolGlobalString('MAIN_LANG_DEFAULT') ? 'auto' : $conf->global->MAIN_LANG_DEFAULT);
$langcode = getDolGlobalString('MAIN_LANG_DEFAULT', 'auto');
if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
$langcode = $user->conf->MAIN_LANG_DEFAULT;
}
@@ -209,8 +217,9 @@ class DolibarrApiAccess implements iAuthenticate
$langs->loadLangs(array('main'));
}
}
if ($conf->entity != ($tokenentity ? $tokenentity : 1)) {
throw new RestException(401, 'Forbidden');
if ($conf->entity != ($token_entity ? $token_entity : 1)) {
throw new RestException(401, "functions_isallowed::check_user_api_key Authentication KO for '".$login."': Token not valid (may be a typo or a wrong entity)");
}
} elseif ($nbrows > 1) {
throw new RestException(503, 'Error when fetching user api_key : More than 1 user with this apikey');
@@ -232,12 +241,6 @@ class DolibarrApiAccess implements iAuthenticate
throw new RestException(401, $genericmessageerroruser);
}
if (!$use_api) {
dol_syslog("functions_isallowed::check_user_api_key Authentication KO for api key: API not enabled for user", LOG_NOTICE);
sleep(1); // Anti brute force protection. Must be same delay when user and password are not valid.
throw new RestException(401, $genericmessageerroruser);
}
$fuser = new User($this->db);
$result = $fuser->fetch(0, $login, '', 0, (empty($userentity) ? -1 : $conf->entity)); // If user is not entity 0, we search in working entity $conf->entity (that may have been forced to a different value than user entity)
if ($result <= 0) {
@@ -270,16 +273,31 @@ class DolibarrApiAccess implements iAuthenticate
throw new RestException(401, $genericmessageerroruser);
}
// TODO
// Increase counter of API access
if (getDolGlobalString('API_COUNTER_ENABLED')) {
include DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
dolibarr_set_const($this->db, 'API_COUNTER_COUNT', getDolGlobalInt('API_COUNTER_COUNT') + 1);
//var_dump('eeee');exit;
if (!getDolGlobalString('API_IN_TOKEN_TABLE')) {
// Update the counter into table llx_const
include DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
dolibarr_set_const($this->db, 'API_COUNTER_COUNT', getDolGlobalInt('API_COUNTER_COUNT') + 1);
//var_dump('eeee');exit;
} else {
// Update the counter into table llx_oauth_token
$tmpnow = dol_getdate(dol_now('gmt'), true, 'gmt');
$sqlforcounter = "UPDATE ".$db->prefix()."oauth_token SET ";
$sqlforcounter .= " apicount_total = apicount_total + 1,";
$sqlforcounter .= " apicount_month = apicount_month + 1,";
// if last access was done during previous month, we save pageview_month into pageviews_previous_month
$sqlforcounter .= " pageviews_previous_month = ".$db->ifsql("lastaccess < '".$db->idate(dol_mktime(0, 0, 0, $tmpnow['mon'], 1, $tmpnow['year'], 'gmt', 0), 'gmt')."'", 'apicount_month', 'apicount_previous_month').",";
$sqlforcounter .= " lastaccess = '".$db->idate(dol_now('gmt'), 'gmt')."'";
$sqlforcounter .= " WHERE rowid = ".((int) $token_rowid);
$this->db->query($sqlforcounter);
}
}
// User seems valid
$fuser->loadRights('', 0, $stored_key);
$fuser->loadRights();
// Set the property $user to the $user of API
static::$user = $fuser;