diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 114ad545..97265baa 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['8.1', '8.2', '8.3', '8.4'] + php-version: ['8.2', '8.3', '8.4', '8.5'] uses: simplesamlphp/simplesamlphp-test-framework/.github/workflows/reusable_phplinter.yml@v1.10.6 with: @@ -45,7 +45,7 @@ jobs: fail-fast: false matrix: operating-system: [ubuntu-latest] - php-versions: ['8.1', '8.2', '8.3', '8.4'] + php-versions: ['8.2', '8.3', '8.4', '8.5'] steps: - name: Setup PHP, with composer and extensions @@ -85,15 +85,15 @@ jobs: run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Run unit tests with coverage - if: ${{ matrix.php-versions == '8.4' }} + if: ${{ matrix.php-versions == '8.5' }} run: vendor/bin/phpunit - name: Run unit tests (no coverage) - if: ${{ matrix.php-versions != '8.4' }} + if: ${{ matrix.php-versions != '8.5' }} run: vendor/bin/phpunit --no-coverage - name: Save coverage data - if: ${{ matrix.php-versions == '8.4' }} + if: ${{ matrix.php-versions == '8.5' }} uses: actions/upload-artifact@v6 with: name: coverage-data @@ -108,7 +108,7 @@ jobs: fail-fast: true matrix: operating-system: [windows-latest] - php-versions: ['8.1', '8.2', '8.3', '8.4'] + php-versions: ['8.2', '8.3', '8.4', '8.5'] steps: - name: Setup PHP, with composer and extensions @@ -161,8 +161,8 @@ jobs: uses: shivammathur/setup-php@v2 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 + php-version: '8.5' + tools: composer, composer-require-checker, composer-unused extensions: ctype, date, dom, fileinfo, filter, hash, intl, mbstring, openssl, pcre, spl, xml - name: Setup problem matchers for PHP @@ -193,7 +193,7 @@ jobs: run: composer-unused - name: PHP Code Sniffer - run: phpcs + run: vendor/bin/phpcs - name: PHPStan run: | @@ -214,7 +214,7 @@ jobs: uses: shivammathur/setup-php@v2 with: # Should be the lowest supported version - php-version: '8.1' + php-version: '8.2' extensions: ctype, date, dom, fileinfo, filter, hash, intl, mbstring, openssl, pcre, spl, xml tools: composer coverage: none diff --git a/composer.json b/composer.json index 92b27f9e..44764491 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ } }, "require": { - "php": "^8.1", + "php": "^8.2", "ext-ctype": "*", "ext-dom": "*", "ext-filter": "*", @@ -37,18 +37,19 @@ "ext-SimpleXML": "*", "ext-session": "*", - "simplesamlphp/assert": "~1.8", - "simplesamlphp/composer-module-installer": "~1.4", - "simplesamlphp/simplesamlphp": "~2.4", - "simplesamlphp/xml-cas": "~1.3", - "simplesamlphp/xml-common": "~1.17", - "simplesamlphp/xml-soap": "~1.5", - "symfony/http-foundation": "~6.4", - "symfony/http-kernel": "~6.4", - "simplesamlphp/saml11": "~1.3" + "beste/clock": "~3.0", + "simplesamlphp/assert": "~1.9", + "simplesamlphp/composer-module-installer": "~1.6", + "simplesamlphp/simplesamlphp": "~2.5@dev", + "simplesamlphp/xml-cas": "~2.0", + "simplesamlphp/xml-common": "~2.0", + "simplesamlphp/xml-soap": "~2.0", + "symfony/http-foundation": "~7.4", + "symfony/http-kernel": "~7.4", + "simplesamlphp/saml11": "~2.0" }, "require-dev": { - "simplesamlphp/simplesamlphp-test-framework": "~1.9" + "simplesamlphp/simplesamlphp-test-framework": "~1.10" }, "support": { "issues": "https://github.com/simplesamlphp/simplesamlphp-module-casserver/issues", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 63d26452..a9070719 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,371 +1,397 @@ parameters: ignoreErrors: - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\AttributeExtractor\\:\\:extractUserAndAttributes\\(\\) has parameter \\$state with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\AttributeExtractor\:\:extractUserAndAttributes\(\) has parameter \$state with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/AttributeExtractor.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\AttributeExtractor\\:\\:extractUserAndAttributes\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\AttributeExtractor\:\:extractUserAndAttributes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/AttributeExtractor.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\AttributeExtractor\\:\\:loadState\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\AttributeExtractor\:\:loadState\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/AttributeExtractor.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\AttributeExtractor\\:\\:manageState\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\AttributeExtractor\:\:manageState\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/AttributeExtractor.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\AttributeExtractor\\:\\:runAuthProcs\\(\\) has parameter \\$state with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\AttributeExtractor\:\:runAuthProcs\(\) has parameter \$state with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/AttributeExtractor.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\AttributeExtractor\\:\\:\\$authSourceId is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Cas\\AttributeExtractor\:\:\$authSourceId is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: src/Cas/AttributeExtractor.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\ProcessingChainFactory\\:\\:build\\(\\) has parameter \\$state with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\ProcessingChainFactory\:\:build\(\) has parameter \$state with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/ProcessingChainFactory.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\ProcessingChainFactory\\:\\:\\$casconfig is never read, only written\\.$#" - count: 1 - path: src/Cas/Factories/ProcessingChainFactory.php - - - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createProxyGrantingTicket\\(\\) has parameter \\$content with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createProxyGrantingTicket\(\) has parameter \$content with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createProxyGrantingTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createProxyGrantingTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createProxyTicket\\(\\) has parameter \\$content with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createProxyTicket\(\) has parameter \$content with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createProxyTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createProxyTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createServiceTicket\\(\\) has parameter \\$content with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createServiceTicket\(\) has parameter \$content with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createServiceTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createServiceTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:createSessionTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:createSessionTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:isExpired\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:isExpired\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:isProxyGrantingTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:isProxyGrantingTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:isProxyTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:isProxyTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:isServiceTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:isServiceTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:isSessionTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Factories\\TicketFactory\:\:isSessionTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Factories/TicketFactory.php - - message: "#^Constructor of class SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Protocol\\\\Cas10 has an unused parameter \\$config\\.$#" + message: '#^Constructor of class SimpleSAML\\Module\\casserver\\Cas\\Protocol\\Cas10 has an unused parameter \$config\.$#' + identifier: constructor.unusedParameter count: 1 path: src/Cas/Protocol/Cas10.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Protocol\\\\Cas20\\:\\:getAttributes\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Protocol\\Cas20\:\:getAttributes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Protocol/Cas20.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Protocol\\\\Cas20\\:\\:setAttributes\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Protocol\\Cas20\:\:setAttributes\(\) has parameter \$attributes with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Protocol/Cas20.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Protocol\\\\Cas20\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Cas\\Protocol\\Cas20\:\:\$attributes type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Protocol/Cas20.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Protocol\\\\SamlValidateResponder\\:\\:convertToSaml\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Protocol\\SamlValidateResponder\:\:convertToSaml\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Protocol/SamlValidateResponder.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\DelegatingTicketStore\\:\\:addTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\DelegatingTicketStore\:\:addTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/DelegatingTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\DelegatingTicketStore\\:\\:getTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\DelegatingTicketStore\:\:getTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/DelegatingTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\FileSystemTicketStore\\:\\:addTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\FileSystemTicketStore\:\:addTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/FileSystemTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\FileSystemTicketStore\\:\\:getTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\FileSystemTicketStore\:\:getTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/FileSystemTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\MemCacheTicketStore\\:\\:addTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\MemCacheTicketStore\:\:addTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/MemCacheTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\MemCacheTicketStore\\:\\:getTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\MemCacheTicketStore\:\:getTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/MemCacheTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\RedisTicketStore\\:\\:addTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\RedisTicketStore\:\:addTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/RedisTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\RedisTicketStore\\:\\:getTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\RedisTicketStore\:\:getTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/RedisTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:addTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:addTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:get\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:get\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:getTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:getTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:insertOrUpdate\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:insertOrUpdate\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:insertOrUpdate\\(\\) has parameter \\$keys with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:insertOrUpdate\(\) has parameter \$keys with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:insertOrUpdateFallback\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:insertOrUpdateFallback\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:insertOrUpdateFallback\\(\\) has parameter \\$keys with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:insertOrUpdateFallback\(\) has parameter \$keys with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:set\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:set\(\) has parameter \$value with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\SQLTicketStore\\:\\:\\$tableVersions type has no value type specified in iterable type array\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Cas\\Ticket\\SQLTicketStore\:\:\$tableVersions type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/SQLTicketStore.php - - message: "#^Constructor of class SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\TicketStore has an unused parameter \\$config\\.$#" + message: '#^Constructor of class SimpleSAML\\Module\\casserver\\Cas\\Ticket\\TicketStore has an unused parameter \$config\.$#' + identifier: constructor.unusedParameter count: 1 path: src/Cas/Ticket/TicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\TicketStore\\:\\:addTicket\\(\\) has parameter \\$ticket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\TicketStore\:\:addTicket\(\) has parameter \$ticket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/TicketStore.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\TicketStore\\:\\:getTicket\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\Ticket\\TicketStore\:\:getTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/Ticket/TicketStore.php - - message: "#^Access to an undefined property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Factories\\\\TicketFactory\\:\\:\\$ticketFactory\\.$#" - count: 1 - path: src/Cas/TicketValidator.php - - - - message: "#^Access to an undefined property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\Ticket\\\\TicketStore\\:\\:\\$ticketStore\\.$#" - count: 1 - path: src/Cas/TicketValidator.php - - - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\TicketValidator\\:\\:validateAndDeleteTicket\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Cas/TicketValidator.php - - - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\TicketValidator\\:\\:\\$casconfig is never read, only written\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Cas\\TicketValidator\:\:validateAndDeleteTicket\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cas/TicketValidator.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\TicketValidator\\:\\:\\$ticketFactory is never written, only read\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Cas\\TicketValidator\:\:\$casconfig is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: src/Cas/TicketValidator.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\TicketValidator\\:\\:\\$ticketStore is never written, only read\\.$#" - count: 1 - path: src/Cas/TicketValidator.php - - - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\Cas10Controller\\:\\:\\$sspConfig is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Controller\\Cas10Controller\:\:\$sspConfig is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: src/Controller/Cas10Controller.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\Cas20Controller\\:\\:\\$sspConfig is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Controller\\Cas20Controller\:\:\$sspConfig is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: src/Controller/Cas20Controller.php - - message: "#^Call to an undefined method SimpleSAML\\\\XML\\\\SerializableElementInterface\\:\\:getXML\\(\\)\\.$#" + message: '#^Call to an undefined method SimpleSAML\\XML\\SerializableElementInterface\:\:getXML\(\)\.$#' + identifier: method.notFound count: 1 path: src/Controller/Cas30Controller.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\Cas30Controller\\:\\:\\$sspConfig is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Controller\\Cas30Controller\:\:\$sspConfig is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: src/Controller/Cas30Controller.php - - message: "#^Cannot cast array to string\\.$#" + message: '#^Cannot cast array to string\.$#' + identifier: cast.string count: 2 path: src/Controller/LoginController.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:getReturnUrl\\(\\) has parameter \\$sessionTicket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:getReturnUrl\(\) has parameter \$sessionTicket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Controller/LoginController.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:getState\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:getState\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Controller/LoginController.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:handleDebugMode\\(\\) has parameter \\$serviceTicket with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:handleDebugMode\(\) has parameter \$serviceTicket with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Controller/LoginController.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:handleDebugMode\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:handleDebugMode\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Controller/LoginController.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:handleDebugMode\\(\\) should return array\\ but returns array\\\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:handleDebugMode\(\) should return array\ but returns array\\.$#' + identifier: return.type count: 2 path: src/Controller/LoginController.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:handleDebugMode\\(\\) should return array\\ but returns array\\\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:handleDebugMode\(\) should return array\ but returns array\\.$#' + identifier: return.type count: 1 path: src/Controller/LoginController.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LogoutController\\:\\:\\$sspConfig is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Controller\\LogoutController\:\:\$sspConfig is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: src/Controller/LogoutController.php - - message: "#^Access to an undefined property DOMNode\\:\\:\\$value\\.$#" + message: '#^Call to an undefined method SimpleSAML\\XML\\Validator\:\:validateCA\(\)\.$#' + identifier: method.notFound count: 1 path: src/Shib13/AuthnResponse.php - - message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#" - count: 4 - path: src/Shib13/AuthnResponse.php - - - - message: "#^Call to an undefined method SimpleSAML\\\\XML\\\\Validator\\:\\:validateCA\\(\\)\\.$#" - count: 1 - path: src/Shib13/AuthnResponse.php - - - - message: "#^Call to an undefined method SimpleSAML\\\\XML\\\\Validator\\:\\:validateFingerprint\\(\\)\\.$#" + message: '#^Call to an undefined method SimpleSAML\\XML\\Validator\:\:validateFingerprint\(\)\.$#' + identifier: method.notFound count: 2 path: src/Shib13/AuthnResponse.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:doXPathQuery\\(\\) return type with generic class DOMNodeList does not specify its types\\: TNode$#" - count: 1 - path: src/Shib13/AuthnResponse.php - - - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:encAttribute\\(\\) has parameter \\$scopedAttributes with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Shib13\\AuthnResponse\:\:doXPathQuery\(\) return type with generic class DOMNodeList does not specify its types\: TNode$#' + identifier: missingType.generics count: 1 path: src/Shib13/AuthnResponse.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:encAttribute\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Shib13\\AuthnResponse\:\:encAttribute\(\) has parameter \$scopedAttributes with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Shib13/AuthnResponse.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:generate\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Shib13\\AuthnResponse\:\:encAttribute\(\) has parameter \$values with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Shib13/AuthnResponse.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:getAttributes\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Shib13\\AuthnResponse\:\:generate\(\) has parameter \$attributes with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Shib13/AuthnResponse.php - - message: "#^Method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:getNameID\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Shib13\\AuthnResponse\:\:getAttributes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Shib13/AuthnResponse.php - - message: "#^Parameter \\#1 \\$node of method SimpleSAML\\\\Module\\\\casserver\\\\Shib13\\\\AuthnResponse\\:\\:isNodeValidated\\(\\) expects DOMElement\\|SimpleXMLElement, DOMNode given\\.$#" + message: '#^Method SimpleSAML\\Module\\casserver\\Shib13\\AuthnResponse\:\:getNameID\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Shib13/AuthnResponse.php diff --git a/phpstan-dev-baseline.neon b/phpstan-dev-baseline.neon index 7c7f4f01..0d60c92e 100644 --- a/phpstan-dev-baseline.neon +++ b/phpstan-dev-baseline.neon @@ -1,36 +1,43 @@ parameters: ignoreErrors: - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Tests\\\\Controller\\\\Cas10ControllerTest\\:\\:\\$sessionMock is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Tests\\Controller\\Cas10ControllerTest\:\:\$sessionMock is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: tests/src/Controller/Cas10ControllerTest.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Tests\\\\Controller\\\\Cas20ControllerTest\\:\\:\\$sessionMock is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Tests\\Controller\\Cas20ControllerTest\:\:\$sessionMock is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: tests/src/Controller/Cas20ControllerTest.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Tests\\\\Controller\\\\Cas20ControllerTest\\:\\:\\$ticketValidatorMock is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Tests\\Controller\\Cas20ControllerTest\:\:\$ticketValidatorMock is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: tests/src/Controller/Cas20ControllerTest.php - - message: "#^Call to an undefined method SimpleSAML\\\\Module\\\\casserver\\\\Cas\\\\TicketValidator\\:\\:expects\\(\\)\\.$#" + message: '#^Call to an undefined method SimpleSAML\\Module\\casserver\\Cas\\TicketValidator\:\:expects\(\)\.$#' + identifier: method.notFound count: 2 path: tests/src/Controller/Cas30ControllerTest.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Tests\\\\Controller\\\\Cas30ControllerTest\\:\\:\\$sessionMock is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Tests\\Controller\\Cas30ControllerTest\:\:\$sessionMock is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: tests/src/Controller/Cas30ControllerTest.php - - message: "#^Parameter \\#2 \\$renew of method SimpleSAML\\\\Module\\\\casserver\\\\Controller\\\\LoginController\\:\\:login\\(\\) expects bool, string given\\.$#" + message: '#^Parameter \#2 \$renew of method SimpleSAML\\Module\\casserver\\Controller\\LoginController\:\:login\(\) expects bool, string given\.$#' + identifier: argument.type count: 1 path: tests/src/Controller/LoginControllerTest.php - - message: "#^Property SimpleSAML\\\\Module\\\\casserver\\\\Tests\\\\Controller\\\\LoginControllerTest\\:\\:\\$sspContainer is never read, only written\\.$#" + message: '#^Property SimpleSAML\\Module\\casserver\\Tests\\Controller\\LoginControllerTest\:\:\$sspContainer is never read, only written\.$#' + identifier: property.onlyWritten count: 1 path: tests/src/Controller/LoginControllerTest.php diff --git a/src/Cas/AttributeExtractor.php b/src/Cas/AttributeExtractor.php index 4893c901..c8e3c1d9 100644 --- a/src/Cas/AttributeExtractor.php +++ b/src/Cas/AttributeExtractor.php @@ -16,13 +16,7 @@ */ class AttributeExtractor { - /** @var Configuration */ - private Configuration $casconfig; - - /** @var ProcessingChainFactory */ - private ProcessingChainFactory $processingChainFactory; - - /** @var State */ + /** @var \SimpleSAML\Auth\State */ private State $authState; /** @@ -30,15 +24,15 @@ class AttributeExtractor */ private ?string $authSourceId = null; + public function __construct( - Configuration $casconfig, - ProcessingChainFactory $processingChainFactory, + protected Configuration $casconfig, + protected ProcessingChainFactory $processingChainFactory, ) { - $this->casconfig = $casconfig; - $this->processingChainFactory = $processingChainFactory; $this->authState = new State(); } + /** * Determine the user and any CAS attributes based on the attributes from the * authsource and the CAS configuration. @@ -97,6 +91,7 @@ public function extractUserAndAttributes(?array $state): array ]; } + /** * Run authproc filters with the processing chain * Creating the ProcessingChain require metadata. @@ -129,6 +124,7 @@ protected function runAuthProcs(array &$state): void $this->processingChainFactory->build($state)->processState($state); } + /** * This is a wrapper around Auth/State::loadState that facilitates testing by * hiding the static method @@ -136,7 +132,7 @@ protected function runAuthProcs(array &$state): void * @param string $stateId * * @return array|null - * @throws NoState + * @throws \SimpleSAML\Error\NoState */ public function manageState(string $stateId): ?array { @@ -154,6 +150,7 @@ public function manageState(string $stateId): ?array return $state; } + /** * @param string $id * @param string $stage diff --git a/src/Cas/CasException.php b/src/Cas/CasException.php index f479216e..0957b80a 100644 --- a/src/Cas/CasException.php +++ b/src/Cas/CasException.php @@ -12,20 +12,20 @@ */ class CasException extends Exception { - /** @var string */ - private string $casCode; - /** * CasException constructor. + * * @param string $casCode * @param string $message */ - public function __construct(string $casCode, string $message) - { + public function __construct( + protected string $casCode, + string $message, + ) { parent::__construct($message); - $this->casCode = $casCode; } + /** * @return string */ diff --git a/src/Cas/Factories/ProcessingChainFactory.php b/src/Cas/Factories/ProcessingChainFactory.php index beaeb85c..61acf7e0 100644 --- a/src/Cas/Factories/ProcessingChainFactory.php +++ b/src/Cas/Factories/ProcessingChainFactory.php @@ -16,15 +16,12 @@ class ProcessingChainFactory { - /** @var Configuration */ - private Configuration $casconfig; - public function __construct( - Configuration $casconfig, + protected Configuration $casconfig, ) { - $this->casconfig = $casconfig; } + /** * @codeCoverageIgnore * @throws \Exception diff --git a/src/Cas/Protocol/Cas20.php b/src/Cas/Protocol/Cas20.php index 23077392..4e7807f0 100644 --- a/src/Cas/Protocol/Cas20.php +++ b/src/Cas/Protocol/Cas20.php @@ -25,27 +25,33 @@ namespace SimpleSAML\Module\casserver\Cas\Protocol; -use DateTimeImmutable; -use SimpleSAML\CAS\XML\cas\Attributes; -use SimpleSAML\CAS\XML\cas\AuthenticationDate; -use SimpleSAML\CAS\XML\cas\AuthenticationFailure; -use SimpleSAML\CAS\XML\cas\AuthenticationSuccess; -use SimpleSAML\CAS\XML\cas\IsFromNewLogin; -use SimpleSAML\CAS\XML\cas\LongTermAuthenticationRequestTokenUsed; -use SimpleSAML\CAS\XML\cas\ProxyFailure; -use SimpleSAML\CAS\XML\cas\ProxyGrantingTicket; -use SimpleSAML\CAS\XML\cas\ProxySuccess; -use SimpleSAML\CAS\XML\cas\ProxyTicket; -use SimpleSAML\CAS\XML\cas\ServiceResponse; -use SimpleSAML\CAS\XML\cas\User; +use Beste\Clock\LocalizedClock; +use DateTimeZone; +use SimpleSAML\Assert\AssertionFailedException; +use SimpleSAML\CAS\Type\CodeValue; +use SimpleSAML\CAS\XML\Attributes; +use SimpleSAML\CAS\XML\AuthenticationDate; +use SimpleSAML\CAS\XML\AuthenticationFailure; +use SimpleSAML\CAS\XML\AuthenticationSuccess; +use SimpleSAML\CAS\XML\IsFromNewLogin; +use SimpleSAML\CAS\XML\LongTermAuthenticationRequestTokenUsed; +use SimpleSAML\CAS\XML\ProxyFailure; +use SimpleSAML\CAS\XML\ProxyGrantingTicket; +use SimpleSAML\CAS\XML\ProxySuccess; +use SimpleSAML\CAS\XML\ProxyTicket; +use SimpleSAML\CAS\XML\ServiceResponse; +use SimpleSAML\CAS\XML\User; use SimpleSAML\Configuration; use SimpleSAML\Logger; +use SimpleSAML\XML\Assert\Assert; use SimpleSAML\XML\Chunk; use SimpleSAML\XML\DOMDocumentFactory; +use SimpleSAML\XMLSchema\Type\BooleanValue; +use SimpleSAML\XMLSchema\Type\DateTimeValue; +use SimpleSAML\XMLSchema\Type\StringValue; use function base64_encode; use function count; -use function filter_var; use function is_null; use function is_string; use function str_replace; @@ -117,15 +123,15 @@ public function getProxyGrantingTicketIOU(): ?string /** * @param string $username - * @return \SimpleSAML\CAS\XML\cas\ServiceResponse + * @return \SimpleSAML\CAS\XML\ServiceResponse */ public function getValidateSuccessResponse(string $username): ServiceResponse { - $user = new User($username); + $user = new User(StringValue::fromString($username)); $proxyGrantingTicket = null; if (is_string($this->proxyGrantingTicketIOU)) { - $proxyGrantingTicket = new ProxyGrantingTicket($this->proxyGrantingTicketIOU); + $proxyGrantingTicket = new ProxyGrantingTicket(StringValue::fromString($this->proxyGrantingTicketIOU)); } $attr = []; @@ -133,11 +139,12 @@ public function getValidateSuccessResponse(string $username): ServiceResponse foreach ($this->attributes as $name => $values) { // Fix the most common cause of invalid XML elements $_name = str_replace(':', '_', $name); - if ($this->isValidXmlName($_name) === true) { + try { + Assert::validNCName($_name); foreach ($values as $value) { $attr[] = $this->generateCas20Attribute($_name, $value); } - } else { + } catch (AssertionFailedException) { Logger::warning("DOMException creating attribute '$_name'. Continuing without attribute'"); } } @@ -150,10 +157,11 @@ public function getValidateSuccessResponse(string $username): ServiceResponse } } + $systemClock = LocalizedClock::in(new DateTimeZone('Z')); $attributes = new Attributes( - new AuthenticationDate(new DateTimeImmutable('now')), - new LongTermAuthenticationRequestTokenUsed('true'), - new IsFromNewLogin('true'), + new AuthenticationDate(DateTimeValue::now($systemClock)), + new LongTermAuthenticationRequestTokenUsed(BooleanValue::fromBoolean(true)), + new IsFromNewLogin(BooleanValue::fromBoolean(true)), $attr, ); @@ -167,11 +175,14 @@ public function getValidateSuccessResponse(string $username): ServiceResponse /** * @param string $errorCode * @param string $explanation - * @return \SimpleSAML\CAS\XML\cas\ServiceResponse + * @return \SimpleSAML\CAS\XML\ServiceResponse */ public function getValidateFailureResponse(string $errorCode, string $explanation): ServiceResponse { - $authenticationFailure = new AuthenticationFailure($explanation, $errorCode); + $authenticationFailure = new AuthenticationFailure( + StringValue::fromString($explanation), + CodeValue::fromString($errorCode), + ); $serviceResponse = new ServiceResponse($authenticationFailure); return $serviceResponse; @@ -180,11 +191,11 @@ public function getValidateFailureResponse(string $errorCode, string $explanatio /** * @param string $proxyTicketId - * @return \SimpleSAML\CAS\XML\cas\ServiceResponse + * @return \SimpleSAML\CAS\XML\ServiceResponse */ public function getProxySuccessResponse(string $proxyTicketId): ServiceResponse { - $proxyTicket = new ProxyTicket($proxyTicketId); + $proxyTicket = new ProxyTicket(StringValue::fromString($proxyTicketId)); $proxySuccess = new ProxySuccess($proxyTicket); $serviceResponse = new ServiceResponse($proxySuccess); @@ -195,11 +206,14 @@ public function getProxySuccessResponse(string $proxyTicketId): ServiceResponse /** * @param string $errorCode * @param string $explanation - * @return \SimpleSAML\CAS\XML\cas\ServiceResponse + * @return \SimpleSAML\CAS\XML\ServiceResponse */ public function getProxyFailureResponse(string $errorCode, string $explanation): ServiceResponse { - $proxyFailure = new ProxyFailure($explanation, $errorCode); + $proxyFailure = new ProxyFailure( + StringValue::fromString($explanation), + CodeValue::fromString($errorCode), + ); $serviceResponse = new ServiceResponse($proxyFailure); return $serviceResponse; @@ -222,26 +236,4 @@ private function generateCas20Attribute( return new Chunk($attributeElement); } - - - /** - * XML element names have a lot of rules and not every SAML attribute name can be converted. - * Ref: https://www.w3.org/TR/REC-xml/#NT-NameChar - * https://stackoverflow.com/q/2519845/54396 - * must only start with letter or underscore - * cannot start with 'xml' (or maybe it can - stackoverflow commenters don't agree) - * cannot contain a ':' since those are for namespaces - * cannot contains space - * can only contain letters, digits, hyphens, underscores, and periods - * @param string $name The attribute name to be used as an element - * @return bool true if $name would make a valid xml element. - */ - private function isValidXmlName(string $name): bool - { - return filter_var( - $name, - FILTER_VALIDATE_REGEXP, - ['options' => ['regexp' => '/^[a-zA-Z_][\w.-]*$/']], - ) !== false; - } } diff --git a/src/Cas/Protocol/SamlValidateResponder.php b/src/Cas/Protocol/SamlValidateResponder.php index 0ce7115e..85e16d8f 100644 --- a/src/Cas/Protocol/SamlValidateResponder.php +++ b/src/Cas/Protocol/SamlValidateResponder.php @@ -6,8 +6,9 @@ use SimpleSAML\Configuration; use SimpleSAML\Module\casserver\Shib13\AuthnResponse; -use SimpleSAML\SOAP\XML\env_200106\Body; -use SimpleSAML\SOAP\XML\env_200106\Envelope; +use SimpleSAML\SAML11\Constants as C; +use SimpleSAML\SOAP11\XML\Body; +use SimpleSAML\SOAP11\XML\Envelope; use SimpleSAML\XML\Chunk; use SimpleSAML\XML\DOMDocumentFactory; use SimpleSAML\XML\SerializableElementInterface; @@ -48,8 +49,8 @@ public function convertToSaml(array $ticket): Chunk '' . htmlspecialchars($user) . '', $authnResponseXML, ); - // CAS seems to prefer this type of assertiond - $ret = str_replace('urn:oasis:names:tc:SAML:1.0:cm:bearer', 'urn:oasis:names:tc:SAML:1.0:cm:artifact', $ret); + // CAS seems to prefer this type of assertion + $ret = str_replace(C::CM_BEARER, C::CM_ARTIFACT, $ret); // CAS uses a different namespace for attributes $ret = str_replace( 'urn:mace:shibboleth:1.0:attributeNamespace:uri', @@ -64,7 +65,7 @@ public function convertToSaml(array $ticket): Chunk /** * @param \SimpleSAML\XML\SerializableElementInterface $samlResponse - * @return \SimpleSAML\SOAP\XML\env_200106\Envelope + * @return \SimpleSAML\SOAP11\XML\Envelope */ public function wrapInSoap(SerializableElementInterface $samlResponse): Envelope { diff --git a/src/Cas/ServiceValidator.php b/src/Cas/ServiceValidator.php index 8caa4dda..b29af548 100644 --- a/src/Cas/ServiceValidator.php +++ b/src/Cas/ServiceValidator.php @@ -4,6 +4,7 @@ namespace SimpleSAML\Module\casserver\Cas; +use ErrorException; use SimpleSAML\Configuration; use SimpleSAML\Logger; use SimpleSAML\Module\casserver\Codebooks\OverrideConfigPropertiesEnum; @@ -14,26 +15,23 @@ */ class ServiceValidator { - /** - * @var \SimpleSAML\Configuration - */ - private Configuration $mainConfig; - /** * ServiceValidator constructor. - * @param Configuration $mainConfig + * @param \SimpleSAML\Configuration $mainConfig */ - public function __construct(Configuration $mainConfig) - { - $this->mainConfig = $mainConfig; + public function __construct( + protected Configuration $mainConfig, + ) { } + /** * Check that the $service is allowed, and if so return the configuration to use. * * @param string $service The service url. Assume to already be url decoded * - * @return Configuration|null Return the configuration to use for this service, or null if service is not allowed + * @return \SimpleSAML\Configuration|null Return the configuration to use for this service, + * or null if service is not allowed * @throws \ErrorException */ public function checkServiceURL(string $service): ?Configuration @@ -82,6 +80,7 @@ public function checkServiceURL(string $service): ?Configuration return Configuration::loadFromArray($serviceConfig); } + /** * @param string $legalUrl The string or regex to use for comparison * @param string $service The service to compare @@ -96,14 +95,14 @@ protected function validateServiceIsLegal(string $legalUrl, string $service): bo // Since "If the regex pattern passed does not compile to a valid regex, an E_WARNING is emitted. " // we will throw an exception if the warning is emitted and use try-catch to handle it set_error_handler(static function ($severity, $message, $file, $line) { - throw new \ErrorException($message, $severity, $severity, $file, $line); + throw new ErrorException($message, $severity, $severity, $file, $line); }, E_WARNING); try { if (preg_match($legalUrl, $service) === 1) { $isValid = true; } - } catch (\ErrorException $e) { + } catch (ErrorException $e) { // do nothing Logger::warning("Invalid CAS legal service url '$legalUrl'. Error " . preg_last_error_msg()); } finally { diff --git a/src/Cas/Ticket/DelegatingTicketStore.php b/src/Cas/Ticket/DelegatingTicketStore.php index 7d13be7e..b01e5644 100644 --- a/src/Cas/Ticket/DelegatingTicketStore.php +++ b/src/Cas/Ticket/DelegatingTicketStore.php @@ -69,6 +69,7 @@ public function __construct(Configuration $casConfig) } } + /** * Get the ticket, searching one or all of the delegates * @param string $ticketId The ticket to find @@ -103,6 +104,7 @@ public function getTicket(string $ticketId): ?array } } + /** * @param array $ticket Ticket to add * @throws \Exception from any delegate stores ONLY if no delegates worked diff --git a/src/Cas/Ticket/SQLTicketStore.php b/src/Cas/Ticket/SQLTicketStore.php index a6790076..d5749c09 100644 --- a/src/Cas/Ticket/SQLTicketStore.php +++ b/src/Cas/Ticket/SQLTicketStore.php @@ -325,7 +325,7 @@ private function get(string $key): ?array * @param array $value * @param int|null $expire * - * @throws Exception + * @throws \Exception */ private function set(string $key, array $value, ?int $expire = null): void { diff --git a/src/Cas/TicketValidator.php b/src/Cas/TicketValidator.php index 3c4fbdee..4a8298f3 100644 --- a/src/Cas/TicketValidator.php +++ b/src/Cas/TicketValidator.php @@ -38,11 +38,9 @@ public function __construct(Configuration $casconfig) ); $ticketStoreClass = Module::resolveClass($ticketStoreConfig['class'], 'Cas\Ticket'); - /** @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore $this->ticketStore */ $this->ticketStore = new $ticketStoreClass($casconfig); $ticketFactoryClass = Module::resolveClass('casserver:TicketFactory', 'Cas\Factories'); - /** @var \SimpleSAML\Module\casserver\Cas\Factories\TicketFactory $this->ticketStore */ $this->ticketFactory = new $ticketFactoryClass($casconfig); } diff --git a/src/Controller/Cas10Controller.php b/src/Controller/Cas10Controller.php index c1b5f9c2..340238c9 100644 --- a/src/Controller/Cas10Controller.php +++ b/src/Controller/Cas10Controller.php @@ -24,6 +24,7 @@ class Cas10Controller { use UrlTrait; + /** @var \SimpleSAML\Logger */ protected Logger $logger; @@ -39,6 +40,7 @@ class Cas10Controller /** @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore */ protected TicketStore $ticketStore; + /** * @param \SimpleSAML\Configuration $sspConfig * @param \SimpleSAML\Configuration|null $casConfig @@ -68,6 +70,7 @@ public function __construct( $this->ticketStore = $ticketStore ?? new $ticketStoreClass($this->casConfig); } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param bool $renew [OPTIONAL] - if this parameter is set, ticket validation will only succeed if the @@ -76,7 +79,7 @@ public function __construct( * @param string|null $ticket [REQUIRED] - the service ticket issued by /login. * @param string|null $service [REQUIRED] - the identifier of the service for which the ticket was issued * - * @return Response + * @return \Symfony\Component\HttpFoundation\Response */ public function validate( Request $request, @@ -162,6 +165,7 @@ public function validate( ); } + /** * Used by the unit tests * diff --git a/src/Controller/Cas20Controller.php b/src/Controller/Cas20Controller.php index 76de66a3..d387ade3 100644 --- a/src/Controller/Cas20Controller.php +++ b/src/Controller/Cas20Controller.php @@ -26,6 +26,7 @@ class Cas20Controller use UrlTrait; use TicketValidatorTrait; + /** @var \SimpleSAML\Logger */ protected Logger $logger; @@ -44,6 +45,7 @@ class Cas20Controller /** @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore */ protected TicketStore $ticketStore; + /** * @param \SimpleSAML\Configuration $sspConfig * @param \SimpleSAML\Configuration|null $casConfig @@ -78,6 +80,7 @@ public function __construct( $this->httpUtils = $httpUtils ?? new Utils\HTTP(); } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $TARGET Query parameter name for "service" used by older CAS clients' @@ -109,6 +112,7 @@ public function serviceValidate( ); } + /** * /proxy provides proxy tickets to services that have * acquired proxy-granting tickets and will be proxying authentication to back-end services. @@ -199,6 +203,7 @@ public function proxy( ); } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $TARGET Query parameter name for "service" used by older CAS clients' @@ -230,6 +235,7 @@ public function proxyValidate( ); } + /** * Used by the unit tests * diff --git a/src/Controller/Cas30Controller.php b/src/Controller/Cas30Controller.php index 3728719e..20c4a865 100644 --- a/src/Controller/Cas30Controller.php +++ b/src/Controller/Cas30Controller.php @@ -15,7 +15,7 @@ use SimpleSAML\Module\casserver\Http\XmlResponse; use SimpleSAML\SAML11\Exception\ProtocolViolationException; use SimpleSAML\SAML11\XML\samlp\Request as SamlRequest; -use SimpleSAML\SOAP\XML\env_200106\Envelope; +use SimpleSAML\SOAP11\XML\Envelope; use SimpleSAML\XML\DOMDocumentFactory; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -29,6 +29,7 @@ class Cas30Controller { use UrlTrait; + /** @var \SimpleSAML\Logger */ protected Logger $logger; @@ -44,6 +45,7 @@ class Cas30Controller /** @var \SimpleSAML\Module\casserver\Cas\Protocol\SamlValidateResponder */ protected SamlValidateResponder $validateResponder; + /** * @param \SimpleSAML\Configuration $sspConfig * @param \SimpleSAML\Configuration|null $casConfig @@ -77,7 +79,7 @@ public function __construct( * @param string $TARGET URL encoded service identifier of the back-end service. * * @throw SimpleSAML\SAML11\Exception\ProtocolViolationException - * @throw SimpleSAML\XML\Exception\MissingAttributeException + * @throw SimpleSAML\XMLSchema\Exception\MissingAttributeException * @throw \RuntimeException * @return \SimpleSAML\Module\casserver\Http\XmlResponse * @link https://apereo.github.io/cas/7.1.x/protocol/CAS-Protocol-Specification.html#42-samlvalidate-cas-30 @@ -112,7 +114,7 @@ public function samlValidate( // Assertion Artifact Element $assertionArtifactParsed = $samlpRequestParsed->getRequest()[0]; - $ticketId = $assertionArtifactParsed->getContent(); + $ticketId = $assertionArtifactParsed->getContent()->getValue(); Logger::debug('samlvalidate: Checking ticket ' . $ticketId); try { diff --git a/src/Controller/LoggedInController.php b/src/Controller/LoggedInController.php index 0159b171..afab516c 100644 --- a/src/Controller/LoggedInController.php +++ b/src/Controller/LoggedInController.php @@ -18,6 +18,7 @@ class LoggedInController /** @var \SimpleSAML\Configuration */ protected Configuration $config; + /** * Controller constructor. * @@ -32,6 +33,7 @@ public function __construct(?Configuration $config = null) $this->config = $config ?? Configuration::getInstance(); } + /** * Show Log out view. * diff --git a/src/Controller/LoggedOutController.php b/src/Controller/LoggedOutController.php index add43a15..86294343 100644 --- a/src/Controller/LoggedOutController.php +++ b/src/Controller/LoggedOutController.php @@ -16,6 +16,7 @@ class LoggedOutController /** @var \SimpleSAML\Configuration */ protected Configuration $config; + /** * Controller constructor. * @@ -30,6 +31,7 @@ public function __construct(?Configuration $config = null) $this->config = $config ?? Configuration::getInstance(); } + /** * Show Log out view. * diff --git a/src/Controller/LoginController.php b/src/Controller/LoginController.php index a16562ec..314edbad 100644 --- a/src/Controller/LoginController.php +++ b/src/Controller/LoginController.php @@ -38,6 +38,11 @@ class LoginController use UrlTrait; use TicketValidatorTrait; + + /** @var string[] */ + private const DEBUG_MODES = ['true', 'samlValidate']; + + /** @var \SimpleSAML\Logger */ protected Logger $logger; @@ -71,15 +76,13 @@ class LoginController /** @var string[] */ protected array $postAuthUrlParameters = []; - /** @var string[] */ - private const DEBUG_MODES = ['true', 'samlValidate']; - /** @var \SimpleSAML\Module\casserver\Cas\AttributeExtractor */ protected AttributeExtractor $attributeExtractor; /** @var \SimpleSAML\Module\casserver\Cas\Protocol\SamlValidateResponder */ private SamlValidateResponder $samlValidateResponder; + /** * @param \SimpleSAML\Configuration $sspConfig * @param \SimpleSAML\Configuration|null $casConfig @@ -105,6 +108,7 @@ public function __construct( $this->httpUtils = $httpUtils ?? new Utils\HTTP(); } + /** * * @param \Symfony\Component\HttpFoundation\Request $request @@ -266,6 +270,7 @@ public function login( ); } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $debugMode @@ -306,6 +311,7 @@ public function handleDebugMode( ]; } + /** * @return array|null * @throws \SimpleSAML\Error\NoState @@ -320,6 +326,7 @@ public function getState(): ?array $this->authSource->getAuthDataArray(); } + /** * Construct the ticket name * @@ -333,6 +340,7 @@ public function calculateTicketName(?string $service): string return $this->casConfig->getOptionalValue('ticketName', $defaultTicketName); } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param array|null $sessionTicket @@ -347,6 +355,7 @@ public function getReturnUrl(Request $request, ?array $sessionTicket): string return $this->httpUtils->getSelfURLNoQuery() . '?' . http_build_query($query); } + /** * @param string|null $serviceUrl * @@ -371,6 +380,7 @@ public function handleServiceConfiguration(?string $serviceUrl): void $this->casConfig = $serviceCasConfig; } + /** * @param string|null $language * @@ -386,6 +396,7 @@ public function handleLanguage(?string $language): void $this->postAuthUrlParameters['language'] = $language; } + /** * @param string|null $scope * @@ -415,6 +426,7 @@ public function handleScope(?string $scope): void $this->idpList = $scopes[$scope]; } + /** * Get the Session * @@ -426,6 +438,7 @@ public function getSession(): ?Session return Session::getSessionFromRequest(); } + /** * @return \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore */ @@ -434,6 +447,7 @@ public function getTicketStore(): TicketStore return $this->ticketStore; } + /** * @return void * @throws \Exception @@ -462,6 +476,7 @@ private function instantiateClassDependencies(): void $this->attributeExtractor = new AttributeExtractor($this->casConfig, $processingChainFactory); } + /** * Trigger interactive authentication via the AuthSource. * @@ -469,7 +484,7 @@ private function instantiateClassDependencies(): void * @param string $returnToUrl * @param string|null $entityId * - * @return RunnableResponse + * @return \SimpleSAML\HTTP\RunnableResponse */ private function handleInteractiveAuthenticate( bool $forceAuthn, @@ -484,11 +499,12 @@ private function handleInteractiveAuthenticate( ); } + /** * Handle the gateway flow when the user is NOT authenticated. * Passive mode is only attempted if 'enable_passive_mode' is enabled in configuration. * - * Returns: RunnableResponse|null + * Returns: \SimpleSAML\HTTP\RunnableResponse|null * - RunnableResponse for either a passive attempt or a redirect to service without ticket. * - null to indicate: proceed with interactive login (non-passive). */ @@ -517,6 +533,7 @@ private function handleUnauthenticatedGateway( ); } + /** * Handle authentication request by configuring parameters and triggering login via auth source. * @@ -525,7 +542,7 @@ private function handleUnauthenticatedGateway( * @param string $returnToUrl URL to return to after authentication * @param string|null $entityId Optional specific IdP entity ID to use * - * @return RunnableResponse Response containing the login redirect + * @return \SimpleSAML\HTTP\RunnableResponse Response containing the login redirect */ private function handleAuthenticate( bool $forceAuthn, diff --git a/src/Controller/LogoutController.php b/src/Controller/LogoutController.php index 8db01a80..586894d7 100644 --- a/src/Controller/LogoutController.php +++ b/src/Controller/LogoutController.php @@ -24,6 +24,7 @@ class LogoutController { use UrlTrait; + /** @var \SimpleSAML\Logger */ protected Logger $logger; @@ -78,6 +79,7 @@ public function __construct( $this->ticketStore = new $ticketStoreClass($this->casConfig); } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $url @@ -127,6 +129,7 @@ public function logout( ); } + /** * @return \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore */ @@ -135,6 +138,7 @@ public function getTicketStore(): TicketStore return $this->ticketStore; } + /** * @param string $message * @@ -146,6 +150,7 @@ protected function handleExceptionThrown(string $message): void throw new RuntimeException($message); } + /** * Get the Session * diff --git a/src/Controller/Traits/TicketValidatorTrait.php b/src/Controller/Traits/TicketValidatorTrait.php index 23cd9e04..14fdfcd6 100644 --- a/src/Controller/Traits/TicketValidatorTrait.php +++ b/src/Controller/Traits/TicketValidatorTrait.php @@ -172,6 +172,7 @@ public function validate( ); } + /** * @param array{'id': string}|null $serviceTicket * diff --git a/src/Controller/Traits/UrlTrait.php b/src/Controller/Traits/UrlTrait.php index a1487715..eb82243a 100644 --- a/src/Controller/Traits/UrlTrait.php +++ b/src/Controller/Traits/UrlTrait.php @@ -30,6 +30,7 @@ public function checkServiceURL(string $service, array $legal_service_urls): boo return $serviceValidator->checkServiceURL($service) !== null; } + /** * @param string $parameter * @return string @@ -39,6 +40,7 @@ public function sanitize(string $parameter): string return TicketValidator::sanitize($parameter); } + /** * Parse the query Parameters from $_GET global and return them in an array. * @@ -68,6 +70,7 @@ public function parseQueryParameters(Request $request, ?array $sessionTicket): a return $query; } + /** * @param \Symfony\Component\HttpFoundation\Request $request * @param string $paramName diff --git a/src/Shib13/AuthnResponse.php b/src/Shib13/AuthnResponse.php index a6491e92..0fc3199b 100644 --- a/src/Shib13/AuthnResponse.php +++ b/src/Shib13/AuthnResponse.php @@ -5,6 +5,7 @@ namespace SimpleSAML\Module\casserver\Shib13; use DOMDocument; +use DOMElement; use DOMNode; use DOMNodeList; use DOMXPath; @@ -27,6 +28,13 @@ */ class AuthnResponse { + /** @var string */ + public const SHIB_PROTOCOL_NS = 'urn:oasis:names:tc:SAML:1.0:protocol'; + + /** @var string */ + public const SHIB_ASSERT_NS = 'urn:oasis:names:tc:SAML:1.0:assertion'; + + /** * @var \SimpleSAML\XML\Validator|null This variable contains an XML validator for this message. */ @@ -37,12 +45,6 @@ class AuthnResponse */ private bool $messageValidated = false; - /** @var string */ - public const SHIB_PROTOCOL_NS = 'urn:oasis:names:tc:SAML:1.0:protocol'; - - /** @var string */ - public const SHIB_ASSERT_NS = 'urn:oasis:names:tc:SAML:1.0:assertion'; - /** * @var \DOMDocument|null The DOMDocument which represents this message. */ @@ -214,6 +216,8 @@ public function getSessionIndex(): ?string $nodelist = $this->doXPathQuery($query); if ($node = $nodelist->item(0)) { + Assert::isInstanceOf($node, DOMElement::class); + /** @var \DOMElement $node */ return $node->getAttribute('SessionIndex'); } @@ -240,12 +244,14 @@ public function getAttributes(): array $assertions = $this->doXPathQuery('/shibp:Response/shib:Assertion'); foreach ($assertions as $assertion) { + /** @var \DOMElement $assertion */ if (!$this->isNodeValidated($assertion)) { throw new Exception('Shib13 AuthnResponse contained an unsigned assertion.'); } $conditions = $this->doXPathQuery('shib:Conditions', $assertion); if ($conditions->length > 0) { + /** @var \DOMElement $condition */ $condition = $conditions->item(0); $start = $condition->getAttribute('NotBefore'); @@ -311,6 +317,7 @@ public function getIssuer(): string $nodelist = $this->doXPathQuery($query); if ($attr = $nodelist->item(0)) { + /** @var \DOMAttr $attr */ return $attr->value; } else { throw new Exception('Could not find Issuer field in Authentication response'); @@ -329,6 +336,7 @@ public function getNameID(): array $nodelist = $this->doXPathQuery($query); if ($node = $nodelist->item(0)) { + /** @var \DOMElement $node */ $nameID["Value"] = $node->nodeValue; $nameID["Format"] = $node->getAttribute('Format'); } @@ -475,6 +483,7 @@ private function encAttribute(string $name, array $values, bool $base64, array $ return $attr; } + /** * Check if we are currently between the given date & time conditions. * diff --git a/tests/src/Cas/Protocol/SamlValidateTest.php b/tests/src/Cas/Protocol/SamlValidateTest.php index 908d970d..201fc423 100644 --- a/tests/src/Cas/Protocol/SamlValidateTest.php +++ b/tests/src/Cas/Protocol/SamlValidateTest.php @@ -6,7 +6,6 @@ use PHPUnit\Framework\TestCase; use SimpleSAML\Module\casserver\Cas\Protocol\SamlValidateResponder; -use SimpleSAML\SOAP\XML\env_200106\Envelope; class SamlValidateTest extends TestCase { @@ -65,8 +64,7 @@ public function testSamlValidateXmlGeneration(): void $asSoap = $samlValidate->wrapInSoap($xmlString); - $this->assertInstanceOf(Envelope::class, $asSoap); $this->assertNull($asSoap->getHeader()); - $this->assertNotEmpty($asSoap->getBody()); + $this->assertFalse($asSoap->getBody()->isEmptyElement()); } } diff --git a/tests/src/Controller/Cas10ControllerTest.php b/tests/src/Controller/Cas10ControllerTest.php index 742886e3..e8d0416f 100644 --- a/tests/src/Controller/Cas10ControllerTest.php +++ b/tests/src/Controller/Cas10ControllerTest.php @@ -25,6 +25,7 @@ class Cas10ControllerTest extends TestCase private Configuration $sspConfig; + protected function setUp(): void { $this->sspConfig = Configuration::getConfig('config.php'); @@ -62,6 +63,7 @@ protected function setUp(): void ]; } + public static function setUpBeforeClass(): void { // Some of the constructs in this test cause a Configuration to be created prior to us @@ -73,6 +75,7 @@ public static function setUpBeforeClass(): void $_SERVER['REQUEST_URI'] = '/'; } + public static function queryParameterValues(): array { return [ @@ -88,11 +91,12 @@ public static function queryParameterValues(): array ]; } + /** * @param array $params * * @return void - * @throws Exception + * @throws \Exception */ #[DataProvider('queryParameterValues')] public function testReturnBadRequestOnEmptyServiceOrTicket(array $params): void @@ -111,9 +115,10 @@ public function testReturnBadRequestOnEmptyServiceOrTicket(array $params): void $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturn500OnDeleteTicketThatThrows(): void { @@ -142,9 +147,10 @@ public function getTicket(string $ticketId): ?array $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturnBadRequestOnTicketNotExist(): void { @@ -166,9 +172,10 @@ public function testReturnBadRequestOnTicketNotExist(): void $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturnBadRequestOnTicketExpired(): void { @@ -192,9 +199,10 @@ public function testReturnBadRequestOnTicketExpired(): void $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturnBadRequestOnTicketNotService(): void { @@ -220,9 +228,10 @@ public function testReturnBadRequestOnTicketNotService(): void $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturnBadRequestOnTicketMissingUsernameField(): void { @@ -248,9 +257,10 @@ public function testReturnBadRequestOnTicketMissingUsernameField(): void $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturnBadRequestOnTicketServiceQueryAndTicketMismatch(): void { @@ -276,9 +286,10 @@ public function testReturnBadRequestOnTicketServiceQueryAndTicketMismatch(): voi $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testReturnBadRequestOnTicketIssuedBySingleSignOnSession(): void { @@ -304,9 +315,10 @@ public function testReturnBadRequestOnTicketIssuedBySingleSignOnSession(): void $this->assertEquals("no\n\n", $response->getContent()); } + /** * @return void - * @throws Exception + * @throws \Exception */ public function testSuccessfullValidation(): void { diff --git a/tests/src/Controller/Cas20ControllerTest.php b/tests/src/Controller/Cas20ControllerTest.php index 1dcdf763..be810ef0 100644 --- a/tests/src/Controller/Cas20ControllerTest.php +++ b/tests/src/Controller/Cas20ControllerTest.php @@ -38,8 +38,10 @@ class Cas20ControllerTest extends TestCase private Utils\HTTP|MockObject $utilsHttpMock; private array $ticket; + private array $proxyTicket; + /** * @throws \Exception */ @@ -111,6 +113,7 @@ protected function setUp(): void ]; } + public static function validateMethods(): array { return [ @@ -125,6 +128,7 @@ public static function validateMethods(): array ]; } + #[DataProvider('validateMethods')] public function testProxyValidatePassesTheCorrectMethodToValidate(string $prefix, string $method): void { @@ -140,26 +144,18 @@ public function testProxyValidatePassesTheCorrectMethodToValidate(string $prefix parameters: $requestParameters, ); - $expectedArguments = [ - 'request' => $request, - 'method' => $method, - 'renew' => false, - 'target' => null, - 'ticket' => $prefix . $this->sessionId, - 'service' => 'https://myservice.com/abcd', - 'pgtUrl' => null, - ]; - $controllerMock = $this->getMockBuilder(Cas20Controller::class) ->setConstructorArgs([$this->sspConfig, $casconfig]) ->onlyMethods(['validate']) ->getMock(); - $controllerMock->expects($this->once())->method('validate') - ->with(...$expectedArguments); + $controllerMock->expects($this->once()) + ->method('validate') + ->with($request, $method, false, null, $prefix . $this->sessionId, 'https://myservice.com/abcd', null); $controllerMock->$method($request, ...$requestParameters); } + public static function queryParameterValues(): array { return [ @@ -182,6 +178,7 @@ public static function queryParameterValues(): array ]; } + #[DataProvider('queryParameterValues')] public function testProxyRequestFails(array $params, string $message): void { @@ -209,6 +206,7 @@ public function testProxyRequestFails(array $params, string $message): void ); } + public function testProxyRequestFailsWhenPgtNotRecognized(): void { $this->moduleConfig['legal_target_service_urls'] = ['https://myservice.com/abcd']; @@ -238,6 +236,7 @@ public function testProxyRequestFailsWhenPgtNotRecognized(): void ); } + public function testProxyRequestFailsWhenPgtNotValid(): void { $this->moduleConfig['legal_target_service_urls'] = ['https://myservice.com/abcd']; @@ -269,6 +268,7 @@ public function testProxyRequestFailsWhenPgtNotValid(): void ); } + public function testProxyRequestFailsWhenPgtExpired(): void { $this->moduleConfig['legal_target_service_urls'] = ['https://myservice.com/abcd']; @@ -305,6 +305,7 @@ public function testProxyRequestFailsWhenPgtExpired(): void ); } + public function testProxyReturnsProxyTicket(): void { $this->moduleConfig['legal_target_service_urls'] = ['https://myservice.com/abcd']; @@ -359,6 +360,7 @@ public function testProxyReturnsProxyTicket(): void $this->assertTrue(filter_var($ticketFactory->isProxyTicket($proxyTicket), FILTER_VALIDATE_BOOLEAN)); } + public static function validateFailsForEmptyServiceTicket(): array { return [ @@ -377,6 +379,7 @@ public static function validateFailsForEmptyServiceTicket(): array ]; } + #[DataProvider('validateFailsForEmptyServiceTicket')] public function testServiceValidateFailing(array $requestParams, string $message): void { @@ -402,6 +405,7 @@ public function testServiceValidateFailing(array $requestParams, string $message ); } + public function testReturn500OnDeleteTicketThatThrows(): void { $config = Configuration::loadFromArray($this->moduleConfig); @@ -437,6 +441,7 @@ public function getTicket(string $ticketId): ?array ); } + public static function validateOnDifferentQueryParameterCombinations(): array { $sessionId = session_create_id(); @@ -495,6 +500,7 @@ public static function validateOnDifferentQueryParameterCombinations(): array ]; } + #[DataProvider('validateOnDifferentQueryParameterCombinations')] public function testServiceValidate( array $requestParams, @@ -596,6 +602,7 @@ public static function validateOnDifferentQueryParameterCombinationsProxyValidat ]; } + #[DataProvider('validateOnDifferentQueryParameterCombinationsProxyValidate')] public function testProxyValidate( array $requestParams, @@ -645,6 +652,7 @@ public function testProxyValidate( } } + public function testReturnBadRequestOnTicketServiceQueryAndTicketMismatch(): void { $config = Configuration::loadFromArray($this->moduleConfig); @@ -678,6 +686,7 @@ public function testReturnBadRequestOnTicketServiceQueryAndTicketMismatch(): voi ); } + public function testThrowOnProxyServiceIdentityFail(): void { $config = Configuration::loadFromArray($this->moduleConfig); diff --git a/tests/src/Controller/Cas30ControllerTest.php b/tests/src/Controller/Cas30ControllerTest.php index d5d8e8f7..2ebdda88 100644 --- a/tests/src/Controller/Cas30ControllerTest.php +++ b/tests/src/Controller/Cas30ControllerTest.php @@ -33,6 +33,7 @@ class Cas30ControllerTest extends TestCase private array $ticket; + /** * @throws \Exception */ @@ -80,6 +81,7 @@ protected function setUp(): void ]; } + /** * @return void * @throws \Exception @@ -110,6 +112,7 @@ public function testNoSoapBody(): void $cas30Controller->samlValidate($this->samlValidateRequest, $target); } + public static function soapEnvelopes(): array { return [ @@ -128,7 +131,7 @@ public static function soapEnvelopes(): array SOAP, "Missing 'RequestID' attribute on samlp:Request.", - 'SimpleSAML\XML\Exception\MissingAttributeException', + 'SimpleSAML\XMLSchema\Exception\MissingAttributeException', ], 'Body Missing IssueInstant Attribute' => [ << SOAP, "Missing 'IssueInstant' attribute on samlp:Request.", - 'SimpleSAML\XML\Exception\MissingAttributeException', + 'SimpleSAML\XMLSchema\Exception\MissingAttributeException', ], 'Body Missing Ticket Id' => [ << SOAP, - 'Expected a non-whitespace string. Got: ""', + '"" is not a SAML1.1-compliant string', 'SimpleSAML\SAML11\Exception\ProtocolViolationException', ], ]; } + #[DataProvider('soapEnvelopes')] public function testSoapMessageIsInvalid( string $soapMessage, @@ -250,6 +254,7 @@ public function testCasValidateAndDeleteTicketThrowsException(): void $cas30Controller->samlValidate($this->samlValidateRequest, $target); } + /** * @return void * @throws \Exception @@ -302,6 +307,7 @@ public function testUnableToLoadTicket(): void $cas30Controller->samlValidate($this->samlValidateRequest, $target); } + /** * @return void * @throws \Exception diff --git a/tests/src/Controller/LoginControllerTest.php b/tests/src/Controller/LoginControllerTest.php index a9f6a60e..0896d7f4 100644 --- a/tests/src/Controller/LoginControllerTest.php +++ b/tests/src/Controller/LoginControllerTest.php @@ -31,6 +31,7 @@ class LoginControllerTest extends TestCase private Session|MockObject $sessionMock; + protected function setUp(): void { $this->authSimpleMock = $this->getMockBuilder(Simple::class) @@ -68,6 +69,7 @@ protected function setUp(): void $this->sspConfig = Configuration::getConfig('config.php'); } + public static function setUpBeforeClass(): void { // Some of the constructs in this test cause a Configuration to be created prior to us @@ -79,6 +81,7 @@ public static function setUpBeforeClass(): void $_SERVER['REQUEST_URI'] = '/'; } + public static function loginParameters(): array { return [ @@ -98,6 +101,7 @@ public static function loginParameters(): array ]; } + /** * Test incorrect service url * @throws \Exception @@ -123,6 +127,7 @@ public function testLoginFails(array $params, string $message): void $loginController->login($loginRequest, ...$params); } + public static function loginOnAuthenticateParameters(): array { return [ @@ -193,6 +198,7 @@ public static function loginOnAuthenticateParameters(): array ]; } + #[DataProvider('loginOnAuthenticateParameters')] public function testAuthSourceLogin(array $requestParameters, array $loginParameters, array $scopes): void { @@ -226,6 +232,7 @@ public function testAuthSourceLogin(array $requestParameters, array $loginParame $this->assertEquals($loginParameters, $actualLoginParams); } + /** * Check authenticated with debugMode false */ @@ -255,6 +262,7 @@ public function testIsAuthenticatedRedirectsToLoggedIn(): void $this->assertEquals('redirectTrustedURL', $callable[1] ?? ''); } + public static function validServiceUrlProvider(): array { return [ @@ -276,6 +284,7 @@ public static function validServiceUrlProvider(): array ]; } + /** * Test a valid service URL * @@ -328,6 +337,7 @@ public function testValidServiceUrl(string $serviceParam, string $redirectURL, b $this->assertEquals('redirectTrustedURL', $callable[1] ?? ''); } + /** * @return array */ @@ -339,11 +349,11 @@ public static function serviceUrlsProvider(): array ]; } + /** * When passive is disabled and a service is provided, CAS must redirect to the service without appending CAS params - * - * @dataProvider serviceUrlsProvider */ + #[DataProvider('serviceUrlsProvider')] public function testGatewayPassiveDisabledRedirectsWithoutParams(string $serviceUrl): void { // enable_passive_mode disabled @@ -389,6 +399,7 @@ public function testGatewayPassiveDisabledRedirectsWithoutParams(string $service $this->assertSame([], $arguments[1] ?? []); } + public function testGatewayPassiveEnabledPerformsPassiveAttempt(): void { // enable_passive_mode enabled @@ -436,6 +447,7 @@ public function testGatewayPassiveEnabledPerformsPassiveAttempt(): void $this->assertIsString($actualLoginParams['ReturnTo']); } + public function testGatewayNoServicePassiveDisabledFallsBackToInteractive(): void { // enable_passive_mode disabled @@ -474,6 +486,7 @@ public function testGatewayNoServicePassiveDisabledFallsBackToInteractive(): voi $this->assertFalse($loginArgs['isPassive']); } + public function testRenewAndGatewayConflictDisablesPassive(): void { // enable_passive_mode enabled (doesn't matter because renew must disable passive) @@ -516,6 +529,7 @@ public function testRenewAndGatewayConflictDisablesPassive(): void $this->assertTrue($loginArgs['ForceAuthn']); } + public function testAuthenticatedPostSubmitsViaPostWithTicket(): void { $moduleConfig = $this->moduleConfig; diff --git a/tests/src/Controller/LogoutControllerTest.php b/tests/src/Controller/LogoutControllerTest.php index 60d6490b..a474a891 100644 --- a/tests/src/Controller/LogoutControllerTest.php +++ b/tests/src/Controller/LogoutControllerTest.php @@ -6,9 +6,9 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use RuntimeException; use SimpleSAML\Auth\Simple; use SimpleSAML\Configuration; -use SimpleSAML\HTTP\RunnableResponse; use SimpleSAML\Module; use SimpleSAML\Module\casserver\Controller\LogoutController; use SimpleSAML\Session; @@ -25,6 +25,7 @@ class LogoutControllerTest extends TestCase private Configuration $sspConfig; + protected function setUp(): void { $this->authSimpleMock = $this->getMockBuilder(Simple::class) @@ -43,6 +44,7 @@ protected function setUp(): void $this->sspConfig = Configuration::getConfig('config.php'); } + public static function setUpBeforeClass(): void { // Some of the constructs in this test cause a Configuration to be created prior to us @@ -54,31 +56,34 @@ public static function setUpBeforeClass(): void $_SERVER['REQUEST_URI'] = '/'; } + public function testLogoutNotAllowed(): void { $this->moduleConfig['enable_logout'] = false; $config = Configuration::loadFromArray($this->moduleConfig); - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Logout not allowed'); $controller = new LogoutController($this->sspConfig, $config, $this->authSimpleMock); $controller->logout(Request::create('/')); } + public function testLogoutNoRedirectUrlOnSkipLogout(): void { $this->moduleConfig['enable_logout'] = true; $this->moduleConfig['skip_logout_page'] = true; $config = Configuration::loadFromArray($this->moduleConfig); - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Required URL query parameter [url] not provided. (CAS Server)'); $controller = new LogoutController($this->sspConfig, $config, $this->authSimpleMock); $controller->logout(Request::create('/')); } + public function testLogoutWithRedirectUrlOnSkipLogout(): void { $this->moduleConfig['enable_logout'] = true; @@ -107,6 +112,7 @@ public function testLogoutWithRedirectUrlOnSkipLogout(): void $this->assertEquals($urlParam, $arguments[0]); } + public function testLogoutNoRedirectUrlOnNoSkipLogoutUnAuthenticated(): void { $this->moduleConfig['enable_logout'] = true; @@ -127,6 +133,7 @@ public function testLogoutNoRedirectUrlOnNoSkipLogoutUnAuthenticated(): void $this->assertEquals('http://localhost/module.php/casserver/loggedOut', $arguments[0]); } + public function testLogoutWithRedirectUrlOnNoSkipLogoutUnAuthenticated(): void { $this->moduleConfig['enable_logout'] = true; @@ -153,6 +160,7 @@ public function testLogoutWithRedirectUrlOnNoSkipLogoutUnAuthenticated(): void $this->assertEquals('http://localhost/module.php/casserver/loggedOut', $arguments[0]); } + public function testLogoutNoRedirectUrlOnNoSkipLogoutAuthenticated(): void { $this->moduleConfig['enable_logout'] = true; @@ -171,11 +179,11 @@ public function testLogoutNoRedirectUrlOnNoSkipLogoutAuthenticated(): void $response = $controller->logout($logoutRequest, ...$queryParameters); - $this->assertInstanceOf(RunnableResponse::class, $response); $callable = (array)$response->getCallable(); $this->assertEquals('logout', $callable[1] ?? ''); } + public function testTicketIdGetsDeletedOnLogout(): void { $this->moduleConfig['enable_logout'] = true; diff --git a/tests/src/Controller/Traits/UrlTraitTest.php b/tests/src/Controller/Traits/UrlTraitTest.php index 25b5dbb1..a8727582 100644 --- a/tests/src/Controller/Traits/UrlTraitTest.php +++ b/tests/src/Controller/Traits/UrlTraitTest.php @@ -13,6 +13,7 @@ class UrlTraitTest extends TestCase { use UrlTrait; + /** * @return array */ @@ -54,6 +55,7 @@ public static function checkServiceURLProvider(): array ]; } + /** * @param string $service the service url to check * @param bool $allowed is the service url allowed? @@ -86,6 +88,7 @@ public function testCheckServiceURL(string $service, bool $allowed): void ); } + public static function requestParameters(): array { return [ @@ -117,6 +120,7 @@ public static function requestParameters(): array ]; } + #[DataProvider('requestParameters')] public function testParseQueryParameters(array $requestParams, array $query, array $sessionTicket): void { diff --git a/tests/src/Ticket/ErroringTicketStore.php b/tests/src/Ticket/ErroringTicketStore.php index 44eab1ff..4ed5d4c0 100644 --- a/tests/src/Ticket/ErroringTicketStore.php +++ b/tests/src/Ticket/ErroringTicketStore.php @@ -22,6 +22,7 @@ public function getTicket(string $ticketId): ?array throw new Exception("Sample get error"); } + /** * @param array $ticket The ticket to store * @throws \Exception for all invocations. @@ -31,6 +32,7 @@ public function addTicket(array $ticket): void throw new Exception("Sample add error"); } + /** * @param string $ticketId The ticket id * @throws \Exception for all invocations. diff --git a/tests/src/TicketValidatorTest.php b/tests/src/TicketValidatorTest.php index 38a85e9f..8907aa59 100644 --- a/tests/src/TicketValidatorTest.php +++ b/tests/src/TicketValidatorTest.php @@ -126,6 +126,7 @@ public function testExpiredTicket(): void $this->assertNull($this->ticketStore->getTicket($id), "ticket deleted after loading"); } + /** * @param string $serviceUrl The service url that will get sanitized * @param string $expectedSanitzedUrl The expected result @@ -136,6 +137,7 @@ public function testUrlSanitization(string $serviceUrl, string $expectedSanitzed $this->assertEquals($expectedSanitzedUrl, TicketValidator::sanitize($serviceUrl)); } + /** * Urls to test * @return array