2015-05-02 18:14:51 +02:00
< ? php
2015-05-06 01:25:56 +02:00
/* Copyright ( C ) 2015 Jean - François Ferry < jfefe @ aternatik . fr >
2016-09-26 02:18:11 +02:00
* Copyright ( C ) 2016 Laurent Destailleur < eldy @ users . sourceforge . net >
2023-01-18 10:48:18 +01:00
* Copyright ( C ) 2023 Ferran Marcet < fmarcet @ 2 byte . es >
2015-05-06 01:25:56 +02:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2019-09-23 21:55:30 +02:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2015-05-06 01:25:56 +02:00
*/
2015-05-02 18:14:51 +02:00
2015-11-22 17:17:06 +01:00
// Create the autoloader for Luracast
require_once DOL_DOCUMENT_ROOT . '/includes/restler/framework/Luracast/Restler/AutoLoader.php' ;
2020-10-31 21:04:38 +01:00
call_user_func ( function () {
2020-12-01 02:41:19 +01:00
$loader = Luracast\Restler\AutoLoader :: instance ();
spl_autoload_register ( $loader );
return $loader ;
2015-11-22 17:17:06 +01:00
});
require_once DOL_DOCUMENT_ROOT . '/includes/restler/framework/Luracast/Restler/iAuthenticate.php' ;
require_once DOL_DOCUMENT_ROOT . '/includes/restler/framework/Luracast/Restler/iUseAuthentication.php' ;
require_once DOL_DOCUMENT_ROOT . '/includes/restler/framework/Luracast/Restler/Resources.php' ;
require_once DOL_DOCUMENT_ROOT . '/includes/restler/framework/Luracast/Restler/Defaults.php' ;
require_once DOL_DOCUMENT_ROOT . '/includes/restler/framework/Luracast/Restler/RestException.php' ;
2023-04-30 03:10:20 +02:00
use Luracast\Restler\iAuthenticate ;
use Luracast\Restler\iUseAuthentication ;
use Luracast\Restler\Resources ;
use Luracast\Restler\Defaults ;
use Luracast\Restler\RestException ;
2015-05-02 18:14:51 +02:00
/**
2015-05-06 00:55:42 +02:00
* Dolibarr API access class
2015-05-02 18:14:51 +02:00
*
*/
class DolibarrApiAccess implements iAuthenticate
{
const REALM = 'Restricted Dolibarr API' ;
2015-05-12 16:42:35 +02:00
2015-05-02 18:14:51 +02:00
/**
2015-05-12 16:42:35 +02:00
* @ var array $requires role required by API method user / external / admin
2015-05-02 18:14:51 +02:00
*/
2020-04-10 10:59:32 +02:00
public static $requires = array ( 'user' , 'external' , 'admin' );
2015-05-12 16:42:35 +02:00
2015-05-02 23:54:35 +02:00
/**
* @ var string $role user role
*/
2020-10-31 14:32:18 +01:00
public static $role = 'user' ;
2015-05-12 16:42:35 +02:00
2015-05-03 01:54:04 +02:00
/**
2015-05-12 16:42:35 +02:00
* @ var User $user Loggued user
2015-05-03 01:54:04 +02:00
*/
2015-05-03 14:44:37 +02:00
public static $user = '' ;
2015-05-12 16:42:35 +02:00
2020-09-19 23:30:29 +02:00
/**
* Constructor
*/
public function __construct ()
{
global $db ;
$this -> db = $db ;
}
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName
2015-05-02 23:54:35 +02:00
/**
* Check access
2015-05-12 16:42:35 +02:00
*
2015-06-02 15:12:19 +02:00
* @ return bool
2020-04-03 03:04:23 +02:00
*
* @ throws RestException 401 Forbidden
* @ throws RestException 503 Technical error
2015-05-02 23:54:35 +02:00
*/
2015-06-15 13:25:02 +02:00
public function __isAllowed ()
2015-06-02 15:12:19 +02:00
{
2020-10-31 14:32:18 +01:00
// phpcs:enable
2021-08-29 21:13:41 +02:00
global $conf , $db , $user ;
2015-05-03 01:54:04 +02:00
2016-05-08 12:32:18 +02:00
$login = '' ;
2015-05-02 23:54:35 +02:00
$stored_key = '' ;
2015-05-12 16:42:35 +02:00
2015-05-02 18:14:51 +02:00
$userClass = Defaults :: $userIdentifierClass ;
2015-05-12 16:42:35 +02:00
2017-01-22 20:55:26 +01:00
/* foreach ( $_SERVER as $key => $val )
{
2021-02-23 17:44:43 +01:00
dol_syslog ( $key . ' - ' . $val );
2017-01-22 20:55:26 +01:00
} */
2017-06-25 12:26:16 +02:00
2017-01-22 20:55:26 +01:00
// api key can be provided in url with parameter api_key=xxx or ni header with header DOLAPIKEY:xxx
$api_key = '' ;
2021-02-23 17:44:43 +01:00
if ( isset ( $_GET [ 'api_key' ])) { // For backward compatibility
2020-10-31 14:32:18 +01:00
// TODO Add option to disable use of api key on url. Return errors if used.
$api_key = $_GET [ 'api_key' ];
2017-01-22 20:55:26 +01:00
}
2021-02-23 17:44:43 +01:00
if ( isset ( $_GET [ 'DOLAPIKEY' ])) {
2020-10-31 14:32:18 +01:00
// TODO Add option to disable use of api key on url. Return errors if used.
$api_key = $_GET [ 'DOLAPIKEY' ]; // With GET method
2017-01-22 20:55:26 +01:00
}
2021-02-23 17:44:43 +01:00
if ( isset ( $_SERVER [ 'HTTP_DOLAPIKEY' ])) { // Param DOLAPIKEY in header can be read with HTTP_DOLAPIKEY
2020-10-31 14:32:18 +01:00
$api_key = $_SERVER [ 'HTTP_DOLAPIKEY' ]; // With header method (recommanded)
2017-01-22 20:55:26 +01:00
}
2023-03-15 12:54:04 +01:00
if ( preg_match ( '/^dolcrypt:/i' , $api_key )) {
throw new RestException ( 503 , 'Bad value for the API key. An API key should not start with dolcrypt:' );
}
2017-06-25 12:26:16 +02:00
2021-02-23 17:44:43 +01:00
if ( $api_key ) {
2017-10-16 16:23:50 +02:00
$userentity = 0 ;
2023-07-19 04:14:49 +02:00
$sql = " SELECT u.login, u.datec, u.api_key, " ;
2020-04-10 10:59:32 +02:00
$sql .= " u.tms as date_modification, u.entity " ;
$sql .= " FROM " . MAIN_DB_PREFIX . " user as u " ;
2023-03-13 12:48:25 +01:00
$sql .= " WHERE u.api_key = ' " . $this -> db -> escape ( $api_key ) . " ' OR u.api_key = ' " . $this -> db -> escape ( dolEncrypt ( $api_key , '' , '' , 'dolibarr' )) . " ' " ;
2017-10-19 17:30:08 +02:00
2020-09-19 23:30:29 +02:00
$result = $this -> db -> query ( $sql );
2021-02-23 17:44:43 +01:00
if ( $result ) {
2023-03-13 12:48:25 +01:00
$nbrows = $this -> db -> num_rows ( $result );
if ( $nbrows == 1 ) {
2020-09-19 23:30:29 +02:00
$obj = $this -> db -> fetch_object ( $result );
2015-05-02 23:54:35 +02:00
$login = $obj -> login ;
2023-03-13 12:48:25 +01:00
$stored_key = dolDecrypt ( $obj -> api_key );
2017-10-16 16:23:50 +02:00
$userentity = $obj -> entity ;
2021-02-23 17:44:43 +01:00
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
2020-04-10 10:59:32 +02:00
$conf -> entity = ( $obj -> entity ? $obj -> entity : 1 );
2017-10-19 17:30:08 +02:00
// We must also reload global conf to get params from the entity
2020-04-10 10:59:32 +02:00
dol_syslog ( " Entity was not set on http header with HTTP_DOLAPIENTITY (recommanded for performance purpose), so we switch now on entity of user ( " . $conf -> entity . " ) and we have to reload configuration. " , LOG_WARNING );
2020-09-19 23:30:29 +02:00
$conf -> setValues ( $this -> db );
2017-10-16 16:23:50 +02:00
}
2023-03-13 12:48:25 +01:00
} elseif ( $nbrows > 1 ) {
throw new RestException ( 503 , 'Error when fetching user api_key : More than 1 user with this apikey' );
2015-05-02 23:54:35 +02:00
}
2020-05-21 15:05:19 +02:00
} else {
2020-09-19 23:30:29 +02:00
throw new RestException ( 503 , 'Error when fetching user api_key :' . $this -> db -> error_msg );
2015-05-03 01:54:04 +02:00
}
2015-05-02 23:54:35 +02:00
2023-07-19 04:14:49 +02:00
if ( $login && $stored_key != $api_key ) { // This should not happen since we did a search on api_key
2017-01-22 20:55:26 +01:00
$userClass :: setCacheIdentifier ( $api_key );
2015-05-02 18:14:51 +02:00
return false ;
}
2015-05-12 16:42:35 +02:00
2023-07-19 04:14:49 +02:00
$genericmessageerroruser = 'Error user not valid (not found with api key or bad status or bad validity dates) (conf->entity=' . $conf -> entity . ')' ;
2021-02-23 17:44:43 +01:00
if ( ! $login ) {
2023-07-19 04:14:49 +02:00
dol_syslog ( " functions_isallowed::check_user_api_key Authentication KO for api key: Error when searching login user from api key " , LOG_NOTICE );
sleep ( 1 ); // Anti brut force protection. Must be same delay when user and password are not valid.
throw new RestException ( 401 , $genericmessageerroruser );
2016-05-08 12:32:18 +02:00
}
2023-01-18 19:54:38 +01:00
2020-09-19 23:30:29 +02:00
$fuser = new User ( $this -> db );
2020-04-10 10:59:32 +02:00
$result = $fuser -> fetch ( '' , $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)
2017-10-16 16:23:50 +02:00
if ( $result <= 0 ) {
2023-07-19 04:14:49 +02:00
dol_syslog ( " functions_isallowed::check_user_api_key Authentication KO for ' " . $login . " ': Failed to fetch on entity " , LOG_NOTICE );
sleep ( 1 ); // Anti brut force protection. Must be same delay when user and password are not valid.
throw new RestException ( 401 , $genericmessageerroruser );
2015-05-03 01:54:04 +02:00
}
2023-01-27 14:27:51 +01:00
// Check if user status is enabled
if ( $fuser -> statut != $fuser :: STATUS_ENABLED ) {
// Status is disabled
2023-07-19 04:14:49 +02:00
dol_syslog ( " functions_isallowed::check_user_api_key Authentication KO for ' " . $login . " ': The user has been disabled " , LOG_NOTICE );
sleep ( 1 ); // Anti brut force protection. Must be same delay when user and password are not valid.
throw new RestException ( 401 , $genericmessageerroruser );
2023-01-18 10:48:18 +01:00
}
2021-09-02 16:06:19 +02:00
2023-01-27 14:06:31 +01:00
// Check if session was unvalidated by a password change
if (( $fuser -> flagdelsessionsbefore && ! empty ( $_SESSION [ " dol_logindate " ]) && $fuser -> flagdelsessionsbefore > $_SESSION [ " dol_logindate " ])) {
// Session is no more valid
2023-07-19 04:14:49 +02:00
dol_syslog ( " functions_isallowed::check_user_api_key Authentication KO for ' " . $login . " ': The user has a date for session invalidation = " . $fuser -> flagdelsessionsbefore . " and a session date = " . $_SESSION [ " dol_logindate " ] . " . We must invalidate its sessions. " );
sleep ( 1 ); // Anti brut force protection. Must be same delay when user and password are not valid.
throw new RestException ( 401 , $genericmessageerroruser );
2023-01-18 19:54:38 +01:00
}
2023-01-27 14:06:31 +01:00
// Check date validity
if ( $fuser -> isNotIntoValidityDateRange ()) {
// User validity dates are no more valid
2023-07-19 04:14:49 +02:00
dol_syslog ( " functions_isallowed::check_user_api_key Authentication KO for ' " . $login . " ': The user login has a validity between [ " . $fuser -> datestartvalidity . " and " . $fuser -> dateendvalidity . " ], curren date is " . dol_now ());
sleep ( 1 ); // Anti brut force protection. Must be same delay when user and password are not valid.
throw new RestException ( 401 , $genericmessageerroruser );
2023-01-18 19:54:38 +01:00
}
// User seems valid
2015-05-03 01:54:04 +02:00
$fuser -> getrights ();
2021-09-02 16:06:19 +02:00
// Set the property $user to the $user of API
2015-05-03 14:44:37 +02:00
static :: $user = $fuser ;
2015-05-12 16:42:35 +02:00
2021-09-02 16:06:19 +02:00
// Set also the global variable $user to the $user of API
2021-08-29 21:13:41 +02:00
$user = $fuser ;
2015-05-12 16:42:35 +02:00
2019-11-04 20:20:38 +01:00
if ( $fuser -> socid ) {
2015-05-02 23:54:35 +02:00
static :: $role = 'external' ;
2020-10-31 14:32:18 +01:00
}
2015-05-12 16:42:35 +02:00
2019-11-04 20:20:38 +01:00
if ( $fuser -> admin ) {
2015-05-02 23:54:35 +02:00
static :: $role = 'admin' ;
2020-10-31 14:32:18 +01:00
}
} else {
throw new RestException ( 401 , " Failed to login to API. No parameter 'HTTP_DOLAPIKEY' on HTTP header (and no parameter DOLAPIKEY in URL). " );
2015-05-02 18:14:51 +02:00
}
2015-05-03 01:54:04 +02:00
2020-10-31 14:32:18 +01:00
$userClass :: setCacheIdentifier ( static :: $role );
Resources :: $accessControlFunction = 'DolibarrApiAccess::verifyAccess' ;
$requirefortest = static :: $requires ;
2021-02-23 17:44:43 +01:00
if ( ! is_array ( $requirefortest )) {
$requirefortest = explode ( ',' , $requirefortest );
}
2020-10-31 14:32:18 +01:00
return in_array ( static :: $role , ( array ) $requirefortest ) || static :: $role == 'admin' ;
2015-05-02 18:14:51 +02:00
}
2015-05-12 16:42:35 +02:00
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName
2015-06-02 15:12:19 +02:00
/**
* @ return string string to be used with WWW - Authenticate header
*/
2020-10-31 14:32:18 +01:00
public function __getWWWAuthenticateString ()
{
// phpcs:enable
return '' ;
}
/**
* Verify access
*
* @ param array $m Properties of method
*
* @ access private
* @ return bool
*/
public static function verifyAccess ( array $m )
{
$requires = isset ( $m [ 'class' ][ 'DolibarrApiAccess' ][ 'properties' ][ 'requires' ])
? $m [ 'class' ][ 'DolibarrApiAccess' ][ 'properties' ][ 'requires' ]
: false ;
return $requires
? static :: $role == 'admin' || in_array ( static :: $role , ( array ) $requires )
: true ;
}
2015-05-02 18:14:51 +02:00
}