From 933b72195dfa890fac80c60dd64268d5d5f70f07 Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 9 Feb 2026 23:08:48 +0100 Subject: [PATCH] be able to call cron locally/remotely via any authentication mode that implements iTokenLoginUIExtension (interface badly named) --- application/loginbasic.class.inc.php | 19 ++++++++++- application/loginform.class.inc.php | 27 ++++++++++++--- application/loginurl.class.inc.php | 28 ++++++++++++---- application/loginwebpage.class.inc.php | 20 +++++++++++ lib/autoload.php | 5 ++- lib/composer/autoload_classmap.php | 1 + lib/composer/autoload_psr4.php | 2 +- lib/composer/autoload_static.php | 5 +-- lib/composer/platform_check.php | 5 +-- webservices/cron.php | 39 +++++++++++++++------- webservices/cron_status.php | 19 +++-------- webservices/launch_cron_asynchronously.php | 34 +++++++++---------- 12 files changed, 143 insertions(+), 61 deletions(-) diff --git a/application/loginbasic.class.inc.php b/application/loginbasic.class.inc.php index 1ee64ee77..bccee3f0e 100644 --- a/application/loginbasic.class.inc.php +++ b/application/loginbasic.class.inc.php @@ -9,7 +9,7 @@ use Combodo\iTop\Application\Helper\Session; * @license http://opensource.org/licenses/AGPL-3.0 */ -class LoginBasic extends AbstractLoginFSMExtension +class LoginBasic extends AbstractLoginFSMExtension implements iTokenLoginUIExtension { /** * Return the list of supported login modes for this plugin @@ -143,4 +143,21 @@ class LoginBasic extends AbstractLoginFSMExtension } return array($sAuthUser, $sAuthPwd); } + + public function GetTokenInfo(): array + { + return $this->GetAuthUserAndPassword(); + } + + public function GetUserLogin(array $aTokenInfo): string + { + $sLogin = $aTokenInfo[0]; + $sLoginMode = 'basic'; + if (UserRights::CheckCredentials($sLogin, $aTokenInfo[1], $sLoginMode, 'internal')) + { + return $sLogin; + } + + throw new Exception("Cannot CheckCredentials user login ($sLogin) with ($sLoginMode) mode"); + } } diff --git a/application/loginform.class.inc.php b/application/loginform.class.inc.php index e10a97af4..ebd418f34 100644 --- a/application/loginform.class.inc.php +++ b/application/loginform.class.inc.php @@ -12,7 +12,7 @@ use Combodo\iTop\Application\Helper\Session; * * @since 2.7.0 */ -class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension +class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension, iTokenLoginUIExtension { private $bForceFormOnError = false; @@ -32,8 +32,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension protected function OnReadCredentials(&$iErrorCode) { if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'form') { - $sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data'); - $sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data'); + list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo(); if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd)) { if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER)) @@ -68,8 +67,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension { if (Session::Get('login_mode') == 'form') { - $sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data'); - $sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data'); + list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo(); if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) { $iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS; @@ -153,4 +151,23 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension return $oLoginContext; } + + public function GetTokenInfo(): array + { + $sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data'); + $sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data'); + return [$sAuthUser, $sAuthPwd]; + } + + public function GetUserLogin(array $aTokenInfo): string + { + $sLogin = $aTokenInfo[0]; + $sLoginMode = 'form'; + if (UserRights::CheckCredentials($sLogin, $aTokenInfo[1], $sLoginMode, 'internal')) + { + return $sLogin; + } + + throw new Exception("Cannot CheckCredentials user login ($sLogin) with ($sLoginMode) mode"); + } } diff --git a/application/loginurl.class.inc.php b/application/loginurl.class.inc.php index 616f88af4..beb4dbe5c 100644 --- a/application/loginurl.class.inc.php +++ b/application/loginurl.class.inc.php @@ -9,7 +9,7 @@ use Combodo\iTop\Application\Helper\Session; * @license http://opensource.org/licenses/AGPL-3.0 */ -class LoginURL extends AbstractLoginFSMExtension +class LoginURL extends AbstractLoginFSMExtension implements iTokenLoginUIExtension { /** * @var bool @@ -30,9 +30,7 @@ class LoginURL extends AbstractLoginFSMExtension { if (!Session::IsSet('login_mode') && !$this->bErrorOccurred) { - $sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data'); - $sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data'); - if (!empty($sAuthUser) && !empty($sAuthPwd)) + list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo(); { Session::Set('login_mode', 'url'); } @@ -53,8 +51,7 @@ class LoginURL extends AbstractLoginFSMExtension { if (Session::Get('login_mode') == 'url') { - $sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data'); - $sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data'); + list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo(); if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) { $iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS; @@ -92,4 +89,23 @@ class LoginURL extends AbstractLoginFSMExtension } return LoginWebPage::LOGIN_FSM_CONTINUE; } + + public function GetTokenInfo(): array + { + $sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data'); + $sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data'); + return [$sAuthUser, $sAuthPwd]; + } + + public function GetUserLogin(array $aTokenInfo): string + { + $sLogin = $aTokenInfo[0]; + $sLoginMode = 'url'; + if (UserRights::CheckCredentials($sLogin, $aTokenInfo[1], $sLoginMode, 'internal')) + { + return $sLogin; + } + + throw new Exception("Cannot CheckCredentials user login ($sLogin) with ($sLoginMode) mode"); + } } diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index e047cf761..3f6740b1d 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -581,6 +581,26 @@ class LoginWebPage extends NiceWebPage return $aPlugins; } + public static function GetCurrentLoginPlugin(string $sCurrentLoginMode) : iLoginExtension + { + /** @var iLoginExtension $oLoginExtensionInstance */ + foreach (MetaModel::EnumPlugins('iLoginFSMExtension') as $oLoginExtensionInstance) + { + $aLoginModes = $oLoginExtensionInstance->ListSupportedLoginModes(); + $aLoginModes = (is_array($aLoginModes) ? $aLoginModes : array()); + foreach ($aLoginModes as $sLoginMode) + { + // Keep only the plugins for the current login mode + before + after + if ($sLoginMode == $sCurrentLoginMode) + { + return $oLoginExtensionInstance; + } + } + } + + throw new \Exception("should not happen"); + } + /** * Advance Login Finite State Machine to the next step * diff --git a/lib/autoload.php b/lib/autoload.php index 9ee03077e..db10dc867 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -14,7 +14,10 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - throw new RuntimeException($err); + trigger_error( + $err, + E_USER_ERROR + ); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 8c7dad6c2..fba5d2036 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -3169,6 +3169,7 @@ return array( 'iRestServiceProvider' => $baseDir . '/application/applicationextension/rest/iRestServiceProvider.php', 'iScheduledProcess' => $baseDir . '/core/backgroundprocess.inc.php', 'iSelfRegister' => $baseDir . '/core/userrights.class.inc.php', + 'iTokenLoginUIExtension' => $baseDir . '/application/applicationextension/login/iTokenLoginUIExtension.php', 'iTopConfigParser' => $baseDir . '/core/iTopConfigParser.php', 'iTopMutex' => $baseDir . '/core/mutex.class.inc.php', 'iTopOwnershipLock' => $baseDir . '/core/ownershiplock.class.inc.php', diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index 6816f3d26..eb84c457f 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -56,7 +56,7 @@ return array( 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'Pelago\\Emogrifier\\' => array($vendorDir . '/pelago/emogrifier/src'), - 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-google/src', $vendorDir . '/league/oauth2-client/src'), + 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-client/src', $vendorDir . '/league/oauth2-google/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index e8dbf5f91..312bca006 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -314,8 +314,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f ), 'League\\OAuth2\\Client\\' => array ( - 0 => __DIR__ . '/..' . '/league/oauth2-google/src', - 1 => __DIR__ . '/..' . '/league/oauth2-client/src', + 0 => __DIR__ . '/..' . '/league/oauth2-client/src', + 1 => __DIR__ . '/..' . '/league/oauth2-google/src', ), 'GuzzleHttp\\Psr7\\' => array ( @@ -3524,6 +3524,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'iRestServiceProvider' => __DIR__ . '/../..' . '/application/applicationextension/rest/iRestServiceProvider.php', 'iScheduledProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php', 'iSelfRegister' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'iTokenLoginUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/login/iTokenLoginUIExtension.php', 'iTopConfigParser' => __DIR__ . '/../..' . '/core/iTopConfigParser.php', 'iTopMutex' => __DIR__ . '/../..' . '/core/mutex.class.inc.php', 'iTopOwnershipLock' => __DIR__ . '/../..' . '/core/ownershiplock.class.inc.php', diff --git a/lib/composer/platform_check.php b/lib/composer/platform_check.php index 72145773d..dee74e173 100644 --- a/lib/composer/platform_check.php +++ b/lib/composer/platform_check.php @@ -36,7 +36,8 @@ if ($issues) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } - throw new \RuntimeException( - 'Composer detected issues in your platform: ' . implode(' ', $issues) + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR ); } diff --git a/webservices/cron.php b/webservices/cron.php index f51a46f64..681a62af0 100644 --- a/webservices/cron.php +++ b/webservices/cron.php @@ -518,22 +518,37 @@ try $bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */); $bDebug = utils::ReadParam('debug', false, true /* Allow CLI */); - if ($bIsModeCLI) - { + if ($bIsModeCLI) { // Next steps: // specific arguments: 'csv file' // - $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data'); - $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data'); - if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) + $sTokenInfo = utils::ReadParam('auth_token_info', null, true, 'raw_data'); + $sLoginMode = utils::ReadParam('login_mode', null, true, 'raw_data'); + if (is_null($sLoginMode) || is_null($sTokenInfo)) { + $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data'); + $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data'); + if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) { + UserRights::Login($sAuthUser); // Login & set the user's language + } else { + $oP->p("Access wrong credentials ('$sAuthUser')"); + $oP->output(); + exit(EXIT_CODE_ERROR); + } + } else { - UserRights::Login($sAuthUser); // Login & set the user's language - } - else - { - $oP->p("Access wrong credentials ('$sAuthUser')"); - $oP->output(); - exit(EXIT_CODE_ERROR); + $oLoginFSMExtensionInstance = LoginWebPage::GetCurrentLoginPlugin($sLoginMode); + + if ($oLoginFSMExtensionInstance instanceof iTokenLoginUIExtension){ + $aTokenInfo = json_decode(base64_decode($sTokenInfo), true); + + /** @var iTokenLoginUIExtension $oLoginFSMExtensionInstance */ + $sAuthUser = $oLoginFSMExtensionInstance->GetUserLogin($aTokenInfo); + UserRights::Login($sAuthUser); // Login & set the user's language + } else { + $oP->p("cannot call cron asynchronously via current login mode $sLoginMode"); + $oP->output(); + exit(EXIT_CODE_ERROR); + } } } else diff --git a/webservices/cron_status.php b/webservices/cron_status.php index e26a7701b..649025294 100644 --- a/webservices/cron_status.php +++ b/webservices/cron_status.php @@ -9,21 +9,12 @@ const RUNNING = "running"; const STOPPED = "stopped"; const ERROR = "error"; -$sAuthUser = ReadParam("auth_user"); -$sAuthPwd = ReadParam("auth_pwd"); - try { - $sAuthUser = ReadParam("auth_user"); - $sAuthPwd = ReadParam("auth_pwd"); - - if (is_null($sAuthUser) || is_null($sAuthPwd)) { - throw new \Exception("Missing credentials"); - } - - if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) { - UserRights::Login($sAuthUser); // Login & set the user's language - } else { - throw new \Exception("Invalid credentials"); + $oCtx = new ContextTag(ContextTag::TAG_CRON); + LoginWebPage::ResetSession(true); + $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN); + if ($iRet != LoginWebPage::EXIT_CODE_OK){ + throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED); } $sLogFilename = ReadParam("cron_log_file", "cron.log"); diff --git a/webservices/launch_cron_asynchronously.php b/webservices/launch_cron_asynchronously.php index 728ae1d0d..38b47c1f1 100644 --- a/webservices/launch_cron_asynchronously.php +++ b/webservices/launch_cron_asynchronously.php @@ -1,21 +1,28 @@ GetTokenInfo(); + $sTokenInfo = base64_encode(json_encode($aTokenInfo)); } else { - throw new \Exception("Invalid credentials"); + throw new \Exception("cannot call cron asynchronously via current login mode $sCurrentLoginMode"); } $sLogFilename = ReadParam("cron_log_file", "cron.log"); @@ -30,13 +37,8 @@ try { } else { $sCliParams = trim(base64_decode($sCliParams, true)); - if (false === strpos($sCliParams, '--auth_user=')) { - $sCliParams = "--auth_user=$sAuthUser ".$sCliParams; - } - - if (false === strpos($sCliParams, '--auth_pwd=')) { - $sCliParams = "--auth_pwd=$sAuthPwd ".$sCliParams; - } + $sCliParams = "--auth_token_info=$sTokenInfo ".$sCliParams; + $sCliParams = "--login_mode=$sCurrentLoginMode ".$sCliParams; if (false !== strpos($sCliParams, '--status_only=1')) { $bAsynchronous = false; @@ -51,8 +53,6 @@ try { file_put_contents($sLogFile, $sCli); $process = popen($sCli, 'r'); } else { - - $sCli = sprintf("\n $sPHPExec %s/cron.php $sCliParams", __DIR__); $fp = fopen($sLogFile, 'a+'); fwrite($fp, $sCli);