From a83832f7e8c61a09945c45edc392f36eb22091df Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 7 Jul 2025 23:35:41 +0200 Subject: [PATCH] Replace Psalm with PHPstan --- .github/workflows/php.yml | 27 ++++++--------------------- composer.json | 5 +++-- phpstan-dev.neon | 4 ++++ phpstan.neon | 4 ++++ psalm-dev.xml | 27 --------------------------- psalm.xml | 30 ------------------------------ src/Auth/Source/CAS.php | 28 ++++++++++++++++------------ src/Controller/CAS.php | 4 ++-- tests/src/Controller/CASTest.php | 12 ++++++++++-- 9 files changed, 45 insertions(+), 96 deletions(-) create mode 100644 phpstan-dev.neon create mode 100644 phpstan.neon delete mode 100644 psalm-dev.xml delete mode 100644 psalm.xml diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index c8be83c..4eed08c 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -162,9 +162,8 @@ jobs: with: # Should be the higest supported version, so we can use the newest tools php-version: '8.4' - tools: composer, composer-require-checker, composer-unused, phpcs, psalm - # optional performance gain for psalm: opcache - extensions: ctype, date, dom, fileinfo, filter, hash, intl, mbstring, opcache, openssl, pcre, spl, xml + tools: composer, composer-require-checker, composer-unused, phpcs + extensions: ctype, date, dom, fileinfo, filter, hash, intl, mbstring, openssl, pcre, spl, xml - name: Setup problem matchers for PHP run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" @@ -196,27 +195,13 @@ jobs: - name: PHP Code Sniffer run: phpcs - - name: Psalm - continue-on-error: true - run: | - psalm -c psalm.xml \ - --show-info=true \ - --shepherd \ - --php-version=${{ steps.setup-php.outputs.php-version }} - - - name: Psalm (testsuite) + - name: PHPStan run: | - psalm -c psalm-dev.xml \ - --show-info=true \ - --shepherd \ - --php-version=${{ steps.setup-php.outputs.php-version }} + vendor/bin/phpstan analyze -c phpstan.neon - - name: Psalter + - name: PHPStan (testsuite) run: | - psalm --alter \ - --issues=UnnecessaryVarAnnotation \ - --dry-run \ - --php-version=${{ steps.setup-php.outputs.php-version }} + vendor/bin/phpstan analyze -c phpstan-dev.neon security: name: Security checks diff --git a/composer.json b/composer.json index 7675981..b9817c8 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "composer/package-versions-deprecated": true, "simplesamlphp/composer-module-installer": true, "dealerdirect/phpcodesniffer-composer-installer": true, - "phpstan/extension-installer": true + "phpstan/extension-installer": true, + "simplesamlphp/composer-xmlprovider-installer": true } }, "autoload": { @@ -35,7 +36,7 @@ "require": { "php": "^8.1", "simplesamlphp/composer-module-installer": "^1.3.4", - "simplesamlphp/simplesamlphp": "^3@dev", + "simplesamlphp/simplesamlphp": "~2.4.0", "simplesamlphp/simplesamlphp-module-ldap": "~1.2", "symfony/http-foundation": "^6.4" }, diff --git a/phpstan-dev.neon b/phpstan-dev.neon new file mode 100644 index 0000000..116dfc6 --- /dev/null +++ b/phpstan-dev.neon @@ -0,0 +1,4 @@ +parameters: + level: 8 + paths: + - tests diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..d5341b1 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 7 + paths: + - src diff --git a/psalm-dev.xml b/psalm-dev.xml deleted file mode 100644 index 6116331..0000000 --- a/psalm-dev.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 9feb67a..0000000 --- a/psalm.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Auth/Source/CAS.php b/src/Auth/Source/CAS.php index af55991..30ad6b6 100644 --- a/src/Auth/Source/CAS.php +++ b/src/Auth/Source/CAS.php @@ -41,12 +41,12 @@ class CAS extends Auth\Source public const AUTHID = '\SimpleSAML\Module\cas\Auth\Source\CAS.AuthId'; /** - * @var array with ldap configuration + * @var array with ldap configuration */ private array $ldapConfig; /** - * @var array cas configuration + * @var array cas configuration */ private array $casConfig; @@ -64,8 +64,8 @@ class CAS extends Auth\Source /** * Constructor for this authentication source. * - * @param array $info Information about this authentication source. - * @param array $config Configuration. + * @param array $info Information about this authentication source. + * @param array $config Configuration. */ public function __construct(array $info, array $config) { @@ -105,7 +105,7 @@ public function __construct(array $info, array $config) * @param string $ticket * @param string $service * - * @return array username and attributes + * @return array username and attributes */ private function casValidate(string $ticket, string $service): array { @@ -114,9 +114,11 @@ private function casValidate(string $ticket, string $service): array 'ticket' => $ticket, 'service' => $service, ]); - $result = $httpUtils->fetch($url); /** @var string $result */ + $result = $httpUtils->fetch($url); + + /** @var string $res */ $res = preg_split("/\r?\n/", $result); if (strcmp($res[0], "yes") == 0) { @@ -133,7 +135,7 @@ private function casValidate(string $ticket, string $service): array * @param string $ticket * @param string $service * - * @return array username and attributes + * @return array username and attributes */ private function casServiceValidate(string $ticket, string $service): array { @@ -151,8 +153,9 @@ private function casServiceValidate(string $ticket, string $service): array $dom = DOMDocumentFactory::fromString($result); $xPath = new DOMXpath($dom); $xPath->registerNamespace("cas", 'http://www.yale.edu/tp/cas'); + $success = $xPath->query("/cas:serviceResponse/cas:authenticationSuccess/cas:user"); - if ($success->length == 0) { + if ($success === false || $success->length === 0) { $failure = $xPath->evaluate("/cas:serviceResponse/cas:authenticationFailure"); throw new Exception("Error when validating CAS service ticket: " . $failure->item(0)->textContent); } else { @@ -160,6 +163,7 @@ private function casServiceValidate(string $ticket, string $service): array if ($casattributes = $this->casConfig['attributes']) { // Some has attributes in the xml - attributes is a list of XPath expressions to get them foreach ($casattributes as $name => $query) { + /** @var \DOMNodeList<\DOMNode> $attrs */ $attrs = $xPath->query($query); foreach ($attrs as $attrvalue) { $attributes[$name][] = $attrvalue->textContent; @@ -184,7 +188,7 @@ private function casServiceValidate(string $ticket, string $service): array * * @param string $ticket * @param string $service - * @return array username and attributes + * @return array username and attributes */ protected function casValidation(string $ticket, string $service): array { @@ -201,7 +205,7 @@ protected function casValidation(string $ticket, string $service): array /** * Called by linkback, to finish validate/ finish logging in. - * @param array $state + * @param array $state */ public function finalStep(array &$state): void { @@ -237,7 +241,7 @@ public function finalStep(array &$state): void /** * Log-in using cas * - * @param array &$state Information about the current authentication. + * @param array &$state Information about the current authentication. */ public function authenticate(array &$state): void { @@ -264,7 +268,7 @@ public function authenticate(array &$state): void * should be called with the state. If this operation can be completed without * showing the user a page, or redirecting, this function should return. * - * @param array &$state Information about the current logout operation. + * @param array &$state Information about the current logout operation. */ public function logout(array &$state): void { diff --git a/src/Controller/CAS.php b/src/Controller/CAS.php index b949fe3..d3bac27 100644 --- a/src/Controller/CAS.php +++ b/src/Controller/CAS.php @@ -84,14 +84,14 @@ public function linkback(Request $request): RunnableResponse throw new Error\BadRequest('Missing StateId parameter.'); } - $stateId = $request->query->get('stateId'); + $stateId = $request->query->getString('stateId'); $state = $this->authState::loadState($stateId, CASSource::STAGE_INIT); if (!$request->query->has('ticket')) { throw new Error\BadRequest('Missing ticket parameter.'); } - $ticket = $request->query->get('ticket'); + $ticket = $request->query->getString('ticket'); $state['cas:ticket'] = $ticket; // Find authentication source diff --git a/tests/src/Controller/CASTest.php b/tests/src/Controller/CASTest.php index f3856e8..65b4430 100644 --- a/tests/src/Controller/CASTest.php +++ b/tests/src/Controller/CASTest.php @@ -19,7 +19,7 @@ * * @package SimpleSAML\Test */ -class CASTest extends TestCase +final class CASTest extends TestCase { /** @var \SimpleSAML\Configuration */ protected Configuration $config; @@ -112,6 +112,7 @@ public function testNoTicket(): void $c = new Controller\CAS($this->config); $c->setAuthState(new class () extends Auth\State { + /** @return array|null */ public static function loadState(string $id, string $stage, bool $allowMissing = false): ?array { return []; @@ -141,6 +142,7 @@ public function testUnknownAuthSource(): void $c = new Controller\CAS($this->config); $c->setAuthState(new class () extends Auth\State { + /** @return array|null */ public static function loadState(string $id, string $stage, bool $allowMissing = false): ?array { return [CAS::AUTHID => 'somethingElse']; @@ -169,6 +171,7 @@ public function testNormalOperation(): void $c = new Controller\CAS($this->config); $c->setAuthState(new class () extends Auth\State { + /** @return array|null */ public static function loadState(string $id, string $stage, bool $allowMissing = false): ?array { return [CAS::AUTHID => 'something']; @@ -180,7 +183,10 @@ public function __construct() //dummy } - public function authenticate(Request $request, array &$state): Response + /** + * @param array $state + */ + public function authenticate(array &$state): void { //dummy } @@ -192,6 +198,8 @@ public function __construct() { //dummy } + + /** @param array $state */ public function finalStep(array &$state): void { //dummy