diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 5fd203d..0262287 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -1,20 +1,52 @@ in(__DIR__) + ->ignoreVCSIgnored(true) ->name('*.php'); $config = new Config(); $rules = [ - '@PER-CS2.0' => true, - 'trailing_comma_in_multiline' => ['elements' => ['arguments', 'array_destructuring', 'arrays']], // For PHP 7.4 compatibility + '@PER-CS' => true, // Latest PER rules. ]; return $config ->setRules($rules) ->setFinder($finder) - ->setUsingCache(false); + ->setCacheFile(__DIR__ . '/var/php-cs-fixer/.php-cs-fixer.cache') +; diff --git a/CHANGELOG.md b/CHANGELOG.md index 7314036..8abf19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Show the specific OAuth authorization error instead of a generic message +- Ignore the connection host/port/security level entered on the authorization diagnostic form and always use the provider's own values +- Escape mail collector host and login when displayed on the application's "Mail collectors" tab ## [1.5.3] - 2026-06-24 diff --git a/hook.php b/hook.php index 6a16da5..6886989 100644 --- a/hook.php +++ b/hook.php @@ -76,6 +76,7 @@ function plugin_oauthimap_install() } } } + $migration->executeMigration(); return true; diff --git a/inc/application.class.php b/inc/application.class.php index 2a62b33..c10ad7a 100644 --- a/inc/application.class.php +++ b/inc/application.class.php @@ -53,7 +53,8 @@ public static function getMenuContent() $menu['page'] = '/plugins/oauthimap/front/application.php'; $menu['icon'] = self::getIcon(); } - if (count($menu)) { + + if ($menu !== []) { return $menu; } @@ -169,6 +170,7 @@ public function displaySpecificTypeField($ID, $field = [], array $options = []) $values[$provider_class] = $provider_class::getName(); $icons[$provider_class] = $provider_class::getIcon(); } + Dropdown::showFromArray( $field_name, $values, @@ -186,7 +188,7 @@ public function displaySpecificTypeField($ID, $field = [], array $options = []) $json_icons = json_encode($icons); $js = << $values]; } - switch ($field) { - case 'provider': - $value = $values[$field]; - if (in_array($value, self::getSupportedProviders())) { - return ' ' . $value::getName(); - } + if ($field === 'provider') { + $value = $values[$field]; + if (in_array($value, self::getSupportedProviders())) { + return ' ' . $value::getName(); + } - return $value; + return $value; } return parent::getSpecificValueToDisplay($field, $values, $options); @@ -253,25 +254,24 @@ public static function getSpecificValueToSelect($field, $name = '', $values = '' $values = [$field => $values]; } - switch ($field) { - case 'provider': - $selected = ''; - $elements = ['' => Dropdown::EMPTY_VALUE]; - foreach (self::getSupportedProviders() as $class) { - $elements[$class] = $class::getName(); - if ($class === $values[$field]) { - $selected = $class; - } + if ($field === 'provider') { + $selected = ''; + $elements = ['' => Dropdown::EMPTY_VALUE]; + foreach (self::getSupportedProviders() as $class) { + $elements[$class] = $class::getName(); + if ($class === $values[$field]) { + $selected = $class; } + } - return Dropdown::showFromArray( - $name, - $elements, - [ - 'display' => false, - 'value' => $selected, - ], - ); + return Dropdown::showFromArray( + $name, + $elements, + [ + 'display' => false, + 'value' => $selected, + ], + ); } return parent::getSpecificValueToSelect($field, $name, $values, $options); @@ -280,9 +280,7 @@ public static function getSpecificValueToSelect($field, $name = '', $values = '' /** * Displays form extra fields/scripts. * - * @param int $id * - * @return void */ public static function showFormExtra(int $id): void { @@ -343,7 +341,7 @@ public function prepareInputForUpdate($input) */ private function prepareInput($input) { - if (array_key_exists('name', $input) && empty(trim($input['name']))) { + if (array_key_exists('name', $input) && in_array(trim($input['name']), ['', '0'], true)) { Session::addMessageAfterRedirect(__s('Name cannot be empty', 'oauthimap'), false, ERROR); return false; @@ -400,8 +398,6 @@ public function post_updateItem($history = true) * * @param callable|null $callback_callable Callable to call on authorization callback * @param array $callback_params Parameters to pass to callable - * - * @return void */ public function redirectToAuthorizationUrl(?callable $callback_callable = null, array $callback_params = []): void { @@ -412,7 +408,7 @@ public function redirectToAuthorizationUrl(?callable $callback_callable = null, $provider = $this->getProvider(); $options = [ - 'scope' => self::getProviderScopes($this->fields['provider']), + 'scope' => $this->getProviderScopes($this->fields['provider']), ]; switch ($this->fields['provider']) { case Azure::class: @@ -436,8 +432,6 @@ public function redirectToAuthorizationUrl(?callable $callback_callable = null, /** * Check if credentials are valid (i.e. all fields are correclty set). - * - * @return bool */ private function areCredentialsValid(): bool { @@ -452,8 +446,6 @@ private function areCredentialsValid(): bool /** * Get list of supported providers classnames. - * - * @return array */ private static function getSupportedProviders(): array { @@ -485,21 +477,21 @@ public function getProvider() 'clientId' => $this->fields['client_id'], 'clientSecret' => (new GLPIKey())->decrypt($this->fields['client_secret']), 'redirectUri' => self::getCallbackUrl(), - 'scope' => self::getProviderScopes($this->fields['provider']), + 'scope' => $this->getProviderScopes($this->fields['provider']), ]; if (!empty($CFG_GLPI['proxy_name'])) { // Connection using proxy - $params['proxy'] = !empty($CFG_GLPI['proxy_user']) + $params['proxy'] = empty($CFG_GLPI['proxy_user']) ? sprintf( - '%s:%s@%s:%s', - rawurlencode($CFG_GLPI['proxy_user']), - rawurlencode((new GLPIKey())->decrypt($CFG_GLPI['proxy_passwd'])), + '%s:%s', $CFG_GLPI['proxy_name'], $CFG_GLPI['proxy_port'], ) : sprintf( - '%s:%s', + '%s:%s@%s:%s', + rawurlencode($CFG_GLPI['proxy_user']), + rawurlencode((string) (new GLPIKey())->decrypt($CFG_GLPI['proxy_passwd'])), $CFG_GLPI['proxy_name'], $CFG_GLPI['proxy_port'], ); @@ -512,6 +504,7 @@ public function getProvider() if (!empty($this->fields['tenant_id'])) { $params['tenant'] = $this->fields['tenant_id']; } + break; case Google::class: $params['accessType'] = 'offline'; @@ -525,36 +518,27 @@ public function getProvider() * Get required scopes for given provider. * * @param string $provider Provider classname - * - * @return array */ - private static function getProviderScopes(string $provider): array + private function getProviderScopes(string $provider): array { $scopes = []; - switch ($provider) { - case Azure::class: - $scopes = [ - 'openid', 'email', // required to be able to fetch owner details - 'offline_access', - 'https://outlook.office.com/IMAP.AccessAsUser.All', - ]; - break; - case Google::class: - $scopes = [ - 'https://mail.google.com/', - ]; - break; - } - - return $scopes; + return match ($provider) { + Azure::class => [ + 'openid', 'email', // required to be able to fetch owner details + 'offline_access', + 'https://outlook.office.com/IMAP.AccessAsUser.All', + ], + Google::class => [ + 'https://mail.google.com/', + ], + default => $scopes, + }; } /** * Get documentation URLs. * Keys are providers classnames, values are URL. - * - * @return array */ private static function getProvidersDocumentationUrls(): array { @@ -566,8 +550,6 @@ private static function getProvidersDocumentationUrls(): array /** * Get callback URL used during authorization process. - * - * @return string */ private static function getCallbackUrl(): string { @@ -599,10 +581,10 @@ public static function install(Migration $migration) $table = self::getTable(); if (!$DB->tableExists($table)) { - $migration->displayMessage("Installing $table"); + $migration->displayMessage('Installing ' . $table); $query = <<displayMessage("Uninstalling $table"); + $migration->displayMessage('Uninstalling ' . $table); $migration->dropTable($table); $DB->delete('glpi_displaypreferences', [ diff --git a/inc/authorization.class.php b/inc/authorization.class.php index 7d2fbfe..978c24e 100644 --- a/inc/authorization.class.php +++ b/inc/authorization.class.php @@ -48,6 +48,7 @@ class PluginOauthimapAuthorization extends CommonDBChild // From CommonDBChild public static $itemtype = 'PluginOauthimapApplication'; + public static $items_id = 'plugin_oauthimap_applications_id'; /** @@ -60,12 +61,10 @@ class PluginOauthimapAuthorization extends CommonDBChild * Detail of the last error encountered in createFromCode(), if any. * @var string|null */ - private $error = null; + private $error; /** * Get detail of the last error encountered in createFromCode(). - * - * @return string|null */ public function getLastError(): ?string { @@ -181,8 +180,10 @@ public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $ echo ''; } + echo ''; } + echo ''; return true; @@ -223,7 +224,6 @@ public function showForm($id, $options = []) /** * Displays diagnostic form. * - * @param array $params * * @return void */ @@ -238,9 +238,11 @@ public function showDiagnosticForm(array $params) } $user = $params['user'] ?? $this->fields['email']; - $host = $params['host'] ?? $provider->getDefaultHost(); - $port = (int) ($params['port'] ?? $provider->getDefaultPort()); - $ssl = $params['ssl'] ?? $provider->getDefaultSslFlag(); + // Host/port/ssl are never taken from user input: the diagnostic must only ever connect + // to the provider's own IMAP endpoint, not an arbitrary attacker-supplied target. + $host = $provider->getDefaultHost(); + $port = $provider->getDefaultPort(); + $ssl = $provider->getDefaultSslFlag(); $timeout = (int) ($params['timeout'] ?? 2); // 2 seconds timeout by default echo '
'; @@ -274,7 +276,8 @@ public function showDiagnosticForm(array $params) echo Html::input( 'host', [ - 'value' => $host, + 'disabled' => 'disabled', + 'value' => $host, ], ); echo ''; @@ -285,10 +288,11 @@ public function showDiagnosticForm(array $params) echo Html::input( 'port', [ - 'type' => 'integer', - 'min' => 1, - 'value' => $port, - 'size' => 5, + 'type' => 'integer', + 'min' => 1, + 'disabled' => 'disabled', + 'value' => $port, + 'size' => 5, ], ); echo ''; @@ -308,6 +312,7 @@ public function showDiagnosticForm(array $params) ], [ 'selected' => $ssl, + 'disabled' => 'disabled', 'class' => 'form-select', ], ); @@ -350,23 +355,26 @@ public function showDiagnosticForm(array $params) $protocol = new ImapOauthProtocol($application->fields['id']); $protocol->enableDiagnostic(); $protocol->setTimeout($timeout); + $error = null; try { $protocol->connect($host, $port, $ssl); if ($protocol->login($user, '')) { new ImapOauthStorage($protocol); // Will automatically send 'select INBOX'. } - } catch (Throwable $e) { - $error = $e; + } catch (Throwable $throwable) { + $error = $throwable; } + echo '
'; echo htmlspecialchars($protocol->getDiagnosticLog()); echo ''; - if ($error !== null) { + if ($error instanceof Throwable) { echo '
'; echo sprintf(__s('Unexpected error: %s', 'oauthimap'), $error->getMessage()); echo '
'; } + echo ''; echo ''; @@ -428,11 +436,8 @@ private function prepareInput($input) /** * Create an authorization based on authorizarion code. * - * @param int $application_id - * @param string $code * @param (AbstractProvider&ProviderInterface)|null $provider Injected provider, mainly for testing purposes. * - * @return bool */ public function createFromCode(int $application_id, string $code, ?AbstractProvider $provider = null): bool { @@ -447,13 +452,13 @@ public function createFromCode(int $application_id, string $code, ?AbstractProvi // Get token try { $token = $provider->getAccessToken('authorization_code', ['code' => $code]); - } catch (Throwable $e) { + } catch (Throwable $throwable) { trigger_error( - sprintf('Error during authorization code fetching: %s', $e->getMessage()), + sprintf('Error during authorization code fetching: %s', $throwable->getMessage()), E_USER_WARNING, ); - $this->error = sprintf(__('Unable to obtain access token from provider: %s', 'oauthimap'), $e->getMessage()); + $this->error = sprintf(__('Unable to obtain access token from provider: %s', 'oauthimap'), $throwable->getMessage()); return false; } @@ -497,8 +502,6 @@ public function createFromCode(int $application_id, string $code, ?AbstractProvi * * @param int $application_id * @param string $email - * - * @return string|null */ public static function getAccessTokenForApplicationAndEmail($application_id, $email): ?string { @@ -514,7 +517,7 @@ public static function getAccessTokenForApplicationAndEmail($application_id, $em try { $token = new AccessToken(json_decode((new GLPIKey())->decrypt($self->fields['token']), true)); - } catch (Throwable $e) { + } catch (Throwable) { return null; // Field value may be corrupted } @@ -547,14 +550,12 @@ public static function getAccessTokenForApplicationAndEmail($application_id, $em /** * Get existing access token. - * - * @return AccessToken|null */ public function getAccessToken(): ?AccessToken { try { $token = new AccessToken(json_decode((new GLPIKey())->decrypt($this->fields['token']), true)); - } catch (Throwable $e) { + } catch (Throwable) { return null; // Field value may be corrupted } @@ -563,8 +564,6 @@ public function getAccessToken(): ?AccessToken /** * Returns owner details fetched when creating authorization. - * - * @return OwnerDetails|null */ public function getOwnerDetails(): ?OwnerDetails { @@ -600,11 +599,11 @@ public static function install(Migration $migration) $application_fkey = PluginOauthimapApplication::getForeignKeyField(); if (!$DB->tableExists($table)) { - $migration->displayMessage("Installing $table"); + $migration->displayMessage('Installing ' . $table); $query = <<doQuery($query); @@ -659,7 +658,7 @@ public static function uninstall(Migration $migration) global $DB; $table = self::getTable(); - $migration->displayMessage("Uninstalling $table"); + $migration->displayMessage('Uninstalling ' . $table); $migration->dropTable($table); $DB->delete('glpi_displaypreferences', [ diff --git a/inc/hook.class.php b/inc/hook.class.php index af23fe6..e2ddd1a 100644 --- a/inc/hook.class.php +++ b/inc/hook.class.php @@ -35,9 +35,7 @@ class PluginOauthimapHook /** * Handle post_item_form hook. * - * @param array $params * - * @return void */ public static function postItemForm(array $params): void { @@ -47,7 +45,7 @@ public static function postItemForm(array $params): void return; } - switch (get_class($item)) { + switch ($item::class) { case MailCollector::class: MailCollectorFeature::alterMailCollectorForm(); break; diff --git a/inc/imap/imapoauthprotocol.class.php b/inc/imap/imapoauthprotocol.class.php index 32a6d88..4063355 100644 --- a/inc/imap/imapoauthprotocol.class.php +++ b/inc/imap/imapoauthprotocol.class.php @@ -54,13 +54,6 @@ class ImapOauthProtocol extends Imap implements ProtocolInterface */ private const DIAGNOSTIC_PREFIX_RECEIVED = '<<< '; - /** - * ID of PluginOauthimapApplication to use. - * - * @var int - */ - private $application_id; - /** * Indicates whether diagnostic is enabled. * @@ -85,9 +78,11 @@ class ImapOauthProtocol extends Imap implements ProtocolInterface /** * @param int $application_id ID of PluginOauthimapApplication to use */ - public function __construct($application_id) - { - $this->application_id = $application_id; + public function __construct(/** + * ID of PluginOauthimapApplication to use. + */ + private $application_id, + ) { parent::__construct(); } @@ -111,6 +106,7 @@ public function connect($host, $port = null, $ssl = false) if (!$port) { $port = 993; } + break; case 'tls': $isTls = true; @@ -125,7 +121,7 @@ public function connect($host, $port = null, $ssl = false) $this->socket = $this->setupSocket($transport, $host, $port, $this->timeout); if (!$this->assumedNextLine('* OK')) { - throw new RuntimeException('host doesn\'t allow connection'); + throw new RuntimeException("host doesn't allow connection"); } if ($isTls) { @@ -167,6 +163,7 @@ public function login($user, $password) ) { return false; } + if (preg_match('/^OK /i', $response) !== 0) { return true; } @@ -196,6 +193,7 @@ public function sendRequest($command, $tokens = [], &$tag = '') if (!$this->assumedNextLine('+ ')) { throw new RuntimeException('cannot send literal string'); } + $line = $token[1]; } else { $line .= ' ' . $token; @@ -218,6 +216,7 @@ protected function nextLine() if ($line === false) { throw new RuntimeException('cannot read - connection closed?'); } + $this->addToDiagnosticLog($line, self::DIAGNOSTIC_PREFIX_RECEIVED); return $line; @@ -225,8 +224,6 @@ protected function nextLine() /** * Enable diagnostic. - * - * @return void */ public function enableDiagnostic(): void { @@ -235,8 +232,6 @@ public function enableDiagnostic(): void /** * Get the diagnostic log. - * - * @return string */ public function getDiagnosticLog(): string { @@ -246,8 +241,6 @@ public function getDiagnosticLog(): string /** * Add line to diagnostic log. * - * @param string $line - * @param string $prefix * * @return void */ @@ -256,15 +249,14 @@ private function addToDiagnosticLog(string $line, string $prefix = '') if (!$this->diagnostic_enabled) { return; } + $this->diagnostic_log[] = $prefix . $line; } /** * Defines socket timeout. * - * @param int $timeout * - * @return void */ public function setTimeout(int $timeout): void { diff --git a/inc/imap/imapoauthstorage.class.php b/inc/imap/imapoauthstorage.class.php index ec8cde5..feb6404 100644 --- a/inc/imap/imapoauthstorage.class.php +++ b/inc/imap/imapoauthstorage.class.php @@ -79,6 +79,7 @@ public function __construct($params) if (!$this->protocol->login($params->user, $password)) { throw new RuntimeException('cannot login, user or password wrong'); } + $this->selectFolder($params->folder ?? 'INBOX'); } } diff --git a/inc/mailcollectorfeature.class.php b/inc/mailcollectorfeature.class.php index 1f8fe44..5e7c460 100644 --- a/inc/mailcollectorfeature.class.php +++ b/inc/mailcollectorfeature.class.php @@ -120,8 +120,6 @@ public static function getMailProtocolTypeIdentifier($application_id) /** * Alter MailCollector form in order to handle IMAP Oauth connections. - * - * @return void */ public static function alterMailCollectorForm(): void { @@ -195,7 +193,6 @@ function (evt) { /** * Force mailcollector update if oauth fields should trigger an authorization request. * - * @param MailCollector $item * * @return boolean */ @@ -214,6 +211,7 @@ public static function forceMailCollectorUpdate(MailCollector $item) // Return true to continue update. return true; } + if ($item->input['plugin_oauthimap_authorizations_id'] > 0) { // Existing authorization selected => no need to trigger authorization request. // Return true to continue update. @@ -231,9 +229,7 @@ public static function forceMailCollectorUpdate(MailCollector $item) /** * Handle authorization process after creation/update of a mail collector. * - * @param MailCollector $item * - * @return void */ public static function handleMailCollectorSaving(MailCollector $item): void { @@ -255,6 +251,7 @@ public static function handleMailCollectorSaving(MailCollector $item): void $application = new PluginOauthimapApplication(); $application->getFromDB($applications_id); + $authorization = new PluginOauthimapAuthorization(); if ($authorizations_id > 0 && $authorization->getFromDB($authorizations_id)) { @@ -269,7 +266,7 @@ public static function handleMailCollectorSaving(MailCollector $item): void } else { // Create new authorization $application->redirectToAuthorizationUrl( - [self::class, 'updateMailCollectorOnAuthorizationCallback'], + self::updateMailCollectorOnAuthorizationCallback(...), [ MailCollector::getForeignKeyField() => $item->getID(), ], @@ -280,16 +277,12 @@ public static function handleMailCollectorSaving(MailCollector $item): void /** * Update login field of mail collector on authorization callback. * - * @param bool $success - * @param PluginOauthimapAuthorization $authorization - * @param array $params * - * @return void */ public static function updateMailCollectorOnAuthorizationCallback( bool $success, PluginOauthimapAuthorization $authorization, - array $params = [] + array $params = [], ): void { $mailcollector = new MailCollector(); $redirect = $mailcollector->getSearchURL(); @@ -313,9 +306,7 @@ public static function updateMailCollectorOnAuthorizationCallback( /** * Deactivate mail collectors linked to the application. * - * @param PluginOauthimapApplication $application * - * @return void */ public static function postDeactivateApplication(PluginOauthimapApplication $application): void { @@ -327,9 +318,7 @@ public static function postDeactivateApplication(PluginOauthimapApplication $app /** * Deactivate mail collectors linked to the authorization. * - * @param PluginOauthimapAuthorization $authorization * - * @return void */ public static function postPurgeAuthorization(PluginOauthimapAuthorization $authorization): void { @@ -343,9 +332,7 @@ public static function postPurgeAuthorization(PluginOauthimapAuthorization $auth /** * Update mail collectors linked to the authorization. * - * @param PluginOauthimapAuthorization $authorization * - * @return void */ public static function postUpdateAuthorization(PluginOauthimapAuthorization $authorization): void { @@ -375,9 +362,7 @@ public static function postUpdateAuthorization(PluginOauthimapAuthorization $aut /** * Deactivate mail collectors using given protocol type and given login. * - * @param string $protocol_type * @param string $login - * * @return void */ private static function deactivateMailCollectors(string $protocol_type, ?string $login = null) @@ -404,21 +389,20 @@ private static function deactivateMailCollectors(string $protocol_type, ?string /** * Return mail collectors using given protocol type and given login. * - * @param string $protocol_type * @param string $login - * @param bool $only_active * * @return array */ private static function getAssociatedMailCollectors( string $protocol_type, ?string $login = null, - bool $only_active = true + bool $only_active = true, ) { $criteria = []; if ($only_active) { $criteria['is_active'] = 1; } + if ($login !== null) { $criteria['login'] = $login; } @@ -445,9 +429,7 @@ private static function getAssociatedMailCollectors( /** * Display "mail collectors" tab of application page. * - * @param PluginOauthimapApplication $application * - * @return void */ public static function showMailCollectorsForApplication(PluginOauthimapApplication $application): void { @@ -476,12 +458,13 @@ public static function showMailCollectorsForApplication(PluginOauthimapApplicati echo ''; echo '' . $name . ''; - echo '' . $row['host'] . ''; - echo '' . $row['login'] . ''; + echo '' . htmlspecialchars($row['host'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . ''; + echo '' . htmlspecialchars($row['login'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . ''; echo '' . Dropdown::getYesNo($row['is_active']) . ''; echo ''; } } + echo ''; echo '
'; } diff --git a/inc/provider/azure.class.php b/inc/provider/azure.class.php index 552fd82..de24a46 100644 --- a/inc/provider/azure.class.php +++ b/inc/provider/azure.class.php @@ -57,6 +57,7 @@ public function getOwnerDetails(AccessToken $token): ?OwnerDetails } elseif (($upn = $owner->claim('upn')) !== null) { $owner_details->email = $upn; } + $owner_details->firstname = $owner->getFirstName(); $owner_details->lastname = $owner->getLastName(); diff --git a/inc/provider/providerinterface.class.php b/inc/provider/providerinterface.class.php index bf1d100..1f139d3 100644 --- a/inc/provider/providerinterface.class.php +++ b/inc/provider/providerinterface.class.php @@ -37,45 +37,33 @@ interface ProviderInterface { /** * Return provider name. - * - * @return string */ public static function getName(): string; /** * Return provider icon (Font-Awesome identifier). - * - * @return string */ public static function getIcon(): string; /** * Return token owner details. * - * @param AccessToken $token * - * @return OwnerDetails|null */ public function getOwnerDetails(AccessToken $token): ?OwnerDetails; /** * Returns default host for IMAP connection. - * - * @return string */ public function getDefaultHost(): string; /** * Returns default port for IMAP connection. - * - * @return int|null */ public function getDefaultPort(): ?int; /** * Returns default SSL flag ('SSL', 'TLS' or null) for IMAP connection. - * - * @return string|null */ public function getDefaultSslFlag(): ?string; } diff --git a/psalm.xml b/psalm.xml index f051d3d..d550b73 100644 --- a/psalm.xml +++ b/psalm.xml @@ -6,18 +6,8 @@ + - - - - - diff --git a/rector.php b/rector.php index b26c965..2187f30 100644 --- a/rector.php +++ b/rector.php @@ -28,71 +28,28 @@ * ------------------------------------------------------------------------- */ +use Rector\Configuration\RectorConfigBuilder; + require_once __DIR__ . '/../../src/Plugin.php'; -use Rector\Caching\ValueObject\Storage\FileCacheStorage; -use Rector\CodeQuality\Rector as CodeQuality; -use Rector\Config\RectorConfig; -use Rector\DeadCode\Rector as DeadCode; -use Rector\ValueObject\PhpVersion; +$baseline_file = __DIR__ . '/../../PluginsRector.php'; +if (!file_exists($baseline_file)) { + throw new RuntimeException( + sprintf( + 'Unable to find "%s". Running rector on a plugin requires a GLPI development checkout that ships PluginsRector.php.', + $baseline_file, + ), + ); +} + +$baseline = require $baseline_file; + +/** @var RectorConfigBuilder $config */ +$config = $baseline([ + __DIR__ . '/ajax', + __DIR__ . '/front', + __DIR__ . '/inc', + __DIR__ . '/tests', +]); -return RectorConfig::configure() - ->withPaths([ - __DIR__ . '/ajax', - __DIR__ . '/front', - __DIR__ . '/inc', - ]) - ->withPhpVersion(PhpVersion::PHP_82) - ->withCache( - cacheClass: FileCacheStorage::class, - cacheDirectory: sys_get_temp_dir() . '/oauthimap-rector', - ) - ->withRootFiles() - ->withParallel(timeoutSeconds: 300) - ->withImportNames(removeUnusedImports: true) - ->withRules([ - CodeQuality\Assign\CombinedAssignRector::class, - CodeQuality\BooleanAnd\RemoveUselessIsObjectCheckRector::class, - CodeQuality\BooleanAnd\SimplifyEmptyArrayCheckRector::class, - CodeQuality\BooleanNot\ReplaceMultipleBooleanNotRector::class, - CodeQuality\Catch_\ThrowWithPreviousExceptionRector::class, - CodeQuality\Empty_\SimplifyEmptyCheckOnEmptyArrayRector::class, - CodeQuality\Expression\InlineIfToExplicitIfRector::class, - CodeQuality\Expression\TernaryFalseExpressionToIfRector::class, - CodeQuality\For_\ForRepeatedCountToOwnVariableRector::class, - CodeQuality\Foreach_\ForeachItemsAssignToEmptyArrayToAssignRector::class, - CodeQuality\Foreach_\ForeachToInArrayRector::class, - CodeQuality\Foreach_\SimplifyForeachToCoalescingRector::class, - CodeQuality\Foreach_\UnusedForeachValueToArrayKeysRector::class, - CodeQuality\FuncCall\ChangeArrayPushToArrayAssignRector::class, - CodeQuality\FuncCall\CompactToVariablesRector::class, - CodeQuality\FuncCall\InlineIsAInstanceOfRector::class, - CodeQuality\FuncCall\IsAWithStringWithThirdArgumentRector::class, - CodeQuality\FuncCall\RemoveSoleValueSprintfRector::class, - CodeQuality\FuncCall\SetTypeToCastRector::class, - CodeQuality\FuncCall\SimplifyFuncGetArgsCountRector::class, - CodeQuality\FuncCall\SimplifyInArrayValuesRector::class, - CodeQuality\FuncCall\SimplifyStrposLowerRector::class, - CodeQuality\FuncCall\UnwrapSprintfOneArgumentRector::class, - CodeQuality\Identical\BooleanNotIdenticalToNotIdenticalRector::class, - CodeQuality\Identical\SimplifyArraySearchRector::class, - CodeQuality\Identical\SimplifyConditionsRector::class, - CodeQuality\Identical\StrlenZeroToIdenticalEmptyStringRector::class, - CodeQuality\If_\CombineIfRector::class, - CodeQuality\If_\CompleteMissingIfElseBracketRector::class, - CodeQuality\If_\ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class, - CodeQuality\If_\ExplicitBoolCompareRector::class, - CodeQuality\If_\ShortenElseIfRector::class, - CodeQuality\If_\SimplifyIfElseToTernaryRector::class, - CodeQuality\If_\SimplifyIfNotNullReturnRector::class, - CodeQuality\If_\SimplifyIfNullableReturnRector::class, - CodeQuality\If_\SimplifyIfReturnBoolRector::class, - CodeQuality\Include_\AbsolutizeRequireAndIncludePathRector::class, - CodeQuality\LogicalAnd\AndAssignsToSeparateLinesRector::class, - CodeQuality\LogicalAnd\LogicalToBooleanRector::class, - CodeQuality\NotEqual\CommonNotEqualRector::class, - CodeQuality\Ternary\UnnecessaryTernaryExpressionRector::class, - DeadCode\Assign\RemoveUnusedVariableAssignRector::class, - ]) - ->withPhpSets(php74: true) // apply PHP sets up to PHP 7.4 -; +return $config; diff --git a/setup.php b/setup.php index 5473f75..a4ff355 100644 --- a/setup.php +++ b/setup.php @@ -75,18 +75,18 @@ function plugin_init_oauthimap() ]; // Plugin hooks - $PLUGIN_HOOKS['post_item_form']['oauthimap'] = [PluginOauthimapHook::class, 'postItemForm']; + $PLUGIN_HOOKS['post_item_form']['oauthimap'] = PluginOauthimapHook::postItemForm(...); // MailCollector hooks $PLUGIN_HOOKS['mail_server_protocols']['oauthimap'] = (fn(array $additionnal_protocols) => array_merge($additionnal_protocols, MailCollectorFeature::getMailProtocols())); $PLUGIN_HOOKS['pre_item_update']['oauthimap'] = [ - 'MailCollector' => [MailCollectorFeature::class, 'forceMailCollectorUpdate'], + 'MailCollector' => MailCollectorFeature::forceMailCollectorUpdate(...), ]; $PLUGIN_HOOKS['item_add']['oauthimap'] = [ - 'MailCollector' => [MailCollectorFeature::class, 'handleMailCollectorSaving'], + 'MailCollector' => MailCollectorFeature::handleMailCollectorSaving(...), ]; $PLUGIN_HOOKS['item_update']['oauthimap'] = [ - 'MailCollector' => [MailCollectorFeature::class, 'handleMailCollectorSaving'], + 'MailCollector' => MailCollectorFeature::handleMailCollectorSaving(...), ]; } } @@ -101,7 +101,7 @@ function plugin_version_oauthimap() return [ 'name' => __s('OAuth IMAP', 'oauthimap'), 'version' => PLUGIN_OAUTHIMAP_VERSION, - 'author' => 'Teclib\'', + 'author' => "Teclib'", 'license' => 'GPL v3+', 'homepage' => 'https://www.teclib-edition.com', 'requirements' => [ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index cd5e76a..4ce9e0e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -28,11 +28,13 @@ * ------------------------------------------------------------------------- */ +use function Safe\realpath; + $current_plugin_folder = basename(realpath(__DIR__ . '/../')); require __DIR__ . '/../../../tests/bootstrap.php'; require dirname(__DIR__) . '/vendor/autoload.php'; if (!Plugin::isPluginActive($current_plugin_folder)) { - throw new RuntimeException("Plugin $current_plugin_folder is not active in the test database"); + throw new RuntimeException(sprintf('Plugin %s is not active in the test database', $current_plugin_folder)); }