diff --git a/composer.json b/composer.json index 83470fed9f648b7cbe57ab5e3540009c342c9c1c..7d27d1b16e685327f7a42ffd264c9a977e838f64 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,13 @@ }, "autoload-dev": { "psr-4": { - "SimpleSAML\\Test\\": ["tests", "tests/lib/SimpleSAML"] + "SimpleSAML\\Test\\": ["tests", "tests/lib/SimpleSAML"], + "SimpleSAML\\Test\\Module\\admin\\": ["tests/lib/SimpleSAML/modules/admin/lib"], + "SimpleSAML\\Test\\Module\\core\\": ["tests/lib/SimpleSAML/modules/core/lib"], + "SimpleSAML\\Test\\Module\\cron\\": ["tests/lib/SimpleSAML/modules/cron/lib"], + "SimpleSAML\\Test\\Module\\exampleauth\\": ["tests/lib/SimpleSAML/modules/exampleauth/lib"], + "SimpleSAML\\Test\\Module\\multiauth\\": ["tests/lib/SimpleSAML/modules/multiauth/lib"], + "SimpleSAML\\Test\\Module\\saml\\": ["tests/lib/SimpleSAML/modules/saml/lib"] }, "files": ["tests/_autoload_modules.php"] }, @@ -64,7 +70,7 @@ "ext-curl": "*", "mikey179/vfsstream": "~1.6", "simplesamlphp/simplesamlphp-module-adfs": "dev-master", - "simplesamlphp/simplesamlphp-test-framework": "^1.0.5", + "simplesamlphp/simplesamlphp-test-framework": "^1.1.0", "simplesamlphp/xml-security": "^0.0.10" }, "suggest": { diff --git a/composer.lock b/composer.lock index 5ea17a269183bd0d276338d2cd5e43df8580b7a6..0a66fc080537f74821fa637799dca3701cd9763d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f6088fd1c312b8c7a5a987ee2fbd95f", + "content-hash": "7239289669a01ad2473e76a4705bee61", "packages": [ { "name": "gettext/gettext", - "version": "v4.8.3", + "version": "v4.8.4", "source": { "type": "git", "url": "https://github.com/php-gettext/Gettext.git", - "reference": "57ff4fb16647e78e80a5909fe3c190f1c3110321" + "reference": "58bc0f7f37e78efb0f9758f93d4a0f669f0f84a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/57ff4fb16647e78e80a5909fe3c190f1c3110321", - "reference": "57ff4fb16647e78e80a5909fe3c190f1c3110321", + "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/58bc0f7f37e78efb0f9758f93d4a0f669f0f84a1", + "reference": "58bc0f7f37e78efb0f9758f93d4a0f669f0f84a1", "shasum": "" }, "require": { @@ -66,7 +66,26 @@ "po", "translation" ], - "time": "2020-11-18T22:35:49+00:00" + "support": { + "email": "oom@oscarotero.com", + "issues": "https://github.com/oscarotero/Gettext/issues", + "source": "https://github.com/php-gettext/Gettext/tree/v4.8.4" + }, + "funding": [ + { + "url": "https://paypal.me/oscarotero", + "type": "custom" + }, + { + "url": "https://github.com/oscarotero", + "type": "github" + }, + { + "url": "https://www.patreon.com/misteroom", + "type": "patreon" + } + ], + "time": "2021-03-10T19:35:49+00:00" }, { "name": "gettext/languages", @@ -127,20 +146,24 @@ "translations", "unicode" ], + "support": { + "issues": "https://github.com/php-gettext/Languages/issues", + "source": "https://github.com/php-gettext/Languages/tree/2.6.0" + }, "time": "2019-11-13T10:30:21+00:00" }, { "name": "phpmailer/phpmailer", - "version": "v6.2.0", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "e38888a75c070304ca5514197d4847a59a5c853f" + "reference": "4a08cf4cdd2c38d12ee2b9fa69e5d235f37a6dcb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e38888a75c070304ca5514197d4847a59a5c853f", - "reference": "e38888a75c070304ca5514197d4847a59a5c853f", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/4a08cf4cdd2c38d12ee2b9fa69e5d235f37a6dcb", + "reference": "4a08cf4cdd2c38d12ee2b9fa69e5d235f37a6dcb", "shasum": "" }, "require": { @@ -193,13 +216,17 @@ } ], "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.3.0" + }, "funding": [ { "url": "https://github.com/Synchro", "type": "github" } ], - "time": "2020-11-25T15:24:57+00:00" + "time": "2021-02-19T15:28:08+00:00" }, { "name": "psr/cache", @@ -245,31 +272,29 @@ "psr", "psr-6" ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, "time": "2016-08-06T20:24:11+00:00" }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -282,7 +307,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -294,7 +319,11 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/event-dispatcher", @@ -340,6 +369,10 @@ "psr", "psr-14" ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, "time": "2019-01-08T18:20:26+00:00" }, { @@ -387,6 +420,9 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, "time": "2020-03-23T09:12:05+00:00" }, { @@ -425,6 +461,10 @@ "xml", "xmldsig" ], + "support": { + "issues": "https://github.com/robrichards/xmlseclibs/issues", + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.1" + }, "time": "2020-09-05T13:00:25+00:00" }, { @@ -479,20 +519,24 @@ } ], "description": "A wrapper around webmozart/assert to make it useful beyond checking method arguments", + "support": { + "issues": "https://github.com/simplesamlphp/assert/issues", + "source": "https://github.com/simplesamlphp/assert/tree/v0.1.0" + }, "time": "2020-09-10T19:35:42+00:00" }, { "name": "simplesamlphp/saml2", - "version": "v4.1.12", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/simplesamlphp/saml2.git", - "reference": "f6186d07a3a59c6dc506ff0ec63b65051570a6d5" + "reference": "d4038b83be50ccd64ecdc0b7c68e66d63c899d2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/f6186d07a3a59c6dc506ff0ec63b65051570a6d5", - "reference": "f6186d07a3a59c6dc506ff0ec63b65051570a6d5", + "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/d4038b83be50ccd64ecdc0b7c68e66d63c899d2c", + "reference": "d4038b83be50ccd64ecdc0b7c68e66d63c899d2c", "shasum": "" }, "require": { @@ -534,7 +578,11 @@ } ], "description": "SAML2 PHP library from SimpleSAMLphp", - "time": "2021-01-17T14:08:48+00:00" + "support": { + "issues": "https://github.com/simplesamlphp/saml2/issues", + "source": "https://github.com/simplesamlphp/saml2/tree/v4.2.0" + }, + "time": "2021-01-28T21:35:22+00:00" }, { "name": "simplesamlphp/twig-configurable-i18n", @@ -586,25 +634,29 @@ "translation", "twig" ], + "support": { + "issues": "https://github.com/simplesamlphp/twig-configurable-i18n/issues", + "source": "https://github.com/simplesamlphp/twig-configurable-i18n" + }, "time": "2020-08-27T12:51:10+00:00" }, { "name": "symfony/cache", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "5e61d63b1ef4fb4852994038267ad45e12f3ec52" + "reference": "d15fb2576cdbe2c40d7c851e62f85b0faff3dd3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/5e61d63b1ef4fb4852994038267ad45e12f3ec52", - "reference": "5e61d63b1ef4fb4852994038267ad45e12f3ec52", + "url": "https://api.github.com/repos/symfony/cache/zipball/d15fb2576cdbe2c40d7c851e62f85b0faff3dd3d", + "reference": "d15fb2576cdbe2c40d7c851e62f85b0faff3dd3d", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/cache": "~1.0", + "psr/cache": "^1.0|^2.0", "psr/log": "^1.1", "symfony/cache-contracts": "^1.1.7|^2", "symfony/polyfill-php80": "^1.15", @@ -618,9 +670,9 @@ "symfony/var-dumper": "<4.4" }, "provide": { - "psr/cache-implementation": "1.0", + "psr/cache-implementation": "1.0|2.0", "psr/simple-cache-implementation": "1.0", - "symfony/cache-implementation": "1.0" + "symfony/cache-implementation": "1.0|2.0" }, "require-dev": { "cache/integration-tests": "dev-master", @@ -658,12 +710,15 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation", "homepage": "https://symfony.com", "keywords": [ "caching", "psr6" ], + "support": { + "source": "https://github.com/symfony/cache/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -678,7 +733,7 @@ "type": "tidelift" } ], - "time": "2020-12-10T19:16:15+00:00" + "time": "2021-02-25T23:54:56+00:00" }, { "name": "symfony/cache-contracts", @@ -740,6 +795,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v2.2.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -758,16 +816,16 @@ }, { "name": "symfony/config", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "d0a82d965296083fe463d655a3644cbe49cbaa80" + "reference": "212d54675bf203ff8aef7d8cee8eecfb72f4a263" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/d0a82d965296083fe463d655a3644cbe49cbaa80", - "reference": "d0a82d965296083fe463d655a3644cbe49cbaa80", + "url": "https://api.github.com/repos/symfony/config/zipball/212d54675bf203ff8aef7d8cee8eecfb72f4a263", + "reference": "212d54675bf203ff8aef7d8cee8eecfb72f4a263", "shasum": "" }, "require": { @@ -813,8 +871,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Config Component", + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -829,20 +890,20 @@ "type": "tidelift" } ], - "time": "2020-12-09T18:54:12+00:00" + "time": "2021-02-23T23:58:19+00:00" }, { "name": "symfony/console", - "version": "v5.2.1", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "47c02526c532fb381374dab26df05e7313978976" + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/47c02526c532fb381374dab26df05e7313978976", - "reference": "47c02526c532fb381374dab26df05e7313978976", + "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79", + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79", "shasum": "" }, "require": { @@ -901,7 +962,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "keywords": [ "cli", @@ -909,6 +970,9 @@ "console", "terminal" ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -923,20 +987,20 @@ "type": "tidelift" } ], - "time": "2020-12-18T08:03:05+00:00" + "time": "2021-03-06T13:42:15+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.2.1", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "7f8a9e9eff0581a33e20f6c5d41096fe22832d25" + "reference": "be0c7926f5729b15e4e79fd2bf917cac584b1970" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/7f8a9e9eff0581a33e20f6c5d41096fe22832d25", - "reference": "7f8a9e9eff0581a33e20f6c5d41096fe22832d25", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/be0c7926f5729b15e4e79fd2bf917cac584b1970", + "reference": "be0c7926f5729b15e4e79fd2bf917cac584b1970", "shasum": "" }, "require": { @@ -954,7 +1018,7 @@ }, "provide": { "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0" + "symfony/service-implementation": "1.0|2.0" }, "require-dev": { "symfony/config": "^5.1", @@ -991,8 +1055,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony DependencyInjection Component", + "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v5.2.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1007,7 +1074,7 @@ "type": "tidelift" } ], - "time": "2020-12-18T08:03:05+00:00" + "time": "2021-03-05T20:13:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1057,6 +1124,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1075,16 +1145,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "59b190ce16ddf32771a22087b60f6dafd3407147" + "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/59b190ce16ddf32771a22087b60f6dafd3407147", - "reference": "59b190ce16ddf32771a22087b60f6dafd3407147", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/b547d3babcab5c31e01de59ee33e9d9c1421d7d0", + "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0", "shasum": "" }, "require": { @@ -1121,8 +1191,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony ErrorHandler Component", + "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1137,20 +1210,20 @@ "type": "tidelift" } ], - "time": "2020-12-09T18:54:12+00:00" + "time": "2021-02-11T08:21:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "1c93f7a1dff592c252574c79a8635a8a80856042" + "reference": "d08d6ec121a425897951900ab692b612a61d6240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1c93f7a1dff592c252574c79a8635a8a80856042", - "reference": "1c93f7a1dff592c252574c79a8635a8a80856042", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", + "reference": "d08d6ec121a425897951900ab692b612a61d6240", "shasum": "" }, "require": { @@ -1203,8 +1276,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1219,7 +1295,7 @@ "type": "tidelift" } ], - "time": "2020-12-18T08:03:05+00:00" + "time": "2021-02-18T17:12:37+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -1281,6 +1357,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.2.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1299,16 +1378,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d" + "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fa8f8cab6b65e2d99a118e082935344c5ba8c60d", - "reference": "fa8f8cab6b65e2d99a118e082935344c5ba8c60d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/710d364200997a5afde34d9fe57bd52f3cc1e108", + "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108", "shasum": "" }, "require": { @@ -1338,8 +1417,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1354,20 +1436,20 @@ "type": "tidelift" } ], - "time": "2020-11-30T17:05:38+00:00" + "time": "2021-02-12T10:38:38+00:00" }, { "name": "symfony/finder", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "0b9231a5922fd7287ba5b411893c0ecd2733e5ba" + "reference": "0d639a0943822626290d169965804f79400e6a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/0b9231a5922fd7287ba5b411893c0ecd2733e5ba", - "reference": "0b9231a5922fd7287ba5b411893c0ecd2733e5ba", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", "shasum": "" }, "require": { @@ -1396,8 +1478,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1412,20 +1497,20 @@ "type": "tidelift" } ], - "time": "2020-12-08T17:02:38+00:00" + "time": "2021-02-15T18:55:04+00:00" }, { "name": "symfony/framework-bundle", - "version": "v5.2.1", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "0663407ca5ad12e2e3fe657b32266904b3dc1e3f" + "reference": "4dae531503072a57cf26f7f4beb4c3ef8a061f8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/0663407ca5ad12e2e3fe657b32266904b3dc1e3f", - "reference": "0663407ca5ad12e2e3fe657b32266904b3dc1e3f", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/4dae531503072a57cf26f7f4beb4c3ef8a061f8f", + "reference": "4dae531503072a57cf26f7f4beb4c3ef8a061f8f", "shasum": "" }, "require": { @@ -1447,12 +1532,12 @@ }, "conflict": { "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.0", - "phpdocumentor/type-resolver": "<0.2.1", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", "phpunit/phpunit": "<5.4.3", "symfony/asset": "<5.1", "symfony/browser-kit": "<4.4", - "symfony/console": "<5.2", + "symfony/console": "<5.2.5", "symfony/dom-crawler": "<4.4", "symfony/dotenv": "<5.1", "symfony/form": "<5.2", @@ -1473,8 +1558,9 @@ "symfony/workflow": "<5.2" }, "require-dev": { - "doctrine/annotations": "~1.7", + "doctrine/annotations": "^1.10.4", "doctrine/cache": "~1.0", + "doctrine/persistence": "^1.3|^2.0", "paragonie/sodium_compat": "^1.8", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "symfony/asset": "^5.1", @@ -1494,6 +1580,7 @@ "symfony/process": "^4.4|^5.0", "symfony/property-info": "^4.4|^5.0", "symfony/security-bundle": "^5.1", + "symfony/security-core": "^4.4|^5.2", "symfony/security-csrf": "^4.4|^5.0", "symfony/security-http": "^4.4|^5.0", "symfony/serializer": "^5.2", @@ -1540,8 +1627,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony FrameworkBundle", + "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/framework-bundle/tree/v5.2.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1556,7 +1646,7 @@ "type": "tidelift" } ], - "time": "2020-12-18T11:40:59+00:00" + "time": "2021-03-09T08:47:49+00:00" }, { "name": "symfony/http-client-contracts", @@ -1618,6 +1708,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1636,16 +1729,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "a1f6218b29897ab52acba58cfa905b83625bef8d" + "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a1f6218b29897ab52acba58cfa905b83625bef8d", - "reference": "a1f6218b29897ab52acba58cfa905b83625bef8d", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/54499baea7f7418bce7b5ec92770fd0799e8e9bf", + "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf", "shasum": "" }, "require": { @@ -1686,8 +1779,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony HttpFoundation Component", + "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1702,20 +1798,20 @@ "type": "tidelift" } ], - "time": "2020-12-18T10:00:10+00:00" + "time": "2021-02-25T17:16:57+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.2.1", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "1feb619286d819180f7b8bc0dc44f516d9c62647" + "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1feb619286d819180f7b8bc0dc44f516d9c62647", - "reference": "1feb619286d819180f7b8bc0dc44f516d9c62647", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b8c63ef63c2364e174c3b3e0ba0bf83455f97f73", + "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73", "shasum": "" }, "require": { @@ -1744,13 +1840,13 @@ "symfony/translation": "<5.0", "symfony/twig-bridge": "<5.0", "symfony/validator": "<5.0", - "twig/twig": "<2.4" + "twig/twig": "<2.13" }, "provide": { "psr/log-implementation": "1.0" }, "require-dev": { - "psr/cache": "~1.0", + "psr/cache": "^1.0|^2.0|^3.0", "symfony/browser-kit": "^4.4|^5.0", "symfony/config": "^5.0", "symfony/console": "^4.4|^5.0", @@ -1764,7 +1860,7 @@ "symfony/stopwatch": "^4.4|^5.0", "symfony/translation": "^4.4|^5.0", "symfony/translation-contracts": "^1.1|^2", - "twig/twig": "^2.4|^3.0" + "twig/twig": "^2.13|^3.0.4" }, "suggest": { "symfony/browser-kit": "", @@ -1795,8 +1891,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony HttpKernel Component", + "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v5.2.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1811,11 +1910,11 @@ "type": "tidelift" } ], - "time": "2020-12-18T13:49:39+00:00" + "time": "2021-03-10T17:07:35+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1873,6 +1972,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1891,16 +1993,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "267a9adeb8ecb8071040a740930e077cdfb987af" + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/267a9adeb8ecb8071040a740930e077cdfb987af", - "reference": "267a9adeb8ecb8071040a740930e077cdfb987af", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "shasum": "" }, "require": { @@ -1951,6 +2053,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1965,20 +2070,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "6e971c891537eb617a00bb07a43d182a6915faba" + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba", - "reference": "6e971c891537eb617a00bb07a43d182a6915faba", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", "shasum": "" }, "require": { @@ -2032,6 +2137,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2046,20 +2154,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T17:09:11+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13" + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", - "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", "shasum": "" }, "require": { @@ -2109,6 +2217,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2123,11 +2234,11 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -2185,6 +2296,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2203,7 +2317,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -2265,6 +2379,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2283,16 +2400,16 @@ }, { "name": "symfony/routing", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "934ac2720dcc878a47a45c986b483a7ee7193620" + "reference": "cafa138128dfd6ab6be1abf6279169957b34f662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/934ac2720dcc878a47a45c986b483a7ee7193620", - "reference": "934ac2720dcc878a47a45c986b483a7ee7193620", + "url": "https://api.github.com/repos/symfony/routing/zipball/cafa138128dfd6ab6be1abf6279169957b34f662", + "reference": "cafa138128dfd6ab6be1abf6279169957b34f662", "shasum": "" }, "require": { @@ -2306,7 +2423,7 @@ "symfony/yaml": "<4.4" }, "require-dev": { - "doctrine/annotations": "^1.7", + "doctrine/annotations": "^1.10.4", "psr/log": "~1.0", "symfony/config": "^5.0", "symfony/dependency-injection": "^4.4|^5.0", @@ -2344,7 +2461,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Routing Component", + "description": "Maps an HTTP request to a set of configuration variables", "homepage": "https://symfony.com", "keywords": [ "router", @@ -2352,6 +2469,9 @@ "uri", "url" ], + "support": { + "source": "https://github.com/symfony/routing/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2366,7 +2486,7 @@ "type": "tidelift" } ], - "time": "2020-12-08T17:03:37+00:00" + "time": "2021-02-22T15:48:39+00:00" }, { "name": "symfony/service-contracts", @@ -2428,6 +2548,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2446,16 +2569,16 @@ }, { "name": "symfony/string", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed" + "reference": "4e78d7d47061fa183639927ec40d607973699609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", - "reference": "5bd67751d2e3f7d6f770c9154b8fbcb2aa05f7ed", + "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", + "reference": "4e78d7d47061fa183639927ec40d607973699609", "shasum": "" }, "require": { @@ -2498,7 +2621,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony String component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", @@ -2508,6 +2631,9 @@ "utf-8", "utf8" ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2522,20 +2648,20 @@ "type": "tidelift" } ], - "time": "2020-12-05T07:33:16+00:00" + "time": "2021-02-16T10:20:28+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.2.1", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "13e7e882eaa55863faa7c4ad7c60f12f1a8b5089" + "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/13e7e882eaa55863faa7c4ad7c60f12f1a8b5089", - "reference": "13e7e882eaa55863faa7c4ad7c60f12f1a8b5089", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/002ab5a36702adf0c9a11e6d8836623253e9045e", + "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e", "shasum": "" }, "require": { @@ -2551,7 +2677,7 @@ "ext-iconv": "*", "symfony/console": "^4.4|^5.0", "symfony/process": "^4.4|^5.0", - "twig/twig": "^2.4|^3.0" + "twig/twig": "^2.13|^3.0.4" }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", @@ -2587,12 +2713,15 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony mechanism for exploring and dumping PHP variables", + "description": "Provides mechanisms for walking through any arbitrary PHP variable", "homepage": "https://symfony.com", "keywords": [ "debug", "dump" ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v5.2.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2607,20 +2736,20 @@ "type": "tidelift" } ], - "time": "2020-12-16T17:02:19+00:00" + "time": "2021-03-06T07:59:01+00:00" }, { "name": "symfony/var-exporter", - "version": "v5.2.1", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "fbc3507f23d263d75417e09a12d77c009f39676c" + "reference": "5aed4875ab514c8cb9b6ff4772baa25fa4c10307" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/fbc3507f23d263d75417e09a12d77c009f39676c", - "reference": "fbc3507f23d263d75417e09a12d77c009f39676c", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/5aed4875ab514c8cb9b6ff4772baa25fa4c10307", + "reference": "5aed4875ab514c8cb9b6ff4772baa25fa4c10307", "shasum": "" }, "require": { @@ -2653,7 +2782,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code", + "description": "Allows exporting any serializable PHP data structure to plain PHP code", "homepage": "https://symfony.com", "keywords": [ "clone", @@ -2663,6 +2792,9 @@ "instantiate", "serialize" ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2677,20 +2809,20 @@ "type": "tidelift" } ], - "time": "2020-10-28T21:31:18+00:00" + "time": "2021-01-27T10:01:46+00:00" }, { "name": "symfony/yaml", - "version": "v5.2.1", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "290ea5e03b8cf9b42c783163123f54441fb06939" + "reference": "298a08ddda623485208506fcee08817807a251dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/290ea5e03b8cf9b42c783163123f54441fb06939", - "reference": "290ea5e03b8cf9b42c783163123f54441fb06939", + "url": "https://api.github.com/repos/symfony/yaml/zipball/298a08ddda623485208506fcee08817807a251dd", + "reference": "298a08ddda623485208506fcee08817807a251dd", "shasum": "" }, "require": { @@ -2733,8 +2865,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v5.2.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2749,7 +2884,7 @@ "type": "tidelift" } ], - "time": "2020-12-08T17:02:38+00:00" + "time": "2021-03-06T07:59:01+00:00" }, { "name": "twig/extensions", @@ -2804,21 +2939,25 @@ "i18n", "text" ], + "support": { + "issues": "https://github.com/twigphp/Twig-extensions/issues", + "source": "https://github.com/twigphp/Twig-extensions/tree/master" + }, "abandoned": true, "time": "2018-12-05T18:34:18+00:00" }, { "name": "twig/twig", - "version": "v2.14.3", + "version": "v2.14.4", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "8bc568d460d88b25c00c046256ec14a787ea60d9" + "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/8bc568d460d88b25c00c046256ec14a787ea60d9", - "reference": "8bc568d460d88b25c00c046256ec14a787ea60d9", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b4ba691fb99ec7952d25deb36c0a83061b93bbf", + "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf", "shasum": "" }, "require": { @@ -2870,6 +3009,10 @@ "keywords": [ "templating" ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v2.14.4" + }, "funding": [ { "url": "https://github.com/fabpot", @@ -2880,34 +3023,39 @@ "type": "tidelift" } ], - "time": "2021-01-05T15:34:33+00:00" + "time": "2021-03-10T10:05:55+00:00" }, { "name": "webmozart/assert", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -2929,7 +3077,11 @@ "check", "validate" ], - "time": "2020-07-08T17:02:28+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" } ], "packages-dev": [ @@ -3009,6 +3161,11 @@ "non-blocking", "promise" ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.5.2" + }, "funding": [ { "url": "https://github.com/amphp", @@ -3081,6 +3238,11 @@ "non-blocking", "stream" ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/master" + }, "time": "2020-06-29T18:35:05+00:00" }, { @@ -3136,6 +3298,10 @@ } ], "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.1" + }, "funding": [ { "url": "https://packagist.com", @@ -3212,6 +3378,11 @@ "validation", "versioning" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.2.4" + }, "funding": [ { "url": "https://packagist.com", @@ -3270,6 +3441,11 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/1.4.5" + }, "funding": [ { "url": "https://packagist.com", @@ -3317,6 +3493,10 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, "time": "2019-12-04T15:06:13+00:00" }, { @@ -3368,6 +3548,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -3423,20 +3607,24 @@ } ], "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.0" + }, "time": "2021-01-10T17:48:47+00:00" }, { "name": "felixfbecker/language-server-protocol", - "version": "v1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541" + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/85e83cacd2ed573238678c6875f8f0d7ec699541", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", "shasum": "" }, "require": { @@ -3475,7 +3663,11 @@ "php", "server" ], - "time": "2020-10-23T13:55:30+00:00" + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + }, + "time": "2021-02-22T14:02:09+00:00" }, { "name": "mikey179/vfsstream", @@ -3521,6 +3713,11 @@ ], "description": "Virtual file system to mock the real file system in unit tests.", "homepage": "http://vfs.bovigo.org/", + "support": { + "issues": "https://github.com/bovigo/vfsStream/issues", + "source": "https://github.com/bovigo/vfsStream/tree/master", + "wiki": "https://github.com/bovigo/vfsStream/wiki" + }, "time": "2019-10-30T15:31:00+00:00" }, { @@ -3569,6 +3766,10 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", @@ -3621,6 +3822,11 @@ } ], "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/master" + }, "time": "2020-04-16T18:48:43+00:00" }, { @@ -3673,6 +3879,10 @@ "parser", "php" ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + }, "time": "2020-12-20T10:01:03+00:00" }, { @@ -3722,6 +3932,10 @@ "xml", "xml conversion" ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, "time": "2019-03-29T20:06:56+00:00" }, { @@ -3778,20 +3992,24 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", - "version": "3.0.4", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { @@ -3825,7 +4043,11 @@ } ], "description": "Library for handling version information and constraints", - "time": "2020-12-13T23:18:30+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -3874,6 +4096,10 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -3926,6 +4152,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, "time": "2020-09-03T19:13:55+00:00" }, { @@ -3971,6 +4201,10 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + }, "time": "2020-09-17T18:55:26+00:00" }, { @@ -4034,6 +4268,10 @@ "spy", "stub" ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.12.2" + }, "time": "2020-12-19T10:15:11+00:00" }, { @@ -4101,6 +4339,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4157,6 +4399,10 @@ "filesystem", "iterator" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4216,6 +4462,10 @@ "keywords": [ "process" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4271,6 +4521,10 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4326,6 +4580,10 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4336,16 +4594,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.1", + "version": "9.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360" + "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7bdf4085de85a825f4424eae52c99a1cec2f360", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f661659747f2f87f9e72095bb207bceb0f151cb4", + "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4", "shasum": "" }, "require": { @@ -4421,6 +4679,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -4431,7 +4693,7 @@ "type": "github" } ], - "time": "2021-01-17T07:42:25+00:00" + "time": "2021-02-02T14:45:58+00:00" }, { "name": "sebastian/cli-parser", @@ -4477,6 +4739,10 @@ ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4529,6 +4795,10 @@ ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4580,6 +4850,10 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4650,6 +4924,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4703,6 +4981,10 @@ ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4765,6 +5047,10 @@ "unidiff", "unified diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4824,6 +5110,10 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4897,6 +5187,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -4957,6 +5251,10 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5010,6 +5308,10 @@ ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5063,6 +5365,10 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5114,6 +5420,10 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5173,6 +5483,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5224,6 +5538,10 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5276,6 +5594,10 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5325,6 +5647,10 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5365,6 +5691,10 @@ "LGPL-2.1-only" ], "description": "A Composer plugin that allows installing SimpleSAMLphp modules through Composer.", + "support": { + "issues": "https://github.com/simplesamlphp/composer-module-installer/issues", + "source": "https://github.com/simplesamlphp/composer-module-installer/tree/v1.1.8" + }, "time": "2020-08-25T19:04:33+00:00" }, { @@ -5373,16 +5703,16 @@ "source": { "type": "git", "url": "https://github.com/simplesamlphp/simplesamlphp-module-adfs.git", - "reference": "577a0ddc975a154f4ea3535c29f934489ccb24eb" + "reference": "d11fafd22d6dfe6edecb4bd2505138452da05967" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-module-adfs/zipball/577a0ddc975a154f4ea3535c29f934489ccb24eb", - "reference": "577a0ddc975a154f4ea3535c29f934489ccb24eb", + "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-module-adfs/zipball/d11fafd22d6dfe6edecb4bd2505138452da05967", + "reference": "d11fafd22d6dfe6edecb4bd2505138452da05967", "shasum": "" }, "require": { - "php": ">=7.4", + "php": ">=7.4 || ^8.0", "simplesamlphp/assert": "^0.1.0", "simplesamlphp/composer-module-installer": "^1.1.7" }, @@ -5390,6 +5720,7 @@ "simplesamlphp/simplesamlphp": "dev-master", "simplesamlphp/simplesamlphp-test-framework": "^1.0.4" }, + "default-branch": true, "type": "simplesamlphp-module", "autoload": { "psr-4": { @@ -5411,20 +5742,24 @@ "adfs", "simplesamlphp" ], - "time": "2021-01-16T10:29:01+00:00" + "support": { + "issues": "https://github.com/simplesamlphp/simplesamlphp-module-adfs/issues", + "source": "https://github.com/simplesamlphp/simplesamlphp-module-adfs" + }, + "time": "2021-01-24T20:36:57+00:00" }, { "name": "simplesamlphp/simplesamlphp-test-framework", - "version": "v1.0.5", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/simplesamlphp/simplesamlphp-test-framework.git", - "reference": "ad313899c83f03ce75a2539454e10eab62d81e1c" + "reference": "2951b7b760a9f9b752debe5b8d7f191efd8cf1a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-test-framework/zipball/ad313899c83f03ce75a2539454e10eab62d81e1c", - "reference": "ad313899c83f03ce75a2539454e10eab62d81e1c", + "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-test-framework/zipball/2951b7b760a9f9b752debe5b8d7f191efd8cf1a7", + "reference": "2951b7b760a9f9b752debe5b8d7f191efd8cf1a7", "shasum": "" }, "require": { @@ -5463,7 +5798,11 @@ "keywords": [ "test-framework" ], - "time": "2021-01-16T12:50:54+00:00" + "support": { + "issues": "https://github.com/simplesamlphp/simplesamlphp-test-framework/issues", + "source": "https://github.com/simplesamlphp/simplesamlphp-test-framework" + }, + "time": "2021-03-14T00:30:00+00:00" }, { "name": "simplesamlphp/xml-common", @@ -5513,6 +5852,10 @@ "saml", "xml" ], + "support": { + "issues": "https://github.com/simplesamlphp/xml-common/issues", + "source": "https://github.com/simplesamlphp/xml-common" + }, "time": "2020-12-18T09:00:39+00:00" }, { @@ -5570,6 +5913,10 @@ "xml", "xmldsig" ], + "support": { + "issues": "https://github.com/simplesamlphp/xml-security/issues", + "source": "https://github.com/simplesamlphp/xml-security/tree/v0.0.10" + }, "time": "2021-01-15T21:38:05+00:00" }, { @@ -5621,6 +5968,11 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, "time": "2020-10-23T02:01:07+00:00" }, { @@ -5661,6 +6013,10 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, "funding": [ { "url": "https://github.com/theseer", @@ -5671,16 +6027,16 @@ }, { "name": "vimeo/psalm", - "version": "4.4.1", + "version": "4.6.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "9fd7a7d885b3a216cff8dec9d8c21a132f275224" + "reference": "e93e532e4eaad6d68c4d7b606853800eaceccc72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/9fd7a7d885b3a216cff8dec9d8c21a132f275224", - "reference": "9fd7a7d885b3a216cff8dec9d8c21a132f275224", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/e93e532e4eaad6d68c4d7b606853800eaceccc72", + "reference": "e93e532e4eaad6d68c4d7b606853800eaceccc72", "shasum": "" }, "require": { @@ -5698,7 +6054,7 @@ "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.0.3", "felixfbecker/language-server-protocol": "^1.4", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", "nikic/php-parser": "^4.10.1", "openlss/lib-array2xml": "^1.0", "php": "^7.1|^8", @@ -5714,6 +6070,7 @@ "bamarni/composer-bin-plugin": "^1.2", "brianium/paratest": "^4.0||^6.0", "ext-curl": "*", + "php-parallel-lint/php-parallel-lint": "^1.2", "phpdocumentor/reflection-docblock": "^5", "phpmyadmin/sql-parser": "5.1.0||dev-master", "phpspec/prophecy": ">=1.9.0", @@ -5767,7 +6124,11 @@ "inspection", "php" ], - "time": "2021-01-14T21:44:29+00:00" + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/4.6.1" + }, + "time": "2021-02-17T21:54:11+00:00" }, { "name": "webmozart/path-util", @@ -5813,6 +6174,10 @@ } ], "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, "time": "2015-12-17T08:42:14+00:00" } ], @@ -5838,5 +6203,5 @@ "platform-dev": { "ext-curl": "*" }, - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.0.0" } diff --git a/config-templates/config.php b/config-templates/config.php index db59453a96918e5fbe0ffc027bbe279162b451a1..0bf9def7cd6bd5acb1b0e2835851e6694ad91e64 100644 --- a/config-templates/config.php +++ b/config-templates/config.php @@ -3,6 +3,7 @@ /** * The configuration of SimpleSAMLphp */ +$httpUtils = new \SimpleSAML\Utils\HTTP(); $config = [ @@ -552,7 +553,7 @@ $config = [ * through https. If the user can access the service through * both http and https, this must be set to FALSE. */ - 'session.cookie.secure' => false, + 'session.cookie.secure' => true, /* * Set the SameSite attribute in the cookie. @@ -564,7 +565,7 @@ $config = [ * Example: * 'session.cookie.samesite' => 'None', */ - 'session.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null, + 'session.cookie.samesite' => $httpUtils->canSetSameSiteNone() ? 'None' : null, /* * Options to override the default settings for php sessions. @@ -789,10 +790,10 @@ $config = [ 'language.cookie.name' => 'language', 'language.cookie.domain' => null, 'language.cookie.path' => '/', - 'language.cookie.secure' => false, + 'language.cookie.secure' => true, 'language.cookie.httponly' => false, 'language.cookie.lifetime' => (60 * 60 * 24 * 900), - 'language.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null, + 'language.cookie.samesite' => $httpUtils->canSetSameSiteNone() ? 'None' : null, /** * Custom getLanguage function called from SimpleSAML\Locale\Language::getLanguage(). diff --git a/docs/index.md b/docs/index.md index 6fa1cf1a92c5aee242b5c337397cd08cdbb12a16..7ccd397d763591bb870ed0822794ec00ee3f6c33 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ SimpleSAMLphp Documentation * [Identity Provider QuickStart](simplesamlphp-idp) * [IdP hosted reference](simplesamlphp-reference-idp-hosted) * [SP remote reference](simplesamlphp-reference-sp-remote) - * [Use case: Setting up an IdP for G Suite (Google Apps)](simplesamlphp-googleapps) + * [Use case: Setting up an IdP for Google Workspace (G Suite / Google Apps)](simplesamlphp-googleapps) * [Configuring HTTP-Artifact](./simplesamlphp-artifact-idp) * [Identity Provider Advanced Topics](simplesamlphp-idp-more) * [Holder-of-Key profile](simplesamlphp-hok-idp) diff --git a/docs/simplesamlphp-googleapps.md b/docs/simplesamlphp-googleapps.md index d74a04dbf972f67ed000c6c597101f8847f3a665..62ac8d7553457d82bd2bffe8dd4e022d0be9636b 100644 --- a/docs/simplesamlphp-googleapps.md +++ b/docs/simplesamlphp-googleapps.md @@ -1,4 +1,4 @@ -Setting up a SimpleSAMLphp SAML 2.0 IdP to use with Google Apps / G Suite for Education +Setting up a SimpleSAMLphp SAML 2.0 IdP to use with Google Workspace (G Suite / Google Apps) for Education ============================================ <!-- @@ -22,11 +22,12 @@ This document is part of the SimpleSAMLphp documentation suite. ## Introduction -This article assumes that you have already read the SimpleSAMLphp installation manual, and installed a version of SimpleSAMLphp at your -server. - -In this example we will setup this server as an IdP for Google Apps for Education: +This article describes how to configure a Google Workspace (formerly G Suite, formerly Google Apps) +instance as a service provider to use with a SimpleSAMLphp identity provider. +This article assumes that you have already read the SimpleSAMLphp installation manual, and installed +a version of SimpleSAMLphp at your server. +In this example we will setup this server as an IdP for Google Workspace: dev2.andreas.feide.no @@ -41,7 +42,7 @@ Edit `config.php`, and enable the SAML 2.0 IdP: You must generate a certificate for your IdP. Here is an example of an openssl command to generate a new key and a self signed certificate to use for signing SAML messages: - openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes -out googleappsidp.crt -keyout googleappsidp.pem + openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes -out googleworkspaceidp.crt -keyout googleworkspaceidp.pem The certificate above will be valid for 10 years. @@ -118,7 +119,7 @@ This configuration creates two users - `student` and `employee`, with the passwo ## Configuring metadata for an SAML 2.0 IdP -If you want to setup a SAML 2.0 IdP for Google Apps, you need to configure two metadata files: `saml20-idp-hosted.php` and `saml20-sp-remote.php`. +If you want to setup a SAML 2.0 IdP for Google Workspace, you need to configure two metadata files: `saml20-idp-hosted.php` and `saml20-sp-remote.php`. ### Configuring SAML 2.0 IdP Hosted metadata @@ -132,7 +133,7 @@ This is the configuration of the IdP itself. Here is some example config: 'host' => '__DEFAULT__', // X.509 key and certificate. Relative to the cert directory. - 'privatekey' => 'googleappsidp.pem', + 'privatekey' => 'googleworkspaceidp.pem', 'certificate' => 'googleappsidp.crt', 'auth' => 'example-userpass', @@ -143,12 +144,12 @@ This is the configuration of the IdP itself. Here is some example config: ### Configuring SAML 2.0 SP Remote metadata -In the `saml20-sp-remote.php` file we will configure an entry for G Suite (Google Apps) for Education. There is already an entry for G Suite in the template, but we will change the domain name: +In the `saml20-sp-remote.php` file we will configure an entry for Google Workspace for Education. There is already an entry for Google Workspace in the template, but we will change the domain name: /* - * This example shows an example config that works with G Suite (Google Apps) for education. + * This example shows an example config that works with Google Workspace (G Suite / Google Apps) for education. * What is important is that you have an attribute in your IdP that maps to the local part of the email address - * at G Suite. E.g. if your google account is foo.com, and you have a user with email john@foo.com, then you + * at Google Workspace. E.g. if your google account is foo.com, and you have a user with email john@foo.com, then you * must set the simplesaml.nameidattribute to be the name of an attribute that for this user has the value of 'john'. */ $metadata['https://www.google.com/a/g.feide.no'] => [ @@ -158,7 +159,7 @@ In the `saml20-sp-remote.php` file we will configure an entry for G Suite (Googl 'simplesaml.attributes' => false ]; -You must also map some attributes received from the authentication module into email field sent to Google Apps. In this example, the `uid` attribute is set. When you later configure the IdP to connect to a LDAP directory or some other authentication source, make sure that the `uid` attribute is set properly, or you can configure another attribute to use here. The `uid` attribute contains the local part of the user name. +You must also map some attributes received from the authentication module into email field sent to Google Workspace. In this example, the `uid` attribute is set. When you later configure the IdP to connect to a LDAP directory or some other authentication source, make sure that the `uid` attribute is set properly, or you can configure another attribute to use here. The `uid` attribute contains the local part of the user name. For an e-mail address `student@g.feide.no`, the `uid` should be set to `student`. @@ -167,9 +168,9 @@ You should modify the `AssertionConsumerService` to include your G Suite domain For an explanation of the parameters, see the [SimpleSAMLphp Identity Provider QuickStart](simplesamlphp-idp). -## Configure G Suite for education +## Configure Google Workspace -Start by logging in to our G SUite for education account panel. +Start by logging in to our Google Workspace for education account panel. Then select "Advanced tools": **Figure 1. We go to advanced tools** @@ -181,7 +182,7 @@ Then select "Set up single sign-on (SSO)": **Figure 2. We go to setup SSO**  -Upload a certificate, such as the googleappsidp.crt created above: +Upload a certificate, such as the googleworkspaceidp.crt created above: **Figure 3. Uploading certificate** @@ -209,7 +210,7 @@ again, using the host name of your IdP server. The Sign-out page or change password URL can be static pages on your server. The network mask determines which IP addresses will be asked for SSO login. -IP addresses not matching this mask will be presented with the normal G Suite login page. +IP addresses not matching this mask will be presented with the normal Google Workspace login page. It is normally best to leave this field empty to enable authentication for all URLs. **Figure 4. Fill out the remaining fields** @@ -218,7 +219,7 @@ It is normally best to leave this field empty to enable authentication for all U ### Add a user in G Suite that is known to the IdP -Before we can test login, a new user must be defined in G Suite. This user must have a mail field matching the email prefix mapped from the attribute as described above in the metadata section. +Before we can test login, a new user must be defined in Google Workspace. This user must have a mail field matching the email prefix mapped from the attribute as described above in the metadata section. ## Test to login to G Suite for education @@ -226,7 +227,7 @@ Go to the URL of your mail account for this domain, the URL is similar to the fo http://mail.google.com/a/yourgoogleappsdomain.com -replacing the last part with your own G Suite domain name. +replacing the last part with your own Google Workspace domain name. ## Security Considerations diff --git a/docs/simplesamlphp-install.md b/docs/simplesamlphp-install.md index 88af9a7cc421e2d55e59bf41e67ae98000baae72..5680ba79cf5f6c55ae4536d45eebae187745e170 100644 --- a/docs/simplesamlphp-install.md +++ b/docs/simplesamlphp-install.md @@ -392,7 +392,7 @@ in a separate document. - [Using SimpleSAMLphp as an Identity Provider (IdP)](simplesamlphp-idp) + [Hosted IdP reference](simplesamlphp-reference-idp-hosted) + [Remote SP reference](simplesamlphp-reference-sp-remote) - + [Setting up an IdP for G Suite (Google Apps)](simplesamlphp-googleapps) + + [Setting up an IdP for Google Workspace (G Suite / Google Apps)](simplesamlphp-googleapps) + [Advanced Topics](simplesamlphp-idp-more) - [Automated Metadata Management](./metarefresh:simplesamlphp-automated_metadata) - [Maintenance and configuration](simplesamlphp-maintenance) diff --git a/docs/simplesamlphp-reference-idp-hosted.md b/docs/simplesamlphp-reference-idp-hosted.md index 8291a610b1f70eb3ff4b3ff78ec7b3b57d1b1d5b..c0655fee3500480cac4860113acd3565fb945b43 100644 --- a/docs/simplesamlphp-reference-idp-hosted.md +++ b/docs/simplesamlphp-reference-idp-hosted.md @@ -355,6 +355,11 @@ The following SAML 2.0 options are available: `validate.authnrequest` : Whether we require signatures on authentication requests sent to this IdP. + Set it to: + + true: authnrequest must be signed (and signature will be validated) + null: authnrequest may be signed, if it is, signature will be validated + false: authnrequest signature is never checked : Note that this option also exists in the SP-remote metadata, and any value in the SP-remote metadata overrides the one configured diff --git a/docs/simplesamlphp-reference-sp-remote.md b/docs/simplesamlphp-reference-sp-remote.md index 2acc6e8e4036700d0f0a59139548a606092ce96c..b3557c0e9915ecbdbab2c27d0ff9e1fa21cbb62c 100644 --- a/docs/simplesamlphp-reference-sp-remote.md +++ b/docs/simplesamlphp-reference-sp-remote.md @@ -296,6 +296,11 @@ The following options can be set: `validate.authnrequest` : Whether we require signatures on authentication requests sent from this SP. + Set it to: + + true: authnrequest must be signed (and signature will be validated) + null: authnrequest may be signed, if it is, signature will be validated + false: authnrequest signature is never checked : Note that this option also exists in the IdP-hosted metadata. The value in the SP-remote metadata overrides the value in the IdP-hosted metadata. diff --git a/docs/simplesamlphp-upgrade-notes-2.0.md b/docs/simplesamlphp-upgrade-notes-2.0.md index 08c4f712cdb57d63781d4923adc935521c05b7aa..979d6d96ede499023d1a2d96bf95ec589ce672c2 100644 --- a/docs/simplesamlphp-upgrade-notes-2.0.md +++ b/docs/simplesamlphp-upgrade-notes-2.0.md @@ -13,3 +13,28 @@ Upgrade notes for SimpleSAMLphp 2.0 - If you're using the core:TargetedID authproc-filter, note that the `attributename` setting has been renamed to `identifyingAttribute`. - The default encryption algorithm is set from AES128_CBC to AES128_GCM. If you're upgrading from an existing implementation, you may want to manually switch back the `sharedkey_algorithm`. Note that CBC is vulnerable to the Padding oracle attack. +- In compliancy with SAML2INT, AuthnRequests that are signed will have their signature validated unless specifically disabled by setting `validate.authnrequest` to `false`. If unset, or set to true, signatures will be validated and requests not passing validation will be refused. +- The following classes have been migrated to non-static: + + lib/SimpleSAMLphp\Utils\Arrays + + lib/SimpleSAMLphp\Utils\Attributes + + lib/SimpleSAMLphp\Utils\Auth + + lib/SimpleSAMLphp\Utils\Config + + lib/SimpleSAMLphp\Utils\Crypto + + lib/SimpleSAMLphp\Utils\EMail + + lib/SimpleSAMLphp\Utils\HTTP + + lib/SimpleSAMLphp\Utils\Net + + lib/SimpleSAMLphp\Utils\Random + + lib/SimpleSAMLphp\Utils\System + + lib/SimpleSAMLphp\Utils\Time + + lib/SimpleSAMLphp\Utils\XML + + If you use any of these classes in your modules or themes, you will now have to instantiate them so that: + + // Old style + $x = \SimpleSAML\Utils\Arrays::arrayize($someVar) + + becomes: + + // New style + $arrayUtils = new \SimpleSAML\Utils\Arrays(); + $x = $arrayUtils->arrayize($someVar); diff --git a/docs/simplesamlphp-upgrade-notes.md b/docs/simplesamlphp-upgrade-notes.md index 58833c9eda846a691314563eee449c513e690f56..0f8e63ad97e2c5934fe60ba5d50473964ded41e7 100644 --- a/docs/simplesamlphp-upgrade-notes.md +++ b/docs/simplesamlphp-upgrade-notes.md @@ -4,6 +4,8 @@ SimpeSAMLphp Upgrade Notes See the following pages for important information for users upgrading from older versions of SimpleSAMLphp: +* [Upgrade notes for version 2.0](simplesamlphp-upgrade-notes-2.0) +* [Upgrade notes for version 1.19](simplesamlphp-upgrade-notes-1.19) * [Upgrade notes for version 1.18](simplesamlphp-upgrade-notes-1.18) * [Upgrade notes for version 1.17](simplesamlphp-upgrade-notes-1.17) * [Upgrade notes for version 1.16](simplesamlphp-upgrade-notes-1.16) diff --git a/lib/SimpleSAML/Auth/ProcessingChain.php b/lib/SimpleSAML/Auth/ProcessingChain.php index a3612ff1981c85583738f14832f1e12f037a1d57..9ffbccceed71143ea6a7c97aacbbac79dafd977d 100644 --- a/lib/SimpleSAML/Auth/ProcessingChain.php +++ b/lib/SimpleSAML/Auth/ProcessingChain.php @@ -248,7 +248,8 @@ class ProcessingChain * in $state['ReturnURL']. */ $id = State::saveState($state, self::COMPLETED_STAGE); - Utils\HTTP::redirectTrustedURL($state['ReturnURL'], [self::AUTHPARAM => $id]); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($state['ReturnURL'], [self::AUTHPARAM => $id]); } else { /* Pass the state to the function defined in $state['ReturnCall']. */ diff --git a/lib/SimpleSAML/Auth/Simple.php b/lib/SimpleSAML/Auth/Simple.php index 6e0ac4e07c94364824d4ff32412cbc2debcf3586..a82792de60f515c87351155ecaa65e3e79f93d3b 100644 --- a/lib/SimpleSAML/Auth/Simple.php +++ b/lib/SimpleSAML/Auth/Simple.php @@ -133,18 +133,19 @@ class Simple $keepPost = true; } + $httpUtils = new Utils\HTTP(); if (array_key_exists('ReturnTo', $params)) { $returnTo = (string) $params['ReturnTo']; } else { if (array_key_exists('ReturnCallback', $params)) { $returnTo = (array) $params['ReturnCallback']; } else { - $returnTo = Utils\HTTP::getSelfURL(); + $returnTo = $httpUtils->getSelfURL(); } } if (is_string($returnTo) && $keepPost && $_SERVER['REQUEST_METHOD'] === 'POST') { - $returnTo = Utils\HTTP::getPOSTRedirectURL($returnTo, $_POST); + $returnTo = $httpUtils->getPOSTRedirectURL($returnTo, $_POST); } if (array_key_exists('ErrorURL', $params)) { @@ -189,7 +190,8 @@ class Simple Assert::true(is_array($params) || is_string($params) || $params === null); if ($params === null) { - $params = Utils\HTTP::getSelfURL(); + $httpUtils = new Utils\HTTP(); + $params = $httpUtils->getSelfURL(); } if (is_string($params)) { @@ -246,7 +248,8 @@ class Simple $stateID = State::saveState($state, $state['ReturnStateStage']); $params[$state['ReturnStateParam']] = $stateID; } - Utils\HTTP::redirectTrustedURL($state['ReturnTo'], $params); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($state['ReturnTo'], $params); } } @@ -314,7 +317,8 @@ class Simple public function getLoginURL(?string $returnTo = null): string { if ($returnTo === null) { - $returnTo = Utils\HTTP::getSelfURL(); + $httpUtils = new Utils\HTTP(); + $returnTo = $httpUtils->getSelfURL(); } $login = Module::getModuleURL('core/as_login.php', [ @@ -337,7 +341,8 @@ class Simple public function getLogoutURL(?string $returnTo = null): string { if ($returnTo === null) { - $returnTo = Utils\HTTP::getSelfURL(); + $httpUtils = new Utils\HTTP(); + $returnTo = $httpUtils->getSelfURL(); } $logout = Module::getModuleURL('core/logout/' . $this->authSource, [ @@ -360,16 +365,17 @@ class Simple */ protected function getProcessedURL(?string $url = null): string { + $httpUtils = new Utils\HTTP(); if ($url === null) { - $url = Utils\HTTP::getSelfURL(); + $url = $httpUtils->getSelfURL(); } $scheme = parse_url($url, PHP_URL_SCHEME); - $host = parse_url($url, PHP_URL_HOST) ? : Utils\HTTP::getSelfHost(); + $host = parse_url($url, PHP_URL_HOST) ? : $httpUtils->getSelfHost(); $port = parse_url($url, PHP_URL_PORT) ? : ( - $scheme ? '' : ltrim(Utils\HTTP::getServerPort(), ':') + $scheme ? '' : ltrim($httpUtils->getServerPort(), ':') ); - $scheme = $scheme ? : (Utils\HTTP::getServerHTTPS() ? 'https' : 'http'); + $scheme = $scheme ? : ($httpUtils->getServerHTTPS() ? 'https' : 'http'); $path = parse_url($url, PHP_URL_PATH) ? : '/'; $query = parse_url($url, PHP_URL_QUERY) ? : ''; $fragment = parse_url($url, PHP_URL_FRAGMENT) ? : ''; diff --git a/lib/SimpleSAML/Auth/Source.php b/lib/SimpleSAML/Auth/Source.php index 80617d427cf25b70815a5f80daee898817559219..79fec9e021c594b0b0d8e72f0575c45de7be22ad 100644 --- a/lib/SimpleSAML/Auth/Source.php +++ b/lib/SimpleSAML/Auth/Source.php @@ -226,7 +226,8 @@ abstract class Source if (is_string($return)) { // redirect... - Utils\HTTP::redirectTrustedURL($return); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($return); } else { call_user_func($return, $state); } diff --git a/lib/SimpleSAML/Auth/State.php b/lib/SimpleSAML/Auth/State.php index 1e7153ea5c57a6b6c4e70243d661242a58463a08..596ce65b9e4cb70aed54690f727d1591ee8d3f0b 100644 --- a/lib/SimpleSAML/Auth/State.php +++ b/lib/SimpleSAML/Auth/State.php @@ -156,7 +156,8 @@ class State public static function getStateId(array &$state, bool $rawId = false): string { if (!array_key_exists(self::ID, $state)) { - $state[self::ID] = Utils\Random::generateID(); + $randomUtils = new Utils\Random(); + $state[self::ID] = $randomUtils->generateID(); } $id = $state[self::ID]; @@ -269,6 +270,7 @@ class State $session = Session::getSessionFromRequest(); $state = $session->getData('\SimpleSAML\Auth\State', $sid['id']); + $httpUtils = new Utils\HTTP(); if ($state === null) { // Could not find saved data if ($allowMissing) { @@ -279,7 +281,7 @@ class State throw new Error\NoState(); } - Utils\HTTP::redirectUntrustedURL($sid['url']); + $httpUtils->redirectUntrustedURL($sid['url']); } $state = unserialize($state); @@ -303,7 +305,7 @@ class State throw new \Exception($msg); } - Utils\HTTP::redirectUntrustedURL($sid['url']); + $httpUtils->redirectUntrustedURL($sid['url']); } return $state; @@ -341,13 +343,14 @@ class State */ public static function throwException(array $state, Error\Exception $exception): void { + $httpUtils = new Utils\HTTP(); if (array_key_exists(self::EXCEPTION_HANDLER_URL, $state)) { // Save the exception $state[self::EXCEPTION_DATA] = $exception; $id = self::saveState($state, self::EXCEPTION_STAGE); // Redirect to the exception handler - Utils\HTTP::redirectTrustedURL( + $httpUtils->redirectTrustedURL( $state[self::EXCEPTION_HANDLER_URL], [self::EXCEPTION_PARAM => $id] ); diff --git a/lib/SimpleSAML/Compat/Logger.php b/lib/SimpleSAML/Compat/Logger.php new file mode 100644 index 0000000000000000000000000000000000000000..0b0bede4ecd36ae2ef25091fbf3da2bb45703c62 --- /dev/null +++ b/lib/SimpleSAML/Compat/Logger.php @@ -0,0 +1,186 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Compat; + +use Psr\Log\InvalidArgumentException; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use SimpleSAML\Assert\Assert; +use SimpleSAML\Logger as SspLogger; + +class Logger implements LoggerInterface +{ + /** + * System is unusable. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function emergency($message, array $context = []): void + { + SspLogger::emergency($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function alert($message, array $context = []): void + { + SspLogger::alert($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function critical($message, array $context = []): void + { + SspLogger::critical($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function error($message, array $context = []): void + { + SspLogger::error($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function warning($message, array $context = []): void + { + SspLogger::warning($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function notice($message, array $context = []): void + { + SspLogger::notice($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function info($message, array $context = []): void + { + SspLogger::info($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * Type hint not possible due to upstream method signature + */ + public function debug($message, array $context = []): void + { + SspLogger::debug($message . ($context ? " " . var_export($context, true) : "")); + } + + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @throws \SimpleSAML\Assert\AssertionFailedException if assertions are false + * + * Type hint not possible due to upstream method signature + */ + public function log($level, $message, array $context = []): void + { + Assert::string($message); + + switch ($level) { + /* From PSR: Calling this method with one of the log level constants + * MUST have the same result as calling the level-specific method + */ + case LogLevel::ALERT: + $this->alert($message, $context); + break; + case LogLevel::CRITICAL: + $this->critical($message, $context); + break; + case LogLevel::DEBUG: + $this->debug($message, $context); + break; + case LogLevel::EMERGENCY: + $this->emergency($message, $context); + break; + case LogLevel::ERROR: + $this->error($message, $context); + break; + case LogLevel::INFO: + $this->info($message, $context); + break; + case LogLevel::NOTICE: + $this->notice($message, $context); + break; + case LogLevel::WARNING: + $this->warning($message, $context); + break; + default: + throw new InvalidArgumentException("Unrecognized log level '$level''"); + } + } +} + diff --git a/lib/SimpleSAML/Compat/SspContainer.php b/lib/SimpleSAML/Compat/SspContainer.php new file mode 100644 index 0000000000000000000000000000000000000000..dd15975a42ccbf63d92d037c7d9a49afa3e1d704 --- /dev/null +++ b/lib/SimpleSAML/Compat/SspContainer.php @@ -0,0 +1,115 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Compat; + +use Psr\Log\LoggerInterface; +use SAML2\Compat\AbstractContainer; +use SAML2\XML\saml\CustomIdentifierInterface; +use SimpleSAML\Assert\Assert; +use SimpleSAML\Utils; +use SimpleSAML\XML\AbstractXMLElement; + +class SspContainer extends AbstractContainer +{ + /** @var \Psr\Log\LoggerInterface */ + protected LoggerInterface $logger; + + /** @var array */ + protected array $registry = []; + + + /** + * Create a new SimpleSAMLphp compatible container. + */ + public function __construct() + { + $this->logger = new Logger(); + } + + + /** + * {@inheritdoc} + * @return \Psr\Log\LoggerInterface + */ + public function getLogger(): LoggerInterface + { + return $this->logger; + } + + + /** + * {@inheritdoc} + * @return string + */ + public function generateId(): string + { + $randomUtils = new Utils\Random(); + return $randomUtils->generateID(); + } + + + /** + * {@inheritdoc} + * @param mixed $message + * @param string $type + */ + public function debugMessage($message, string $type): void + { + $xmlUtils = new Utils\XML(); + $xmlUtils->debugSAMLMessage($message, $type); + } + + + /** + * {@inheritdoc} + * @param string $url + * @param array $data + */ + public function redirect(string $url, array $data = []): void + { + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, $data); + } + + + /** + * {@inheritdoc} + * @param string $url + * @param array $data + */ + public function postRedirect(string $url, array $data = []): void + { + $httpUtils = new Utils\HTTP(); + $httpUtils->submitPOSTData($url, $data); + } + + + /** + * {@inheritdoc} + * @return string + */ + public function getTempDir(): string + { + $sysUtils = new Utils\System(); + return $sysUtils->getTempDir(); + } + + + /** + * {@inheritdoc} + * @param string $filename + * @param string $date + * @param int|null $mode + */ + public function writeFile(string $filename, string $data, int $mode = null): void + { + $sysUtils = new Utils\System(); + + if ($mode === null) { + $mode = 0600; + } + $sysUtils->writeFile($filename, $data, $mode); + } +} diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php index 78e1cf80e067a0617b25b7ab058eb8eb6c35ee2e..bc55b38eca8f32993665fbd4ca0316f4fe56530d 100644 --- a/lib/SimpleSAML/Configuration.php +++ b/lib/SimpleSAML/Configuration.php @@ -233,7 +233,8 @@ class Configuration implements Utils\ClearableState if ($configSet !== 'simplesaml') { throw new \Exception('Configuration set \'' . $configSet . '\' not initialized.'); } else { - self::$configDirs['simplesaml'] = Utils\Config::getConfigDir(); + $configUtils = new Utils\Config(); + self::$configDirs['simplesaml'] = $configUtils->getConfigDir(); } } @@ -262,7 +263,8 @@ class Configuration implements Utils\ClearableState if ($configSet !== 'simplesaml') { throw new \Exception('Configuration set \'' . $configSet . '\' not initialized.'); } else { - self::$configDirs['simplesaml'] = Utils\Config::getConfigDir(); + $configUtils = new Utils\Config(); + self::$configDirs['simplesaml'] = $configUtils->getConfigDir(); } } @@ -435,7 +437,8 @@ class Configuration implements Utils\ClearableState * with the configuration. Use a guessed base path instead of the one provided. */ $c = $this->toArray(); - $c['baseurlpath'] = Utils\HTTP::guessBasePath(); + $httpUtils = new Utils\HTTP(); + $c['baseurlpath'] = $httpUtils->guessBasePath(); throw new Error\CriticalConfigurationError( 'Incorrect format for option \'baseurlpath\'. Value is: "' . $this->getString('baseurlpath', 'simplesaml/') . '". Valid format is in the form' . @@ -463,7 +466,8 @@ class Configuration implements Utils\ClearableState return null; } - return Utils\System::resolvePath($path, $this->getBaseDir()); + $sysUtils = new Utils\System(); + return $sysUtils->resolvePath($path, $this->getBaseDir()); } @@ -1158,8 +1162,10 @@ class Configuration implements Utils\ClearableState ], ]; } elseif ($this->hasValue($prefix . 'certificate')) { + $configUtils = new Utils\Config(); + $file = $this->getString($prefix . 'certificate'); - $file = Utils\Config::getCertPath($file); + $file = $configUtils->getCertPath($file); $data = @file_get_contents($file); if ($data === false) { diff --git a/lib/SimpleSAML/Error/CriticalConfigurationError.php b/lib/SimpleSAML/Error/CriticalConfigurationError.php index b48581cb43db5777b37dc0b398d1de4c3eeffee6..9add0d009dc3007efb1dcdfb20243b18e478cd8e 100644 --- a/lib/SimpleSAML/Error/CriticalConfigurationError.php +++ b/lib/SimpleSAML/Error/CriticalConfigurationError.php @@ -53,7 +53,8 @@ class CriticalConfigurationError extends ConfigurationError { if ($config === null) { $config = self::$minimum_config; - $config['baseurlpath'] = Utils\HTTP::guessBasePath(); + $httpUtils = new Utils\HTTP(); + $config['baseurlpath'] = $httpUtils->guessBasePath(); } Configuration::loadFromArray( diff --git a/lib/SimpleSAML/Error/Error.php b/lib/SimpleSAML/Error/Error.php index e376027dfa33c828c261dfc30c9fabfb8545e812..a73305672f9f42434b0458cb2834089c24eb3c60 100644 --- a/lib/SimpleSAML/Error/Error.php +++ b/lib/SimpleSAML/Error/Error.php @@ -199,12 +199,13 @@ class Error extends Exception } else { $referer = 'unknown'; } + $httpUtils = new Utils\HTTP(); $errorData = [ 'exceptionMsg' => $emsg, 'exceptionTrace' => $etrace, 'reportId' => $reportId, 'trackId' => $session->getTrackID(), - 'url' => Utils\HTTP::getSelfURLNoQuery(), + 'url' => $httpUtils->getSelfURLNoQuery(), 'version' => $config->getVersion(), 'referer' => $referer, ]; @@ -244,7 +245,8 @@ class Error extends Exception && $config->getString('technicalcontact_email', 'na@example.org') !== 'na@example.org' ) { // enable error reporting - $baseurl = Utils\HTTP::getBaseURL(); + $httpUtils = new Utils\HTTP(); + $baseurl = $httpUtils->getBaseURL(); $data['errorReportAddress'] = $baseurl . 'errorreport.php'; } diff --git a/lib/SimpleSAML/Error/ErrorCodes.php b/lib/SimpleSAML/Error/ErrorCodes.php index f581cef8fd1066565b4dfbf4c4ea92c3943d2c11..9f5fbc8e18f893112ddbc41cb273e9e9fc680f71 100644 --- a/lib/SimpleSAML/Error/ErrorCodes.php +++ b/lib/SimpleSAML/Error/ErrorCodes.php @@ -11,7 +11,6 @@ use SimpleSAML\Locale\Translate; * * @package SimpleSAMLphp */ - class ErrorCodes { /** @@ -160,8 +159,12 @@ class ErrorCodes */ public static function getErrorCodeTitle(string $errorCode): string { - $errorCodeTitles = self::getAllErrorCodeTitles(); - return $errorCodeTitles[$errorCode]; + if (array_key_exists($errorCode, self::getAllErrorCodeTitles())) { + $errorCodeTitles = self::getAllErrorCodeTitles(); + return $errorCodeTitles[$errorCode]; + } else { + return Translate::addTagPrefix($errorCode, 'title_'); + } } @@ -174,8 +177,12 @@ class ErrorCodes */ public static function getErrorCodeDescription(string $errorCode): string { - $errorCodeDescriptions = self::getAllErrorCodeDescriptions(); - return $errorCodeDescriptions[$errorCode]; + if (array_key_exists($errorCode, self::getAllErrorCodeTitles())) { + $errorCodeDescriptions = self::getAllErrorCodeDescriptions(); + return $errorCodeDescriptions[$errorCode]; + } else { + return Translate::addTagPrefix($errorCode, 'descr_'); + } } diff --git a/lib/SimpleSAML/Error/NotFound.php b/lib/SimpleSAML/Error/NotFound.php index f7f180463a0dff1a0b57ae6514539030f0139e1a..e73da0e1f367a5e64a92674c11ab5183a6b0ab77 100644 --- a/lib/SimpleSAML/Error/NotFound.php +++ b/lib/SimpleSAML/Error/NotFound.php @@ -31,7 +31,8 @@ class NotFound extends Error */ public function __construct(?string $reason = null) { - $url = Utils\HTTP::getSelfURL(); + $httpUtils = new Utils\HTTP(); + $url = $httpUtils->getSelfURL(); if ($reason === null) { parent::__construct(['NOTFOUND', '%URL%' => $url]); diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php index c5f983791fbdb73beb241063846a65f00c4d54b4..7b9ea5be373bd2911e902960adf65ffd706e8fb7 100644 --- a/lib/SimpleSAML/IdP.php +++ b/lib/SimpleSAML/IdP.php @@ -547,7 +547,8 @@ class IdP { Assert::notNull($state['core:Logout:URL']); - Utils\HTTP::redirectTrustedURL($state['core:Logout:URL']); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($state['core:Logout:URL']); Assert::true(false); } } diff --git a/lib/SimpleSAML/IdP/IFrameLogoutHandler.php b/lib/SimpleSAML/IdP/IFrameLogoutHandler.php index 9cf2f30ff8ee5cac6913978ba347a8b83eb20ebb..5d811b107d85bb700fff8f39a8c1516e632f83c4 100644 --- a/lib/SimpleSAML/IdP/IFrameLogoutHandler.php +++ b/lib/SimpleSAML/IdP/IFrameLogoutHandler.php @@ -80,7 +80,8 @@ class IFrameLogoutHandler implements LogoutHandlerInterface } $url = Module::getModuleURL('core/idp/logout-iframe.php', $params); - Utils\HTTP::redirectTrustedURL($url); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url); } diff --git a/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php b/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php index a22834aff93e636f4a1a3456f8a09cc4541ecac0..e5b02908c62c31a37e43f891d3d05afb973ea6f7 100644 --- a/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php +++ b/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php @@ -60,7 +60,8 @@ class TraditionalLogoutHandler implements LogoutHandlerInterface try { $idp = IdP::getByState($association); $url = call_user_func([$association['Handler'], 'getLogoutURL'], $idp, $association, $relayState); - Utils\HTTP::redirectTrustedURL($url); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url); } catch (\Exception $e) { Logger::warning('Unable to initialize logout to ' . var_export($id, true) . '.'); $this->idp->terminateAssociation($id); diff --git a/lib/SimpleSAML/Kernel.php b/lib/SimpleSAML/Kernel.php index be7321a35b1b83d05b129f0827ce7f5d3509e8e0..2c13a76c34a5185672ea3053688f76cbdbbe3b1b 100644 --- a/lib/SimpleSAML/Kernel.php +++ b/lib/SimpleSAML/Kernel.php @@ -50,7 +50,8 @@ class Kernel extends BaseKernel $configuration = Configuration::getInstance(); $cachePath = $configuration->getString('tempdir') . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . $this->module; - if (System::isAbsolutePath($cachePath)) { + $sysUtils = new System(); + if ($sysUtils->isAbsolutePath($cachePath)) { return $cachePath; } @@ -66,7 +67,8 @@ class Kernel extends BaseKernel $configuration = Configuration::getInstance(); $loggingPath = $configuration->getString('loggingdir'); - if (System::isAbsolutePath($loggingPath)) { + $sysUtils = new System(); + if ($sysUtils->isAbsolutePath($loggingPath)) { return $loggingPath; } diff --git a/lib/SimpleSAML/Locale/Language.php b/lib/SimpleSAML/Locale/Language.php index 347b41232e8b55cf4d2473c61709e61e4748fab1..6d3cd9d5dcbe21be671266cb58fc02b9fbb186c0 100644 --- a/lib/SimpleSAML/Locale/Language.php +++ b/lib/SimpleSAML/Locale/Language.php @@ -290,7 +290,8 @@ class Language */ private function getHTTPLanguage(): ?string { - $languageScore = Utils\HTTP::getAcceptLanguage(); + $httpUtils = new Utils\HTTP(); + $languageScore = $httpUtils->getAcceptLanguage(); // for now we only use the default language map. We may use a configurable language map in the future $languageMap = self::$defaultLanguageMap; @@ -425,6 +426,7 @@ class Language 'samesite' => ($config->getString('language.cookie.samesite', null)), ]; - Utils\HTTP::setCookie($name, $language, $params, false); + $httpUtils = new Utils\HTTP(); + $httpUtils->setCookie($name, $language, $params, false); } } diff --git a/lib/SimpleSAML/Locale/Translate.php b/lib/SimpleSAML/Locale/Translate.php index be9ac59ed704537a8c8f46956df4476644824879..9285aaf90eb81dbf7efc6dde406d9c75a9a1d436 100644 --- a/lib/SimpleSAML/Locale/Translate.php +++ b/lib/SimpleSAML/Locale/Translate.php @@ -58,7 +58,7 @@ class Translate * Constructor * * @param \SimpleSAML\Configuration $configuration Configuration object - * @param string|null $defaultDictionary The default dictionary where tags will come from. + * @param string|null $defaultDictionary The default dictionary where tags will come from. */ public function __construct(Configuration $configuration, ?string $defaultDictionary = null) { @@ -218,7 +218,7 @@ class Translate /** * Mark a string for translation without translating it. * - * @param string $tag A tag name to mark for translation. + * @param string $tag A tag name to mark for translation. * * @return string The tag, unchanged. */ @@ -234,7 +234,7 @@ class Translate * or in metadata. * * @param string $tag The tag that has a translation - * @param mixed $translation The translation array + * @param mixed $translation The translation array * * @throws \Exception If $translation is neither a string nor an array. */ @@ -256,7 +256,7 @@ class Translate /** * Include a language file from the dictionaries directory. * - * @param string $file File name of dictionary to include + * @param string $file File name of dictionary to include * @param \SimpleSAML\Configuration|null $otherConfig Optionally provide a different configuration object than the * one provided in the constructor to be used to find the directory of the dictionary. This allows to combine * dictionaries inside the SimpleSAMLphp main code distribution together with external dictionaries. Defaults to @@ -449,4 +449,21 @@ class Translate // nothing we can use, return null so that we can set a default return null; } + + /** + * Prefix tag + * + * @param string $tag Translation tag + * @param string $prefix Prefix to be added + * + * @return string Prefixed tag + */ + public static function addTagPrefix(string $tag, string $prefix): string + { + $tagPos = strrpos($tag, ':'); + // if tag contains ':' target actual tag + $tagPos = ($tagPos === false) ? 0 : $tagPos + 1; + // add prefix at $tagPos + return substr_replace($tag, $prefix, $tagPos, 0); + } } diff --git a/lib/SimpleSAML/Logger.php b/lib/SimpleSAML/Logger.php index 49a390dcae82555538ce599e3db878457750a83f..c0b642ec296381640199156e12c09067db7da77a 100644 --- a/lib/SimpleSAML/Logger.php +++ b/lib/SimpleSAML/Logger.php @@ -20,9 +20,9 @@ use SimpleSAML\Logger\SyslogLoggingHandler; class Logger { /** - * @var \SimpleSAML\Logger\LoggingHandlerInterface + * @var \SimpleSAML\Logger\LoggingHandlerInterface|null */ - private static LoggingHandlerInterface $loggingHandler; + private static ?LoggingHandlerInterface $loggingHandler = null; /** * @var bool @@ -275,6 +275,15 @@ class Logger } + /** + * Clears the captured log. + */ + public static function clearCapturedLog(): void + { + self::$capturedLog = []; + } + + /** * Set the track identifier to use in all logs. * @@ -368,6 +377,38 @@ class Logger } + /** + * Returns the current logging handler + * + * @return LoggingHandlerInterface + */ + public static function getLoggingHandler(): ?LoggingHandlerInterface + { + return self::$loggingHandler; + } + + + /** + * Sets the current logging handler + * + * @param LoggingHandlerInterface|null $loggingHandler The logging handler to set + */ + public static function setLoggingHandler(?LoggingHandlerInterface $loggingHandler): void + { + self::$initializing = false; + self::$loggingHandler = $loggingHandler; + } + + /** + * Sets the log level. + * + * @param int $level One of the Logger class constants. + */ + public static function setLogLevel(int $level): void + { + self::$logLevel = $level; + } + /** * Defer a message for later logging. * @@ -412,7 +453,10 @@ class Logger // get the metadata handler option from the configuration if (is_null($handler)) { - $handler = $config->getString('logging.handler', 'syslog'); + $handler = $config->getString( + 'logging.handler', + php_sapi_name() === 'cli' || defined('STDIN') ? 'stderr' : 'syslog' + ); } if (!array_key_exists($handler, $known_handlers) && class_exists($handler)) { @@ -456,15 +500,13 @@ class Logger self::defer($level, $string, $statsLog); return; } elseif (php_sapi_name() === 'cli' || defined('STDIN')) { - // we are being executed from the CLI, nowhere to log - if (!isset(self::$loggingHandler)) { - self::createLoggingHandler(StandardErrorLoggingHandler::class); - } $_SERVER['REMOTE_ADDR'] = "CLI"; if (self::$trackid === self::NO_TRACKID) { self::$trackid = 'CL' . bin2hex(openssl_random_pseudo_bytes(4)); } - } elseif (!isset(self::$loggingHandler)) { + } + + if (!isset(self::$loggingHandler)) { // Initialize logging self::createLoggingHandler(); } diff --git a/lib/SimpleSAML/Logger/FileLoggingHandler.php b/lib/SimpleSAML/Logger/FileLoggingHandler.php index ca2c87bcdfaa9543b3c5545ee63ad7d754d75cf0..0771ce3395ecf4d1013aa7007e7d48efb99521d6 100644 --- a/lib/SimpleSAML/Logger/FileLoggingHandler.php +++ b/lib/SimpleSAML/Logger/FileLoggingHandler.php @@ -72,7 +72,8 @@ class FileLoggingHandler implements LoggingHandlerInterface } } - Utils\Time::initTimezone(); + $timeUtils = new Utils\Time(); + $timeUtils->initTimezone(); } diff --git a/lib/SimpleSAML/Logger/SyslogLoggingHandler.php b/lib/SimpleSAML/Logger/SyslogLoggingHandler.php index 7cf2fc6d05b022bc39547069e7a1888cdae4edad..b08328ab29681c155de940043249fbd94ce081a9 100644 --- a/lib/SimpleSAML/Logger/SyslogLoggingHandler.php +++ b/lib/SimpleSAML/Logger/SyslogLoggingHandler.php @@ -33,7 +33,8 @@ class SyslogLoggingHandler implements LoggingHandlerInterface $processname = preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $config->getString('logging.processname', 'SimpleSAMLphp')); // Setting facility to LOG_USER (only valid in Windows), enable log level rewrite on windows systems - if (Utils\System::getOS() === Utils\System::WINDOWS) { + $sysUtils = new Utils\System(); + if ($sysUtils->getOS() === $sysUtils::WINDOWS) { $this->isWindows = true; $facility = LOG_USER; } diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php index 156e0bafc1a2636806d78f6e3557bfe760a9b43b..0a9dbf8288e9046002003f07ef700bb079296875 100644 --- a/lib/SimpleSAML/Memcache.php +++ b/lib/SimpleSAML/Memcache.php @@ -417,8 +417,9 @@ class Memcache } } + $arrayUtils = new Utils\Arrays(); /** @psalm-var array $stats */ - $stats = Utils\Arrays::transpose($stats); + $stats = $arrayUtils->transpose($stats); $ret = array_merge_recursive($ret, $stats); } diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php index 2af578cd8655dd07f7cf868ef3e270d8a6be5567..4291327288cd4bac81a45765a73582091d3e6cf4 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php @@ -108,7 +108,8 @@ class MetaDataStorageHandler implements ClearableState // get the configuration $config = Configuration::getInstance(); - $baseurl = Utils\HTTP::getSelfURLHost() . $config->getBasePath(); + $httpUtils = new Utils\HTTP(); + $baseurl = $httpUtils->getSelfURLHost() . $config->getBasePath(); if ($set == 'saml20-sp-hosted') { if ($property === 'SingleLogoutServiceBinding') { @@ -146,6 +147,7 @@ class MetaDataStorageHandler implements ClearableState public function getList(string $set = 'saml20-idp-remote', bool $showExpired = false): array { $result = []; + $timeUtils = new Utils\Time(); foreach ($this->sources as $source) { $srcList = $source->getMetadataSet($set); @@ -156,7 +158,7 @@ class MetaDataStorageHandler implements ClearableState unset($srcList[$key]); Logger::warning( "Dropping metadata entity " . var_export($key, true) . ", expired " . - Utils\Time::generateTimestamp($le['expire']) . "." + $timeUtils->generateTimestamp($le['expire']) . "." ); } } @@ -199,7 +201,8 @@ class MetaDataStorageHandler implements ClearableState public function getMetaDataCurrentEntityID(string $set, string $type = 'entityid'): string { // first we look for the hostname/path combination - $currenthostwithpath = Utils\HTTP::getSelfHostWithPath(); // sp.example.org/university + $httpUtils = new Utils\HTTP(); + $currenthostwithpath = $httpUtils->getSelfHostWithPath(); // sp.example.org/university foreach ($this->sources as $source) { $index = $source->getEntityIdFromHostPath($currenthostwithpath, $set, $type); @@ -209,7 +212,7 @@ class MetaDataStorageHandler implements ClearableState } // then we look for the hostname - $currenthost = Utils\HTTP::getSelfHost(); // sp.example.org + $currenthost = $httpUtils->getSelfHost(); // sp.example.org foreach ($this->sources as $source) { $index = $source->getEntityIdFromHostPath($currenthost, $set, $type); @@ -267,15 +270,16 @@ class MetaDataStorageHandler implements ClearableState public function getMetaDataForEntities(array $entityIds, string $set): array { $result = []; + $timeUtils = new Utils\Time(); foreach ($this->sources as $source) { $srcList = $source->getMetaDataForEntities($entityIds, $set); foreach ($srcList as $key => $le) { if (array_key_exists('expire', $le)) { if ($le['expire'] < time()) { unset($srcList[$key]); - \SimpleSAML\Logger::warning( + Logger::warning( "Dropping metadata entity " . var_export($key, true) . ", expired " . - \SimpleSAML\Utils\Time::generateTimestamp($le['expire']) . "." + $timeUtils->generateTimestamp($le['expire']) . "." ); continue; } diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php index 358ba5839e32efd4e089247e05450c3772ef9b30..ef7ed5c63317baef3cbe81225d15ae25994bf6fd 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php @@ -51,7 +51,8 @@ class MetaDataStorageHandlerSerialize extends MetaDataStorageSource /* Resolve this directory relative to the SimpleSAMLphp directory (unless it is * an absolute path). */ - $this->directory = Utils\System::resolvePath($this->directory, $globalConfig->getBaseDir()); + $sysUtils = new Utils\System(); + $this->directory = $sysUtils->resolvePath($this->directory, $globalConfig->getBaseDir()); } diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php index bf7a425bcde2771a74698ba33edfbb102c0d7027..faef115c5ff069dce575b690464018afa936ff4c 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php @@ -194,8 +194,9 @@ abstract class MetaDataStorageSource continue; } + $netUtils = new Utils\Net(); foreach ($cidrHints as $hint_entry) { - if (Utils\Net::ipCIDRcheck($hint_entry, $ip)) { + if ($netUtils->ipCIDRcheck($hint_entry, $ip)) { if ($type === 'entityid') { return $entry['entityid']; } else { @@ -288,7 +289,8 @@ abstract class MetaDataStorageSource protected function lookupIndexFromEntityId(string $entityId, array $metadataSet) { // check for hostname - $currentHost = Utils\HTTP::getSelfHost(); // sp.example.org + $httpUtils = new Utils\HTTP(); + $currentHost = $httpUtils->getSelfHost(); // sp.example.org foreach ($metadataSet as $index => $entry) { // explicit index match @@ -315,14 +317,15 @@ abstract class MetaDataStorageSource private function getDynamicHostedUrl(string $set): string { // get the configuration - $baseUrl = Utils\HTTP::getBaseURL(); + $httpUtils = new Utils\HTTP(); + $baseUrl = $httpUtils->getBaseURL(); if ($set === 'saml20-idp-hosted') { return $baseUrl . 'saml2/idp/metadata.php'; } elseif ($set === 'saml20-sp-hosted') { return $baseUrl . 'saml2/sp/metadata.php'; } elseif ($set === 'adfs-idp-hosted') { - return 'urn:federation:' . Utils\HTTP::getSelfHost() . ':idp'; + return 'urn:federation:' . $httpUtils->getSelfHost() . ':idp'; } else { throw new \Exception('Can not generate dynamic EntityID for metadata of this type: [' . $set . ']'); } diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php index c6155407b78a6bae1268039acdca52fb44199148..ffd0545938007dc3b45a9ec70da3b93b577fff05 100644 --- a/lib/SimpleSAML/Metadata/SAMLBuilder.php +++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php @@ -130,7 +130,8 @@ class SAMLBuilder { $xml = $this->getEntityDescriptor(); if ($formatted) { - Utils\XML::formatDOMElement($xml); + $xmlUtils = new Utils\XML(); + $xmlUtils->formatDOMElement($xml); } return $xml->ownerDocument->saveXML(); @@ -331,9 +332,11 @@ class SAMLBuilder return; } - $orgName = Utils\Arrays::arrayize($metadata['OrganizationName'], 'en'); - $orgDisplayName = Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en'); - $orgURL = Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en'); + $arrayUtils = new Utils\Arrays(); + + $orgName = $arrayUtils->arrayize($metadata['OrganizationName'], 'en'); + $orgDisplayName = $arrayUtils->arrayize($metadata['OrganizationDisplayName'], 'en'); + $orgURL = $arrayUtils->arrayize($metadata['OrganizationURL'], 'en'); $this->addOrganization($orgName, $orgDisplayName, $orgURL); } diff --git a/lib/SimpleSAML/Metadata/SAMLParser.php b/lib/SimpleSAML/Metadata/SAMLParser.php index 41481d538da0c208b493027afcfb01f60306bb74..3b79cd0e6835fbf41bf11c84df50996d93f8cd6d 100644 --- a/lib/SimpleSAML/Metadata/SAMLParser.php +++ b/lib/SimpleSAML/Metadata/SAMLParser.php @@ -231,7 +231,8 @@ class SAMLParser public static function parseFile(string $file): SAMLParser { /** @var string $data */ - $data = Utils\HTTP::fetch($file); + $httpUtils = new Utils\HTTP(); + $data = $httpUtils->fetch($file); try { $doc = DOMDocumentFactory::fromString($data); @@ -310,7 +311,8 @@ class SAMLParser } /** @var string $data */ - $data = Utils\HTTP::fetch($file); + $httpUtils = new Utils\HTTP(); + $data = $httpUtils->fetch($file); try { $doc = DOMDocumentFactory::fromString($data); @@ -362,9 +364,10 @@ class SAMLParser throw new \Exception('Document was empty.'); } - if (Utils\XML::isDOMNodeOfType($element, 'EntityDescriptor', '@md') === true) { + $xmlUtils = new Utils\XML(); + if ($xmlUtils->isDOMNodeOfType($element, 'EntityDescriptor', '@md') === true) { return self::processDescriptorsElement(new EntityDescriptor($element)); - } elseif (Utils\XML::isDOMNodeOfType($element, 'EntitiesDescriptor', '@md') === true) { + } elseif ($xmlUtils->isDOMNodeOfType($element, 'EntitiesDescriptor', '@md') === true) { return self::processDescriptorsElement(new EntitiesDescriptor($element)); } else { throw new \Exception('Unexpected root node: [' . $element->namespaceURI . ']:' . $element->localName); @@ -1242,7 +1245,8 @@ class SAMLParser // find the EntityDescriptor DOMElement. This should be the first (and only) child of the DOMDocument $ed = $doc->documentElement; - if (Utils\XML::isDOMNodeOfType($ed, 'EntityDescriptor', '@md') === false) { + $xmlUtils = new Utils\XML(); + if ($xmlUtils->isDOMNodeOfType($ed, 'EntityDescriptor', '@md') === false) { throw new \Exception('Expected first element in the metadata document to be an EntityDescriptor element.'); } @@ -1261,9 +1265,11 @@ class SAMLParser */ public function validateSignature(array $certificates): bool { + $configUtils = new Utils\Config(); + foreach ($certificates as $cert) { Assert::string($cert); - $certFile = Utils\Config::getCertPath($cert); + $certFile = $configUtils->getCertPath($cert); if (!file_exists($certFile)) { throw new \Exception( 'Could not find certificate file [' . $certFile . '], which is needed to validate signature' diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php index 7d5d6a0a8cd9714939d3772a210dfc959afcd674..01c1cffc3596fcce924c6cffa83cda8ff7edcf55 100644 --- a/lib/SimpleSAML/Metadata/Signer.php +++ b/lib/SimpleSAML/Metadata/Signer.php @@ -226,6 +226,7 @@ class Signer public static function sign(string $metadataString, array $entityMetadata, string $type): string { $config = Configuration::getInstance(); + $configUtils = new Utils\Config(); // check if metadata signing is enabled if (!self::isMetadataSigningEnabled($config, $entityMetadata, $type)) { @@ -235,7 +236,7 @@ class Signer // find the key & certificate which should be used to sign the metadata $keyCertFiles = self::findKeyCert($config, $entityMetadata, $type); - $keyFile = Utils\Config::getCertPath($keyCertFiles['privatekey']); + $keyFile = $configUtils->getCertPath($keyCertFiles['privatekey']); if (!file_exists($keyFile)) { throw new \Exception( 'Could not find private key file [' . $keyFile . '], which is needed to sign the metadata' @@ -243,7 +244,7 @@ class Signer } $keyData = file_get_contents($keyFile); - $certFile = Utils\Config::getCertPath($keyCertFiles['certificate']); + $certFile = $configUtils->getCertPath($keyCertFiles['certificate']); if (!file_exists($certFile)) { throw new \Exception( 'Could not find certificate file [' . $certFile . '], which is needed to sign the metadata' diff --git a/lib/SimpleSAML/Metadata/Sources/MDQ.php b/lib/SimpleSAML/Metadata/Sources/MDQ.php index c4ccb31f38bec4ff2fdc668e2f9f09c7624b1610..278cc57a29e10dcadb1560dbb7a5df88e7f29d21 100644 --- a/lib/SimpleSAML/Metadata/Sources/MDQ.php +++ b/lib/SimpleSAML/Metadata/Sources/MDQ.php @@ -267,8 +267,9 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource $mdq_url = $this->server . '/entities/' . urlencode($entityId); Logger::debug(__CLASS__ . ': downloading metadata for "' . $entityId . '" from [' . $mdq_url . ']'); + $httpUtils = new Utils\HTTP(); try { - $xmldata = Utils\HTTP::fetch($mdq_url); + $xmldata = $httpUtils->fetch($mdq_url); } catch (\Exception $e) { // Avoid propagating the exception, make sure we can handle the error later $xmldata = false; diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php index ef622231928f0f06fd34f642633748108f529175..4139c9dbd763b5c50164426b7d4593f5c7a6edff 100644 --- a/lib/SimpleSAML/Module.php +++ b/lib/SimpleSAML/Module.php @@ -461,9 +461,10 @@ class Module { Assert::notSame($resource[0], '/'); - $url = Utils\HTTP::getBaseURL() . 'module.php/' . $resource; + $httpUtils = new Utils\HTTP(); + $url = $httpUtils->getBaseURL() . 'module.php/' . $resource; if (!empty($parameters)) { - $url = Utils\HTTP::addURLParameters($url, $parameters); + $url = $httpUtils->addURLParameters($url, $parameters); } return $url; } @@ -496,7 +497,7 @@ class Module continue; } - if (!preg_match('/hook_(\w+)\.php/', $file, $matches)) { + if (!preg_match('/^hook_(\w+)\.php$/', $file, $matches)) { continue; } $hook_name = $matches[1]; diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php index 438abff785d89539c23de8e2cc160b216f740c4b..4d8360f4a5bd54624e6a3baa2a05570508c12a82 100644 --- a/lib/SimpleSAML/Session.php +++ b/lib/SimpleSAML/Session.php @@ -368,7 +368,8 @@ class Session implements Serializable, Utils\ClearableState Logger::warning('Missing AuthToken cookie.'); return null; } - if (!Utils\Crypto::secureCompare($session->authToken, $_COOKIE[$authTokenCookieName])) { + $cryptoUtils = new Utils\Crypto(); + if (!$cryptoUtils->secureCompare($session->authToken, $_COOKIE[$authTokenCookieName])) { Logger::warning('Invalid AuthToken cookie.'); return null; } @@ -649,7 +650,8 @@ class Session implements Serializable, Utils\ClearableState $this->authData[$authority] = $data; - $this->authToken = Utils\Random::generateID(); + $randomUtils = new Utils\Random(); + $this->authToken = $randomUtils->generateID(); $sessionHandler = SessionHandler::getSessionHandler(); if ( @@ -660,8 +662,9 @@ class Session implements Serializable, Utils\ClearableState ) { $this->setRememberMeExpire(); } else { + $httpUtils = new Utils\HTTP(); try { - Utils\HTTP::setCookie( + $httpUtils->setCookie( self::$config->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'), $this->authToken, $sessionHandler->getCookieParams() @@ -790,7 +793,8 @@ class Session implements Serializable, Utils\ClearableState $params = array_merge($sessionHandler->getCookieParams(), $params); if ($this->authToken !== null) { - Utils\HTTP::setCookie( + $httpUtils = new Utils\HTTP(); + $httpUtils->setCookie( self::$config->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'), $this->authToken, $params diff --git a/lib/SimpleSAML/SessionHandlerCookie.php b/lib/SimpleSAML/SessionHandlerCookie.php index a8d2c868d631ddbb53b7eab6bd2429aa5c3837ef..86bad84468289287e7ec569af70858ea43d60135 100644 --- a/lib/SimpleSAML/SessionHandlerCookie.php +++ b/lib/SimpleSAML/SessionHandlerCookie.php @@ -161,6 +161,7 @@ abstract class SessionHandlerCookie extends SessionHandler $params = $this->getCookieParams(); } - Utils\HTTP::setCookie($sessionName, $sessionID, $params, true); + $httpUtils = new Utils\HTTP(); + $httpUtils->setCookie($sessionName, $sessionID, $params, true); } } diff --git a/lib/SimpleSAML/SessionHandlerPHP.php b/lib/SimpleSAML/SessionHandlerPHP.php index e0f2646148b9852b7e22f5be1956001319637b88..1e779ce8b6af1ca2104f11c14e7f1fa887bc9dee 100644 --- a/lib/SimpleSAML/SessionHandlerPHP.php +++ b/lib/SimpleSAML/SessionHandlerPHP.php @@ -178,7 +178,8 @@ class SessionHandlerPHP extends SessionHandler $session_cookie_params = session_get_cookie_params(); - if ($session_cookie_params['secure'] && !Utils\HTTP::isHTTPS()) { + $httpUtils = new Utils\HTTP(); + if ($session_cookie_params['secure'] && !$httpUtils->isHTTPS()) { throw new Error\Exception('Session start with secure cookie not allowed on http.'); } @@ -302,7 +303,8 @@ class SessionHandlerPHP extends SessionHandler $cookieParams = session_get_cookie_params(); } - if ($cookieParams['secure'] && !Utils\HTTP::isHTTPS()) { + $httpUtils = new Utils\HTTP(); + if ($cookieParams['secure'] && !$httpUtils->isHTTPS()) { throw new Error\CannotSetCookie( 'Setting secure cookie on plain HTTP is not allowed.', Error\CannotSetCookie::SECURE_COOKIE diff --git a/lib/SimpleSAML/Utils/Arrays.php b/lib/SimpleSAML/Utils/Arrays.php index b667a7fe4d596dce1d4cf7225166e42f6fde015a..b736f130f185432a385515a0b2be84d43258474f 100644 --- a/lib/SimpleSAML/Utils/Arrays.php +++ b/lib/SimpleSAML/Utils/Arrays.php @@ -21,7 +21,7 @@ class Arrays * array. * */ - public static function arrayize($data, $index = 0): array + public function arrayize($data, $index = 0): array { return (is_array($data)) ? $data : [$index => $data]; } @@ -35,7 +35,7 @@ class Arrays * @return array|false The transposed array, or false if $array is not a valid two-dimensional array. * */ - public static function transpose(array $array) + public function transpose(array $array) { $ret = []; foreach ($array as $k1 => $a2) { diff --git a/lib/SimpleSAML/Utils/Attributes.php b/lib/SimpleSAML/Utils/Attributes.php index 047efd52235d230e145f186c0fd5fe3ded070894..e778cb12bfb8ee8cefc55ef3ec49c7f687908aa0 100644 --- a/lib/SimpleSAML/Utils/Attributes.php +++ b/lib/SimpleSAML/Utils/Attributes.php @@ -27,7 +27,7 @@ class Attributes * @throws \InvalidArgumentException If $attributes is not an array or $expected is not a string. * @throws \SimpleSAML\Error\Exception If the expected attribute was not found in the attributes array. */ - public static function getExpectedAttribute(array $attributes, string $expected, bool $allow_multiple = false) + public function getExpectedAttribute(array $attributes, string $expected, bool $allow_multiple = false) { if (!array_key_exists($expected, $attributes)) { throw new Error\Exception("No such attribute '" . $expected . "' found."); @@ -66,7 +66,7 @@ class Attributes * not strings. * */ - public static function normalizeAttributesArray(array $attributes): array + public function normalizeAttributesArray(array $attributes): array { $newAttrs = []; foreach ($attributes as $name => $values) { @@ -74,7 +74,8 @@ class Attributes throw new \InvalidArgumentException('Invalid attribute name: "' . print_r($name, true) . '".'); } - $values = Arrays::arrayize($values); + $arrayUtils = new Arrays(); + $values = $arrayUtils->arrayize($values); foreach ($values as $value) { if (!is_string($value)) { @@ -103,7 +104,7 @@ class Attributes * * @return array The attribute name, split to the namespace and the actual attribute name. */ - public static function getAttributeNamespace(string $name, string $defaultns): array + public function getAttributeNamespace(string $name, string $defaultns): array { $slash = strrpos($name, '/'); if ($slash !== false) { diff --git a/lib/SimpleSAML/Utils/Auth.php b/lib/SimpleSAML/Utils/Auth.php index 614749aee98382b54d0f76667d10faa4f40f277c..827d01ac72c1fa7861695452cf761abcd02704d9 100644 --- a/lib/SimpleSAML/Utils/Auth.php +++ b/lib/SimpleSAML/Utils/Auth.php @@ -24,10 +24,11 @@ class Auth * @return string A URL which can be used for admin authentication. * @throws \InvalidArgumentException If $returnTo is neither a string nor null. */ - public static function getAdminLoginURL(?string $returnTo = null): string + public function getAdminLoginURL(?string $returnTo = null): string { + $httpUtils = new HTTP(); if ($returnTo === null) { - $returnTo = HTTP::getSelfURL(); + $returnTo = $httpUtils->getSelfURL(); } return Module::getModuleURL('core/login-admin.php', ['ReturnTo' => $returnTo]); @@ -42,7 +43,7 @@ class Auth * @return string A URL which can be used for logging out. * @throws \InvalidArgumentException If $returnTo is neither a string nor null. */ - public static function getAdminLogoutURL(?string $returnTo = null): string + public function getAdminLogoutURL(?string $returnTo = null): string { $as = new Authentication\Simple('admin'); return $as->getLogoutURL($returnTo); @@ -55,7 +56,7 @@ class Auth * @return boolean True if the current user is an admin user, false otherwise. * */ - public static function isAdmin(): bool + public function isAdmin(): bool { $session = Session::getSessionFromRequest(); return $session->isValid('admin') || $session->isValid('login-admin'); @@ -71,9 +72,9 @@ class Auth * @throws \SimpleSAML\Error\Exception If no "admin" authentication source was configured. * */ - public static function requireAdmin(): void + public function requireAdmin(): void { - if (self::isAdmin()) { + if ($this->isAdmin()) { return; } diff --git a/lib/SimpleSAML/Utils/Config.php b/lib/SimpleSAML/Utils/Config.php index 6e4c029f6e757adf3e90a39dd4d77013e5f188dd..567ad67de5be52706f4586a69a70484da3837b77 100644 --- a/lib/SimpleSAML/Utils/Config.php +++ b/lib/SimpleSAML/Utils/Config.php @@ -22,11 +22,12 @@ class Config * @throws \InvalidArgumentException If $path is not a string. * */ - public static function getCertPath(string $path): string + public function getCertPath(string $path): string { $globalConfig = Configuration::getInstance(); $base = $globalConfig->getPathValue('certdir', 'cert/'); - return System::resolvePath($path, $base); + $sysUtils = new System(); + return $sysUtils->resolvePath($path, $base); } @@ -44,7 +45,7 @@ class Config * @throws \InvalidArgumentException If the secret salt hasn't been configured. * */ - public static function getSecretSalt(): string + public function getSecretSalt(): string { $secretSalt = Configuration::getInstance()->getString('secretsalt'); if ($secretSalt === 'defaultsecretsalt') { @@ -62,7 +63,7 @@ class Config * * @return string The path to the configuration directory. */ - public static function getConfigDir(): string + public function getConfigDir(): string { $configDir = dirname(dirname(dirname(__DIR__))) . '/config'; $configDirEnv = getenv('SIMPLESAMLPHP_CONFIG_DIR'); diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php index efd2aba4320b8334211986bb343d4df8ee79bc84..d7c9bfbae2b35fab0d3b5ca1c729f5c7f9aebdba 100644 --- a/lib/SimpleSAML/Utils/Crypto.php +++ b/lib/SimpleSAML/Utils/Crypto.php @@ -29,8 +29,12 @@ class Crypto * * @see \SimpleSAML\Utils\Crypto::aesDecrypt() */ - private static function aesDecryptInternal(string $ciphertext, string $secret): string + private function aesDecryptInternal(string $ciphertext, string $secret): string { + if (!extension_loaded('openssl')) { + throw new Error\Exception("The openssl PHP module is not loaded."); + } + /** @var int $len */ $len = mb_strlen($ciphertext, '8bit'); if ($len < 48) { @@ -38,9 +42,6 @@ class Crypto 'Input parameter "$ciphertext" must be a string with more than 48 characters.' ); } - if (!function_exists("openssl_decrypt")) { - throw new Error\Exception("The openssl PHP module is not loaded."); - } // derive encryption and authentication keys from the secret $key = openssl_digest($secret, 'sha512'); @@ -50,7 +51,7 @@ class Crypto $msg = mb_substr($ciphertext, 48, $len - 48, '8bit'); // authenticate the ciphertext - if (self::secureCompare(hash_hmac('sha256', $iv . $msg, substr($key, 64, 64), true), $hmac)) { + if ($this->secureCompare(hash_hmac('sha256', $iv . $msg, substr($key, 64, 64), true), $hmac)) { $plaintext = openssl_decrypt( $msg, 'AES-256-CBC', @@ -72,15 +73,22 @@ class Crypto * Decrypt data using AES-256-CBC and the system-wide secret salt as key. * * @param string $ciphertext The HMAC of the encrypted data, the IV used and the encrypted data, concatenated. + * @param string $secret The secret to use to decrypt the data. + * If not provided, the secret salt from the configuration will be used * * @return string The decrypted data. * @throws \InvalidArgumentException If $ciphertext is not a string. * @throws Error\Exception If the openssl module is not loaded. * */ - public static function aesDecrypt(string $ciphertext): string + public function aesDecrypt(string $ciphertext, string $secret = null): string { - return self::aesDecryptInternal($ciphertext, Config::getSecretSalt()); + if ($secret === null) { + $configUtils = new Config(); + $secret = $configUtils->getSecretSalt(); + } + + return $this->aesDecryptInternal($ciphertext, $secret); } @@ -96,9 +104,9 @@ class Crypto * * @see \SimpleSAML\Utils\Crypto::aesEncrypt() */ - private static function aesEncryptInternal(string $data, string $secret): string + private function aesEncryptInternal(string $data, string $secret): string { - if (!function_exists("openssl_encrypt")) { + if (!extension_loaded('openssl')) { throw new Error\Exception('The openssl PHP module is not loaded.'); } @@ -130,15 +138,22 @@ class Crypto * Encrypt data using AES-256-CBC and the system-wide secret salt as key. * * @param string $data The data to encrypt. + * @param string $secret The secret to use to decrypt the data. + * If not provided, the secret salt from the configuration will be used * * @return string An HMAC of the encrypted data, the IV and the encrypted data, concatenated. * @throws \InvalidArgumentException If $data is not a string. * @throws Error\Exception If the openssl module is not loaded. * */ - public static function aesEncrypt(string $data): string + public function aesEncrypt(string $data, string $secret = null): string { - return self::aesEncryptInternal($data, Config::getSecretSalt()); + if ($secret === null) { + $configUtils = new Config(); + $secret = $configUtils->getSecretSalt(); + } + + return $this->aesEncryptInternal($data, $secret); } @@ -150,7 +165,7 @@ class Crypto * @return string The same data encoded in PEM format. * @see RFC7648 for known types and PEM format specifics. */ - public static function der2pem(string $der, string $type = 'CERTIFICATE'): string + public function der2pem(string $der, string $type = 'CERTIFICATE'): string { return "-----BEGIN " . $type . "-----\n" . chunk_split(base64_encode($der), 64, "\n") . @@ -183,7 +198,7 @@ class Crypto * it. * */ - public static function loadPrivateKey( + public function loadPrivateKey( Configuration $metadata, bool $required = false, string $prefix = '', @@ -200,7 +215,8 @@ class Crypto } if (!$full_path) { - $file = Config::getCertPath($file); + $configUtils = new Config(); + $file = $configUtils->getCertPath($file); } $data = @file_get_contents($file); @@ -243,7 +259,7 @@ class Crypto * it. * */ - public static function loadPublicKey(Configuration $metadata, bool $required = false, string $prefix = ''): ?array + public function loadPublicKey(Configuration $metadata, bool $required = false, string $prefix = ''): ?array { $keys = $metadata->getPublicKeys(null, false, $prefix); if (!empty($keys)) { @@ -283,7 +299,7 @@ class Crypto * @throws \InvalidArgumentException If $pem is not encoded in PEM format. * @see RFC7648 for PEM format specifics. */ - public static function pem2der(string $pem): string + public function pem2der(string $pem): string { $pem = trim($pem); $begin = "-----BEGIN "; @@ -311,18 +327,15 @@ class Crypto * @param mixed $algorithm The algorithm to use. Defaults to the system default * * @return string The hashed password. - * @throws \InvalidArgumentException If the input parameter is not a string. + * @throws \Exception If the algorithm is not known ti PHP. * @throws Error\Exception If the algorithm specified is not supported. * * @see hash_algos() * */ - public static function pwHash(string $password, $algorithm = PASSWORD_DEFAULT): string + public function pwHash(string $password, $algorithm = PASSWORD_DEFAULT): string { - if (!is_string($hash = password_hash($password, $algorithm))) { - throw new InvalidArgumentException('Error while hashing password.'); - } - return $hash; + return password_hash($password, $algorithm); } @@ -337,7 +350,7 @@ class Crypto * * @return bool True if both strings are equal, false otherwise. */ - public static function secureCompare(string $known, string $user): bool + public function secureCompare(string $known, string $user): bool { return hash_equals($known, $user); } @@ -354,7 +367,7 @@ class Crypto * @throws Error\Exception If the algorithm specified is not supported. * */ - public static function pwValid(string $hash, string $password): bool + public function pwValid(string $hash, string $password): bool { if (!is_null(password_get_info($password)['algo'])) { throw new Error\Exception("Cannot use a hash value for authentication."); diff --git a/lib/SimpleSAML/Utils/EMail.php b/lib/SimpleSAML/Utils/EMail.php index 38a71ad872977d981d0aa9593cfb704ec89248c7..8a99fe0de908ad7da546db779a2c9b51f343e344 100644 --- a/lib/SimpleSAML/Utils/EMail.php +++ b/lib/SimpleSAML/Utils/EMail.php @@ -45,10 +45,10 @@ class EMail { $this->mail = new PHPMailer(true); $this->mail->Subject = $subject; - $this->mail->setFrom($from ?: static::getDefaultMailAddress()); - $this->mail->addAddress($to ?: static::getDefaultMailAddress()); + $this->mail->setFrom($from ?: $this->getDefaultMailAddress()); + $this->mail->addAddress($to ?: $this->getDefaultMailAddress()); - static::initFromConfig($this); + $this->initFromConfig($this); } @@ -62,7 +62,7 @@ class EMail * * @return string Default mail address */ - public static function getDefaultMailAddress(): string + public function getDefaultMailAddress(): string { $config = Configuration::getInstance(); $address = $config->getString('technicalcontact_email', 'na@example.org'); @@ -224,7 +224,7 @@ class EMail * @return EMail * @throws \Exception */ - public static function initFromConfig(EMail $EMail): EMail + public function initFromConfig(EMail $EMail): EMail { $config = Configuration::getInstance(); $EMail->setTransportMethod( diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php index 7ebe99637c369220bc8f68ee2c30401a9b4c29cc..dbddd18b4c0ad2041487aa8b53b0ab35b8f02b3a 100644 --- a/lib/SimpleSAML/Utils/HTTP.php +++ b/lib/SimpleSAML/Utils/HTTP.php @@ -28,7 +28,7 @@ class HTTP * https://www.chromium.org/updates/same-site/incompatible-clients * @return bool true if user agent supports a None value for SameSite. */ - public static function canSetSameSiteNone(): bool + public function canSetSameSiteNone(): bool { $useragent = $_SERVER['HTTP_USER_AGENT'] ?? null; if (!$useragent) { @@ -73,10 +73,10 @@ class HTTP * @return string A URL which allows to securely post a form to $destination. * */ - private static function getSecurePOSTRedirectURL(string $destination, array $data): string + private function getSecurePOSTRedirectURL(string $destination, array $data): string { $session = Session::getSessionFromRequest(); - $id = self::savePOSTData($session, $destination, $data); + $id = $this->savePOSTData($session, $destination, $data); if ($session->isTransient()) { // this is a transient session, it is pointless to continue @@ -87,7 +87,8 @@ class HTTP $session_id = $session->getSessionId(); // encrypt the session ID and the random ID - $info = base64_encode(Crypto::aesEncrypt($session_id . ':' . $id)); + $cryptoUtils = new Crypto(); + $info = base64_encode($cryptoUtils->aesEncrypt($session_id . ':' . $id)); $url = Module::getModuleURL('core/postredirect.php', ['RedirInfo' => $info]); return preg_replace('#^https:#', 'http:', $url); @@ -101,7 +102,7 @@ class HTTP * determine the current host. * */ - private static function getServerHost(): string + private function getServerHost(): string { if (array_key_exists('HTTP_HOST', $_SERVER)) { $current = $_SERVER['HTTP_HOST']; @@ -130,7 +131,7 @@ class HTTP * @return boolean True if the request was performed through HTTPS, false otherwise. * */ - public static function getServerHTTPS(): bool + public function getServerHTTPS(): bool { if (!array_key_exists('HTTPS', $_SERVER)) { // not an https-request @@ -154,9 +155,9 @@ class HTTP * (80 for HTTP, 443 for HTTPS), or an empty string otherwise. * */ - public static function getServerPort(): string + public function getServerPort(): string { - $default_port = self::getServerHTTPS() ? '443' : '80'; + $default_port = $this->getServerHTTPS() ? '443' : '80'; $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $default_port; // Take care of edge-case where SERVER_PORT is an integer @@ -176,7 +177,7 @@ class HTTP * * @return boolean True if the given URL is valid, false otherwise. */ - public static function isValidURL(string $url): bool + public function isValidURL(string $url): bool { $url = filter_var($url, FILTER_VALIDATE_URL); if ($url === false) { @@ -210,18 +211,18 @@ class HTTP * @throws \SimpleSAML\Error\Exception If $url is not a valid HTTP URL. * */ - private static function redirect(string $url, array $parameters = []): void + private function redirect(string $url, array $parameters = []): void { if (empty($url)) { throw new \InvalidArgumentException('Invalid input parameters.'); } - if (!self::isValidURL($url)) { + if (!$this->isValidURL($url)) { throw new Error\Exception('Invalid destination URL.'); } if (!empty($parameters)) { - $url = self::addURLParameters($url, $parameters); + $url = $this->addURLParameters($url, $parameters); } /* Set the HTTP result code. This is either 303 See Other or @@ -284,10 +285,11 @@ class HTTP * @return string A random identifier that can be used to retrieve the data from the current session. * */ - private static function savePOSTData(Session $session, string $destination, array $data): string + private function savePOSTData(Session $session, string $destination, array $data): string { // generate a random ID to avoid replay attacks - $id = Random::generateID(); + $randomUtils = new Random(); + $id = $randomUtils->generateID(); $postData = [ 'post' => $data, 'url' => $destination, @@ -311,7 +313,7 @@ class HTTP * @throws \InvalidArgumentException If $url is not a string or $parameters is not an array. * */ - public static function addURLParameters(string $url, array $parameters): string + public function addURLParameters(string $url, array $parameters): string { $queryStart = strpos($url, '?'); if ($queryStart === false) { @@ -322,7 +324,7 @@ class HTTP if ($oldQuery === false) { $oldQuery = []; } else { - $oldQuery = self::parseQueryString($oldQuery); + $oldQuery = $this->parseQueryString($oldQuery); } $url = substr($url, 0, $queryStart + 1); } @@ -343,7 +345,7 @@ class HTTP * @throws \InvalidArgumentException If $retryURL is neither a string nor null. * */ - public static function checkSessionCookie(?string $retryURL = null): void + public function checkSessionCookie(?string $retryURL = null): void { $session = Session::getSessionFromRequest(); if ($session->hasSessionCookie()) { @@ -354,9 +356,9 @@ class HTTP $url = Module::getModuleURL('core/no_cookie.php'); if ($retryURL !== null) { - $url = self::addURLParameters($url, ['retryURL' => $retryURL]); + $url = $this->addURLParameters($url, ['retryURL' => $retryURL]); } - self::redirectTrustedURL($url); + $this->redirectTrustedURL($url); } @@ -373,14 +375,14 @@ class HTTP * @throws Error\Exception If the URL is not allowed by configuration. * */ - public static function checkURLAllowed(string $url, array $trustedSites = null): string + public function checkURLAllowed(string $url, array $trustedSites = null): string { if (empty($url)) { return ''; } - $url = self::normalizeURL($url); + $url = $this->normalizeURL($url); - if (!self::isValidURL($url)) { + if (!$this->isValidURL($url)) { throw new Error\Exception('Invalid URL: ' . $url); } @@ -415,7 +417,7 @@ class HTTP $hostname = $hostname . ':' . $components['port']; } - $self_host = self::getSelfHostWithNonStandardPort(); + $self_host = $this->getSelfHostWithNonStandardPort(); $trustedRegex = Configuration::getInstance()->getValue('trusted.url.regex', false); @@ -462,7 +464,7 @@ class HTTP * @throws Error\Exception If the file or URL cannot be retrieved. * */ - public static function fetch(string $url, array $context = [], bool $getHeaders = false) + public function fetch(string $url, array $context = [], bool $getHeaders = false) { $config = Configuration::getInstance(); @@ -548,7 +550,7 @@ class HTTP * @return array An associative array with each language and the score for that language. * */ - public static function getAcceptLanguage(): array + public function getAcceptLanguage(): array { if (!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) { // no Accept-Language header, return an empty set @@ -612,7 +614,7 @@ class HTTP * * @return string The guessed base path that should correspond to the root installation of SimpleSAMLphp. */ - public static function guessBasePath(): string + public function guessBasePath(): string { if (!array_key_exists('REQUEST_URI', $_SERVER) || !array_key_exists('SCRIPT_FILENAME', $_SERVER)) { return '/'; @@ -646,7 +648,7 @@ class HTTP * @throws \SimpleSAML\Error\CriticalConfigurationError If 'baseurlpath' has an invalid format. * */ - public static function getBaseURL(): string + public function getBaseURL(): string { $globalConfig = Configuration::getInstance(); $baseURL = $globalConfig->getString('baseurlpath', 'simplesaml/'); @@ -661,11 +663,11 @@ class HTTP ) { // get server values $protocol = 'http'; - $protocol .= (self::getServerHTTPS()) ? 's' : ''; + $protocol .= ($this->getServerHTTPS()) ? 's' : ''; $protocol .= '://'; - $hostname = self::getServerHost(); - $port = self::getServerPort(); + $hostname =$this->getServerHost(); + $port = $this->getServerPort(); $path = $globalConfig->getBasePath(); return $protocol . $hostname . $port . $path; @@ -675,7 +677,7 @@ class HTTP * with the configuration. Use a guessed base path instead of the one provided. */ $c = $globalConfig->toArray(); - $c['baseurlpath'] = self::guessBasePath(); + $c['baseurlpath'] = $this->guessBasePath(); throw new Error\CriticalConfigurationError( 'Invalid value for \'baseurlpath\' in config.php. Valid format is in the form: ' . '[(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/]. It must end with a \'/\'.', @@ -694,7 +696,7 @@ class HTTP * @return string The first element of the URL path, with an optional, leading slash. * */ - public static function getFirstPathElement(bool $leadingSlash = true): string + public function getFirstPathElement(bool $leadingSlash = true): string { if (preg_match('|^/(.*?)/|', $_SERVER['SCRIPT_NAME'], $matches)) { return ($leadingSlash ? '/' : '') . $matches[1]; @@ -713,18 +715,18 @@ class HTTP * @throws \InvalidArgumentException If $destination is not a string or $data is not an array. * */ - public static function getPOSTRedirectURL(string $destination, array $data): string + public function getPOSTRedirectURL(string $destination, array $data): string { $config = Configuration::getInstance(); $allowed = $config->getBoolean('enable.http_post', false); - if ($allowed && preg_match("#^http:#", $destination) && self::isHTTPS()) { + if ($allowed && preg_match("#^http:#", $destination) && $this->isHTTPS()) { // we need to post the data to HTTP - $url = self::getSecurePOSTRedirectURL($destination, $data); + $url = $this->getSecurePOSTRedirectURL($destination, $data); } else { // post the data directly $session = Session::getSessionFromRequest(); - $id = self::savePOSTData($session, $destination, $data); + $id = $this->savePOSTData($session, $destination, $data); $url = Module::getModuleURL('core/postredirect.php', ['RedirId' => $id]); } @@ -740,9 +742,9 @@ class HTTP * @return string The current host. * */ - public static function getSelfHost(): string + public function getSelfHost(): string { - $decomposed = explode(':', self::getSelfHostWithNonStandardPort()); + $decomposed = explode(':', $this->getSelfHostWithNonStandardPort()); return array_shift($decomposed); } @@ -757,9 +759,9 @@ class HTTP * the protocol. * */ - public static function getSelfHostWithNonStandardPort(): string + public function getSelfHostWithNonStandardPort(): string { - $url = self::getBaseURL(); + $url = $this->getBaseURL(); /** @var int $colon getBaseURL() will allways return a valid URL */ $colon = strpos($url, '://'); @@ -777,12 +779,12 @@ class HTTP * @return string The current host (with non-default ports included) plus the URL path. * */ - public static function getSelfHostWithPath(): string + public function getSelfHostWithPath(): string { - $baseurl = explode("/", self::getBaseURL()); + $baseurl = explode("/", $this->getBaseURL()); $elements = array_slice($baseurl, 3 - count($baseurl), count($baseurl) - 4); $path = implode("/", $elements); - return self::getSelfHostWithNonStandardPort() . "/" . $path; + return $this->getSelfHostWithNonStandardPort() . "/" . $path; } @@ -798,7 +800,7 @@ class HTTP * @return string The current URL, including query parameters. * */ - public static function getSelfURL(): string + public function getSelfURL(): string { $cfg = Configuration::getInstance(); $baseDir = $cfg->getBaseDir(); @@ -841,14 +843,14 @@ class HTTP $port = !empty($port) ? ':' . $port : ''; } else { // no base URL specified for app, just use the current URL - $protocol = self::getServerHTTPS() ? 'https' : 'http'; - $hostname = self::getServerHost(); - $port = self::getServerPort(); + $protocol = $this->getServerHTTPS() ? 'https' : 'http'; + $hostname = $this->getServerHost(); + $port = $this->getServerPort(); } return $protocol . '://' . $hostname . $port . $_SERVER['REQUEST_URI']; } - return self::getBaseURL() . $url_path . substr($_SERVER['REQUEST_URI'], $uri_pos + strlen($url_path)); + return $this->getBaseURL() . $url_path . substr($_SERVER['REQUEST_URI'], $uri_pos + strlen($url_path)); } @@ -859,9 +861,9 @@ class HTTP * @return string The current URL without path or query parameters. * */ - public static function getSelfURLHost(): string + public function getSelfURLHost(): string { - $url = self::getSelfURL(); + $url = $this->getSelfURL(); /** @var int $colon getBaseURL() will allways return a valid URL */ $colon = strpos($url, '://'); @@ -877,9 +879,9 @@ class HTTP * @return string The current URL, not including query parameters. * */ - public static function getSelfURLNoQuery(): string + public function getSelfURLNoQuery(): string { - $url = self::getSelfURL(); + $url = $this->getSelfURL(); $pos = strpos($url, '?'); if (!$pos) { return $url; @@ -894,9 +896,9 @@ class HTTP * @return boolean True if the HTTPS is used, false otherwise. * */ - public static function isHTTPS(): bool + public function isHTTPS(): bool { - return strpos(self::getSelfURL(), 'https://') === 0; + return strpos($this->getSelfURL(), 'https://') === 0; } @@ -910,9 +912,9 @@ class HTTP * @throws \InvalidArgumentException If $url is not a string or a valid URL. * */ - public static function normalizeURL(string $url): string + public function normalizeURL(string $url): string { - $url = self::resolveURL($url, self::getSelfURL()); + $url = $this->resolveURL($url, $this->getSelfURL()); // verify that the URL is to a http or https site if (!preg_match('@^https?://@i', $url)) { @@ -937,7 +939,7 @@ class HTTP * @throws \InvalidArgumentException If $query_string is not a string. * */ - public static function parseQueryString(string $query_string): array + public function parseQueryString(string $query_string): array { $res = []; if (empty($query_string)) { @@ -978,10 +980,10 @@ class HTTP * @throws \InvalidArgumentException If $url is not a string or $parameters is not an array. * */ - public static function redirectTrustedURL(string $url, array $parameters = []): void + public function redirectTrustedURL(string $url, array $parameters = []): void { - $url = self::normalizeURL($url); - self::redirect($url, $parameters); + $url = $this->normalizeURL($url); + $this->redirect($url, $parameters); } @@ -1004,10 +1006,10 @@ class HTTP * @throws \InvalidArgumentException If $url is not a string or $parameters is not an array. * */ - public static function redirectUntrustedURL(string $url, array $parameters = []): void + public function redirectUntrustedURL(string $url, array $parameters = []): void { - $url = self::checkURLAllowed($url); - self::redirect($url, $parameters); + $url = $this->checkURLAllowed($url); + $this->redirect($url, $parameters); } @@ -1030,10 +1032,10 @@ class HTTP * are not strings. * */ - public static function resolveURL(string $url, string $base = null): string + public function resolveURL(string $url, string $base = null): string { if ($base === null) { - $base = self::getBaseURL(); + $base = $this->getBaseURL(); } if (!preg_match('/^((((\w+:)\/\/[^\/]+)(\/[^?#]*))(?:\?[^#]*)?)(?:#.*)?/', $base, $baseParsed)) { @@ -1085,7 +1087,8 @@ class HTTP $tail = ''; } - $dir = System::resolvePath($dir, $baseDir); + $sysUtils = new System(); + $dir = $sysUtils->resolvePath($dir, $baseDir); return $baseHost . $dir . $tail; } @@ -1104,7 +1107,7 @@ class HTTP * * */ - public static function setCookie(string $name, ?string $value, array $params = null, bool $throw = true): void + public function setCookie(string $name, ?string $value, array $params = null, bool $throw = true): void { $default_params = [ 'lifetime' => 0, @@ -1124,7 +1127,7 @@ class HTTP } // Do not set secure cookie if not on HTTPS - if ($params['secure'] && !self::isHTTPS()) { + if ($params['secure'] && !$this->isHTTPS()) { if ($throw) { throw new Error\CannotSetCookie( 'Setting secure cookie on plain HTTP is not allowed.', @@ -1201,18 +1204,18 @@ class HTTP * * */ - public static function submitPOSTData(string $destination, array $data): void + public function submitPOSTData(string $destination, array $data): void { - if (!self::isValidURL($destination)) { + if (!$this->isValidURL($destination)) { throw new Error\Exception('Invalid destination URL.'); } $config = Configuration::getInstance(); $allowed = $config->getBoolean('enable.http_post', false); - if ($allowed && preg_match("#^http:#", $destination) && self::isHTTPS()) { + if ($allowed && preg_match("#^http:#", $destination) && $this->isHTTPS()) { // we need to post the data to HTTP - self::redirect(self::getSecurePOSTRedirectURL($destination, $data)); + $this->redirect($this->getSecurePOSTRedirectURL($destination, $data)); } $p = new Template($config, 'post.twig'); diff --git a/lib/SimpleSAML/Utils/HttpAdapter.php b/lib/SimpleSAML/Utils/HttpAdapter.php deleted file mode 100644 index 46d32ce3ac97a96fc32057df1f04a392561979a0..0000000000000000000000000000000000000000 --- a/lib/SimpleSAML/Utils/HttpAdapter.php +++ /dev/null @@ -1,294 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace SimpleSAML\Utils; - -/** - * Provides a non-static wrapper for the HTTP utility class. - * - * @package SimpleSAML\Utils - */ -class HttpAdapter -{ - /** - * @see HTTP::getServerHTTPS() - * @return bool - */ - public function getServerHTTPS(): bool - { - return HTTP::getServerHTTPS(); - } - - - /** - * @see HTTP::getServerPort() - * @return string - */ - public function getServerPort(): string - { - return HTTP::getServerPort(); - } - - - /** - * @see HTTP::addURLParameters() - * - * @param string $url - * @param array $parameters - * @return string - */ - public function addURLParameters(string $url, array $parameters): string - { - return HTTP::addURLParameters($url, $parameters); - } - - - /** - * @see HTTP::checkSessionCookie() - * - * @param string|null $retryURL - */ - public function checkSessionCookie(string $retryURL = null): void - { - HTTP::checkSessionCookie($retryURL); - } - - - /** - * @see HTTP::checkURLAllowed() - * - * @param string $url - * @param array|null $trustedSites - * @return string - */ - public function checkURLAllowed(string $url, array $trustedSites = null): string - { - return HTTP::checkURLAllowed($url, $trustedSites); - } - - - /** - * @see HTTP::fetch() - * - * @param string $url - * @param array $context - * @param bool $getHeaders - * @return array|string - */ - public function fetch(string $url, array $context = [], bool $getHeaders = false) - { - return HTTP::fetch($url, $context, $getHeaders); - } - - - /** - * @see HTTP::getAcceptLanguage() - * @return array - */ - public function getAcceptLanguage(): array - { - return HTTP::getAcceptLanguage(); - } - - - /** - * @see HTTP::guessBasePath() - * @return string - */ - public function guessBasePath(): string - { - return HTTP::guessBasePath(); - } - - - /** - * @see HTTP::getBaseURL() - * @return string - */ - public function getBaseURL(): string - { - return HTTP::getBaseURL(); - } - - - /** - * @see HTTP::getFirstPathElement() - * - * @param bool $trailingslash - * @return string - */ - public function getFirstPathElement(bool $trailingslash = true): string - { - return HTTP::getFirstPathElement($trailingslash); - } - - - /** - * @see HTTP::getPOSTRedirectURL() - * - * @param string $destination - * @param array $data - * @return string - */ - public function getPOSTRedirectURL(string $destination, array $data): string - { - return HTTP::getPOSTRedirectURL($destination, $data); - } - - - /** - * @see HTTP::getSelfHost() - * @return string - */ - public function getSelfHost(): string - { - return HTTP::getSelfHost(); - } - - - /** - * @see HTTP::getSelfHostWithNonStandardPort() - * @return string - */ - public function getSelfHostWithNonStandardPort(): string - { - return HTTP::getSelfHostWithNonStandardPort(); - } - - - /** - * @see HTTP::getSelfHostWithPath() - * @return string - */ - public function getSelfHostWithPath(): string - { - return HTTP::getSelfHostWithPath(); - } - - - /** - * @see HTTP::getSelfURL() - * @return string - */ - public function getSelfURL(): string - { - return HTTP::getSelfURL(); - } - - - /** - * @see HTTP::getSelfURLHost() - * @return string - */ - public function getSelfURLHost(): string - { - return HTTP::getSelfURLHost(); - } - - - /** - * @see HTTP::getSelfURLNoQuery() - * @return string - */ - public function getSelfURLNoQuery(): string - { - return HTTP::getSelfURLNoQuery(); - } - - - /** - * @see HTTP::isHTTPS() - * @return bool - */ - public function isHTTPS(): bool - { - return HTTP::isHTTPS(); - } - - - /** - * @see HTTP::normalizeURL() - * @param string $url - * @return string - */ - public function normalizeURL(string $url): string - { - return HTTP::normalizeURL($url); - } - - - /** - * @see HTTP::parseQueryString() - * - * @param string $query_string - * @return array - */ - public function parseQueryString(string $query_string): array - { - return HTTP::parseQueryString($query_string); - } - - - /** - * @see HTTP::redirectTrustedURL() - * - * @param string $url - * @param array $parameters - */ - public function redirectTrustedURL(string $url, array $parameters = []): void - { - HTTP::redirectTrustedURL($url, $parameters); - } - - - /** - * @see HTTP::redirectUntrustedURL() - * - * @param string $url - * @param array $parameters - */ - public function redirectUntrustedURL(string $url, array $parameters = []): void - { - HTTP::redirectUntrustedURL($url, $parameters); - } - - - /** - * @see HTTP::resolveURL() - * - * @param string $url - * @param string|null $base - * @return string - */ - public function resolveURL(string $url, string $base = null): string - { - return HTTP::resolveURL($url, $base); - } - - - /** - * @see HTTP::setCookie() - * - * @param string $name - * @param string $value - * @param array|null $params - * @param bool $throw - */ - public function setCookie(string $name, string $value, array $params = null, bool $throw = true): void - { - HTTP::setCookie($name, $value, $params, $throw); - } - - - /** - * @see HTTP::submitPOSTData() - * - * @param string $destination - * @param array $data - */ - public function submitPOSTData(string $destination, array $data): void - { - HTTP::submitPOSTData($destination, $data); - } -} diff --git a/lib/SimpleSAML/Utils/Net.php b/lib/SimpleSAML/Utils/Net.php index 99bb762428bca333b85ec65e1e5616d6998871cd..820cd0bf33f6f8ec2dc1d6f25ea23ecd787b9529 100644 --- a/lib/SimpleSAML/Utils/Net.php +++ b/lib/SimpleSAML/Utils/Net.php @@ -21,7 +21,7 @@ class Net * @return boolean True if the IP address belongs to the specified CIDR, false otherwise. * */ - public static function ipCIDRcheck(string $cidr, string $ip = null): bool + public function ipCIDRcheck(string $cidr, string $ip = null): bool { if ($ip === null) { $ip = $_SERVER['REMOTE_ADDR']; diff --git a/lib/SimpleSAML/Utils/Random.php b/lib/SimpleSAML/Utils/Random.php index 4856c2e04867ae9f89ecb06ba052810918481bf6..d5c7bc8965c2c5143f658046852c73682a3bf44f 100644 --- a/lib/SimpleSAML/Utils/Random.php +++ b/lib/SimpleSAML/Utils/Random.php @@ -22,7 +22,7 @@ class Random * @return string A ID_LENGTH-bytes long string with a random, hex-encoded string. * */ - public static function generateID(): string + public function generateID(): string { return '_' . bin2hex(openssl_random_pseudo_bytes((int) ((self::ID_LENGTH - 1) / 2))); } diff --git a/lib/SimpleSAML/Utils/System.php b/lib/SimpleSAML/Utils/System.php index 424609710f69602b89e87379c3465682e20b924a..4b6913231931afb0b7a0c3508bfd3c0553625daa 100644 --- a/lib/SimpleSAML/Utils/System.php +++ b/lib/SimpleSAML/Utils/System.php @@ -32,7 +32,7 @@ class System * False if we are unable to determine it. * */ - public static function getOS() + public function getOS() { if (stristr(PHP_OS, 'LINUX')) { return self::LINUX; @@ -70,7 +70,7 @@ class System * to by the current user. * */ - public static function getTempDir(): string + public function getTempDir(): string { $globalConfig = Configuration::getInstance(); @@ -121,7 +121,7 @@ class System * @return string An absolute path referring to $path. * */ - public static function resolvePath(string $path, string $base = null): string + public function resolvePath(string $path, string $base = null): string { if ($base === null) { $config = Configuration::getInstance(); @@ -140,14 +140,14 @@ class System if (substr($path, 0, 1) === '/') { // absolute path. */ $ret = '/'; - } elseif (static::pathContainsDriveLetter($path)) { + } elseif ($this->pathContainsDriveLetter($path)) { $ret = ''; } else { // path relative to base $ret = $base; } - if (static::pathContainsStreamWrapper($path)) { + if ($this->pathContainsStreamWrapper($path)) { $ret = $path; } else { $path = explode('/', $path); @@ -185,9 +185,9 @@ class System * possible to write to the target file. * */ - public static function writeFile(string $filename, string $data, int $mode = 0600): void + public function writeFile(string $filename, string $data, int $mode = 0600): void { - $tmpFile = self::getTempDir() . DIRECTORY_SEPARATOR . rand(); + $tmpFile = $this->getTempDir() . DIRECTORY_SEPARATOR . rand(); $res = @file_put_contents($tmpFile, $data); if ($res === false) { @@ -199,7 +199,7 @@ class System ); } - if (self::getOS() !== self::WINDOWS) { + if ($this->getOS() !== self::WINDOWS) { if (!chmod($tmpFile, $mode)) { unlink($tmpFile); /** @var array|null $error */ @@ -234,9 +234,9 @@ class System * * @return bool */ - public static function isAbsolutePath(string $path): bool + public function isAbsolutePath(string $path): bool { - return (0 === strpos($path, '/') || self::pathContainsDriveLetter($path)); + return (0 === strpos($path, '/') || $this->pathContainsDriveLetter($path)); } @@ -247,7 +247,7 @@ class System * * @return bool */ - private static function pathContainsDriveLetter(string $path): bool + private function pathContainsDriveLetter(string $path): bool { $letterAsciiValue = ord(strtoupper(substr($path, 0, 1))); return substr($path, 1, 1) === ':' @@ -259,7 +259,7 @@ class System * @param string $path * @return bool */ - private static function pathContainsStreamWrapper(string $path): bool + private function pathContainsStreamWrapper(string $path): bool { return preg_match('/^[\w\d]*:\/{2}/', $path) === 1; } diff --git a/lib/SimpleSAML/Utils/Time.php b/lib/SimpleSAML/Utils/Time.php index 9540e2646fbf31104cd8a9b6e6fb54289fa65a7a..b152edb05b8c724acf8f61cbed2210543433a75a 100644 --- a/lib/SimpleSAML/Utils/Time.php +++ b/lib/SimpleSAML/Utils/Time.php @@ -31,7 +31,7 @@ class Time * * @return string The timestamp. */ - public static function generateTimestamp(int $instant = null): string + public function generateTimestamp(int $instant = null): string { if ($instant === null) { $instant = time(); @@ -48,7 +48,7 @@ class Time * * @throws \SimpleSAML\Error\Exception If the timezone set in the configuration is invalid. */ - public static function initTimezone(): void + public function initTimezone(): void { if (self::$tz_initialized) { return; @@ -88,7 +88,7 @@ class Time * @throws \InvalidArgumentException If $duration is not a valid ISO 8601 duration or if the input parameters do * not have the right data types. */ - public static function parseDuration(string $duration, int $timestamp = null): int + public function parseDuration(string $duration, int $timestamp = null): int { // parse the duration. We use a very strict pattern $durationRegEx = '#^(-?)P(?:(?:(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)' . diff --git a/lib/SimpleSAML/Utils/XML.php b/lib/SimpleSAML/Utils/XML.php index 076ad8dc43a4051c9e9d81cc3425b31774a0b8cf..348afcbe6407588a7470b17bf5508f03887d8c6f 100644 --- a/lib/SimpleSAML/Utils/XML.php +++ b/lib/SimpleSAML/Utils/XML.php @@ -39,7 +39,7 @@ class XML * * */ - public static function checkSAMLMessage(string $message, string $type): void + public function checkSAMLMessage(string $message, string $type): void { $allowed_types = ['saml20', 'saml-meta']; if (!in_array($type, $allowed_types, true)) { @@ -67,10 +67,10 @@ class XML $result = true; switch ($type) { case 'saml20': - $result = self::isValid($message, 'saml-schema-protocol-2.0.xsd'); + $result = $this->isValid($message, 'saml-schema-protocol-2.0.xsd'); break; case 'saml-meta': - $result = self::isValid($message, 'saml-schema-metadata-2.0.xsd'); + $result = $this->isValid($message, 'saml-schema-metadata-2.0.xsd'); } if (is_string($result)) { Logger::warning($result); @@ -93,7 +93,7 @@ class XML * * */ - public static function debugSAMLMessage($message, string $type): void + public function debugSAMLMessage($message, string $type): void { if (!(is_string($message) || $message instanceof DOMElement)) { throw new \InvalidArgumentException('Invalid input parameters.'); @@ -136,7 +136,7 @@ class XML Assert::true(false); } - $str = self::formatXMLString($message); + $str = $this->formatXMLString($message); foreach (explode("\n", $str) as $line) { Logger::debug($line); } @@ -157,7 +157,7 @@ class XML * * */ - public static function formatDOMElement(DOMNode $root, string $indentBase = ''): void + public function formatDOMElement(DOMNode $root, string $indentBase = ''): void { // check what this element contains $fullText = ''; // all text in this element @@ -219,7 +219,7 @@ class XML // format child elements if ($node instanceof \DOMElement) { - self::formatDOMElement($node, $childIndentation); + $this->formatDOMElement($node, $childIndentation); } } @@ -242,7 +242,7 @@ class XML * @throws \DOMException If the input does not parse correctly as an XML string. * */ - public static function formatXMLString(string $xml, string $indentBase = ''): string + public function formatXMLString(string $xml, string $indentBase = ''): string { try { $doc = DOMDocumentFactory::fromString($xml); @@ -252,7 +252,7 @@ class XML $root = $doc->firstChild; Assert::notNull($root); - self::formatDOMElement($root, $indentBase); + $this->formatDOMElement($root, $indentBase); return $doc->saveXML($root); } @@ -276,7 +276,7 @@ class XML * @throws \InvalidArgumentException If the namespace shortcut is unknown. * */ - public static function isDOMNodeOfType(DOMNode $element, string $name, string $nsURI): bool + public function isDOMNodeOfType(DOMNode $element, string $name, string $nsURI): bool { if (strlen($nsURI) === 0) { // most likely a comment-node @@ -325,7 +325,7 @@ class XML * @throws \InvalidArgumentException If $schema is not a string, or $xml is neither a string nor a \DOMDocument. * */ - public static function isValid($xml, string $schema) + public function isValid($xml, string $schema) { if (!is_string($xml) && !($xml instanceof DOMDocument)) { throw new \InvalidArgumentException('Invalid input parameters.'); diff --git a/lib/SimpleSAML/XHTML/IdPDisco.php b/lib/SimpleSAML/XHTML/IdPDisco.php index ffc7da248dd13a5927b82ac79daaf57ed0dc765c..71555a873e9e4b41abe5ce819480a5c70aaa13c6 100644 --- a/lib/SimpleSAML/XHTML/IdPDisco.php +++ b/lib/SimpleSAML/XHTML/IdPDisco.php @@ -144,7 +144,8 @@ class IdPDisco if (!array_key_exists('return', $_GET)) { throw new \Exception('Missing parameter: return'); } else { - $this->returnURL = Utils\HTTP::checkURLAllowed($_GET['return']); + $httpUtils = new Utils\HTTP(); + $this->returnURL = $httpUtils->checkURLAllowed($_GET['return']); } $this->isPassive = false; @@ -221,7 +222,8 @@ class IdPDisco 'httponly' => false, ]; - Utils\HTTP::setCookie($prefixedName, $value, $params, false); + $httpUtils = new Utils\HTTP(); + $httpUtils->setCookie($prefixedName, $value, $params, false); } @@ -508,12 +510,13 @@ class IdPDisco */ protected function start(): void { + $httpUtils = new Utils\HTTP(); $idp = $this->getTargetIdP(); if ($idp !== null) { $extDiscoveryStorage = $this->config->getString('idpdisco.extDiscoveryStorage', null); if ($extDiscoveryStorage !== null) { $this->log('Choice made [' . $idp . '] (Forwarding to external discovery storage)'); - Utils\HTTP::redirectTrustedURL($extDiscoveryStorage, [ + $httpUtils->redirectTrustedURL($extDiscoveryStorage, [ 'entityID' => $this->spEntityId, 'IdPentityID' => $idp, 'returnIDParam' => $this->returnIdParam, @@ -525,13 +528,13 @@ class IdPDisco 'Choice made [' . $idp . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')' ); - Utils\HTTP::redirectTrustedURL($this->returnURL, [$this->returnIdParam => $idp]); + $httpUtils->redirectTrustedURL($this->returnURL, [$this->returnIdParam => $idp]); } } if ($this->isPassive) { $this->log('Choice not made. (Redirecting the user back without answer)'); - Utils\HTTP::redirectTrustedURL($this->returnURL); + $httpUtils->redirectTrustedURL($this->returnURL); } } @@ -556,13 +559,14 @@ class IdPDisco } $idpintersection = array_values($idpintersection); + $httpUtils = new Utils\HTTP(); if (sizeof($idpintersection) == 1) { $this->log( 'Choice made [' . $idpintersection[0] . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')' ); - Utils\HTTP::redirectTrustedURL( + $httpUtils->redirectTrustedURL( $this->returnURL, [$this->returnIdParam => $idpintersection[0]] ); @@ -611,7 +615,7 @@ class IdPDisco } if (!empty($data['icon'])) { $newlist[$entityid]['icon'] = $data['icon']; - $newlist[$entityid]['iconurl'] = Utils\HTTP::resolveURL($data['icon']); + $newlist[$entityid]['iconurl'] = $httpUtils->resolveURL($data['icon']); } } usort( @@ -631,7 +635,7 @@ class IdPDisco $t->data['return'] = $this->returnURL; $t->data['returnIDParam'] = $this->returnIdParam; $t->data['entityID'] = $this->spEntityId; - $t->data['urlpattern'] = htmlspecialchars(Utils\HTTP::getSelfURLNoQuery()); + $t->data['urlpattern'] = htmlspecialchars($httpUtils->getSelfURLNoQuery()); $t->data['rememberenabled'] = $this->config->getBoolean('idpdisco.enableremember', false); $t->send(); } diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php index 2326b7d29bf1cac208d1f8d134de75cdfb56b197..6c1957c2d2a7f5524dad43b6abc2047bfb9d8360 100644 --- a/lib/SimpleSAML/XHTML/Template.php +++ b/lib/SimpleSAML/XHTML/Template.php @@ -433,7 +433,8 @@ class Template extends Response $langname = $this->translator->getLanguage()->getLanguageLocalizedName($lang); $url = false; if (!$current) { - $url = htmlspecialchars(Utils\HTTP::addURLParameters( + $httpUtils = new Utils\HTTP(); + $url = htmlspecialchars($httpUtils->addURLParameters( '', [$parameterName => $lang] )); diff --git a/lib/SimpleSAML/XML/Signer.php b/lib/SimpleSAML/XML/Signer.php index 898eb639a120ef205967df5c9b76e121fbf32264..24f62d7c3af2004e852f35b7922a5b3c452d8c40 100644 --- a/lib/SimpleSAML/XML/Signer.php +++ b/lib/SimpleSAML/XML/Signer.php @@ -125,7 +125,8 @@ class Signer public function loadPrivateKey(string $file, ?string $pass, bool $full_path = false): void { if (!$full_path) { - $keyFile = Utils\Config::getCertPath($file); + $configUtils = new Utils\Config(); + $keyFile = $configUtils->getCertPath($file); } else { $keyFile = $file; } @@ -182,7 +183,8 @@ class Signer public function loadCertificate(string $file, bool $full_path = false): void { if (!$full_path) { - $certFile = Utils\Config::getCertPath($file); + $configUtils = new Utils\Config(); + $certFile = $configUtils->getCertPath($file); } else { $certFile = $file; } @@ -224,7 +226,8 @@ class Signer public function addCertificate(string $file, bool $full_path = false): void { if (!$full_path) { - $certFile = Utils\Config::getCertPath($file); + $configUtils = new Utils\Config(); + $certFile = $configUtils->getCertPath($file); } else { $certFile = $file; } diff --git a/metadata-templates/saml20-sp-remote.php b/metadata-templates/saml20-sp-remote.php index 0b0315ce02f7a38a5e579f04c33eca13191f41d6..c95da84d78072a8f757778eac4a2531df0362195 100644 --- a/metadata-templates/saml20-sp-remote.php +++ b/metadata-templates/saml20-sp-remote.php @@ -15,9 +15,9 @@ $metadata['https://saml2sp.example.org'] = [ ]; /* - * This example shows an example config that works with G Suite (Google Apps) for education. + * This example shows an example config that works with Google Workspace (G Suite / Google Apps) for education. * What is important is that you have an attribute in your IdP that maps to the local part of the email address at - * G Suite. In example, if your Google account is foo.com, and you have a user that has an email john@foo.com, then you + * Google Workspace. In example, if your Google account is foo.com, and you have a user that has an email john@foo.com, then you * must set the simplesaml.nameidattribute to be the name of an attribute that for this user has the value of 'john'. */ $metadata['google.com'] = [ diff --git a/modules/admin/lib/Controller/Config.php b/modules/admin/lib/Controller/Config.php index e4823002060e6e5eb08a058654d0ddecf8edce07..72c3878adf55acf3b12a17d88dcc9a21f70ca2e1 100644 --- a/modules/admin/lib/Controller/Config.php +++ b/modules/admin/lib/Controller/Config.php @@ -29,11 +29,11 @@ class Config /** @var \SimpleSAML\Configuration */ protected Configuration $config; - /** - * @var \SimpleSAML\Utils\Auth|string - * @psalm-var \SimpleSAML\Utils\Auth|class-string - */ - protected $authUtils = Utils\Auth::class; + /** @var \SimpleSAML\Utils\Auth */ + protected $authUtils; + + /** @var \SimpleSAML\Utils\HTTP */ + protected $httpUtils; /** @var \SimpleSAML\Module\admin\Controller\Menu */ protected Menu $menu; @@ -53,6 +53,8 @@ class Config $this->config = $config; $this->session = $session; $this->menu = new Menu(); + $this->authUtils = new Utils\Auth(); + $this->httpUtils = new Utils\HTTP(); } @@ -76,24 +78,24 @@ class Config */ public function diagnostics(Request $request): Template { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); $t = new Template($this->config, 'admin:diagnostics.twig'); $t->data = [ 'remaining' => $this->session->getAuthData('admin', 'Expire') - time(), - 'logouturl' => Utils\Auth::getAdminLogoutURL(), + 'logouturl' => $this->authUtils->getAdminLogoutURL(), 'items' => [ 'HTTP_HOST' => [$request->getHost()], 'HTTPS' => $request->isSecure() ? ['on'] : [], 'SERVER_PROTOCOL' => [$request->getProtocolVersion()], - 'getBaseURL()' => [Utils\HTTP::getBaseURL()], - 'getSelfHost()' => [Utils\HTTP::getSelfHost()], - 'getSelfHostWithNonStandardPort()' => [Utils\HTTP::getSelfHostWithNonStandardPort()], - 'getSelfURLHost()' => [Utils\HTTP::getSelfURLHost()], - 'getSelfURLNoQuery()' => [Utils\HTTP::getSelfURLNoQuery()], - 'getSelfHostWithPath()' => [Utils\HTTP::getSelfHostWithPath()], - 'getFirstPathElement()' => [Utils\HTTP::getFirstPathElement()], - 'getSelfURL()' => [Utils\HTTP::getSelfURL()], + 'getBaseURL()' => [$this->httpUtils->getBaseURL()], + 'getSelfHost()' => [$this->httpUtils->getSelfHost()], + 'getSelfHostWithNonStandardPort()' => [$this->httpUtils->getSelfHostWithNonStandardPort()], + 'getSelfURLHost()' => [$this->httpUtils->getSelfURLHost()], + 'getSelfURLNoQuery()' => [$this->httpUtils->getSelfURLNoQuery()], + 'getSelfHostWithPath()' => [$this->httpUtils->getSelfHostWithPath()], + 'getFirstPathElement()' => [$this->httpUtils->getFirstPathElement()], + 'getSelfURL()' => [$this->httpUtils->getSelfURL()], ], ]; @@ -111,7 +113,7 @@ class Config */ public function main(/** @scrutinizer ignore-unused */ Request $request): Template { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); $t = new Template($this->config, 'admin:config.twig'); $t->data = [ @@ -132,12 +134,11 @@ class Config 'saml20idp' => $this->config->getBoolean('enable.saml20-idp', false), ], 'funcmatrix' => $this->getPrerequisiteChecks(), - 'logouturl' => Utils\Auth::getAdminLogoutURL(), + 'logouturl' => $this->authUtils->getAdminLogoutURL(), ]; Module::callHooks('configpage', $t); - $this->menu->addOption('logout', Utils\Auth::getAdminLogoutURL(), Translate::noop('Log out')); - /** @psalm-var \SimpleSAML\XHTML\Template $t */ + $this->menu->addOption('logout', $this->authUtils->getAdminLogoutURL(), Translate::noop('Log out')); return $this->menu->insert($t); } @@ -151,7 +152,7 @@ class Config */ public function phpinfo(/** @scrutinizer ignore-unused */ Request $request): RunnableResponse { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); return new RunnableResponse('phpinfo'); } @@ -360,7 +361,7 @@ class Config $warnings = []; // make sure we're using HTTPS - if (!Utils\HTTP::isHTTPS()) { + if (!$this->httpUtils->isHTTPS()) { $warnings[] = Translate::noop( '<strong>You are not using HTTPS</strong> to protect communications with your users. HTTP works fine ' . 'for testing purposes, but in a production environment you should use HTTPS. <a ' . diff --git a/modules/admin/lib/Controller/Federation.php b/modules/admin/lib/Controller/Federation.php index 2b349d877c895026faf3b0b3ca870e661bb23880..2d035d5736c9e832f528409cf698d192dfaf9ce7 100644 --- a/modules/admin/lib/Controller/Federation.php +++ b/modules/admin/lib/Controller/Federation.php @@ -42,11 +42,11 @@ class Federation */ protected $authSource = Auth\Source::class; - /** - * @var \SimpleSAML\Utils\Auth|string - * @psalm-var \SimpleSAML\Utils\Auth|class-string - */ - protected $authUtils = Utils\Auth::class; + /** @var \SimpleSAML\Utils\Auth */ + protected $authUtils; + + /** @var \SimpleSAML\Utils\Crypto */ + protected $cryptoUtils; /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */ protected MetadataStorageHandler $mdHandler; @@ -65,6 +65,8 @@ class Federation $this->config = $config; $this->menu = new Menu(); $this->mdHandler = MetaDataStorageHandler::getMetadataHandler(); + $this->authUtils = new Utils\Auth(); + $this->cryptoUtils = new Utils\Crypto(); } @@ -111,7 +113,7 @@ class Federation */ public function main(/** @scrutinizer ignore-unused */ Request $request): Template { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); // initialize basic metadata array $hostedSPs = $this->getHostedSP(); @@ -188,7 +190,7 @@ class Federation 'adfs-idp-remote' => Translate::noop('ADFS IdP metadata'), 'adfs-idp-hosted' => Translate::noop('ADFS IdP metadata'), ], - 'logouturl' => Utils\Auth::getAdminLogoutURL(), + 'logouturl' => $this->authUtils->getAdminLogoutURL(), ]; Module::callHooks('federationpage', $t); @@ -224,8 +226,9 @@ class Federation $saml2entities[] = $idp; } } else { + $httpUtils = new Utils\HTTP(); $saml2entities['saml20-idp'] = $this->mdHandler->getMetaDataCurrent('saml20-idp-hosted'); - $saml2entities['saml20-idp']['url'] = Utils\HTTP::getBaseURL() . 'saml2/idp/metadata.php'; + $saml2entities['saml20-idp']['url'] = $httpUtils->getBaseURL() . 'saml2/idp/metadata.php'; $saml2entities['saml20-idp']['metadata_array'] = SAML2_IdP::getHostedMetadata( $this->mdHandler->getMetaDataCurrentEntityID('saml20-idp-hosted') ); @@ -396,7 +399,7 @@ class Federation */ public function metadataConverter(Request $request): Template { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); if ($xmlfile = $request->files->get('xmlfile')) { $xmldata = trim(file_get_contents($xmlfile->getPathname())); } elseif ($xmldata = $request->request->get('xmldata')) { @@ -405,7 +408,8 @@ class Federation $error = null; if (!empty($xmldata)) { - Utils\XML::checkSAMLMessage($xmldata, 'saml-meta'); + $xmlUtils = new Utils\XML(); + $xmlUtils->checkSAMLMessage($xmldata, 'saml-meta'); $entities = null; try { @@ -425,7 +429,8 @@ class Federation } // transpose from $entities[entityid][type] to $output[type][entityid] - $output = Utils\Arrays::transpose($entities); + $arrayUtils = new Utils\Arrays(); + $output = $arrayUtils->transpose($entities); // merge all metadata of each type to a single string which should be added to the corresponding file foreach ($output as $type => &$entities) { @@ -461,7 +466,7 @@ class Federation $t = new Template($this->config, 'admin:metadata_converter.twig'); $t->data = [ - 'logouturl' => Utils\Auth::getAdminLogoutURL(), + 'logouturl' => $this->authUtils->getAdminLogoutURL(), 'xmldata' => $xmldata, 'output' => $output, 'error' => $error, @@ -481,7 +486,7 @@ class Federation */ public function downloadCert(Request $request): Response { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); $set = $request->get('set'); $prefix = $request->get('prefix', ''); @@ -500,7 +505,7 @@ class Federation } /** @var array $certInfo Second param ensures non-nullable return-value */ - $certInfo = Utils\Crypto::loadPublicKey($mdconfig, true, $prefix); + $certInfo = $this->cryptoUtils->loadPublicKey($mdconfig, true, $prefix); $response = new Response($certInfo['PEM']); $disposition = $response->headers->makeDisposition( @@ -524,7 +529,7 @@ class Federation */ public function showRemoteEntity(Request $request): Template { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); $entityId = $request->get('entityid'); $set = $request->get('set'); diff --git a/modules/admin/lib/Controller/Test.php b/modules/admin/lib/Controller/Test.php index 3be69e3d967213458d6e496681cfd47c3046e2d0..b93e80e6b50a7f703af020a977db63bd3fa90c90 100644 --- a/modules/admin/lib/Controller/Test.php +++ b/modules/admin/lib/Controller/Test.php @@ -31,10 +31,9 @@ class Test protected Configuration $config; /** - * @var \SimpleSAML\Utils\Auth|string - * @psalm-var \SimpleSAML\Utils\Auth|class-string + * @var \SimpleSAML\Utils\Auth */ - protected $authUtils = Utils\Auth::class; + protected $authUtils; /** * @var \SimpleSAML\Auth\Simple|string @@ -66,6 +65,7 @@ class Test $this->config = $config; $this->session = $session; $this->menu = new Menu(); + $this->authUtils = new Utils\Auth(); } @@ -111,7 +111,7 @@ class Test */ public function main(Request $request, string $as = null) { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); if (is_null($as)) { $t = new Template($this->config, 'admin:authsource_list.twig'); $t->data = [ @@ -144,13 +144,14 @@ class Test $authData = $authsource->getAuthDataArray(); $nameId = $authsource->getAuthData('saml:sp:NameID') ?? false; + $httpUtils = new Utils\HTTP(); $t = new Template($this->config, 'admin:status.twig', 'attributes'); $t->data = [ 'attributes' => $attributes, 'attributesHtml' => $this->getAttributesHTML($t, $attributes, ''), 'authData' => $authData, 'nameid' => $nameId, - 'logouturl' => Utils\HTTP::getSelfURLNoQuery() . '?as=' . urlencode($as) . '&logout', + 'logouturl' => $httpUtils->getSelfURLNoQuery() . '?as=' . urlencode($as) . '&logout', ]; if ($nameId !== false) { @@ -161,8 +162,7 @@ class Test Module::callHooks('configpage', $t); Assert::isInstanceOf($t, Template::class); - $this->menu->addOption('logout', Utils\Auth::getAdminLogoutURL(), Translate::noop('Log out')); - /** @var \SimpleSAML\XHTML\Template $t */ + $this->menu->addOption('logout', $this->authUtils->getAdminLogoutURL(), Translate::noop('Log out')); return $this->menu->insert($t); } diff --git a/modules/core/docs/authproc_pairwiseid.md b/modules/core/docs/authproc_pairwiseid.md new file mode 100644 index 0000000000000000000000000000000000000000..254aa85bdddb00be48e02597217f92e771ee6f05 --- /dev/null +++ b/modules/core/docs/authproc_pairwiseid.md @@ -0,0 +1,19 @@ +`core:PairwiseID` +=================== + +Filter to insert a pairwise-id that complies with the following specification; +http://docs.oasis-open.org/security/saml-subject-id-attr/v1.0/saml-subject-id-attr-v1.0.pdf + +This filter will take an attribute and a scope as input and transforms this into a anonymized and scoped +identifier that is globally unique for a given user & service provider combination. + +Examples +-------- + + 'authproc' => [ + 50 => [ + 'class' => 'core:PairwiseID', + 'identifyingAttribute' => 'uid', + 'scope' => 'example.org', + ], + ], diff --git a/modules/core/docs/authproc_subjectid.md b/modules/core/docs/authproc_subjectid.md new file mode 100644 index 0000000000000000000000000000000000000000..21852fb45e2842bf1a8a4d5ad1bbc174f22b2db2 --- /dev/null +++ b/modules/core/docs/authproc_subjectid.md @@ -0,0 +1,22 @@ +`core:SubjectID` +=================== + +Filter to insert a subject-id that complies with the following specification; +http://docs.oasis-open.org/security/saml-subject-id-attr/v1.0/saml-subject-id-attr-v1.0.pdf + +This filter will take an attribute and a scope as input and transforms this into a scoped identifier that is globally unique for a given user. + +Note: +----- +If privacy is of your concern, you may want to use the PairwiseID-filter instead. + +Examples +-------- + + 'authproc' => [ + 50 => [ + 'class' => 'core:SubjectID', + 'identifyingAttribute' => 'uid', + 'scope' => 'example.org', + ], + ], diff --git a/modules/core/lib/Auth/Process/Cardinality.php b/modules/core/lib/Auth/Process/Cardinality.php index 5ad50c127f25336151b6e7f85ac5b6381422c84c..1c90f269cdb5493da0e2e1c8eb2652cc1fb17980 100644 --- a/modules/core/lib/Auth/Process/Cardinality.php +++ b/modules/core/lib/Auth/Process/Cardinality.php @@ -24,8 +24,8 @@ class Cardinality extends Auth\ProcessingFilter /** @var array Entities that should be ignored */ private array $ignoreEntities = []; - /** @var \SimpleSAML\Utils\HttpAdapter */ - private Utils\HttpAdapter $http; + /** @var \SimpleSAML\Utils\HTTP */ + private Utils\HTTP $httpUtils; /** @@ -33,14 +33,14 @@ class Cardinality extends Auth\ProcessingFilter * * @param array &$config Configuration information about this filter. * @param mixed $reserved For future use. - * @param \SimpleSAML\Utils\HttpAdapter $http HTTP utility service (handles redirects). + * @param \SimpleSAML\Utils\HTTP $httpUtils HTTP utility service (handles redirects). * @throws \SimpleSAML\Error\Exception */ - public function __construct(array &$config, $reserved, Utils\HttpAdapter $http = null) + public function __construct(array &$config, $reserved, Utils\HTTP $httpUtils = null) { parent::__construct($config, $reserved); - $this->http = $http ? : new Utils\HttpAdapter(); + $this->httpUtils = $httpUtils ? : new Utils\HTTP(); foreach ($config as $attribute => $rules) { if ($attribute === '%ignoreEntities') { @@ -195,7 +195,7 @@ class Cardinality extends Auth\ProcessingFilter if (array_key_exists('core:cardinality:errorAttributes', $request)) { $id = Auth\State::saveState($request, 'core:cardinality'); $url = Module::getModuleURL('core/cardinality_error.php'); - $this->http->redirectTrustedURL($url, ['StateId' => $id]); + $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]); return; } } diff --git a/modules/core/lib/Auth/Process/CardinalitySingle.php b/modules/core/lib/Auth/Process/CardinalitySingle.php index c849e7c8b596ea63d1052fa3e57de5237b335c41..98b5f4b0d78b462de48c4c07efaaf9e178380019 100644 --- a/modules/core/lib/Auth/Process/CardinalitySingle.php +++ b/modules/core/lib/Auth/Process/CardinalitySingle.php @@ -35,8 +35,8 @@ class CardinalitySingle extends Auth\ProcessingFilter /** @var array Entities that should be ignored */ private array $ignoreEntities = []; - /** @var \SimpleSAML\Utils\HttpAdapter */ - private Utils\HttpAdapter $http; + /** @var \SimpleSAML\Utils\HTTP */ + private Utils\HTTP $httpUtils; /** @@ -44,13 +44,13 @@ class CardinalitySingle extends Auth\ProcessingFilter * * @param array &$config Configuration information about this filter. * @param mixed $reserved For future use. - * @param \SimpleSAML\Utils\HttpAdapter $http HTTP utility service (handles redirects). + * @param \SimpleSAML\Utils\HTTP $httpUtils HTTP utility service (handles redirects). */ - public function __construct(array &$config, $reserved, Utils\HttpAdapter $http = null) + public function __construct(array &$config, $reserved, Utils\HTTP $httpUtils = null) { parent::__construct($config, $reserved); - $this->http = $http ? : new Utils\HttpAdapter(); + $this->httpUtils = $httpUtils ? : new Utils\HTTP(); if (array_key_exists('singleValued', $config)) { $this->singleValued = $config['singleValued']; @@ -122,7 +122,7 @@ class CardinalitySingle extends Auth\ProcessingFilter if (array_key_exists('core:cardinality:errorAttributes', $request)) { $id = Auth\State::saveState($request, 'core:cardinality'); $url = Module::getModuleURL('core/cardinality_error.php'); - $this->http->redirectTrustedURL($url, ['StateId' => $id]); + $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]); return; } } diff --git a/modules/core/lib/Auth/Process/PairwiseID.php b/modules/core/lib/Auth/Process/PairwiseID.php new file mode 100644 index 0000000000000000000000000000000000000000..1f31045caff94f3f8a3f2791896b9a6a325cb1dd --- /dev/null +++ b/modules/core/lib/Auth/Process/PairwiseID.php @@ -0,0 +1,99 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\core\Auth\Process; + +use Exception; +use SAML2\Constants; +use SAML2\XML\saml\NameID; +use SimpleSAML\Assert\Assert; +use SimpleSAML\Auth; +use SimpleSAML\Utils; + +/** + * Filter to generate the Pairwise ID attribute. + * + * See: http://docs.oasis-open.org/security/saml-subject-id-attr/v1.0/csprd01/saml-subject-id-attr-v1.0-csprd01.html + * + * By default, this filter will generate the ID based on the UserID of the current user. + * This is generated from the attribute configured in 'identifyingAttribute' in the + * authproc-configuration. + * + * Example - generate from attribute: + * <code> + * 'authproc' => [ + * 50 => [ + * 'core:PairwiseID', + * 'identifyingAttribute' => 'uid', + * 'scope' => 'example.org', + * ] + * ] + * </code> + * + * @package SimpleSAMLphp + */ +class PairwiseID extends SubjectID +{ + /** + * The name for this class + */ + public const NAME = 'PairwiseID'; + + /** + * @var \SimpleSAML\Utils\Config + */ + protected $configUtils; + + + /** + * Initialize this filter. + * + * @param array &$config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct(array &$config, $reserved) + { + parent::__construct($config, $reserved); + + $this->configUtils = new Utils\Config(); + } + + + /** + * Apply filter to add the Pairwise ID. + * + * @param array &$state The current state. + */ + public function process(array &$state): void + { + $userID = $this->getIdentifyingAttribute($state); + + if (!empty($state['saml:RequesterID'])) { + // Proxied request - use actual SP entity ID + $sp_entityid = $state['saml:RequesterID'][0]; + } else { + $sp_entityid = $state['core:SP']; + } + + // Calculate hash + $salt = $this->configUtils->getSecretSalt(); + $hash = hash('sha256', $salt . '|' . $userID . '|' . $sp_entityid, false); + + $value = strtolower($hash . '@' . $this->scope); + $this->validateGeneratedIdentifier($value); + + $state['Attributes'][Constants::ATTR_PAIRWISE_ID] = [$value]; + } + + + /** + * Inject the \SimpleSAML\Utils\Config dependency. + * + * @param \SimpleSAML\Utils\Config $configUtils + */ + public function setConfigUtils(Utils\Config $configUtils): void + { + $this->configUtils = $configUtils; + } +} diff --git a/modules/core/lib/Auth/Process/SubjectID.php b/modules/core/lib/Auth/Process/SubjectID.php new file mode 100644 index 0000000000000000000000000000000000000000..edaaf2d4cfeedeb1e2794941cb00e8ec36d6679b --- /dev/null +++ b/modules/core/lib/Auth/Process/SubjectID.php @@ -0,0 +1,185 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\core\Auth\Process; + +use Exception; +use SAML2\Constants; +//use SAML2\Exception\ProtocolViolationException +use SAML2\XML\saml\NameID; +use SimpleSAML\Assert\Assert; +use SimpleSAML\Auth; +use SimpleSAML\Logger; + +/** + * Filter to generate the subject ID attribute. + * + * See: http://docs.oasis-open.org/security/saml-subject-id-attr/v1.0/csprd01/saml-subject-id-attr-v1.0-csprd01.html + * + * By default, this filter will generate the ID based on the UserID of the current user. + * This is generated from the attribute configured in 'identifyingAttribute' in the + * authproc-configuration. + * + * Example - generate from attribute: + * <code> + * 'authproc' => [ + * 50 => [ + * 'core:SubjectID', + * 'identifyingAttribute' => 'uid', + * 'scope' => 'example.org', + * ] + * ] + * </code> + * + * @package SimpleSAMLphp + */ +class SubjectID extends Auth\ProcessingFilter +{ + /** + * The name for this class + */ + public const NAME = 'SubjectID'; + + /** + * The regular expression to match the scope + * + * @var string + */ + public const SCOPE_PATTERN = '/^[a-z0-9][a-z0-9.-]{0,126}$/i'; + + /** + * The regular expression to match the specifications + * + * @var string + */ + public const SPEC_PATTERN = '/^[a-z0-9][a-z0-9=-]{0,126}@[a-z0-9][a-z0-9.-]{0,126}$/i'; + + /** + * The regular expression to match worrisome identifiers that need to raise a warning + * + * @var string + */ + public const WARN_PATTERN = '/^[a-z0-9][a-z0-9=-]{3,126}@[a-z0-9][a-z0-9.-]{3,126}$/i'; + + /** + * The attribute we should generate the subject id from. + * + * @var string + */ + protected string $identifyingAttribute; + + /** + * The scope to use for this attribute. + * + * @var string + */ + protected string $scope; + + /** + * @var \SimpleSAML\Logger|string + * @psalm-var \SimpleSAML\Logger|class-string + */ + protected $logger = Logger::class; + + + /** + * Initialize this filter. + * + * @param array &$config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct(array &$config, $reserved) + { + parent::__construct($config, $reserved); + + Assert::keyExists($config, 'identifyingAttribute', "Missing mandatory 'identifyingAttribute' config setting."); + Assert::keyExists($config, 'scope', "Missing mandatory 'scope' config setting."); + Assert::stringNotEmpty($config['identifyingAttribute']); + Assert::regex( + $config['scope'], + self::SCOPE_PATTERN, + 'core:' . static::NAME . ': \'scope\' contains illegal characters.' +// ProtocolViolationException::class + ); + + $this->identifyingAttribute = $config['identifyingAttribute']; + $this->scope = $config['scope']; + } + + + /** + * Apply filter to add the subject ID. + * + * @param array &$state The current state. + */ + public function process(array &$state): void + { + $userID = $this->getIdentifyingAttribute($state); + + $value = strtolower($userID . '@' . $this->scope); + $this->validateGeneratedIdentifier($value); + + $state['Attributes'][Constants::ATTR_SUBJECT_ID] = [$value]; + } + + + /** + * Retrieve the identifying attribute from the state and test it for erroneous conditions + * + * @param array $state + * @return string + * @throws \SimpleSAML\Assert\AssertionFailedException if the pre-conditions are not met + */ + protected function getIdentifyingAttribute(array $state): string + { + Assert::keyExists($state, 'Attributes'); + Assert::keyExists( + $state['Attributes'], + $this->identifyingAttribute, + sprintf( + "core:" . static::NAME . ": Missing attribute '%s', which is needed to generate the ID.", + $this->identifyingAttribute + ) + ); + + $userID = $state['Attributes'][$this->identifyingAttribute][0]; + Assert::stringNotEmpty($userID, 'core' . static::NAME . ': \'identifyingAttribute\' cannot be an empty string.'); + + return $userID; + } + + + /** + * Test the generated identifier to ensure compliancy with the specifications. + * Log a warning when the generated value is considered to be weak + * + * @param string $value + * @return void + * @throws \SimpleSAML\Assert\AssertionFailedException if the post-conditions are not met + */ + protected function validateGeneratedIdentifier(string $value): void + { + Assert::regex( + $value, + self::SPEC_PATTERN, + 'core:' . static::NAME . ': Generated ID \'' . $value . '\' contains illegal characters.' +// ProtocolViolationException::class + ); + + if (preg_match(self::WARN_PATTERN, $value) === 0) { + $this->logger::warning('core:' . static::NAME . ': Generated ID \'' . $value . '\' can hardly be considered globally unique.'); + } + } + + + /** + * Inject the \SimpleSAML\Logger dependency. + * + * @param \SimpleSAML\Logger $logger + */ + public function setLogger(Logger $logger): void + { + $this->logger = $logger; + } +} diff --git a/modules/core/lib/Auth/Process/TargetedID.php b/modules/core/lib/Auth/Process/TargetedID.php index 04b6aefc2c59d78e24f259fb12d415484da7cca2..b4648d02d16b759a73cf1394236ff47f3522891f 100644 --- a/modules/core/lib/Auth/Process/TargetedID.php +++ b/modules/core/lib/Auth/Process/TargetedID.php @@ -47,10 +47,10 @@ class TargetedID extends Auth\ProcessingFilter private bool $generateNameId = false; /** - * @var \SimpleSAML\Utils\Config|string - * @psalm-var \SimpleSAML\Utils\Config|class-string + * @var \SimpleSAML\Utils\Config */ - protected $configUtils = Utils\Config::class; + protected $configUtils; + /** * Initialize this filter. @@ -76,6 +76,8 @@ class TargetedID extends Auth\ProcessingFilter throw new Exception('Invalid value of \'nameId\'-option to core:TargetedID filter.'); } } + + $this->configUtils = new Utils\Config(); } @@ -122,7 +124,7 @@ class TargetedID extends Auth\ProcessingFilter $dstID = ''; } - $secretSalt = $this->configUtils::getSecretSalt(); + $secretSalt = $this->configUtils->getSecretSalt(); $uidData = 'uidhashbase' . $secretSalt; $uidData .= strlen($srcID) . ':' . $srcID; $uidData .= strlen($dstID) . ':' . $dstID; diff --git a/modules/core/lib/Auth/Process/WarnShortSSOInterval.php b/modules/core/lib/Auth/Process/WarnShortSSOInterval.php index 71e14c7aed4206cde7bd0ebad86e9b02ea5604c4..d5c7fc40a8b2de6eb4cb215dc9fd5c36ef1af8fa 100644 --- a/modules/core/lib/Auth/Process/WarnShortSSOInterval.php +++ b/modules/core/lib/Auth/Process/WarnShortSSOInterval.php @@ -53,6 +53,7 @@ class WarnShortSSOInterval extends Auth\ProcessingFilter // Save state and redirect $id = Auth\State::saveState($state, 'core:short_sso_interval'); $url = Module::getModuleURL('core/short_sso_interval.php'); - Utils\HTTP::redirectTrustedURL($url, ['StateId' => $id]); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); } } diff --git a/modules/core/lib/Auth/Source/AdminPassword.php b/modules/core/lib/Auth/Source/AdminPassword.php index 82ee499abb98e56e4a568a34ca49c483bfafa9a4..0af50a1ed782534d5c814bada7e7c90311135a59 100644 --- a/modules/core/lib/Auth/Source/AdminPassword.php +++ b/modules/core/lib/Auth/Source/AdminPassword.php @@ -8,6 +8,7 @@ use SimpleSAML\Assert\Assert; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\Module\core\Auth\UserPassBase; +use SimpleSAML\Utils; /** * Authentication source which verifies the password against @@ -59,7 +60,8 @@ class AdminPassword extends UserPassBase throw new Error\Error('WRONGUSERPASS'); } - if (!\SimpleSAML\Utils\Crypto::pwValid($adminPassword, $password)) { + $cryptoUtils = new Utils\Crypto(); + if (!$cryptoUtils->pwValid($adminPassword, $password)) { throw new Error\Error('WRONGUSERPASS'); } return ['user' => ['admin']]; diff --git a/modules/core/lib/Auth/UserPassBase.php b/modules/core/lib/Auth/UserPassBase.php index d43fc3d1ae0cd0cb066c862a86dd5211f911eef1..11f18f1d44de0affc33b0279f3cb0ade76a3ebb3 100644 --- a/modules/core/lib/Auth/UserPassBase.php +++ b/modules/core/lib/Auth/UserPassBase.php @@ -11,7 +11,7 @@ use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\Logger; use SimpleSAML\Module; -use SimpleSAML\Utils\HTTP; +use SimpleSAML\Utils; /** * Helper class for username/password authentication. @@ -246,7 +246,8 @@ abstract class UserPassBase extends Auth\Source */ $url = Module::getModuleURL('core/loginuserpass.php'); $params = ['AuthState' => $id]; - HTTP::redirectTrustedURL($url, $params); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, $params); // The previous function never returns, so this code is never executed. assert::true(false); diff --git a/modules/core/lib/Auth/UserPassOrgBase.php b/modules/core/lib/Auth/UserPassOrgBase.php index df6cca39327a1338a7bf9cb0e2dc5d0edbc614da..aaed8a59a5bb237011e2fbcdeaaa2c877b3d9aa7 100644 --- a/modules/core/lib/Auth/UserPassOrgBase.php +++ b/modules/core/lib/Auth/UserPassOrgBase.php @@ -216,7 +216,8 @@ abstract class UserPassOrgBase extends Auth\Source $url = Module::getModuleURL('core/loginuserpassorg.php'); $params = ['AuthState' => $id]; - Utils\HTTP::redirectTrustedURL($url, $params); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, $params); } diff --git a/modules/core/lib/Controller/Exception.php b/modules/core/lib/Controller/Exception.php index 1e5a093b73350f89882c0f7f3c299c961b2f51ab..2b0e5e95b31dd1e00a2bb79bc3b02a4ae94658bd 100644 --- a/modules/core/lib/Controller/Exception.php +++ b/modules/core/lib/Controller/Exception.php @@ -97,7 +97,8 @@ class Exception { $retryURL = $request->get('retryURL', null); if ($retryURL !== null) { - $retryURL = Utils\HTTP::checkURLAllowed(strval($retryURL)); + $httpUtils = new Utils\HTTP(); + $retryURL = $httpUtils->checkURLAllowed(strval($retryURL)); } $t = new Template($this->config, 'core:no_cookie.twig'); diff --git a/modules/core/lib/Controller/Login.php b/modules/core/lib/Controller/Login.php index a4941bc00fa12976abd3637e486405a52db31ef6..5e2dcca87d8b2ecde8ca2c3161a386578e0f31e7 100644 --- a/modules/core/lib/Controller/Login.php +++ b/modules/core/lib/Controller/Login.php @@ -139,8 +139,9 @@ class Login if ($as === null) { // no authentication source specified if (!$default) { + $authUtils = new Utils\Auth(); $t = new Template($this->config, 'core:login.twig'); - $t->data['loginurl'] = Utils\Auth::getAdminLoginURL(); + $t->data['loginurl'] = $authUtils->getAdminLoginURL(); $t->data['sources'] = $this->sources; return $t; } @@ -219,6 +220,8 @@ class Login */ public function cleardiscochoices(Request $request): void { + $httpUtils = new Utils\HTTP(); + // The base path for cookies. This should be the installation directory for SimpleSAMLphp. $cookiePath = $this->config->getBasePath(); @@ -229,19 +232,19 @@ class Login continue; } - Utils\HTTP::setCookie($cookieName, null, ['path' => $cookiePath, 'httponly' => false], false); + $httpUtils->setCookie($cookieName, null, ['path' => $cookiePath, 'httponly' => false], false); } // Find where we should go now. $returnTo = $request->get('ReturnTo', false); if ($returnTo !== false) { - $returnTo = Utils\HTTP::checkURLAllowed($returnTo); + $returnTo = $httpUtils->checkURLAllowed($returnTo); } else { // Return to the front page if no other destination is given. This is the same as the base cookie path. $returnTo = $cookiePath; } // Redirect to destination. - Utils\HTTP::redirectTrustedURL($returnTo); + $httpUtils->redirectTrustedURL($returnTo); } } diff --git a/modules/core/lib/Controller/Redirection.php b/modules/core/lib/Controller/Redirection.php index 942dd675c1a3369cd6930de8e110ffbd22c72b36..d0947029bf94c30a7ec47452456649508211cc0c 100644 --- a/modules/core/lib/Controller/Redirection.php +++ b/modules/core/lib/Controller/Redirection.php @@ -31,6 +31,9 @@ class Redirection /** @var \SimpleSAML\Session */ protected Session $session; + /** @var \SimpleSAML\Utils\Crypto */ + protected $cryptoUtils; + /** * Controller constructor. @@ -48,6 +51,7 @@ class Redirection ) { $this->config = $config; $this->session = $session; + $this->cryptoUtils = new Utils\Crypto(); } @@ -72,7 +76,7 @@ class Redirection throw new Error\BadRequest('Invalid RedirInfo data.'); } - list($sessionId, $postId) = explode(':', Utils\Crypto::aesDecrypt($encData)); + list($sessionId, $postId) = explode(':', $this->cryptoUtils->aesDecrypt($encData)); if (empty($sessionId) || empty($postId)) { throw new Error\BadRequest('Invalid session info data.'); @@ -99,7 +103,8 @@ class Redirection Assert::keyExists($postData, 'url'); Assert::keyExists($postData, 'post'); - if (!Utils\HTTP::isValidURL($postData['url'])) { + $httpUtils = new Utils\HTTP(); + if (!$httpUtils->isValidURL($postData['url'])) { throw new Error\Exception('Invalid destination URL.'); } diff --git a/modules/core/www/login-admin.php b/modules/core/www/login-admin.php index 3b6443432b2fac05f4464804856f8be71cd4f9b6..a430c9c6a22002fbc9e88177fc014c5d9f19b13a 100644 --- a/modules/core/www/login-admin.php +++ b/modules/core/www/login-admin.php @@ -8,5 +8,8 @@ if (!array_key_exists('ReturnTo', $_REQUEST)) { throw new \SimpleSAML\Error\BadRequest('Missing ReturnTo parameter.'); } -\SimpleSAML\Utils\Auth::requireAdmin(); -\SimpleSAML\Utils\HTTP::redirectUntrustedURL($_REQUEST['ReturnTo']); +$authUtils = new \SimpleSAML\Utils\Auth(); +$authUtils->requireAdmin(); + +$httpUtils = new \SimpleSAML\Utils\HTTP(); +$httpUtils->redirectUntrustedURL($_REQUEST['ReturnTo']); diff --git a/modules/core/www/loginuserpass.php b/modules/core/www/loginuserpass.php index bb5d59c2423a56b15ea801d39db3b2645bfbbbaa..b770b889bcae057fdfd7f81975a3a017014a6678 100644 --- a/modules/core/www/loginuserpass.php +++ b/modules/core/www/loginuserpass.php @@ -66,7 +66,9 @@ if (!empty($_REQUEST['username']) || !empty($password)) { } else { $params['expire'] = time() - 300; } - \SimpleSAML\Utils\HTTP::setCookie($source->getAuthId() . '-username', $username, $params, false); + + $httpUtils = new \SimpleSAML\Utils\HTTP(); + $httpUtils->setCookie($source->getAuthId() . '-username', $username, $params, false); } if ($source->isRememberMeEnabled()) { diff --git a/modules/core/www/loginuserpassorg.php b/modules/core/www/loginuserpassorg.php index 636fd969ced1391e35abf1125ce33fde7816228f..295650dcd7bbe4444eb3ed6aaad89dc4f4633317 100644 --- a/modules/core/www/loginuserpassorg.php +++ b/modules/core/www/loginuserpassorg.php @@ -77,7 +77,8 @@ if ($organizations === null || !empty($organization)) { $params['expire'] = time() - 300; } - \SimpleSAML\Utils\HTTP::setCookie($source->getAuthId() . '-username', $username, $params, false); + $httpUtils = new \SimpleSAML\Utils\HTTP(); + $httpUtils->setCookie($source->getAuthId() . '-username', $username, $params, false); } if ($source->getRememberOrganizationEnabled()) { diff --git a/modules/cron/lib/Controller/Cron.php b/modules/cron/lib/Controller/Cron.php index 0b6542d7f5862d4070682595e59f641f50f6d8f7..f90e0e116e705181fedaa33d1840d35513aa2c56 100644 --- a/modules/cron/lib/Controller/Cron.php +++ b/modules/cron/lib/Controller/Cron.php @@ -38,10 +38,9 @@ class Cron protected Session $session; /** - * @var \SimpleSAML\Utils\Auth|string - * @psalm-var \SimpleSAML\Utils\Auth|class-string + * @var \SimpleSAML\Utils\Auth */ - protected $authUtils = Utils\Auth::class; + protected $authUtils; /** @@ -61,6 +60,7 @@ class Cron $this->config = $config; $this->cronconfig = Configuration::getConfig('module_cron.php'); $this->session = $session; + $this->authUtils = new Utils\Auth(); } @@ -83,7 +83,7 @@ class Cron */ public function info(): Template { - $this->authUtils::requireAdmin(); + $this->authUtils->requireAdmin(); $key = $this->cronconfig->getValue('key', 'secret'); $tags = $this->cronconfig->getValue('allowed_tags', []); @@ -139,7 +139,8 @@ class Cron exit; } - $url = Utils\HTTP::getSelfURL(); + $httpUtils = new Utils\HTTP(); + $url = $httpUtils->getSelfURL(); $time = date(DATE_RFC822); $croninfo = $cron->runTag($tag); diff --git a/modules/exampleauth/lib/Auth/Process/RedirectTest.php b/modules/exampleauth/lib/Auth/Process/RedirectTest.php index bb4e7ff6aaf894b72a4a56e009498b3acbf9b99b..34c93221251e3e3db553ef66a335436129922068 100644 --- a/modules/exampleauth/lib/Auth/Process/RedirectTest.php +++ b/modules/exampleauth/lib/Auth/Process/RedirectTest.php @@ -30,6 +30,8 @@ class RedirectTest extends \SimpleSAML\Auth\ProcessingFilter // Save state and redirect $id = Auth\State::saveState($state, 'exampleauth:redirectfilter-test'); $url = Module::getModuleURL('exampleauth/redirecttest.php'); - Utils\HTTP::redirectTrustedURL($url, ['StateId' => $id]); + + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); } } diff --git a/modules/exampleauth/lib/Auth/Source/External.php b/modules/exampleauth/lib/Auth/Source/External.php index 6ed1ef5e9c755bc85c22965d431fa6432c5bae51..44595129d149143d66cb4850d82aa7baf79bac52 100644 --- a/modules/exampleauth/lib/Auth/Source/External.php +++ b/modules/exampleauth/lib/Auth/Source/External.php @@ -167,7 +167,8 @@ class External extends Auth\Source * Note the 'ReturnTo' parameter. This must most likely be replaced with * the real name of the parameter for the login page. */ - Utils\HTTP::redirectTrustedURL($authPage, [ + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($authPage, [ 'ReturnTo' => $returnTo, ]); diff --git a/modules/exampleauth/lib/Auth/Source/StaticSource.php b/modules/exampleauth/lib/Auth/Source/StaticSource.php index fc047932230c450258c5eec79e4413887f09af35..fef70b66816a9b51f75e99a7d342e19eac1bf42b 100644 --- a/modules/exampleauth/lib/Auth/Source/StaticSource.php +++ b/modules/exampleauth/lib/Auth/Source/StaticSource.php @@ -37,9 +37,11 @@ class StaticSource extends Auth\Source // Call the parent constructor first, as required by the interface parent::__construct($info, $config); + $attrUtils = new Utils\Attributes(); + // Parse attributes try { - $this->attributes = Utils\Attributes::normalizeAttributesArray($config); + $this->attributes = $attrUtils->normalizeAttributesArray($config); } catch (Exception $e) { throw new Exception('Invalid attributes for authentication source ' . $this->authId . ': ' . $e->getMessage()); diff --git a/modules/exampleauth/lib/Auth/Source/UserPass.php b/modules/exampleauth/lib/Auth/Source/UserPass.php index e2d08094cdec50b788b5dbb21212147611b99873..b1867f8075971132f538e4c466f4dd6f7f9c528a 100644 --- a/modules/exampleauth/lib/Auth/Source/UserPass.php +++ b/modules/exampleauth/lib/Auth/Source/UserPass.php @@ -60,8 +60,10 @@ class UserPass extends UserPassBase $username = $userpass[0]; $password = $userpass[1]; + $attrUtils = new Utils\Attributes(); + try { - $attributes = Utils\Attributes::normalizeAttributesArray($attributes); + $attributes = $attrUtils->normalizeAttributesArray($attributes); } catch (Exception $e) { throw new Exception('Invalid attributes for user ' . $username . ' in authentication source ' . $this->authId . ': ' . $e->getMessage()); diff --git a/modules/exampleauth/www/authpage.php b/modules/exampleauth/www/authpage.php index 085b486c26d13614b5fe2cb077af52f6f650f8a9..c1ab81bfc63f259f8c4f6e40d9ec9b7bbfff958f 100644 --- a/modules/exampleauth/www/authpage.php +++ b/modules/exampleauth/www/authpage.php @@ -13,7 +13,8 @@ if (!isset($_REQUEST['ReturnTo'])) { die('Missing ReturnTo parameter.'); } -$returnTo = \SimpleSAML\Utils\HTTP::checkURLAllowed($_REQUEST['ReturnTo']); +$httpUtils = new \SimpleSAML\Utils\HTTP(); +$returnTo = $httpUtils->checkURLAllowed($_REQUEST['ReturnTo']); /** * The following piece of code would never be found in a real authentication page. Its @@ -74,7 +75,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $_SESSION['mail'] = $user['mail']; $_SESSION['type'] = $user['type']; - \SimpleSAML\Utils\HTTP::redirectTrustedURL($returnTo); + $httpUtils->redirectTrustedURL($returnTo); } } diff --git a/modules/multiauth/lib/Auth/Source/MultiAuth.php b/modules/multiauth/lib/Auth/Source/MultiAuth.php index 2ad0482b1a20a6905a61fbfa81f32c248a8d05b0..295d8ae678fc94d2d0d01a4f9c5394a1ce308d6b 100644 --- a/modules/multiauth/lib/Auth/Source/MultiAuth.php +++ b/modules/multiauth/lib/Auth/Source/MultiAuth.php @@ -196,7 +196,8 @@ class MultiAuth extends Auth\Source $params['source'] = $_GET['source']; } - Utils\HTTP::redirectTrustedURL($url, $params); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, $params); // The previous function never returns, so this code is never executed Assert::true(false); @@ -309,7 +310,8 @@ class MultiAuth extends Auth\Source 'httponly' => false, ]; - Utils\HTTP::setCookie($cookieName, $source, $params, false); + $httpUtils = new Utils\HTTP(); + $httpUtils->setCookie($cookieName, $source, $params, false); } diff --git a/modules/multiauth/lib/Controller/DiscoController.php b/modules/multiauth/lib/Controller/DiscoController.php index efc685482f8ddb962bbb943d19eb15ac8e22716f..a79ce2ff93871eb905f9bb6375a4f3e943db814e 100644 --- a/modules/multiauth/lib/Controller/DiscoController.php +++ b/modules/multiauth/lib/Controller/DiscoController.php @@ -149,7 +149,8 @@ class DiscoController } } - $baseurl = explode("/", Utils\HTTP::getBaseURL()); + $httpUtils = new Utils\HTTP(); + $baseurl = explode("/", $httpUtils->getBaseURL()); $elements = array_slice($baseurl, 3 - count($baseurl), count($baseurl) - 4); $path = implode("/", $elements); diff --git a/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php b/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php index c4409880803d5fe2dd9f57555d023a3e0b4c4ec2..bc9fdef1b1da061a5132913945308ecb799fd210 100644 --- a/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php +++ b/modules/saml/lib/Auth/Process/ExpectedAuthnContextClassRef.php @@ -107,6 +107,8 @@ class ExpectedAuthnContextClassRef extends ProcessingFilter $url = Module::getModuleURL( 'saml/sp/wrong_authncontextclassref.php' ); - Utils\HTTP::redirectTrustedURL($url, ['StateId' => $id]); + + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); } } diff --git a/modules/saml/lib/Auth/Process/PersistentNameID.php b/modules/saml/lib/Auth/Process/PersistentNameID.php index b7f825a69ded7d71e84afffb2f1be58d39ab8c66..be583d6d882eb9df35d6876167990338e2747c1f 100644 --- a/modules/saml/lib/Auth/Process/PersistentNameID.php +++ b/modules/saml/lib/Auth/Process/PersistentNameID.php @@ -93,7 +93,8 @@ class PersistentNameID extends BaseNameIDGenerator return null; } - $secretSalt = Utils\Config::getSecretSalt(); + $configUtils = new Utils\Config(); + $secretSalt = $configUtils->getSecretSalt(); $uidData = 'uidhashbase' . $secretSalt; $uidData .= strlen($idpEntityId) . ':' . $idpEntityId; diff --git a/modules/saml/lib/Auth/Process/TransientNameID.php b/modules/saml/lib/Auth/Process/TransientNameID.php index 7e3266117c6e58a42dfd044b9f6f3b72ea1a1767..5af217a353e2e27b94b74a72ff6a53c3a9873e45 100644 --- a/modules/saml/lib/Auth/Process/TransientNameID.php +++ b/modules/saml/lib/Auth/Process/TransientNameID.php @@ -39,6 +39,7 @@ class TransientNameID extends BaseNameIDGenerator */ protected function getValue(array &$state): ?string { - return Utils\Random::generateID(); + $randomUtils = new Utils\Random(); + return $randomUtils->generateID(); } } diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php index 9a159796a9978ab1cd53199b23693b94bf439968..8c9d9e21f55e9f04d2ff97c84bc45b3aa1b4904a 100644 --- a/modules/saml/lib/Auth/Source/SP.php +++ b/modules/saml/lib/Auth/Source/SP.php @@ -205,8 +205,10 @@ class SP extends \SimpleSAML\Auth\Source $metadata['contacts'][] = Utils\Config\Metadata::getContact($contact); } + $cryptoUtils = new Utils\Crypto(); + // add certificate(s) - $certInfo = Utils\Crypto::loadPublicKey($this->metadata, false, 'new_'); + $certInfo = $cryptoUtils->loadPublicKey($this->metadata, false, 'new_'); $hasNewCert = false; if ($certInfo !== null && array_key_exists('certData', $certInfo)) { $hasNewCert = true; @@ -228,7 +230,7 @@ class SP extends \SimpleSAML\Auth\Source ]; } - $certInfo = Utils\Crypto::loadPublicKey($this->metadata); + $certInfo = $cryptoUtils->loadPublicKey($this->metadata); if ($certInfo !== null && array_key_exists('certData', $certInfo)) { $metadata['keys'][] = [ 'type' => 'X509Certificate', @@ -456,11 +458,13 @@ class SP extends \SimpleSAML\Auth\Source $ar->setRelayState($state['\SimpleSAML\Auth\Source.ReturnURL']); } + $arrayUtils = new Utils\Arrays(); + $accr = null; if ($idpMetadata->getString('AuthnContextClassRef', false)) { - $accr = Utils\Arrays::arrayize($idpMetadata->getString('AuthnContextClassRef')); + $accr = $arrayUtils->arrayize($idpMetadata->getString('AuthnContextClassRef')); } elseif (isset($state['saml:AuthnContextClassRef'])) { - $accr = Utils\Arrays::arrayize($state['saml:AuthnContextClassRef']); + $accr = $arrayUtils->arrayize($state['saml:AuthnContextClassRef']); } if ($accr !== null) { @@ -707,7 +711,8 @@ class SP extends \SimpleSAML\Auth\Source $params['isPassive'] = 'true'; } - Utils\HTTP::redirectTrustedURL($discoURL, $params); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($discoURL, $params); } @@ -879,7 +884,9 @@ class SP extends \SimpleSAML\Auth\Source // save the state WITHOUT a restart URL, so that we don't try an IdP-initiated login if something goes wrong $id = Auth\State::saveState($state, 'saml:proxy:invalid_idp', true); $url = Module::getModuleURL('saml/proxy/invalid_session.php'); - Utils\HTTP::redirectTrustedURL($url, ['AuthState' => $id]); + + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectTrustedURL($url, ['AuthState' => $id]); Assert::true(false); } @@ -1104,7 +1111,8 @@ class SP extends \SimpleSAML\Auth\Source $session = Session::getSessionFromRequest(); $session->doLogin($authId, Auth\State::getPersistentAuthData($state)); - Utils\HTTP::redirectUntrustedURL($redirectTo); + $httpUtils = new Utils\HTTP(); + $httpUtils->redirectUntrustedURL($redirectTo); } diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php index 74a6dc1a7c493130aa3dc5d34c162c24a433c6ae..578bce57323577add4ad6c523513cece9cb9b853 100644 --- a/modules/saml/lib/IdP/SAML2.php +++ b/modules/saml/lib/IdP/SAML2.php @@ -278,6 +278,7 @@ class SAML2 { $metadata = MetaDataStorageHandler::getMetadataHandler(); $idpMetadata = $idp->getConfig(); + $httpUtils = new Utils\HTTP(); $supportedBindings = [Constants::BINDING_HTTP_POST]; if ($idpMetadata->getBoolean('saml20.sendartifact', false)) { @@ -300,7 +301,7 @@ class SAML2 * Less than five seconds has passed since we were * here the last time. Cookies are probably disabled. */ - Utils\HTTP::checkSessionCookie(Utils\HTTP::getSelfURL()); + $httpUtils->checkSessionCookie($httpUtils->getSelfURL()); } } @@ -449,8 +450,8 @@ class SAML2 */ $sessionLostParams['cookieTime'] = time(); - $sessionLostURL = Utils\HTTP::addURLParameters( - Utils\HTTP::getSelfURLNoQuery(), + $sessionLostURL = $httpUtils->addURLParameters( + $httpUtils->getSelfURLNoQuery(), $sessionLostParams ); @@ -771,9 +772,12 @@ class SAML2 'NameIDFormat' => $config->getArrayizeString('NameIDFormat', Constants::NAMEID_TRANSIENT), ]; + $cryptoUtils = new Utils\Crypto(); + $httpUtils = new Utils\HTTP(); + // add certificates $keys = []; - $certInfo = Utils\Crypto::loadPublicKey($config, false, 'new_'); + $certInfo = $cryptoUtils->loadPublicKey($config, false, 'new_'); $hasNewCert = false; if ($certInfo !== null) { $keys[] = [ @@ -787,7 +791,7 @@ class SAML2 } /** @var array $certInfo */ - $certInfo = Utils\Crypto::loadPublicKey($config, true); + $certInfo = $cryptoUtils->loadPublicKey($config, true); $keys[] = [ 'type' => 'X509Certificate', 'signing' => true, @@ -798,7 +802,7 @@ class SAML2 if ($config->hasValue('https.certificate')) { /** @var array $httpsCert */ - $httpsCert = Utils\Crypto::loadPublicKey($config, true, 'https.'); + $httpsCert = $cryptoUtils->loadPublicKey($config, true, 'https.'); $keys[] = [ 'type' => 'X509Certificate', 'signing' => true, @@ -814,7 +818,7 @@ class SAML2 $metadata['ArtifactResolutionService'][] = [ 'index' => 0, 'Binding' => Constants::BINDING_SOAP, - 'Location' => Utils\HTTP::getBaseURL() . 'saml2/idp/ArtifactResolutionService.php' + 'Location' => $httpUtils->getBaseURL() . 'saml2/idp/ArtifactResolutionService.php' ]; } @@ -825,7 +829,7 @@ class SAML2 [ 'hoksso:ProtocolBinding' => Constants::BINDING_HTTP_REDIRECT, 'Binding' => Constants::BINDING_HOK_SSO, - 'Location' => Utils\HTTP::getBaseURL() . 'saml2/idp/SSOService.php', + 'Location' => $httpUtils->getBaseURL() . 'saml2/idp/SSOService.php', ] ); } @@ -835,7 +839,7 @@ class SAML2 $metadata['SingleSignOnService'][] = [ 'index' => 0, 'Binding' => Constants::BINDING_SOAP, - 'Location' => Utils\HTTP::getBaseURL() . 'saml2/idp/SSOService.php', + 'Location' => $httpUtils->getBaseURL() . 'saml2/idp/SSOService.php', ]; } @@ -1087,6 +1091,7 @@ class SAML2 Assert::notNull($state['Attributes']); Assert::notNull($state['saml:ConsumerURL']); + $httpUtils = new Utils\HTTP(); $now = time(); $signAssertion = $spMetadata->getBoolean('saml20.sign.assertion', null); @@ -1119,7 +1124,7 @@ class SAML2 if (isset($state['saml:AuthnContextClassRef'])) { $a->setAuthnContextClassRef($state['saml:AuthnContextClassRef']); - } elseif (Utils\HTTP::isHTTPS()) { + } elseif ($httpUtils->isHTTPS()) { $a->setAuthnContextClassRef(Constants::AC_PASSWORD_PROTECTED_TRANSPORT); } else { $a->setAuthnContextClassRef(Constants::AC_PASSWORD); @@ -1134,7 +1139,8 @@ class SAML2 $sessionLifetime = $config->getInteger('session.duration', 8 * 60 * 60); $a->setSessionNotOnOrAfter($sessionStart + $sessionLifetime); - $a->setSessionIndex(Utils\Random::generateID()); + $randomUtils = new Utils\Random(); + $a->setSessionIndex($randomUtils->generateID()); $sc = new SubjectConfirmation(); $scd = new SubjectConfirmationData(); @@ -1155,7 +1161,8 @@ class SAML2 if ($hokAssertion) { // Holder-of-Key $sc->setMethod(Constants::CM_HOK); - if (Utils\HTTP::isHTTPS()) { + + if ($httpUtils->isHTTPS()) { if (isset($_SERVER['SSL_CLIENT_CERT']) && !empty($_SERVER['SSL_CLIENT_CERT'])) { // extract certificate data (if this is a certificate) $clientCert = $_SERVER['SSL_CLIENT_CERT']; @@ -1229,7 +1236,7 @@ class SAML2 if ($nameIdFormat === Constants::NAMEID_TRANSIENT) { // generate a random id - $nameIdValue = Utils\Random::generateID(); + $nameIdValue = $randomUtils->generateID(); } else { /* this code will end up generating either a fixed assigned id (via nameid.attribute) or random id if not assigned/configured */ @@ -1237,7 +1244,7 @@ class SAML2 if ($nameIdValue === null) { Logger::warning('Falling back to transient NameID.'); $nameIdFormat = Constants::NAMEID_TRANSIENT; - $nameIdValue = Utils\Random::generateID(); + $nameIdValue = $randomUtils->generateID(); } } diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php index 2104296eec3461dd11d1fa69345b6c4c38733409..2ff01ea528853e035b84f0f5a100f03eb2ed4804 100644 --- a/modules/saml/lib/Message.php +++ b/modules/saml/lib/Message.php @@ -44,15 +44,16 @@ class Message SignedElement $element ): void { $dstPrivateKey = $dstMetadata->getString('signature.privatekey', null); + $cryptoUtils = new Utils\Crypto(); if ($dstPrivateKey !== null) { /** @var array $keyArray */ - $keyArray = Utils\Crypto::loadPrivateKey($dstMetadata, true, 'signature.'); - $certArray = Utils\Crypto::loadPublicKey($dstMetadata, false, 'signature.'); + $keyArray = $cryptoUtils->loadPrivateKey($dstMetadata, true, 'signature.'); + $certArray = $cryptoUtils->loadPublicKey($dstMetadata, false, 'signature.'); } else { /** @var array $keyArray */ - $keyArray = Utils\Crypto::loadPrivateKey($srcMetadata, true); - $certArray = Utils\Crypto::loadPublicKey($srcMetadata, false); + $keyArray = $cryptoUtils->loadPrivateKey($srcMetadata, true); + $certArray = $cryptoUtils->loadPublicKey($srcMetadata, false); } $algo = $dstMetadata->getString('signature.algorithm', null); @@ -212,7 +213,14 @@ class Message } } - if ($enabled === null) { + // If not specifically set to false, the signature must be checked to conform to SAML2INT + if ( + (isset($_REQUEST['Signature']) + || $message->isMessageConstructedWithSignature() === true) + && ($enabled !== false) + ) { + $enabled = true; + } elseif ($enabled === null) { $enabled = $srcMetadata->getBoolean('redirect.validate', null); if ($enabled === null) { $enabled = $dstMetadata->getBoolean('redirect.validate', false); @@ -221,9 +229,7 @@ class Message if (!$enabled) { return; - } - - if (!self::checkSign($srcMetadata, $message)) { + } elseif (!self::checkSign($srcMetadata, $message)) { throw new SSP_Error\Exception( 'Validation of received messages enabled, but no signature found on message.' ); @@ -264,9 +270,10 @@ class Message } $keys = []; + $cryptoUtils = new Utils\Crypto(); // load the new private key if it exists - $keyArray = Utils\Crypto::loadPrivateKey($dstMetadata, false, 'new_'); + $keyArray = $cryptoUtils->loadPrivateKey($dstMetadata, false, 'new_'); if ($keyArray !== null) { assert::keyExists($keyArray, 'PEM'); @@ -283,7 +290,7 @@ class Message * * @var array $keyArray Because the second param is true */ - $keyArray = Utils\Crypto::loadPrivateKey($dstMetadata, true); + $keyArray = $cryptoUtils->loadPrivateKey($dstMetadata, true); Assert::keyExists($keyArray, 'PEM'); $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, ['type' => 'private']); @@ -576,7 +583,8 @@ class Message } // validate Response-element destination - $currentURL = Utils\HTTP::getSelfURLNoQuery(); + $httpUtils = new \SimpleSAML\Utils\HTTP(); + $currentURL = $httpUtils->getSelfURLNoQuery(); $msgDestination = $response->getDestination(); if ($msgDestination !== null && $msgDestination !== $currentURL) { throw new \Exception('Destination in response doesn\'t match the current URL. Destination is "' . @@ -637,7 +645,8 @@ class Message } } // at least one valid signature found - $currentURL = Utils\HTTP::getSelfURLNoQuery(); + $httpUtils = new Utils\HTTP(); + $currentURL = $httpUtils->getSelfURLNoQuery(); // check various properties of the assertion $config = Configuration::getInstance(); @@ -708,7 +717,7 @@ class Message $scd = $sc->getSubjectConfirmationData(); if ($method === Constants::CM_HOK) { // check HoK Assertion - if (Utils\HTTP::isHTTPS() === false) { + if ($httpUtils->isHTTPS() === false) { $lastError = 'No HTTPS connection, but required for Holder-of-Key SSO'; continue; } diff --git a/modules/saml/lib/SP/LogoutStore.php b/modules/saml/lib/SP/LogoutStore.php index aa7de61847023fff92cd19061c9af4f20380e489..13922d1309358b1c9cf76ee000d43cb078b8cf40 100644 --- a/modules/saml/lib/SP/LogoutStore.php +++ b/modules/saml/lib/SP/LogoutStore.php @@ -350,7 +350,8 @@ class LogoutStore * it supports SLO, but we don't want an LogoutRequest with a specific * SessionIndex to match this session. We therefore generate our own session index. */ - $sessionIndex = Utils\Random::generateID(); + $randomUtils = new Utils\Random(); + $sessionIndex = $randomUtils->generateID(); } $store = Store::getInstance(); diff --git a/modules/saml/www/idp/certs.php b/modules/saml/www/idp/certs.php index 5f11e7461db7d38fccabcf090d65fe5996ecfa35..ae3889819943befd4db91ea2f6e1acaa65180222 100644 --- a/modules/saml/www/idp/certs.php +++ b/modules/saml/www/idp/certs.php @@ -10,24 +10,26 @@ if (!$config->getBoolean('enable.saml20-idp', false)) { // Check if valid local session exists.. if ($config->getBoolean('admin.protectmetadata', false)) { - \SimpleSAML\Utils\Auth::requireAdmin(); + $authUtils = new \SimpleSAML\Utils\Auth(); + $authUtils->requireAdmin(); } $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted'); +$cryptoUtils = new \SimpleSAML\Utils\Crypto(); switch ($_SERVER['PATH_INFO']) { case '/new_idp.crt': /** @var array $certInfo */ - $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'new_'); + $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true, 'new_'); break; case '/idp.crt': /** @var array $certInfo */ - $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true); + $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true); break; case '/https.crt': /** @var array $certInfo */ - $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'https.'); + $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true, 'https.'); break; default: throw new \SimpleSAML\Error\NotFound('Unknown certificate.'); diff --git a/modules/saml/www/sp/metadata.php b/modules/saml/www/sp/metadata.php index d54431233014bcf455d6f704dab250c3931062cf..3b4e847eb5759108e229c74e8cdcd1a36fb52630 100644 --- a/modules/saml/www/sp/metadata.php +++ b/modules/saml/www/sp/metadata.php @@ -18,7 +18,8 @@ if (!array_key_exists('PATH_INFO', $_SERVER)) { $config = Configuration::getInstance(); if ($config->getBoolean('admin.protectmetadata', false)) { - Utils\Auth::requireAdmin(); + $authUtils = new Utils\Auth(); + $authUtils->requireAdmin(); } $sourceId = substr($_SERVER['PATH_INFO'], 1); $source = Auth\Source::getById($sourceId); @@ -123,7 +124,8 @@ foreach ($assertionsconsumerservices as $services) { $metaArray20['AssertionConsumerService'] = $spconfig->getArray('AssertionConsumerService', $eps); $keys = []; -$certInfo = Utils\Crypto::loadPublicKey($spconfig, false, 'new_'); +$cryptoUtils = new Utils\Crypto(); +$certInfo = $cryptoUtils->loadPublicKey($spconfig, false, 'new_'); if ($certInfo !== null && array_key_exists('certData', $certInfo)) { $hasNewCert = true; @@ -139,7 +141,7 @@ if ($certInfo !== null && array_key_exists('certData', $certInfo)) { $hasNewCert = false; } -$certInfo = Utils\Crypto::loadPublicKey($spconfig); +$certInfo = $cryptoUtils->loadPublicKey($spconfig); if ($certInfo !== null && array_key_exists('certData', $certInfo)) { $certData = $certInfo['certData']; diff --git a/modules/saml/www/sp/saml2-acs.php b/modules/saml/www/sp/saml2-acs.php index 17528226d42e53222b073881c553370d0355b058..9c56cfd5ddc0e5f95c4ecb55326f43acbc20aaea 100644 --- a/modules/saml/www/sp/saml2-acs.php +++ b/modules/saml/www/sp/saml2-acs.php @@ -70,6 +70,7 @@ $issuer = $issuer->getValue(); $session = Session::getSessionFromRequest(); $prevAuth = $session->getAuthData($sourceId, 'saml:sp:prevAuth'); +$httpUtils = new Utils\HTTP(); if ($prevAuth !== null && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $issuer) { /* OK, it looks like this message has the same issuer * and ID as the SP session we already have active. We @@ -82,7 +83,7 @@ if ($prevAuth !== null && $prevAuth['id'] === $response->getId() && $prevAuth['i 'Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.' ); if (isset($prevAuth['redirect'])) { - Utils\HTTP::redirectTrustedURL($prevAuth['redirect']); + $httpUtils->redirectTrustedURL($prevAuth['redirect']); } Logger::info('No RelayState or ReturnURL available, cannot redirect.'); @@ -130,7 +131,7 @@ if ($state) { $state = [ 'saml:sp:isUnsolicited' => true, 'saml:sp:AuthId' => $sourceId, - 'saml:sp:RelayState' => $relaystate === null ? null : Utils\HTTP::checkURLAllowed($relaystate), + 'saml:sp:RelayState' => $relaystate === null ? null : $httpUtils->checkURLAllowed($relaystate), ]; } diff --git a/modules/saml/www/sp/saml2-logout.php b/modules/saml/www/sp/saml2-logout.php index c7e5bd2cd349a59541558f5a15a30ef6c5052340..a91cf3d3f2b4a5314f378c8b5e4d7f29be422ef8 100644 --- a/modules/saml/www/sp/saml2-logout.php +++ b/modules/saml/www/sp/saml2-logout.php @@ -68,8 +68,9 @@ $spMetadata = $source->getMetadata(); Module\saml\Message::validateMessage($idpMetadata, $spMetadata, $message); +$httpUtils = new Utils\HTTP(); $destination = $message->getDestination(); -if ($destination !== null && $destination !== Utils\HTTP::getSelfURLNoQuery()) { +if ($destination !== null && $destination !== $httpUtils->getSelfURLNoQuery()) { throw new Error\Exception('Destination in logout message is wrong.'); } diff --git a/phpunit.xml b/phpunit.xml index ff697d42f4f83fa4eb91d929c2e9c3a71f00e8d4..e43310ecb5c497c78493f7b955c0a05cd0848af9 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,7 +12,6 @@ </include> <exclude> <directory>./tests</directory> - <file>./lib/SimpleSAML/Utils/HttpAdapter.php</file> </exclude> <report> <clover outputFile="build/logs/clover.xml"/> diff --git a/tests/Utils/ArrayLogger.php b/tests/Utils/ArrayLogger.php new file mode 100644 index 0000000000000000000000000000000000000000..ac15538e79a29d708af720e11cafec0022fbcdc2 --- /dev/null +++ b/tests/Utils/ArrayLogger.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Utils; + +use SimpleSAML\Configuration; +use SimpleSAML\Logger\LoggingHandlerInterface; + +class ArrayLogger implements LoggingHandlerInterface +{ + /** + * @var array List of log entries by level + */ + public array $logs = []; + + public function __construct(Configuration $config) + { + // don't do anything with the configuration + } + + public function log(int $level, string $string): void + { + $this->logs[$level][] = $string; + } + + public function setLogFormat(string $format): void + { + // ignored + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d95d01ca460b26e1b41646f36749fe9b387f635b..e916d45f3ac24b4875a40250ae92acc4e72dea9d 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -7,3 +7,8 @@ require_once($projectRoot . '/vendor/autoload.php'); // Current SSP autoloader can't resolve classes from the tests folder. include($projectRoot . '/tests/Utils/ClearStateTestCase.php'); + +// set the SAML2 container +$container = new \SimpleSAML\Compat\SspContainer(); +\SAML2\Compat\ContainerSingleton::setContainer($container); + diff --git a/tests/lib/SimpleSAML/LoggerTest.php b/tests/lib/SimpleSAML/LoggerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6fdaf00394592e2b8d2a3df20908059f8d1f0294 --- /dev/null +++ b/tests/lib/SimpleSAML/LoggerTest.php @@ -0,0 +1,133 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test; + +use Exception; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Configuration; +use SimpleSAML\Logger; +use SimpleSAML\Test\Utils\ArrayLogger; + +class LoggerTest extends TestCase +{ + /** + * @var Logger\LoggingHandlerInterface|null + */ + protected $originalLogger; + + + /** + * @param string $handler + */ + protected function setLoggingHandler(string $handler): void + { + $this->originalLogger = Logger::getLoggingHandler(); + $config = [ + 'logging.handler' => $handler, + 'logging.level' => Logger::DEBUG + ]; + + // testing statics is slightly painful + Configuration::loadFromArray($config, '[ARRAY]', 'simplesaml'); + Logger::setLoggingHandler(null); + } + + + /** + */ + protected function tearDown(): void + { + if (isset($this->originalLogger)) { + // reset the logger and Configuration + Configuration::clearInternalState(); + Logger::clearCapturedLog(); + Logger::setLogLevel(Logger::INFO); + Logger::setLoggingHandler($this->originalLogger); + } + } + + + /** + */ + public function testCreateLoggingHandlerHonorsCustomHandler(): void + { + $this->setLoggingHandler(ArrayLogger::class); + + Logger::critical('array logger'); + + $logger = Logger::getLoggingHandler(); + + self::assertInstanceOf(ArrayLogger::class, $logger); + } + + + /** + */ + public function testCaptureLog(): void + { + $this->setLoggingHandler(ArrayLogger::class); + + $payload = "catch this error"; + Logger::setCaptureLog(); + Logger::critical($payload); + + // turn logging off + Logger::setCaptureLog(false); + Logger::critical("do not catch this"); + + $log = Logger::getCapturedLog(); + self::assertCount(1, $log); + self::assertMatchesRegularExpression("/^[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z\ {$payload}$/", $log[0]); + } + + + /** + */ + public function testExceptionThrownOnInvalidLoggingHandler(): void + { + $this->setLoggingHandler('nohandler'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage( + "Invalid value for the 'logging.handler' configuration option. Unknown handler 'nohandler'." + ); + + Logger::critical('should throw exception'); + } + + + /** + * @return array + */ + public function provideLogLevels(): array + { + return [ + ['emergency', Logger::EMERG], + ['alert', Logger::ALERT], + ['critical', Logger::CRIT], + ['error', Logger::ERR], + ['warning', Logger::WARNING], + ['notice', Logger::NOTICE], + ['info', Logger::INFO], + ['debug', Logger::DEBUG], + ]; + } + + + /** + * @param string $method + * @param int $level + * @dataProvider provideLogLevels + */ + public function testLevelMethods(string $method, int $level): void + { + $this->setLoggingHandler(ArrayLogger::class); + + Logger::{$method}($payload = "test {$method}"); + + $logger = Logger::getLoggingHandler(); + self::assertMatchesRegularExpression("/\[CL[0-9a-f]{8}\]\ {$payload}$/", $logger->logs[$level][0]); + } +} diff --git a/tests/lib/SimpleSAML/ModuleTest.php b/tests/lib/SimpleSAML/ModuleTest.php index 0a4b69941e628d1bacc03d13662ed6915c442dc6..543e3070905468c636a33380fbf9aa201ad51a21 100644 --- a/tests/lib/SimpleSAML/ModuleTest.php +++ b/tests/lib/SimpleSAML/ModuleTest.php @@ -112,4 +112,26 @@ class ModuleTest extends TestCase '\SimpleSAML\Auth\ProcessingFilter' )); } + + /** + * Test for SimpleSAML\Module::getModuleHooks(). It covers happy path. + */ + public function testGetModuleHooks(): void + { + $hooks = Module::getModuleHooks('saml'); + $this->assertArrayHasKey('metadata_hosted', $hooks); + $this->assertEquals('saml_hook_metadata_hosted', $hooks['metadata_hosted']['func']); + $expectedFile = dirname(__DIR__, 3) . '/modules/saml/hooks/hook_metadata_hosted.php'; + $this->assertEquals($expectedFile, $hooks['metadata_hosted']['file']); + } + + /** + * Test for SimpleSAML\Module::getModuleHooks(). It covers invalid hook names + */ + public function testGetModuleHooksIgnoresInvalidHooks(): void + { + $hooks = Module::getModuleHooks('../tests/modules/unittest'); + $this->assertArrayHasKey('valid', $hooks, 'hooks=' . var_export($hooks, true)); + $this->assertCount(1, $hooks, "Invalid hooks should be ignored"); + } } diff --git a/tests/lib/SimpleSAML/Utils/ArraysTest.php b/tests/lib/SimpleSAML/Utils/ArraysTest.php index 7267b8ab3ca5875078208afc982241b18a021404..8d297cf6a31f062cb2f4e8dc7327db645adc1a31 100644 --- a/tests/lib/SimpleSAML/Utils/ArraysTest.php +++ b/tests/lib/SimpleSAML/Utils/ArraysTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace SimpleSAML\Test\Utils; use PHPUnit\Framework\TestCase; -use SimpleSAML\Utils\Arrays; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\Arrays. @@ -14,6 +14,22 @@ use SimpleSAML\Utils\Arrays; */ class ArraysTest extends TestCase { + /** @var \SimpleSAML\Utils\Arrays */ + protected $arrayUtils; + + + /** + * Set up for each test. + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + + $this->arrayUtils = new Utils\Arrays(); + } + + /** * Test the arrayize() function. */ @@ -21,23 +37,23 @@ class ArraysTest extends TestCase { // check with empty array as input $array = []; - $this->assertEquals($array, Arrays::arrayize($array)); + $this->assertEquals($array, $this->arrayUtils->arrayize($array)); // check non-empty array as input $array = ['key' => 'value']; - $this->assertEquals($array, Arrays::arrayize($array)); + $this->assertEquals($array, $this->arrayUtils->arrayize($array)); // check indexes are ignored when input is an array - $this->assertArrayNotHasKey('invalid', Arrays::arrayize($array, 'invalid')); + $this->assertArrayNotHasKey('invalid', $this->arrayUtils->arrayize($array, 'invalid')); // check default index $expected = ['string']; - $this->assertEquals($expected, Arrays::arrayize($expected[0])); + $this->assertEquals($expected, $this->arrayUtils->arrayize($expected[0])); // check string index $index = 'key'; $expected = [$index => 'string']; - $this->assertEquals($expected, Arrays::arrayize($expected[$index], $index)); + $this->assertEquals($expected, $this->arrayUtils->arrayize($expected[$index], $index)); } @@ -48,11 +64,11 @@ class ArraysTest extends TestCase { // check bad arrays $this->assertFalse( - Arrays::transpose(['1', '2', '3']), + $this->arrayUtils->transpose(['1', '2', '3']), 'Invalid two-dimensional array was accepted' ); $this->assertFalse( - Arrays::transpose(['1' => 0, '2' => '0', '3' => [0]]), + $this->arrayUtils->transpose(['1' => 0, '2' => '0', '3' => [0]]), 'Invalid elements on a two-dimensional array were accepted' ); @@ -77,7 +93,7 @@ class ArraysTest extends TestCase ]; $this->assertEquals( $transposed, - Arrays::transpose($array), + $this->arrayUtils->transpose($array), 'Unexpected result of transpose()' ); @@ -102,7 +118,7 @@ class ArraysTest extends TestCase ]; $this->assertEquals( $transposed, - Arrays::transpose($array), + $this->arrayUtils->transpose($array), 'Unexpected result of transpose()' ); @@ -129,7 +145,7 @@ class ArraysTest extends TestCase ]; $this->assertEquals( $transposed, - Arrays::transpose($array), + $this->arrayUtils->transpose($array), 'Unexpected result of transpose()' ); } diff --git a/tests/lib/SimpleSAML/Utils/AttributesTest.php b/tests/lib/SimpleSAML/Utils/AttributesTest.php index 452fd46fbb80f8d4575ba411b59cb88a490773ca..4f99ee4c0411c3b460aaff3421f9f88499dbf213 100644 --- a/tests/lib/SimpleSAML/Utils/AttributesTest.php +++ b/tests/lib/SimpleSAML/Utils/AttributesTest.php @@ -16,6 +16,22 @@ use SimpleSAML\Utils\Attributes; */ class AttributesTest extends TestCase { + /** @var \SimpleSAML\Utils\Attributes */ + protected $attrUtils; + + + /** + * Set up for each test. + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + + $this->attrUtils = new Attributes(); + } + + /** * Test the getExpectedAttributeMethod() method with a non-normalized attributes array. */ @@ -30,7 +46,7 @@ class AttributesTest extends TestCase $this->expectExceptionMessage( 'The attributes array is not normalized, values should be arrays.' ); - Attributes::getExpectedAttribute($attributes, $expected); + $this->attrUtils->getExpectedAttribute($attributes, $expected); } @@ -46,7 +62,7 @@ class AttributesTest extends TestCase $expected = 'missing'; $this->expectException(Error\Exception::class); $this->expectExceptionMessage("No such attribute '" . $expected . "' found."); - Attributes::getExpectedAttribute($attributes, $expected); + $this->attrUtils->getExpectedAttribute($attributes, $expected); } @@ -62,7 +78,7 @@ class AttributesTest extends TestCase $expected = 'attribute'; $this->expectException(Error\Exception::class); $this->expectExceptionMessage("Empty attribute '" . $expected . "'.'"); - Attributes::getExpectedAttribute($attributes, $expected); + $this->attrUtils->getExpectedAttribute($attributes, $expected); } @@ -83,7 +99,7 @@ class AttributesTest extends TestCase $this->expectExceptionMessage( 'More than one value found for the attribute, multiple values not allowed.' ); - Attributes::getExpectedAttribute($attributes, $expected); + $this->attrUtils->getExpectedAttribute($attributes, $expected); } @@ -98,7 +114,7 @@ class AttributesTest extends TestCase 'attribute' => [$value], ]; $expected = 'attribute'; - $this->assertEquals($value, Attributes::getExpectedAttribute($attributes, $expected)); + $this->assertEquals($value, $this->attrUtils->getExpectedAttribute($attributes, $expected)); // check multiple (allowed) values $value = 'value'; @@ -106,7 +122,7 @@ class AttributesTest extends TestCase 'attribute' => [$value, 'value2', 'value3'], ]; $expected = 'attribute'; - $this->assertEquals($value, Attributes::getExpectedAttribute($attributes, $expected, true)); + $this->assertEquals($value, $this->attrUtils->getExpectedAttribute($attributes, $expected, true)); } @@ -116,7 +132,7 @@ class AttributesTest extends TestCase public function testNormalizeAttributesArrayBadKeys(): void { $this->expectException(InvalidArgumentException::class); - Attributes::normalizeAttributesArray(['attr1' => 'value1', 1 => 'value2']); + $this->attrUtils->normalizeAttributesArray(['attr1' => 'value1', 1 => 'value2']); } @@ -126,7 +142,7 @@ class AttributesTest extends TestCase public function testNormalizeAttributesArrayBadValues(): void { $this->expectException(InvalidArgumentException::class); - Attributes::normalizeAttributesArray(['attr1' => 'value1', 'attr2' => 0]); + $this->attrUtils->normalizeAttributesArray(['attr1' => 'value1', 'attr2' => 0]); } @@ -147,7 +163,7 @@ class AttributesTest extends TestCase ]; $this->assertEquals( $expected, - Attributes::normalizeAttributesArray($attributes), + $this->attrUtils->normalizeAttributesArray($attributes), 'Attribute array normalization failed' ); } @@ -161,13 +177,13 @@ class AttributesTest extends TestCase // test for only the name $this->assertEquals( ['default', 'name'], - Attributes::getAttributeNamespace('name', 'default') + $this->attrUtils->getAttributeNamespace('name', 'default') ); // test for a given namespace and multiple '/' $this->assertEquals( ['some/namespace', 'name'], - Attributes::getAttributeNamespace('some/namespace/name', 'default') + $this->attrUtils->getAttributeNamespace('some/namespace/name', 'default') ); } } diff --git a/tests/lib/SimpleSAML/Utils/ConfigTest.php b/tests/lib/SimpleSAML/Utils/ConfigTest.php index 3516d455483410ed1284af4b4c326efb9ff6d5f3..2e75b09d6117a6d20b44e3aaab278e29590cf7e3 100644 --- a/tests/lib/SimpleSAML/Utils/ConfigTest.php +++ b/tests/lib/SimpleSAML/Utils/ConfigTest.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Test\Utils; use InvalidArgumentException; use PHPUnit\Framework\TestCase; -use SimpleSAML\Utils\Config; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\Config @@ -15,6 +15,22 @@ use SimpleSAML\Utils\Config; */ class ConfigTest extends TestCase { + /** @var \SimpleSAML\Utils\Config */ + protected $configUtils; + + + /** + * Set up for each test. + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + + $this->configUtils = new Utils\Config(); + } + + /** * Test default config dir with not environment variable */ @@ -22,7 +38,7 @@ class ConfigTest extends TestCase { // clear env var putenv('SIMPLESAMLPHP_CONFIG_DIR'); - $configDir = Config::getConfigDir(); + $configDir = $this->configUtils->getConfigDir(); $this->assertEquals($configDir, dirname(dirname(dirname(dirname(__DIR__)))) . '/config'); } @@ -34,7 +50,7 @@ class ConfigTest extends TestCase public function testEnvVariableConfigDir(): void { putenv('SIMPLESAMLPHP_CONFIG_DIR=' . __DIR__); - $configDir = Config::getConfigDir(); + $configDir = $this->configUtils->getConfigDir(); $this->assertEquals($configDir, __DIR__); } @@ -45,7 +61,7 @@ class ConfigTest extends TestCase public function testEnvRedirectVariableConfigDir(): void { putenv('REDIRECT_SIMPLESAMLPHP_CONFIG_DIR=' . __DIR__); - $configDir = Config::getConfigDir(); + $configDir = $this->configUtils->getConfigDir(); $this->assertEquals($configDir, __DIR__); } @@ -58,7 +74,7 @@ class ConfigTest extends TestCase { putenv('SIMPLESAMLPHP_CONFIG_DIR=' . dirname(__DIR__)); putenv('REDIRECT_SIMPLESAMLPHP_CONFIG_DIR=' . __DIR__); - $configDir = Config::getConfigDir(); + $configDir = $this->configUtils->getConfigDir(); $this->assertEquals($configDir, dirname(__DIR__)); } @@ -79,6 +95,6 @@ class ConfigTest extends TestCase 'Given: "' . $invalidDir . '"' ); - Config::getConfigDir(); + $this->configUtils->getConfigDir(); } } diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php index fa06c98ecc5273667c39d4753645ea22302bde41..dcc25aebe4a4882f37e2bb0f0ea1075031f44410 100644 --- a/tests/lib/SimpleSAML/Utils/CryptoTest.php +++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php @@ -4,13 +4,14 @@ declare(strict_types=1); namespace SimpleSAML\Test\Utils; +use InvalidArgumentException; use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStreamDirectory; use PHPUnit\Framework\TestCase; use ReflectionMethod; use SimpleSAML\Configuration; use SimpleSAML\Error; -use SimpleSAML\Utils\Crypto; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\Crypto. @@ -32,11 +33,60 @@ class CryptoTest extends TestCase /** @var string */ protected string $certdir; + /** @var \SimpleSAML\Utils\Crypto */ + protected $cryptoUtils; + + /** @var string */ + protected $pem = <<<PHP +-----BEGIN CERTIFICATE----- +MIIF8zCCA9ugAwIBAgIJANSv0D4ZoP9iMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD +VQQGEwJFWDEQMA4GA1UECAwHRXhhbXBsZTEQMA4GA1UEBwwHRXhhbXBsZTEQMA4G +A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBs +ZS5jb20xIjAgBgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5jb20wHhcNMTcw +MTEwMDk1MTIxWhcNMTgwMTEwMDk1MTIxWjCBjzELMAkGA1UEBhMCRVgxEDAOBgNV +BAgMB0V4YW1wbGUxEDAOBgNVBAcMB0V4YW1wbGUxEDAOBgNVBAoMB0V4YW1wbGUx +EDAOBgNVBAsMB0V4YW1wbGUxFDASBgNVBAMMC2V4YW1wbGUuY29tMSIwIAYJKoZI +hvcNAQkBFhNzb21lb25lQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEA5Mp4xLdV41NtAI3YYr70G4gJYKegTHRwYhMeYAjudmZUng1/ +vbHLFGQybm8C6naEireQhHWzYfmDkOMU8dmdItwN4YLypYWwxYuWutWWIsDHHe0y +CfjVz6nnTPSjZEq5PpJYY+2XTZOP+g8FmDo4nmhEchF+8eiGvHQzdBqh26EwJjQ3 +LMXyc2F2+9Cm/On+M6BQKvvXkg8FqggW8YwcOujZNWGbfG3LVJcZ0p39PbnNgJX2 +ExbscPHfjmv2RlXd5EjruRhW1oX35sB4ycIFfHGWbCl2HPc1VfouJMq/fxgkKJdb +3RNxIBZnGpBdVJ25lCfk6t2dRdWKECrBHmcX/uR19of4H+hd4zOCPrej8IsCF2IS +1umyUBIDyPE4WciWMUERyG1dxSjUI4DBMi4l+LRX1YUrADSthH/0jV1WDsGpHT26 ++at2ZBgPy8tEvpLsITw/opUKWPCx3u5JVwFdduL8i0UF2yHmcsq44TUHVEoA1c55 +T+46ug7zHzhqFrPIwUN0DTKf33pg30xtL4d1rebc5K1KBNd9IDicd2iL8uD3HG6L +dPdt+1OaSbGlMMKdOte31TdOp7WhqcFANkKxd6TzMUHMVmkbYh2NesaQmCgxJdv6 +/pD7L+sbMKdhlcSoJW+1wwtIo5+CzZxPA2ehZ/IWQg+Oh6djvUJzo0/84ncCAwEA +AaNQME4wHQYDVR0OBBYEFOk6cEb397GMRCJe9xMIZ/y3yFvEMB8GA1UdIwQYMBaA +FOk6cEb397GMRCJe9xMIZ/y3yFvEMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBACc1c8j7oZeVDd8O2k97kY/7pHypVZswLfmg1UqbUmYYqQ9lM6FD0J8R +P+B8i7zST09pJ0FOsCsbyUKQmMIq/citTKmgk8NLK8otWHewHs5KTpsEvJm9XV4s +QjF07GBECJdQWu93Rn8FdR9eJ+H0Y0oHbBu3OtSbHFHyDvaCI5bxM/5FPf4HkJil +qIQunhO5gkz21ebukQUgiZ1YmFl0LjxGUDUDwnQ/3kOejlMUQv+ZXdQp/SaX1z5c +dQlGl/8HDs1YAM3duvdMCXn2LP3QuhrphT/+2o+ZkY32I1p/Q0fDNaE4u7JjaxAd +6+ijpmzZwgG5cFVU+sEeDqCI5MFn2JKiSCrHAHFMTnkpq687qBTLWoYTJ4coxtvs +kmvdoZytKiSf7aDzGQK345BSZWJ+D5RJr2250PHMMeNkFBc+GdGiRsABhhHQAqtE +7TVgdwvc8CYCfXlhRzdSowAVWibiftfPMmItM8Z0w5T/iPW0MsiCLGa5AvCHicN7 +pfajpJ9ZzdyLIo6dVjdQtl+S1rpFCx7ziVN8tCCX4fAVCqRqZJaG/UMLvguVqayb +3Aw1B/fVvWoAnAzVN5ZEClZvuyjImnNZpnYSWHzCJ/9JTqB7rq93nf6Olp9QXD5y +5iHKlJ6FlnuhcGCDsUCvG8qCw9FfoS0tuS4tKoQ5WHGQx3sKmr/D +-----END CERTIFICATE----- +PHP; /** */ public function setUp(): void { + $this->config = Configuration::loadFromArray( + [ + 'module.enable' => [], + 'secretsalt' => 'SUPER_SECRET_SALT' + ], + '[ARRAY]', + 'simplesaml' + ); + $this->root = vfsStream::setup( self::ROOTDIRNAME, null, @@ -46,6 +96,7 @@ class CryptoTest extends TestCase ); $this->root_directory = vfsStream::url(self::ROOTDIRNAME); $this->certdir = $this->root_directory . DIRECTORY_SEPARATOR . self::DEFAULTCERTDIR; + $this->cryptoUtils = new Utils\Crypto(); } @@ -57,17 +108,58 @@ class CryptoTest extends TestCase public function testAesDecrypt(): void { if (!extension_loaded('openssl')) { - $this->expectException(Error\Exception::class); + $this->markTestSkipped('The openssl PHP module is not loaded.'); + } + + $plaintext = 'SUPER_SECRET_TEXT'; + $ciphertext = <<<CIPHER +uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY= +CIPHER; + + $decrypted = $this->cryptoUtils->aesDecrypt(base64_decode($ciphertext)); + $this->assertEquals($plaintext, $decrypted); + } + + + /** + * @return void + */ + public function testAesDecryptWithSmallCipherTextThrowsException(): void + { + if (!extension_loaded('openssl')) { + $this->markTestSkipped('The openssl PHP module is not loaded.'); } $secret = 'SUPER_SECRET_SALT'; - $m = new ReflectionMethod(Crypto::class, 'aesDecryptInternal'); - $m->setAccessible(true); + $plaintext = 'SUPER_SECRET_TEXT'; + // This is too small! + $ciphertext = 'AWcTN8tyO82hiSY='; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Input parameter "$ciphertext" must be a string with more than 48 characters.'); + $this->cryptoUtils->aesDecrypt(base64_decode($ciphertext), $secret); + } + + /** + * @return void + */ + public function testAesDecryptWithWrongSecretThrowsException(): void + { + if (!extension_loaded('openssl')) { + $this->markTestSkipped('The openssl PHP module is not loaded.'); + } + + // This is the wrong secret! + $secret = 'notsosecret'; $plaintext = 'SUPER_SECRET_TEXT'; - $ciphertext = 'uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4' - . 'vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY='; - $this->assertEquals($plaintext, $m->invokeArgs(null, [base64_decode($ciphertext), $secret])); + $ciphertext = <<<CIPHER +uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY= +CIPHER; + + $this->expectException(Error\Exception::class); + $this->expectExceptionMessage('Failed to decrypt ciphertext.'); + $this->cryptoUtils->aesDecrypt(base64_decode($ciphertext), $secret); } @@ -78,18 +170,14 @@ class CryptoTest extends TestCase public function testAesEncrypt(): void { if (!extension_loaded('openssl')) { - $this->expectException(Error\Exception::class); + $this->markTestSkipped('The openssl PHP module is not loaded.'); } - $secret = 'SUPER_SECRET_SALT'; - $e = new ReflectionMethod(Crypto::class, 'aesEncryptInternal'); - $d = new ReflectionMethod(Crypto::class, 'aesDecryptInternal'); - $e->setAccessible(true); - $d->setAccessible(true); - $original_plaintext = 'SUPER_SECRET_TEXT'; - $ciphertext = $e->invokeArgs(null, [$original_plaintext, $secret]); - $decrypted_plaintext = $d->invokeArgs(null, [$ciphertext, $secret]); + + $ciphertext = $this->cryptoUtils->aesEncrypt($original_plaintext); + $decrypted_plaintext = $this->cryptoUtils->aesDecrypt($ciphertext); + $this->assertEquals($original_plaintext, $decrypted_plaintext); } @@ -100,43 +188,29 @@ class CryptoTest extends TestCase */ public function testFormatConversion(): void { - $pem = <<<PHP ------BEGIN CERTIFICATE----- -MIIF8zCCA9ugAwIBAgIJANSv0D4ZoP9iMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD -VQQGEwJFWDEQMA4GA1UECAwHRXhhbXBsZTEQMA4GA1UEBwwHRXhhbXBsZTEQMA4G -A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBs -ZS5jb20xIjAgBgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5jb20wHhcNMTcw -MTEwMDk1MTIxWhcNMTgwMTEwMDk1MTIxWjCBjzELMAkGA1UEBhMCRVgxEDAOBgNV -BAgMB0V4YW1wbGUxEDAOBgNVBAcMB0V4YW1wbGUxEDAOBgNVBAoMB0V4YW1wbGUx -EDAOBgNVBAsMB0V4YW1wbGUxFDASBgNVBAMMC2V4YW1wbGUuY29tMSIwIAYJKoZI -hvcNAQkBFhNzb21lb25lQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOC -Ag8AMIICCgKCAgEA5Mp4xLdV41NtAI3YYr70G4gJYKegTHRwYhMeYAjudmZUng1/ -vbHLFGQybm8C6naEireQhHWzYfmDkOMU8dmdItwN4YLypYWwxYuWutWWIsDHHe0y -CfjVz6nnTPSjZEq5PpJYY+2XTZOP+g8FmDo4nmhEchF+8eiGvHQzdBqh26EwJjQ3 -LMXyc2F2+9Cm/On+M6BQKvvXkg8FqggW8YwcOujZNWGbfG3LVJcZ0p39PbnNgJX2 -ExbscPHfjmv2RlXd5EjruRhW1oX35sB4ycIFfHGWbCl2HPc1VfouJMq/fxgkKJdb -3RNxIBZnGpBdVJ25lCfk6t2dRdWKECrBHmcX/uR19of4H+hd4zOCPrej8IsCF2IS -1umyUBIDyPE4WciWMUERyG1dxSjUI4DBMi4l+LRX1YUrADSthH/0jV1WDsGpHT26 -+at2ZBgPy8tEvpLsITw/opUKWPCx3u5JVwFdduL8i0UF2yHmcsq44TUHVEoA1c55 -T+46ug7zHzhqFrPIwUN0DTKf33pg30xtL4d1rebc5K1KBNd9IDicd2iL8uD3HG6L -dPdt+1OaSbGlMMKdOte31TdOp7WhqcFANkKxd6TzMUHMVmkbYh2NesaQmCgxJdv6 -/pD7L+sbMKdhlcSoJW+1wwtIo5+CzZxPA2ehZ/IWQg+Oh6djvUJzo0/84ncCAwEA -AaNQME4wHQYDVR0OBBYEFOk6cEb397GMRCJe9xMIZ/y3yFvEMB8GA1UdIwQYMBaA -FOk6cEb397GMRCJe9xMIZ/y3yFvEMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggIBACc1c8j7oZeVDd8O2k97kY/7pHypVZswLfmg1UqbUmYYqQ9lM6FD0J8R -P+B8i7zST09pJ0FOsCsbyUKQmMIq/citTKmgk8NLK8otWHewHs5KTpsEvJm9XV4s -QjF07GBECJdQWu93Rn8FdR9eJ+H0Y0oHbBu3OtSbHFHyDvaCI5bxM/5FPf4HkJil -qIQunhO5gkz21ebukQUgiZ1YmFl0LjxGUDUDwnQ/3kOejlMUQv+ZXdQp/SaX1z5c -dQlGl/8HDs1YAM3duvdMCXn2LP3QuhrphT/+2o+ZkY32I1p/Q0fDNaE4u7JjaxAd -6+ijpmzZwgG5cFVU+sEeDqCI5MFn2JKiSCrHAHFMTnkpq687qBTLWoYTJ4coxtvs -kmvdoZytKiSf7aDzGQK345BSZWJ+D5RJr2250PHMMeNkFBc+GdGiRsABhhHQAqtE -7TVgdwvc8CYCfXlhRzdSowAVWibiftfPMmItM8Z0w5T/iPW0MsiCLGa5AvCHicN7 -pfajpJ9ZzdyLIo6dVjdQtl+S1rpFCx7ziVN8tCCX4fAVCqRqZJaG/UMLvguVqayb -3Aw1B/fVvWoAnAzVN5ZEClZvuyjImnNZpnYSWHzCJ/9JTqB7rq93nf6Olp9QXD5y -5iHKlJ6FlnuhcGCDsUCvG8qCw9FfoS0tuS4tKoQ5WHGQx3sKmr/D ------END CERTIFICATE----- -PHP; - $this->assertEquals(trim($pem), trim(Crypto::der2pem(Crypto::pem2der($pem)))); + $this->assertEquals(trim($this->pem), trim($this->cryptoUtils->der2pem($this->cryptoUtils->pem2der($this->pem)))); + } + + + /** + * @return void + */ + public function testFormatConversionThrowsExceptionWhenNotPEMStart(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('pem2der: input is not encoded in PEM format.'); + $this->cryptoUtils->pem2der(substr($this->pem, 6)); + } + + + /** + * @return void + */ + public function testFormatConversionThrowsExceptionWhenNotPEMEnd(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('pem2der: input is not encoded in PEM format.'); + $this->cryptoUtils->pem2der(substr($this->pem, 0, -20)); } @@ -146,8 +220,8 @@ PHP; { $pw = "password"; - $hash = Crypto::pwHash($pw); - $res = Crypto::pwValid($hash, $pw); + $hash = $this->cryptoUtils->pwHash($pw); + $res = $this->cryptoUtils->pwValid($hash, $pw); $this->assertTrue($res); } @@ -160,8 +234,8 @@ PHP; $pw = "password"; $pw2 = "password2"; - $hash = Crypto::pwHash($pw); - $res = Crypto::pwValid($hash, $pw2); + $hash = $this->cryptoUtils->pwHash($pw); + $res = $this->cryptoUtils->pwValid($hash, $pw2); $this->assertFalse($res); } @@ -173,9 +247,9 @@ PHP; { $pw = "password"; - $hash = Crypto::pwHash($pw); + $hash = $this->cryptoUtils->pwHash($pw); $this->expectException(Error\Exception::class); - Crypto::pwValid($hash, $hash); + $this->cryptoUtils->pwValid($hash, $hash); } @@ -183,7 +257,7 @@ PHP; */ public function testSecureCompareEqual(): void { - $res = Crypto::secureCompare("string", "string"); + $res = $this->cryptoUtils->secureCompare("string", "string"); $this->assertTrue($res); } @@ -193,7 +267,7 @@ PHP; */ public function testSecureCompareNotEqual(): void { - $res = Crypto::secureCompare("string1", "string2"); + $res = $this->cryptoUtils->secureCompare("string1", "string2"); $this->assertFalse($res); } @@ -207,7 +281,7 @@ PHP; $config = new Configuration([], 'test'); $required = true; - Crypto::loadPrivateKey($config, $required); + $this->cryptoUtils->loadPrivateKey($config, $required); } @@ -218,7 +292,7 @@ PHP; $config = new Configuration([], 'test'); $required = false; - $res = Crypto::loadPrivateKey($config, $required); + $res = $this->cryptoUtils->loadPrivateKey($config, $required); $this->assertNull($res); } @@ -231,7 +305,7 @@ PHP; $this->expectException(Error\Exception::class); $config = new Configuration(['privatekey' => 'nonexistant'], 'test'); - Crypto::loadPrivateKey($config, false, '', true); + $this->cryptoUtils->loadPrivateKey($config, false, '', true); } @@ -246,7 +320,7 @@ PHP; file_put_contents($filename, $data); - $res = Crypto::loadPrivateKey($config, false, '', $full_path); + $res = $this->cryptoUtils->loadPrivateKey($config, false, '', $full_path); $expected = ['PEM' => $data, 'password' => null]; $this->assertEquals($expected, $res); @@ -271,7 +345,7 @@ PHP; file_put_contents($filename, $data); - $res = Crypto::loadPrivateKey($config, false, '', $full_path); + $res = $this->cryptoUtils->loadPrivateKey($config, false, '', $full_path); $expected = ['PEM' => $data, 'password' => $password]; $this->assertEquals($expected, $res); @@ -297,7 +371,7 @@ PHP; file_put_contents($filename, $data); - $res = Crypto::loadPrivateKey($config, false, $prefix, $full_path); + $res = $this->cryptoUtils->loadPrivateKey($config, false, $prefix, $full_path); $expected = ['PEM' => $data, 'password' => $password]; $this->assertEquals($expected, $res); @@ -312,7 +386,7 @@ PHP; $config = new Configuration([], 'test'); $required = true; - Crypto::loadPublicKey($config, $required); + $this->cryptoUtils->loadPublicKey($config, $required); } @@ -323,7 +397,7 @@ PHP; $config = new Configuration([], 'test'); $required = false; - $res = Crypto::loadPublicKey($config, $required); + $res = $this->cryptoUtils->loadPublicKey($config, $required); $this->assertNull($res); } @@ -346,7 +420,7 @@ PHP; 'test' ); - $res = Crypto::loadPublicKey($config); + $res = $this->cryptoUtils->loadPublicKey($config); $this->assertNull($res); } @@ -369,7 +443,7 @@ PHP; 'test' ); - $res = Crypto::loadPublicKey($config); + $res = $this->cryptoUtils->loadPublicKey($config); $this->assertNull($res); } @@ -394,7 +468,7 @@ PHP; ); /** @var array $pubkey */ - $pubkey = Crypto::loadPublicKey($config); + $pubkey = $this->cryptoUtils->loadPublicKey($config); $res = $pubkey['certData']; $expected = $x509certificate; diff --git a/tests/lib/SimpleSAML/Utils/HTTPTest.php b/tests/lib/SimpleSAML/Utils/HTTPTest.php index f6f90fd64b31ee8e3d158540ac4d2c4ecc82db65..71fd7ca2bbfb7fa5a85b37934aa4909b71bfd578 100644 --- a/tests/lib/SimpleSAML/Utils/HTTPTest.php +++ b/tests/lib/SimpleSAML/Utils/HTTPTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\Test\Utils\ClearStateTestCase; -use SimpleSAML\Utils\HTTP; +use SimpleSAML\Utils; /** * @covers \SimpleSAML\Utils\HTTP @@ -50,25 +50,27 @@ class HTTPTest extends ClearStateTestCase */ public function testAddURLParameters(): void { + $httpUtils = new Utils\HTTP(); + $url = 'http://example.com/'; $params = [ 'foo' => 'bar', 'bar' => 'foo', ]; - $this->assertEquals($url . '?foo=bar&bar=foo', HTTP::addURLParameters($url, $params)); + $this->assertEquals($url . '?foo=bar&bar=foo', $httpUtils->addURLParameters($url, $params)); $url = 'http://example.com/?'; $params = [ 'foo' => 'bar', 'bar' => 'foo', ]; - $this->assertEquals($url . 'foo=bar&bar=foo', HTTP::addURLParameters($url, $params)); + $this->assertEquals($url . 'foo=bar&bar=foo', $httpUtils->addURLParameters($url, $params)); $url = 'http://example.com/?foo=bar'; $params = [ 'bar' => 'foo', ]; - $this->assertEquals($url . '&bar=foo', HTTP::addURLParameters($url, $params)); + $this->assertEquals($url . '&bar=foo', $httpUtils->addURLParameters($url, $params)); } @@ -78,38 +80,39 @@ class HTTPTest extends ClearStateTestCase public function testGuessBasePath(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); $_SERVER['REQUEST_URI'] = '/simplesaml/module.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/module.php'; - $this->assertEquals('/simplesaml/', HTTP::guessBasePath()); + $this->assertEquals('/simplesaml/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/simplesaml/module.php/some/path/to/other/script.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/module.php'; - $this->assertEquals('/simplesaml/', HTTP::guessBasePath()); + $this->assertEquals('/simplesaml/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/module.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/module.php'; - $this->assertEquals('/', HTTP::guessBasePath()); + $this->assertEquals('/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/module.php/some/path/to/other/script.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/module.php'; - $this->assertEquals('/', HTTP::guessBasePath()); + $this->assertEquals('/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/some/path/module.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/module.php'; - $this->assertEquals('/some/path/', HTTP::guessBasePath()); + $this->assertEquals('/some/path/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/some/path/module.php/some/path/to/other/script.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/module.php'; - $this->assertEquals('/some/path/', HTTP::guessBasePath()); + $this->assertEquals('/some/path/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/some/dir/in/www/script.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/some/dir/in/www/script.php'; - $this->assertEquals('/', HTTP::guessBasePath()); + $this->assertEquals('/', $httpUtils->guessBasePath()); $_SERVER['REQUEST_URI'] = '/simplesaml/some/dir/in/www/script.php'; $_SERVER['SCRIPT_FILENAME'] = '/some/path/simplesamlphp/www/some/dir/in/www/script.php'; - $this->assertEquals('/simplesaml/', HTTP::guessBasePath()); + $this->assertEquals('/simplesaml/', $httpUtils->guessBasePath()); $_SERVER = $original; } @@ -121,14 +124,15 @@ class HTTPTest extends ClearStateTestCase public function testGetSelfHost(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); Configuration::loadFromArray([ 'baseurlpath' => '', ], '[ARRAY]', 'simplesaml'); $_SERVER['SERVER_PORT'] = '80'; - $this->assertEquals('localhost', HTTP::getSelfHost()); + $this->assertEquals('localhost', $httpUtils->getSelfHost()); $_SERVER['SERVER_PORT'] = '3030'; - $this->assertEquals('localhost', HTTP::getSelfHost()); + $this->assertEquals('localhost', $httpUtils->getSelfHost()); $_SERVER = $original; } @@ -140,6 +144,7 @@ class HTTPTest extends ClearStateTestCase public function testGetSelfHostWithPort(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); Configuration::loadFromArray([ 'baseurlpath' => '', @@ -147,16 +152,16 @@ class HTTPTest extends ClearStateTestCase // standard port for HTTP $_SERVER['SERVER_PORT'] = '80'; - $this->assertEquals('localhost', HTTP::getSelfHostWithNonStandardPort()); + $this->assertEquals('localhost', $httpUtils->getSelfHostWithNonStandardPort()); // non-standard port $_SERVER['SERVER_PORT'] = '3030'; - $this->assertEquals('localhost:3030', HTTP::getSelfHostWithNonStandardPort()); + $this->assertEquals('localhost:3030', $httpUtils->getSelfHostWithNonStandardPort()); // standard port for HTTPS $_SERVER['HTTPS'] = 'on'; $_SERVER['SERVER_PORT'] = '443'; - $this->assertEquals('localhost', HTTP::getSelfHostWithNonStandardPort()); + $this->assertEquals('localhost', $httpUtils->getSelfHostWithNonStandardPort()); $_SERVER = $original; } @@ -168,6 +173,7 @@ class HTTPTest extends ClearStateTestCase public function testGetSelfURLMethods(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); /* * Test a URL pointing to a script that's not part of the public interface. This allows us to test calls to @@ -179,11 +185,11 @@ class HTTPTest extends ClearStateTestCase $url = 'https://example.com/app/script.php/some/path?foo=bar'; $this->setupEnvFromURL($url); $_SERVER['SCRIPT_FILENAME'] = '/var/www/app/script.php'; - $this->assertEquals($url, HTTP::getSelfURL()); - $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); - $this->assertEquals('https://example.com/app/script.php/some/path', HTTP::getSelfURLNoQuery()); - $this->assertTrue(HTTP::isHTTPS()); - $this->assertEquals('https://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertEquals($url, $httpUtils->getSelfURL()); + $this->assertEquals('https://example.com', $httpUtils->getSelfURLHost()); + $this->assertEquals('https://example.com/app/script.php/some/path', $httpUtils->getSelfURLNoQuery()); + $this->assertTrue($httpUtils->isHTTPS()); + $this->assertEquals('https://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); // test a request URI that doesn't match the current script $cfg = Configuration::loadFromArray([ @@ -192,12 +198,12 @@ class HTTPTest extends ClearStateTestCase $baseDir = $cfg->getBaseDir(); $_SERVER['SCRIPT_FILENAME'] = $baseDir . 'www/module.php'; $this->setupEnvFromURL('http://www.example.com/protected/resource.asp?foo=bar'); - $this->assertEquals('http://www.example.com/protected/resource.asp?foo=bar', HTTP::getSelfURL()); - $this->assertEquals('http://www.example.com', HTTP::getSelfURLHost()); - $this->assertEquals('http://www.example.com/protected/resource.asp', HTTP::getSelfURLNoQuery()); - $this->assertFalse(HTTP::isHTTPS()); - $this->assertEquals('example.org', HTTP::getSelfHostWithNonStandardPort()); - $this->assertEquals('http://www.example.com', HTTP::getSelfURLHost()); + $this->assertEquals('http://www.example.com/protected/resource.asp?foo=bar', $httpUtils->getSelfURL()); + $this->assertEquals('http://www.example.com', $httpUtils->getSelfURLHost()); + $this->assertEquals('http://www.example.com/protected/resource.asp', $httpUtils->getSelfURLNoQuery()); + $this->assertFalse($httpUtils->isHTTPS()); + $this->assertEquals('example.org', $httpUtils->getSelfHostWithNonStandardPort()); + $this->assertEquals('http://www.example.com', $httpUtils->getSelfURLHost()); // test a valid, full URL, based on a full URL in the configuration Configuration::loadFromArray([ @@ -206,12 +212,12 @@ class HTTPTest extends ClearStateTestCase $this->setupEnvFromURL('http://www.example.org/module.php/module/file.php?foo=bar'); $this->assertEquals( 'https://example.com/simplesaml/module.php/module/file.php?foo=bar', - HTTP::getSelfURL() + $httpUtils->getSelfURL() ); - $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); - $this->assertEquals('https://example.com/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); - $this->assertTrue(HTTP::isHTTPS()); - $this->assertEquals('https://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com', $httpUtils->getSelfURLHost()); + $this->assertEquals('https://example.com/simplesaml/module.php/module/file.php', $httpUtils->getSelfURLNoQuery()); + $this->assertTrue($httpUtils->isHTTPS()); + $this->assertEquals('https://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); // test a valid, full URL, based on a full URL *without* a trailing slash in the configuration Configuration::loadFromArray([ @@ -219,12 +225,12 @@ class HTTPTest extends ClearStateTestCase ], '[ARRAY]', 'simplesaml'); $this->assertEquals( 'https://example.com/simplesaml/module.php/module/file.php?foo=bar', - HTTP::getSelfURL() + $httpUtils->getSelfURL() ); - $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); - $this->assertEquals('https://example.com/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); - $this->assertTrue(HTTP::isHTTPS()); - $this->assertEquals('https://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com', $httpUtils->getSelfURLHost()); + $this->assertEquals('https://example.com/simplesaml/module.php/module/file.php', $httpUtils->getSelfURLNoQuery()); + $this->assertTrue($httpUtils->isHTTPS()); + $this->assertEquals('https://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); // test a valid, full URL, based on a full URL *without* a path in the configuration Configuration::loadFromArray([ @@ -232,12 +238,12 @@ class HTTPTest extends ClearStateTestCase ], '[ARRAY]', 'simplesaml'); $this->assertEquals( 'https://example.com/module.php/module/file.php?foo=bar', - HTTP::getSelfURL() + $httpUtils->getSelfURL() ); - $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); - $this->assertEquals('https://example.com/module.php/module/file.php', HTTP::getSelfURLNoQuery()); - $this->assertTrue(HTTP::isHTTPS()); - $this->assertEquals('https://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com', $httpUtils->getSelfURLHost()); + $this->assertEquals('https://example.com/module.php/module/file.php', $httpUtils->getSelfURLNoQuery()); + $this->assertTrue($httpUtils->isHTTPS()); + $this->assertEquals('https://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); // test a valid, full URL, based on a relative path in the configuration Configuration::loadFromArray([ @@ -246,12 +252,12 @@ class HTTPTest extends ClearStateTestCase $this->setupEnvFromURL('http://www.example.org/simplesaml/module.php/module/file.php?foo=bar'); $this->assertEquals( 'http://www.example.org/simplesaml/module.php/module/file.php?foo=bar', - HTTP::getSelfURL() + $httpUtils->getSelfURL() ); - $this->assertEquals('http://www.example.org', HTTP::getSelfURLHost()); - $this->assertEquals('http://www.example.org/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); - $this->assertFalse(HTTP::isHTTPS()); - $this->assertEquals('http://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertEquals('http://www.example.org', $httpUtils->getSelfURLHost()); + $this->assertEquals('http://www.example.org/simplesaml/module.php/module/file.php', $httpUtils->getSelfURLNoQuery()); + $this->assertFalse($httpUtils->isHTTPS()); + $this->assertEquals('http://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); // test a valid, full URL, based on a relative path in the configuration and a non standard port Configuration::loadFromArray([ @@ -260,12 +266,12 @@ class HTTPTest extends ClearStateTestCase $this->setupEnvFromURL('http://example.org:8080/simplesaml/module.php/module/file.php?foo=bar'); $this->assertEquals( 'http://example.org:8080/simplesaml/module.php/module/file.php?foo=bar', - HTTP::getSelfURL() + $httpUtils->getSelfURL() ); - $this->assertEquals('http://example.org:8080', HTTP::getSelfURLHost()); - $this->assertEquals('http://example.org:8080/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); - $this->assertFalse(HTTP::isHTTPS()); - $this->assertEquals('http://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertEquals('http://example.org:8080', $httpUtils->getSelfURLHost()); + $this->assertEquals('http://example.org:8080/simplesaml/module.php/module/file.php', $httpUtils->getSelfURLNoQuery()); + $this->assertFalse($httpUtils->isHTTPS()); + $this->assertEquals('http://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); // test a valid, full URL, based on a relative path in the configuration, a non standard port and HTTPS Configuration::loadFromArray([ @@ -274,15 +280,15 @@ class HTTPTest extends ClearStateTestCase $this->setupEnvFromURL('https://example.org:8080/simplesaml/module.php/module/file.php?foo=bar'); $this->assertEquals( 'https://example.org:8080/simplesaml/module.php/module/file.php?foo=bar', - HTTP::getSelfURL() + $httpUtils->getSelfURL() ); - $this->assertEquals('https://example.org:8080', HTTP::getSelfURLHost()); + $this->assertEquals('https://example.org:8080', $httpUtils->getSelfURLHost()); $this->assertEquals( 'https://example.org:8080/simplesaml/module.php/module/file.php', - HTTP::getSelfURLNoQuery() + $httpUtils->getSelfURLNoQuery() ); - $this->assertTrue(HTTP::isHTTPS()); - $this->assertEquals('https://' . HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + $this->assertTrue($httpUtils->isHTTPS()); + $this->assertEquals('https://' . $httpUtils->getSelfHostWithNonStandardPort(), $httpUtils->getSelfURLHost()); $_SERVER = $original; } @@ -308,12 +314,14 @@ class HTTPTest extends ClearStateTestCase 'https://app.example.com/', 'http://app.example.com/', ]; + + $httpUtils = new Utils\HTTP(); foreach ($allowed as $url) { - $this->assertEquals(HTTP::checkURLAllowed($url), $url); + $this->assertEquals($httpUtils->checkURLAllowed($url), $url); } $this->expectException(\SimpleSAML\Error\Exception::class); - HTTP::checkURLAllowed('https://evil.com'); + $httpUtils->checkURLAllowed('https://evil.com'); $_SERVER = $original; } @@ -341,12 +349,14 @@ class HTTPTest extends ClearStateTestCase 'https://app2.example.com/', 'http://app2.example.com/', ]; + + $httpUtils = new Utils\HTTP(); foreach ($allowed as $url) { - $this->assertEquals(HTTP::checkURLAllowed($url), $url); + $this->assertEquals($httpUtils->checkURLAllowed($url), $url); } $this->expectException(\SimpleSAML\Error\Exception::class); - HTTP::checkURLAllowed('https://evil.com'); + $httpUtils->checkURLAllowed('https://evil.com'); $_SERVER = $original; } @@ -358,40 +368,41 @@ class HTTPTest extends ClearStateTestCase public function testGetServerPort(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); // Test HTTP + non-standard port $_SERVER['HTTPS'] = 'off'; $_SERVER['SERVER_PORT'] = '3030'; - $this->assertEquals(HTTP::getServerPort(), ':3030'); + $this->assertEquals($httpUtils->getServerPort(), ':3030'); // Test HTTP + standard port $_SERVER['SERVER_PORT'] = '80'; - $this->assertEquals(HTTP::getServerPort(), ''); + $this->assertEquals($httpUtils->getServerPort(), ''); // Test HTTP + standard integer port $_SERVER['SERVER_PORT'] = 80; - $this->assertEquals(HTTP::getServerPort(), ''); + $this->assertEquals($httpUtils->getServerPort(), ''); // Test HTTP + without port unset($_SERVER['SERVER_PORT']); - $this->assertEquals(HTTP::getServerPort(), ''); + $this->assertEquals($httpUtils->getServerPort(), ''); // Test HTTPS + non-standard port $_SERVER['HTTPS'] = 'on'; $_SERVER['SERVER_PORT'] = '3030'; - $this->assertEquals(HTTP::getServerPort(), ':3030'); + $this->assertEquals($httpUtils->getServerPort(), ':3030'); // Test HTTPS + non-standard integer port $_SERVER['SERVER_PORT'] = 3030; - $this->assertEquals(HTTP::getServerPort(), ':3030'); + $this->assertEquals($httpUtils->getServerPort(), ':3030'); // Test HTTPS + standard port $_SERVER['SERVER_PORT'] = '443'; - $this->assertEquals(HTTP::getServerPort(), ''); + $this->assertEquals($httpUtils->getServerPort(), ''); // Test HTTPS + without port unset($_SERVER['SERVER_PORT']); - $this->assertEquals(HTTP::getServerPort(), ''); + $this->assertEquals($httpUtils->getServerPort(), ''); $_SERVER = $original; } @@ -404,6 +415,7 @@ class HTTPTest extends ClearStateTestCase public function testCheckURLAllowedWithRegexWithoutDelimiters(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); Configuration::loadFromArray([ 'trusted.url.domains' => ['app\.example\.com'], @@ -413,7 +425,7 @@ class HTTPTest extends ClearStateTestCase $_SERVER['REQUEST_URI'] = '/module.php'; $this->expectException(Error\Exception::class); - HTTP::checkURLAllowed('https://app.example.com.evil.com'); + $httpUtils->checkURLAllowed('https://app.example.com.evil.com'); $_SERVER = $original; } @@ -424,9 +436,11 @@ class HTTPTest extends ClearStateTestCase public function testGetFirstPathElement(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); + $_SERVER['SCRIPT_NAME'] = '/test/tmp.php'; - $this->assertEquals(HTTP::getFirstPathElement(), '/test'); - $this->assertEquals(HTTP::getFirstPathElement(false), 'test'); + $this->assertEquals($httpUtils->getFirstPathElement(), '/test'); + $this->assertEquals($httpUtils->getFirstPathElement(false), 'test'); $_SERVER = $original; } @@ -438,13 +452,15 @@ class HTTPTest extends ClearStateTestCase public function testSetCookie(): void { $original = $_SERVER; + $httpUtils = new Utils\HTTP(); + Configuration::loadFromArray([ 'baseurlpath' => 'https://example.com/simplesaml/', ], '[ARRAY]', 'simplesaml'); $url = 'https://example.com/a?b=c'; $this->setupEnvFromURL($url); - HTTP::setCookie( + $httpUtils->setCookie( 'TestCookie', 'value%20', [ @@ -455,7 +471,7 @@ class HTTPTest extends ClearStateTestCase 'httponly' => true ] ); - HTTP::setCookie( + $httpUtils->setCookie( 'RawCookie', 'value%20', [ @@ -497,13 +513,15 @@ class HTTPTest extends ClearStateTestCase $this->expectException(Error\CannotSetCookie::class); $original = $_SERVER; + $httpUtils = new Utils\HTTP(); + Configuration::loadFromArray([ 'baseurlpath' => 'http://example.com/simplesaml/', ], '[ARRAY]', 'simplesaml'); $url = 'http://example.com/a?b=c'; $this->setupEnvFromURL($url); - HTTP::setCookie('testCookie', 'value', ['secure' => true], true); + $httpUtils->setCookie('testCookie', 'value', ['secure' => true], true); $_SERVER = $original; } @@ -515,10 +533,11 @@ class HTTPTest extends ClearStateTestCase */ public function testSetCookieSameSite(): void { - HTTP::setCookie('SSNull', 'value', ['samesite' => null]); - HTTP::setCookie('SSNone', 'value', ['samesite' => 'None']); - HTTP::setCookie('SSLax', 'value', ['samesite' => 'Lax']); - HTTP::setCookie('SSStrict', 'value', ['samesite' => 'Strict']); + $httpUtils = new Utils\HTTP(); + $httpUtils->setCookie('SSNull', 'value', ['samesite' => null]); + $httpUtils->setCookie('SSNone', 'value', ['samesite' => 'None']); + $httpUtils->setCookie('SSLax', 'value', ['samesite' => 'Lax']); + $httpUtils->setCookie('SSStrict', 'value', ['samesite' => 'Strict']); $headers = xdebug_get_headers(); $this->assertDoesNotMatchRegularExpression('/\b[Ss]ame[Ss]ite=/', $headers[0]); @@ -538,7 +557,8 @@ class HTTPTest extends ClearStateTestCase if ($userAgent) { $_SERVER['HTTP_USER_AGENT'] = $userAgent; } - $this->assertEquals($supportsNone, HTTP::canSetSameSiteNone(), $userAgent ?? 'No user agent set'); + $httpUtils = new Utils\HTTP(); + $this->assertEquals($supportsNone, $httpUtils->canSetSameSiteNone(), $userAgent ?? 'No user agent set'); } public function detectSameSiteProvider(): array diff --git a/tests/lib/SimpleSAML/Utils/NetTest.php b/tests/lib/SimpleSAML/Utils/NetTest.php index 2ef7bc2bdd211a1c973a7852a81b2fb9a5c42d50..3e8afc9995540ef0ba3ba7fb0c883b68585d6242 100644 --- a/tests/lib/SimpleSAML/Utils/NetTest.php +++ b/tests/lib/SimpleSAML/Utils/NetTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace SimpleSAML\Test\Utils; use PHPUnit\Framework\TestCase; -use SimpleSAML\Utils\Net; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\Test. @@ -20,31 +20,33 @@ class NetTest extends TestCase */ public function testIpCIDRcheck(): void { + $netUtils = new Utils\Net(); + // check CIDR w/o mask - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0', '127.0.0.1')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0', '127.0.0.1')); // check wrong CIDR w/ mask - $this->assertFalse(Net::ipCIDRcheck('127.0.0.256/24', '127.0.0.1')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.256/24', '127.0.0.1')); // check wrong IP - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/24', '127.0.0')); - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/24', '127.0.0.*')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/24', '127.0.0')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/24', '127.0.0.*')); // check limits for standard classes - $this->assertTrue(Net::ipCIDRcheck('127.0.0.0/24', '127.0.0.0')); - $this->assertTrue(Net::ipCIDRcheck('127.0.0.0/24', '127.0.0.255')); - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/24', '127.0.0.256')); + $this->assertTrue($netUtils->ipCIDRcheck('127.0.0.0/24', '127.0.0.0')); + $this->assertTrue($netUtils->ipCIDRcheck('127.0.0.0/24', '127.0.0.255')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/24', '127.0.0.256')); - $this->assertTrue(Net::ipCIDRcheck('127.0.0.0/16', '127.0.0.0')); - $this->assertTrue(Net::ipCIDRcheck('127.0.0.0/16', '127.0.255.255')); - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/16', '127.0.255.256')); - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/16', '127.0.256.255')); + $this->assertTrue($netUtils->ipCIDRcheck('127.0.0.0/16', '127.0.0.0')); + $this->assertTrue($netUtils->ipCIDRcheck('127.0.0.0/16', '127.0.255.255')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/16', '127.0.255.256')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/16', '127.0.256.255')); // check limits for non-standard classes - $this->assertTrue(Net::ipCIDRcheck('127.0.0.0/23', '127.0.0.0')); - $this->assertTrue(Net::ipCIDRcheck('127.0.0.0/23', '127.0.1.255')); - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/23', '127.0.1.256')); - $this->assertFalse(Net::ipCIDRcheck('127.0.0.0/23', '127.0.2.0')); + $this->assertTrue($netUtils->ipCIDRcheck('127.0.0.0/23', '127.0.0.0')); + $this->assertTrue($netUtils->ipCIDRcheck('127.0.0.0/23', '127.0.1.255')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/23', '127.0.1.256')); + $this->assertFalse($netUtils->ipCIDRcheck('127.0.0.0/23', '127.0.2.0')); } @@ -54,36 +56,38 @@ class NetTest extends TestCase */ public function testIpv6CIDRcheck(): void { + $netUtils = new Utils\Net(); + // check CIDR w/o mask - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::', '2001:0DB8::1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::', '2001:0DB8::1')); // check wrong CIDR w/ mask - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::1')); // check wrong IP - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::Z')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::Z')); // check limits for standard classes - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/128', '2001:0DB8:0000:0000:0000:0000:0000:0000')); - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::0')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::1')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/128', '2001:0DB8:0000:0000:0000:0000:0000:0000')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::0')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/128', '2001:0DB8::1')); - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::1')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::1:1')); - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::FFFF')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::1:FFFF')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::1:1')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::FFFF')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/112', '2001:0DB8::1:FFFF')); // check limits for non-standard classes - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::1:1')); - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::F:1')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::FF:1')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::1FF:1')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::FFFF:1')); - - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::1:1')); - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::F:1')); - $this->assertTrue(Net::ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::FF:1')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::1FF:1')); - $this->assertFalse(Net::ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::FFFF:1')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::1:1')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::F:1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::FF:1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::1FF:1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/108', '2001:0DB8::FFFF:1')); + + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::1:1')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::F:1')); + $this->assertTrue($netUtils->ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::FF:1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::1FF:1')); + $this->assertFalse($netUtils->ipCIDRcheck('2001:0DB8::/104', '2001:0DB8::FFFF:1')); } } diff --git a/tests/lib/SimpleSAML/Utils/RandomTest.php b/tests/lib/SimpleSAML/Utils/RandomTest.php index d84905a32db1f77d7e31c847bc1f2f9a4a28311a..230f54f604ee150d6192027b19136a4a535a13ec 100644 --- a/tests/lib/SimpleSAML/Utils/RandomTest.php +++ b/tests/lib/SimpleSAML/Utils/RandomTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace SimpleSAML\Test\Utils; use PHPUnit\Framework\TestCase; -use SimpleSAML\Utils\Random; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\Random. @@ -20,10 +20,12 @@ class RandomTest extends TestCase */ public function testGenerateID(): void { + $randomUtils = new Utils\Random(); + // check that it always starts with an underscore - $this->assertStringStartsWith('_', Random::generateID()); + $this->assertStringStartsWith('_', $randomUtils->generateID()); // check the length - $this->assertEquals(Random::ID_LENGTH, strlen(Random::generateID())); + $this->assertEquals($randomUtils::ID_LENGTH, strlen($randomUtils->generateID())); } } diff --git a/tests/lib/SimpleSAML/Utils/SystemTest.php b/tests/lib/SimpleSAML/Utils/SystemTest.php index 493ceb94a3b813cb278044ca67d8656d0e3d282f..93c95aec3480100e27d8bc94dd5f6e59220e43d4 100644 --- a/tests/lib/SimpleSAML/Utils/SystemTest.php +++ b/tests/lib/SimpleSAML/Utils/SystemTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; use ReflectionClass; use SimpleSAML\Configuration; use SimpleSAML\Error; -use SimpleSAML\Utils\System; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\System. @@ -27,9 +27,12 @@ class SystemTest extends TestCase /** @var \org\bovigo\vfs\vfsStreamDirectory */ protected VfsStreamDirectory $root; - /** @var string string */ + /** @var string */ protected $root_directory; + /** @var \SimpleSAML\Utils\System */ + protected $sysUtils; + /** */ @@ -43,6 +46,7 @@ class SystemTest extends TestCase ] ); $this->root_directory = vfsStream::url(self::ROOTDIRNAME); + $this->sysUtils = new Utils\System(); } @@ -51,7 +55,7 @@ class SystemTest extends TestCase */ public function testGetOSBasic(): void { - $res = System::getOS(); + $res = $this->sysUtils->getOS(); $this->assertIsInt($res); } @@ -65,7 +69,7 @@ class SystemTest extends TestCase $base = "/base////"; $path = "test"; - $res = System::resolvePath($path, $base); + $res = $this->sysUtils->resolvePath($path, $base); $expected = "/base/test"; $this->assertEquals($expected, $res); @@ -80,7 +84,7 @@ class SystemTest extends TestCase $base = "/base/"; $path = "/test"; - $res = System::resolvePath($path, $base); + $res = $this->sysUtils->resolvePath($path, $base); $expected = "/test"; $this->assertEquals($expected, $res); @@ -95,7 +99,7 @@ class SystemTest extends TestCase $base = "/base/"; $path = "/test/."; - $res = System::resolvePath($path, $base); + $res = $this->sysUtils->resolvePath($path, $base); $expected = "/test"; $this->assertEquals($expected, $res); @@ -110,7 +114,7 @@ class SystemTest extends TestCase $base = "/base/"; $path = "/test/child/.."; - $res = System::resolvePath($path, $base); + $res = $this->sysUtils->resolvePath($path, $base); $expected = "/test"; $this->assertEquals($expected, $res); @@ -125,7 +129,7 @@ class SystemTest extends TestCase $base = '/base/'; $path = 'vfs://simplesaml'; - $res = System::resolvePath($path, $base); + $res = $this->sysUtils->resolvePath($path, $base); $expected = $path; $this->assertEquals($expected, $res); @@ -140,7 +144,7 @@ class SystemTest extends TestCase $base = '/base/'; $path = 's3://bucket-name/key-name'; - $res = System::resolvePath($path, $base); + $res = $this->sysUtils->resolvePath($path, $base); $expected = $path; $this->assertEquals($expected, $res); @@ -157,7 +161,7 @@ class SystemTest extends TestCase $filename = $this->root_directory . DIRECTORY_SEPARATOR . 'test'; - System::writeFile($filename, ''); + $this->sysUtils->writeFile($filename, ''); $this->assertFileExists($filename); @@ -176,7 +180,7 @@ class SystemTest extends TestCase $filename = $this->root_directory . DIRECTORY_SEPARATOR . 'test'; $contents = 'TEST'; - System::writeFile($filename, $contents); + $this->sysUtils->writeFile($filename, $contents); $res = file_get_contents($filename); $expected = $contents; @@ -198,7 +202,7 @@ class SystemTest extends TestCase $filename = $this->root_directory . DIRECTORY_SEPARATOR . 'test'; $mode = 0666; - System::writeFile($filename, '', $mode); + $this->sysUtils->writeFile($filename, '', $mode); $res = $this->root->getChild('test')->getPermissions(); $expected = $mode; @@ -217,7 +221,7 @@ class SystemTest extends TestCase $tempdir = $this->root_directory . DIRECTORY_SEPARATOR . self::DEFAULTTEMPDIR; $config = $this->setConfigurationTempDir($tempdir); - $res = System::getTempDir(); + $res = $this->sysUtils->getTempDir(); $expected = $tempdir; $this->assertEquals($expected, $res); @@ -235,7 +239,7 @@ class SystemTest extends TestCase $tempdir = $this->root_directory . DIRECTORY_SEPARATOR . 'nonexistant'; $config = $this->setConfigurationTempDir($tempdir); - $res = System::getTempDir(); + $res = $this->sysUtils->getTempDir(); $expected = $tempdir; $this->assertEquals($expected, $res); @@ -256,7 +260,7 @@ class SystemTest extends TestCase chmod($tempdir, 0440); $this->expectException(Error\Exception::class); - System::getTempDir(); + $this->sysUtils->getTempDir(); $this->clearInstance($config, Configuration::class); } diff --git a/tests/lib/SimpleSAML/Utils/TimeTest.php b/tests/lib/SimpleSAML/Utils/TimeTest.php index 220e611510afa679fde67bfc89e32172304108d7..c50e3144e42f245d4f442254798c5f1635883f0d 100644 --- a/tests/lib/SimpleSAML/Utils/TimeTest.php +++ b/tests/lib/SimpleSAML/Utils/TimeTest.php @@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase; use ReflectionProperty; use SimpleSAML\Configuration; use SimpleSAML\Error; -use SimpleSAML\Utils\Time; +use SimpleSAML\Utils; /** * @covers \SimpleSAML\Utils\Time @@ -22,11 +22,13 @@ class TimeTest extends TestCase */ public function testGenerateTimestamp(): void { + $timeUtils = new Utils\Time(); + // make sure passed timestamps are used - $this->assertEquals('2016-03-03T14:48:05Z', Time::generateTimestamp(1457016485)); + $this->assertEquals('2016-03-03T14:48:05Z', $timeUtils->generateTimestamp(1457016485)); // test timestamp generation for current time - $this->assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/', Time::generateTimestamp()); + $this->assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/', $timeUtils->generateTimestamp()); } @@ -36,6 +38,7 @@ class TimeTest extends TestCase */ public function testInitTimezone(): void { + $timeUtils = new Utils\Time(); $tz = 'UTC'; $os = @date_default_timezone_get(); if ($os === 'UTC') { // avoid collisions @@ -44,7 +47,7 @@ class TimeTest extends TestCase // test guessing timezone from the OS Configuration::loadFromArray(['timezone' => null], '[ARRAY]', 'simplesaml'); - @Time::initTimezone(); + @$timeUtils->initTimezone(); $this->assertEquals($os, @date_default_timezone_get()); // clear initialization @@ -55,7 +58,7 @@ class TimeTest extends TestCase // test unknown timezone Configuration::loadFromArray(['timezone' => 'INVALID'], '[ARRAY]', 'simplesaml'); try { - @Time::initTimezone(); + @$timeUtils->initTimezone(); $this->fail('Failed to recognize an invalid timezone.'); } catch (Error\Exception $e) { $this->assertEquals('Invalid timezone set in the "timezone" option in config.php.', $e->getMessage()); @@ -63,12 +66,12 @@ class TimeTest extends TestCase // test a valid timezone Configuration::loadFromArray(['timezone' => $tz], '[ARRAY]', 'simplesaml'); - @Time::initTimezone(); + @$timeUtils->initTimezone(); $this->assertEquals($tz, @date_default_timezone_get()); // make sure initialization happens only once Configuration::loadFromArray(['timezone' => 'Europe/Madrid'], '[ARRAY]', 'simplesaml'); - @Time::initTimezone(); + @$timeUtils->initTimezone(); $this->assertEquals($tz, @date_default_timezone_get()); } @@ -94,40 +97,41 @@ class TimeTest extends TestCase $negmonths = gmmktime(0, 0, 0, 10, 1, 1999); // -3 months = -1 year +9 months // test valid duration with timestamp and zeroes - $this->assertEquals($base + (60 * 60) + 60 + 1, Time::parseDuration('P0Y0M0DT1H1M1S', $base)); + $timeUtils = new Utils\Time(); + $this->assertEquals($base + (60 * 60) + 60 + 1, $timeUtils->parseDuration('P0Y0M0DT1H1M1S', $base)); // test seconds - $this->assertEquals($second, Time::parseDuration('PT1S', $base), "Failure checking for 1 second duration."); + $this->assertEquals($second, $timeUtils->parseDuration('PT1S', $base), "Failure checking for 1 second duration."); // test minutes - $this->assertEquals($minute, Time::parseDuration('PT1M', $base), "Failure checking for 1 minute duration."); + $this->assertEquals($minute, $timeUtils->parseDuration('PT1M', $base), "Failure checking for 1 minute duration."); // test hours - $this->assertEquals($hour, Time::parseDuration('PT1H', $base), "Failure checking for 1 hour duration."); + $this->assertEquals($hour, $timeUtils->parseDuration('PT1H', $base), "Failure checking for 1 hour duration."); // test days - $this->assertEquals($day, Time::parseDuration('P1D', $base), "Failure checking for 1 day duration."); + $this->assertEquals($day, $timeUtils->parseDuration('P1D', $base), "Failure checking for 1 day duration."); // test weeks - $this->assertEquals($week, Time::parseDuration('P1W', $base), "Failure checking for 1 week duration."); + $this->assertEquals($week, $timeUtils->parseDuration('P1W', $base), "Failure checking for 1 week duration."); // test month - $this->assertEquals($month, Time::parseDuration('P1M', $base), "Failure checking for 1 month duration."); + $this->assertEquals($month, $timeUtils->parseDuration('P1M', $base), "Failure checking for 1 month duration."); // test year - $this->assertEquals($year, Time::parseDuration('P1Y', $base), "Failure checking for 1 year duration."); + $this->assertEquals($year, $timeUtils->parseDuration('P1Y', $base), "Failure checking for 1 year duration."); // test months > 12 $this->assertEquals( $manymonths, - Time::parseDuration('P14M', $base), + $timeUtils->parseDuration('P14M', $base), "Failure checking for 14 months duration (1 year and 2 months)." ); // test negative months $this->assertEquals( $negmonths, - Time::parseDuration('-P3M', $base), + $timeUtils->parseDuration('-P3M', $base), "Failure checking for -3 months duration (-1 year + 9 months)." ); @@ -135,21 +139,21 @@ class TimeTest extends TestCase $now = time(); $this->assertGreaterThanOrEqual( $now + 60, - Time::parseDuration('PT1M'), + $timeUtils->parseDuration('PT1M'), "Failure testing for 1 minute over current time." ); // test invalid durations try { // invalid string - Time::parseDuration('abcdefg'); + $timeUtils->parseDuration('abcdefg'); $this->fail("Did not fail with invalid ISO 8601 duration."); } catch (InvalidArgumentException $e) { $this->assertStringStartsWith('Invalid ISO 8601 duration: ', $e->getMessage()); } try { // missing T delimiter - Time::parseDuration('P1S'); + $timeUtils->parseDuration('P1S'); $this->fail("Did not fail with duration missing T delimiter."); } catch (InvalidArgumentException $e) { $this->assertStringStartsWith('Invalid ISO 8601 duration: ', $e->getMessage()); diff --git a/tests/lib/SimpleSAML/Utils/XMLTest.php b/tests/lib/SimpleSAML/Utils/XMLTest.php index d0b6b6962b21ac8299b645e04da0acdb2a1bc962..6291fd9578c29b1bf179a3e0efbac68a647f5c5c 100644 --- a/tests/lib/SimpleSAML/Utils/XMLTest.php +++ b/tests/lib/SimpleSAML/Utils/XMLTest.php @@ -13,7 +13,7 @@ use InvalidArgumentException; use PHPUnit\Framework\TestCase; use SimpleSAML\Configuration; use SimpleSAML\Error; -use SimpleSAML\Utils\XML; +use SimpleSAML\Utils; /** * Tests for SimpleSAML\Utils\XML. @@ -29,11 +29,13 @@ class XMLTest extends TestCase */ public function testIsDomNodeOfTypeBasic(): void { + $xmlUtils = new Utils\XML(); + $name = 'name'; $namespace_uri = 'ns'; $element = new DOMElement($name, 'value', $namespace_uri); - $res = XML::isDOMNodeOfType($element, $name, $namespace_uri); + $res = $xmlUtils->isDOMNodeOfType($element, $name, $namespace_uri); $this->assertTrue($res); } @@ -44,12 +46,14 @@ class XMLTest extends TestCase */ public function testIsDomNodeOfTypeMissingNamespace(): void { + $xmlUtils = new Utils\XML(); + $this->expectException(InvalidArgumentException::class); $name = 'name'; $namespace_uri = '@missing'; $element = new DOMElement($name, 'value', $namespace_uri); - XML::isDOMNodeOfType($element, $name, $namespace_uri); + $xmlUtils->isDOMNodeOfType($element, $name, $namespace_uri); } @@ -58,11 +62,13 @@ class XMLTest extends TestCase */ public function testIsDomNodeOfTypeEmpty(): void { + $xmlUtils = new Utils\XML(); + $name = 'name'; $namespace_uri = ''; $element = new DOMElement($name); - $res = XML::isDOMNodeOfType($element, $name, $namespace_uri); + $res = $xmlUtils->isDOMNodeOfType($element, $name, $namespace_uri); $this->assertFalse($res); } @@ -73,12 +79,14 @@ class XMLTest extends TestCase */ public function testIsDomNodeOfTypeShortcut(): void { + $xmlUtils = new Utils\XML(); + $name = 'name'; $namespace_uri = 'urn:oasis:names:tc:SAML:2.0:metadata'; $short_namespace_uri = '@md'; $element = new DOMElement($name, 'value', $namespace_uri); - $res = XML::isDOMNodeOfType($element, $name, $short_namespace_uri); + $res = $xmlUtils->isDOMNodeOfType($element, $name, $short_namespace_uri); $this->assertTrue($res); } @@ -89,12 +97,14 @@ class XMLTest extends TestCase */ public function testIsDomNodeOfTypeIncorrectName(): void { + $xmlUtils = new Utils\XML(); + $name = 'name'; $bad_name = 'bad name'; $namespace_uri = 'ns'; $element = new DOMElement($name, 'value', $namespace_uri); - $res = XML::isDOMNodeOfType($element, $bad_name, $namespace_uri); + $res = $xmlUtils->isDOMNodeOfType($element, $bad_name, $namespace_uri); $this->assertFalse($res); } @@ -105,12 +115,14 @@ class XMLTest extends TestCase */ public function testIsDomNodeOfTypeIncorrectNamespace(): void { + $xmlUtils = new Utils\XML(); + $name = 'name'; $namespace_uri = 'ns'; $bad_namespace_uri = 'bad name'; $element = new DOMElement($name, 'value', $namespace_uri); - $res = XML::isDOMNodeOfType($element, $name, $bad_namespace_uri); + $res = $xmlUtils->isDOMNodeOfType($element, $name, $bad_namespace_uri); $this->assertFalse($res); } @@ -121,12 +133,14 @@ class XMLTest extends TestCase */ public function testFormatDomElementBasic(): void { + $xmlUtils = new Utils\XML(); + $dom = new DOMDocument(); $root = new DOMElement('root'); $dom->appendChild($root); $root->appendChild(new \DOMText('text')); - XML::formatDOMElement($root); + $xmlUtils->formatDOMElement($root); $res = $dom->saveXML(); $expected = <<<'NOWDOC' <?xml version="1.0"?> @@ -143,6 +157,8 @@ NOWDOC; */ public function testFormatDomElementNested(): void { + $xmlUtils = new Utils\XML(); + $dom = new DOMDocument(); $root = new DOMElement('root'); $nested = new DOMElement('nested'); @@ -150,7 +166,7 @@ NOWDOC; $root->appendChild($nested); $nested->appendChild(new DOMText('text')); - XML::formatDOMElement($root); + $xmlUtils->formatDOMElement($root); $res = $dom->saveXML(); $expected = <<<'NOWDOC' <?xml version="1.0"?> @@ -169,6 +185,8 @@ NOWDOC; */ public function testFormatDomElementIndentBase(): void { + $xmlUtils = new Utils\XML(); + $indent_base = 'base'; $dom = new DOMDocument(); $root = new DOMElement('root'); @@ -177,7 +195,7 @@ NOWDOC; $root->appendChild($nested); $nested->appendChild(new DOMText('text')); - XML::formatDOMElement($root, $indent_base); + $xmlUtils->formatDOMElement($root, $indent_base); $res = $dom->saveXML(); $expected = <<<HEREDOC <?xml version="1.0"?> @@ -196,13 +214,15 @@ HEREDOC; */ public function testFormatDomElementTextAndChild(): void { + $xmlUtils = new Utils\XML(); + $dom = new DOMDocument(); $root = new DOMElement('root'); $dom->appendChild($root); $root->appendChild(new DOMText('text')); $root->appendChild(new DOMElement('child')); - XML::formatDOMElement($root); + $xmlUtils->formatDOMElement($root); $res = $dom->saveXML(); $expected = <<<HEREDOC <?xml version="1.0"?> @@ -219,9 +239,11 @@ HEREDOC; */ public function testFormatXmlStringBasic(): void { + $xmlUtils = new Utils\XML(); + $xml = '<root><nested>text</nested></root>'; - $res = XML::formatXMLString($xml); + $res = $xmlUtils->formatXMLString($xml); $expected = <<<'NOWDOC' <root> <nested>text</nested> @@ -237,10 +259,12 @@ NOWDOC; */ public function testFormatXmlStringMalformedXml(): void { + $xmlUtils = new Utils\XML(); + $this->expectException(DOMException::class); $xml = '<root><nested>text'; - XML::formatXMLString($xml); + $xmlUtils->formatXMLString($xml); } @@ -249,9 +273,11 @@ NOWDOC; */ public function testIsValidMalformedXml(): void { + $xmlUtils = new Utils\XML(); + $xml = '<root><nested>text'; - $res = XML::isValid($xml, 'unused'); + $res = $xmlUtils->isValid($xml, 'unused'); $this->assertIsString($res); $expected = 'Failed to parse XML string for schema validation'; @@ -263,13 +289,15 @@ NOWDOC; */ public function testIsValidMetadata(): void { + $xmlUtils = new Utils\XML(); + $schema = 'saml-schema-metadata-2.0.xsd'; $xml = file_get_contents(self::FRAMEWORK . '/metadata/valid-metadata-selfsigned.xml'); $dom = new DOMDocument('1.0'); $dom->loadXML($xml, LIBXML_NONET); - $res = XML::isValid($dom, $schema); + $res = $xmlUtils->isValid($dom, $schema); $this->assertTrue($res === true); } @@ -277,7 +305,9 @@ NOWDOC; */ public function testCheckSAMLMessageInvalidType(): void { + $xmlUtils = new Utils\XML(); + $this->expectException(InvalidArgumentException::class); - XML::checkSAMLMessage('<test></test>', 'blub'); + $xmlUtils->checkSAMLMessage('<test></test>', 'blub'); } } diff --git a/tests/modules/admin/lib/Controller/ConfigTest.php b/tests/modules/admin/lib/Controller/ConfigTest.php index e7e513dca95b52b5ef757a6cf85d964d63064c38..c10f17e79370049b8c15cab5aa875f223ee66660 100644 --- a/tests/modules/admin/lib/Controller/ConfigTest.php +++ b/tests/modules/admin/lib/Controller/ConfigTest.php @@ -61,7 +61,7 @@ class ConfigTest extends TestCase ); $this->authUtils = new class () extends Utils\Auth { - public static function requireAdmin(): void + public function requireAdmin(): void { // stub } diff --git a/tests/modules/admin/lib/Controller/FederationTest.php b/tests/modules/admin/lib/Controller/FederationTest.php index f15cb033c92c1d48b00b6eb9f9fb83b5789f7e4f..417834bd7946b8941ba6bd626ab74e79d1e6698e 100644 --- a/tests/modules/admin/lib/Controller/FederationTest.php +++ b/tests/modules/admin/lib/Controller/FederationTest.php @@ -79,7 +79,7 @@ class FederationTest extends TestCase ); $this->authUtils = new class () extends Utils\Auth { - public static function requireAdmin(): void + public function requireAdmin(): void { // stub } diff --git a/tests/modules/admin/lib/Controller/TestTest.php b/tests/modules/admin/lib/Controller/TestTest.php index 78743f128572c4cd039645395dcc83aa6a14bb5a..a27a83c4f3d967d1afc3c6a916063dbbc79f9995 100644 --- a/tests/modules/admin/lib/Controller/TestTest.php +++ b/tests/modules/admin/lib/Controller/TestTest.php @@ -51,7 +51,7 @@ class TestTest extends TestCase ); $this->authUtils = new class () extends Utils\Auth { - public static function requireAdmin(): void + public function requireAdmin(): void { // stub } diff --git a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php index 3f18d49d44ff6793e009c7bb7e657fab93b9675c..0fb6bf0361fa356a7b11c12b5be45f8e6ee042e8 100644 --- a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php +++ b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace SimpleSAML\Test\Module\core\Auth\Process; use PHPUnit\Framework\TestCase; -use SimpleSAML\Utils\HttpAdapter; +use SimpleSAML\Utils; use SimpleSAML\Module\core\Auth\Process\CardinalitySingle; /** @@ -15,8 +15,8 @@ use SimpleSAML\Module\core\Auth\Process\CardinalitySingle; */ class CardinalitySingleTest extends TestCase { - /** @var \SimpleSAML\Utils\HttpAdapter|\PHPUnit\Framework\MockObject\MockObject */ - private object $http; + /** @var \SimpleSAML\Utils\HTTP|\PHPUnit\Framework\MockObject\MockObject */ + private object $httpUtils; /** @@ -31,10 +31,10 @@ class CardinalitySingleTest extends TestCase $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; $_SERVER['REQUEST_METHOD'] = 'GET'; - /** @var \SimpleSAML\Utils\HttpAdapter $http */ - $http = $this->http; + /** @var \SimpleSAML\Utils\HTTP $httpUtils */ + $httpUtils = $this->httpUtils; - $filter = new CardinalitySingle($config, null, $http); + $filter = new CardinalitySingle($config, null, $httpUtils); $filter->process($request); return $request; } @@ -45,7 +45,7 @@ class CardinalitySingleTest extends TestCase protected function setUp(): void { \SimpleSAML\Configuration::loadFromArray([], '[ARRAY]', 'simplesaml'); - $this->http = $this->getMockBuilder(HttpAdapter::class) + $this->httpUtils = $this->getMockBuilder(Utils\HTTP::class) ->setMethods(['redirectTrustedURL']) ->getMock(); } @@ -166,7 +166,7 @@ class CardinalitySingleTest extends TestCase ]; /** @psalm-suppress UndefinedMethod */ - $this->http->expects($this->once()) + $this->httpUtils->expects($this->once()) ->method('redirectTrustedURL'); $this->processFilter($config, $request); diff --git a/tests/modules/core/lib/Auth/Process/CardinalityTest.php b/tests/modules/core/lib/Auth/Process/CardinalityTest.php index 9934351ae9d4621768088ba732afa6423c376eba..d84e07f8dcaf016f555e3f364a7a8c5c13128169 100644 --- a/tests/modules/core/lib/Auth/Process/CardinalityTest.php +++ b/tests/modules/core/lib/Auth/Process/CardinalityTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase; use SimpleSAML\Configuration; use SimpleSAML\Error\Exception as SspException; use SimpleSAML\Module\core\Auth\Process\Cardinality; -use SimpleSAML\Utils\HttpAdapter; +use SimpleSAML\Utils; /** * Test for the core:Cardinality filter. @@ -17,8 +17,8 @@ use SimpleSAML\Utils\HttpAdapter; */ class CardinalityTest extends TestCase { - /** @var \SimpleSAML\Utils\HttpAdapter|\PHPUnit\Framework\MockObject\MockObject */ - private object $http; + /** @var \SimpleSAML\Utils\HTTP|\PHPUnit\Framework\MockObject\MockObject */ + private object $httpUtils; /** @@ -33,10 +33,10 @@ class CardinalityTest extends TestCase $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; $_SERVER['REQUEST_METHOD'] = 'GET'; - /** @var \SimpleSAML\Utils\HttpAdapter $http */ - $http = $this->http; + /** @var \SimpleSAML\Utils\HTTP $httpUtils */ + $httpUtils = $this->httpUtils; - $filter = new Cardinality($config, null, $http); + $filter = new Cardinality($config, null, $httpUtils); $filter->process($request); return $request; } @@ -47,7 +47,7 @@ class CardinalityTest extends TestCase protected function setUp(): void { Configuration::loadFromArray([], '[ARRAY]', 'simplesaml'); - $this->http = $this->getMockBuilder(HttpAdapter::class) + $this->httpUtils = $this->getMockBuilder(Utils\HTTP::class) ->setMethods(['redirectTrustedURL']) ->getMock(); } @@ -128,7 +128,7 @@ class CardinalityTest extends TestCase ]; /** @psalm-suppress UndefinedMethod It's a mock-object */ - $this->http->expects($this->once()) + $this->httpUtils->expects($this->once()) ->method('redirectTrustedURL'); $this->processFilter($config, $request); @@ -150,7 +150,7 @@ class CardinalityTest extends TestCase ]; /** @psalm-suppress UndefinedMethod It's a mock-object */ - $this->http->expects($this->once()) + $this->httpUtils->expects($this->once()) ->method('redirectTrustedURL'); $this->processFilter($config, $request); @@ -170,7 +170,7 @@ class CardinalityTest extends TestCase ]; /** @psalm-suppress UndefinedMethod It's a mock-object */ - $this->http->expects($this->once()) + $this->httpUtils->expects($this->once()) ->method('redirectTrustedURL'); $this->processFilter($config, $request); diff --git a/tests/modules/core/lib/Auth/Process/PairwiseIDTest.php b/tests/modules/core/lib/Auth/Process/PairwiseIDTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5ebe33c53cb088b4ed47aa02b78065f7380617b1 --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/PairwiseIDTest.php @@ -0,0 +1,317 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Module\core\Auth\Process; + +use PHPUnit\Framework\TestCase; +use RuntimeException; +use SAML2\Constants; +use SAML2\XML\saml\NameID; +use SimpleSAML\Assert\AssertionFailedException; +use SimpleSAML\Configuration; +use SimpleSAML\Logger; +use SimpleSAML\Module\core\Auth\Process\PairwiseID; +use SimpleSAML\Utils; + +/** + * Test for the core:PairwiseID filter. + * + * @covers \SimpleSAML\Module\core\Auth\Process\PairwiseID + */ +class PairwiseIDTest extends TestCase +{ + /** @var \SimpleSAML\Configuration */ + protected Configuration $config; + + /** @var \SimpleSAML\Utils\Config */ + protected static Utils\Config $configUtils; + + /** @var \SimpleSAML\Logger */ + protected static Logger $logger; + + + /** + * Set up for each test. + */ + protected function setUp(): void + { + parent::setUp(); + + self::$configUtils = new class () extends Utils\Config { + public function getSecretSalt(): string + { + // stub + return 'secretsalt'; + } + }; + + self::$logger = new class () extends Logger { + public static function warning(string $string): void + { + // stub + throw new RuntimeException($string); + } + }; + } + + + /** + * Helper function to run the filter with a given configuration. + * + * @param array $config The filter configuration. + * @param array $request The request state. + * @return array The state array after processing. + */ + private static function processFilter(array $config, array $request): array + { + $filter = new PairwiseID($config, null); + $filter->setConfigUtils(self::$configUtils); + $filter->setLogger(self::$logger); + $filter->process($request); + return $request; + } + + + /** + * Test the most basic functionality + */ + public function testBasic(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'ex-ample.org']; + $request = [ + 'Attributes' => ['uid' => ['u=se-r2']], + 'core:SP' => 'urn:sp', + ]; + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $this->assertMatchesRegularExpression( + PairwiseID::SPEC_PATTERN, + $attributes[Constants::ATTR_PAIRWISE_ID][0] + ); + $this->assertEquals( + '53d4f7fe57fb597ada481e81e0f15048bc610774cbb5614ea38f08ea918ba199@ex-ample.org', + $attributes[Constants::ATTR_PAIRWISE_ID][0] + ); + } + + + /** + * Test the most basic functionality on proxied request + */ + public function testBasicProxiedRequest(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'ex-ample.org']; + $request = [ + 'Attributes' => ['uid' => ['u=se-r2']], + 'saml:RequesterID' => [0 => 'urn:sp'], + ]; + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $this->assertMatchesRegularExpression( + PairwiseID::SPEC_PATTERN, + $attributes[Constants::ATTR_PAIRWISE_ID][0] + ); + $this->assertEquals( + '53d4f7fe57fb597ada481e81e0f15048bc610774cbb5614ea38f08ea918ba199@ex-ample.org', + $attributes[Constants::ATTR_PAIRWISE_ID][0] + ); + } + + + /** + * Test the proxied request with multiple hops + */ + public function testProxiedRequestMultipleHops(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'ex-ample.org']; + $request = [ + 'Attributes' => ['uid' => ['u=se-r2']], + 'saml:RequesterID' => [0 => 'urn:sp', 1 => 'urn:some:sp', 2 => 'urn:some:other:sp'], + ]; + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $this->assertMatchesRegularExpression( + PairwiseID::SPEC_PATTERN, + $attributes[Constants::ATTR_PAIRWISE_ID][0] + ); + $this->assertEquals( + '53d4f7fe57fb597ada481e81e0f15048bc610774cbb5614ea38f08ea918ba199@ex-ample.org', + $attributes[Constants::ATTR_PAIRWISE_ID][0] + ); + } + + + /** + * Test that illegal characters in scope throws an exception. + */ + public function testScopeIllegalCharacterThrowsException(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'ex%ample.org']; + $request = [ + 'Attributes' => ['uid' => ['user2']], + 'core:SP' => 'urn:sp', + ]; + + $this->expectException(AssertionFailedException::class); + self::processFilter($config, $request); + } + + + /** + * Test that generated ID's for the same user, but different SP's are NOT equal + */ + public function testUniqueIdentifierPerSPSameUser(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['user1']], + 'core:SP' => 'urn:sp', + ]; + + // Generate first ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value1 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + // Switch SP + $request['core:SP'] = 'urn:some:other:sp'; + + // Generate second ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value2 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + $this->assertNotSame($value1, $value2); + } + + + /** + * Test that generated ID's for different users, but the same SP's are NOT equal + */ + public function testUniqueIdentifierPerUserSameSP(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['user1']], + 'core:SP' => 'urn:sp', + ]; + + // Generate first ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value1 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + // Switch user + $request['Attributes'] = ['uid' => ['user2']]; + + // Generate second ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value2 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + $this->assertNotSame($value1, $value2); + } + + + /** + * Test that generated ID's for the same user and same SP, but with a different salt are NOT equal + */ + public function testUniqueIdentifierDifferentSalts(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['user1']], + 'core:SP' => 'urn:sp', + ]; + + // Generate first ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value1 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + // Change the salt + self::$configUtils = new class () extends Utils\Config { + public function getSecretSalt(): string + { + // stub + return 'pepper'; + } + }; + + // Generate second ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value2 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + $this->assertNotSame($value1, $value2); + } + + + /** + * Test that generated ID's for the same user and same SP, but with a different scope are NOT equal + */ + public function testUniqueIdentifierDifferentScopes(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['user1']], + 'core:SP' => 'urn:sp', + ]; + + // Generate first ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value1 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + // Change the scope + $config['scope'] = 'example.edu'; + + // Generate second ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_PAIRWISE_ID, $attributes); + $value2 = $attributes[Constants::ATTR_PAIRWISE_ID][0]; + + $this->assertNotSame($value1, $value2); + + $this->assertMatchesRegularExpression( + '/@example.org$/i', + $value1 + ); + $this->assertMatchesRegularExpression( + '/@example.edu$/i', + $value2 + ); + } + + + /** + * Test that weak identifiers log a warning + */ + public function testWeakIdentifierLogsWarning(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'b']; + $request = [ + 'Attributes' => ['uid' => ['a']], + 'core:SP' => 'urn:sp', + ]; + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'core:PairwiseID: Generated ID \'c5b54935db5e291a6b94688921fa77ced8ce425ce8c61a448bd4997f494dbebe@b\' can hardly be considered globally unique.' + ); + + self::processFilter($config, $request); + } +} diff --git a/tests/modules/core/lib/Auth/Process/SubjectIDTest.php b/tests/modules/core/lib/Auth/Process/SubjectIDTest.php new file mode 100644 index 0000000000000000000000000000000000000000..81313a6a891f6a4b6da8fb7a59f7521ff65b17dc --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/SubjectIDTest.php @@ -0,0 +1,196 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Module\core\Auth\Process; + +use PHPUnit\Framework\TestCase; +use RuntimeException; +use SAML2\Constants; +use SAML2\XML\saml\NameID; +use SimpleSAML\Assert\AssertionFailedException; +use SimpleSAML\Configuration; +use SimpleSAML\Logger; +use SimpleSAML\Module\core\Auth\Process\SubjectID; +use SimpleSAML\Utils; + +/** + * Test for the core:SubjectID filter. + * + * @covers \SimpleSAML\Module\core\Auth\Process\SubjectID + */ +class SubjectIDTest extends TestCase +{ + /** @var \SimpleSAML\Configuration */ + protected Configuration $config; + + /** @var \SimpleSAML\Logger */ + protected static Logger $logger; + + + /** + * Set up for each test. + */ + protected function setUp(): void + { + parent::setUp(); + + self::$logger = new class () extends Logger { + public static function warning(string $string): void + { + // stub + throw new RuntimeException($string); + } + }; + } + + + /** + * Helper function to run the filter with a given configuration. + * + * @param array $config The filter configuration. + * @param array $request The request state. + * @return array The state array after processing. + */ + private static function processFilter(array $config, array $request): array + { + $filter = new SubjectID($config, null); + $filter->setLogger(self::$logger); + $filter->process($request); + return $request; + } + + + /** + * Test the most basic functionality + */ + public function testBasic(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'ex-ample.org']; + $request = [ + 'Attributes' => ['uid' => ['u=se-r2']], + ]; + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_SUBJECT_ID, $attributes); + $this->assertMatchesRegularExpression( + SubjectID::SPEC_PATTERN, + $attributes[Constants::ATTR_SUBJECT_ID][0] + ); + $this->assertEquals('u=se-r2@ex-ample.org', $attributes[Constants::ATTR_SUBJECT_ID][0]); + } + + + /** + * Test that illegal characters in userID throws an exception. + */ + public function testUserIDIllegalCharacterThrowsException(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['u=se+r2']], + ]; + + $this->expectException(AssertionFailedException::class); + self::processFilter($config, $request); + } + + + /** + * Test that illegal characters in scope throws an exception. + */ + public function testScopeIllegalCharacterThrowsException(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'ex%ample.org']; + $request = [ + 'Attributes' => ['uid' => ['user2']], + ]; + + $this->expectException(AssertionFailedException::class); + self::processFilter($config, $request); + } + + + /** + * Test that generated ID's for different users, but the same SP's are NOT equal + */ + public function testUniqueIdentifierPerUserSameSP(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['user1']], + ]; + + // Generate first ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_SUBJECT_ID, $attributes); + $value1 = $attributes[Constants::ATTR_SUBJECT_ID][0]; + + // Switch user + $request['Attributes'] = ['uid' => ['user2']]; + + // Generate second ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_SUBJECT_ID, $attributes); + $value2 = $attributes[Constants::ATTR_SUBJECT_ID][0]; + + $this->assertNotSame($value1, $value2); + } + + + /** + * Test that generated ID's for the same user and same SP, but with a different scope are NOT equal + */ + public function testUniqueIdentifierDifferentScopes(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'example.org']; + $request = [ + 'Attributes' => ['uid' => ['user1']], + ]; + + // Generate first ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_SUBJECT_ID, $attributes); + $value1 = $attributes[Constants::ATTR_SUBJECT_ID][0]; + + // Change the scope + $config['scope'] = 'example.edu'; + + // Generate second ID + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey(Constants::ATTR_SUBJECT_ID, $attributes); + $value2 = $attributes[Constants::ATTR_SUBJECT_ID][0]; + + $this->assertNotSame($value1, $value2); + + $this->assertMatchesRegularExpression( + '/@example.org$/i', + $value1 + ); + $this->assertMatchesRegularExpression( + '/@example.edu$/i', + $value2 + ); + } + + + /** + * Test that weak identifiers log a warning + */ + public function testWeakIdentifierLogsWarning(): void + { + $config = ['identifyingAttribute' => 'uid', 'scope' => 'b']; + $request = [ + 'Attributes' => ['uid' => ['a']], + ]; + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('core:SubjectID: Generated ID \'a@b\' can hardly be considered globally unique.'); + + self::processFilter($config, $request); + } +} diff --git a/tests/modules/core/lib/Auth/Process/TargetedIDTest.php b/tests/modules/core/lib/Auth/Process/TargetedIDTest.php index 0bccc514e0afbed8a2340224f2af1bf822f3470f..34a740d4171ecd4eb858cff96b86b3df3cea0bb2 100644 --- a/tests/modules/core/lib/Auth/Process/TargetedIDTest.php +++ b/tests/modules/core/lib/Auth/Process/TargetedIDTest.php @@ -33,7 +33,7 @@ class TargetedIDTest extends TestCase parent::setUp(); self::$configUtils = new class () extends Utils\Config { - public static function getSecretSalt(): string + public function getSecretSalt(): string { // stub return 'secretsalt'; diff --git a/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php b/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php index 7e04f21ee2aa628d4fd49f829e4f8fcc47586749..13dc5d1aab9f153ba07114e0ca37b2166b11ef30 100644 --- a/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php +++ b/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php @@ -35,7 +35,7 @@ class SQLPermanentStorageTest extends TestCase */ public static function tearDownAfterClass(): void { - unlink(sys_get_temp_dir() . '/sqllite/test.sqlite'); + unlink(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'sqllite' . DIRECTORY_SEPARATOR . 'test.sqlite'); } diff --git a/tests/modules/cron/lib/Controller/CronTest.php b/tests/modules/cron/lib/Controller/CronTest.php index da032e241f859bf93df551b754d637c7ebd97110..e543a6dad2125b956e817032cb3b740309e68bc1 100644 --- a/tests/modules/cron/lib/Controller/CronTest.php +++ b/tests/modules/cron/lib/Controller/CronTest.php @@ -50,7 +50,7 @@ class CronTest extends TestCase $this->session = Session::getSessionFromRequest(); $this->authUtils = new class () extends Utils\Auth { - public static function requireAdmin(): void + public function requireAdmin(): void { // stub } diff --git a/tests/modules/unittest/README.md b/tests/modules/unittest/README.md new file mode 100644 index 0000000000000000000000000000000000000000..52b5ff5a2fcf33a12173a6da26846b8a2d139f4c --- /dev/null +++ b/tests/modules/unittest/README.md @@ -0,0 +1 @@ +For some unit tests we need a module that has a specific setup or issues. diff --git a/tests/modules/unittest/hooks/hook_invalid.php.orig b/tests/modules/unittest/hooks/hook_invalid.php.orig new file mode 100644 index 0000000000000000000000000000000000000000..d09ba0cd5deaff7120e1f23bc52abac2ae8927c2 --- /dev/null +++ b/tests/modules/unittest/hooks/hook_invalid.php.orig @@ -0,0 +1,3 @@ +<?php + +// Hook detection should not load this file since it does not conform to the hook naming scheme diff --git a/tests/modules/unittest/hooks/hook_valid.php b/tests/modules/unittest/hooks/hook_valid.php new file mode 100644 index 0000000000000000000000000000000000000000..b45dcd25ea7611ac8506ab175f76157e0a603172 --- /dev/null +++ b/tests/modules/unittest/hooks/hook_valid.php @@ -0,0 +1,14 @@ +<?php + +use SimpleSAML\Assert\Assert; +use SimpleSAML\Auth; + +/** + * Hook to for use with unit tests + * + * @param array &$data Some data + */ +function unittest_hook_valid(array &$data) +{ + $data['summary'][] = 'success'; +} diff --git a/tests/modules/unittest/hooks/invalid_hook_prefix.php b/tests/modules/unittest/hooks/invalid_hook_prefix.php new file mode 100644 index 0000000000000000000000000000000000000000..d09ba0cd5deaff7120e1f23bc52abac2ae8927c2 --- /dev/null +++ b/tests/modules/unittest/hooks/invalid_hook_prefix.php @@ -0,0 +1,3 @@ +<?php + +// Hook detection should not load this file since it does not conform to the hook naming scheme diff --git a/www/_include.php b/www/_include.php index 1403bfd429b3af68be0fac6c9dfde286cd3de977..c23afbf721e48dc64903724c139983bf136774c2 100644 --- a/www/_include.php +++ b/www/_include.php @@ -64,4 +64,9 @@ try { } // set the timezone -\SimpleSAML\Utils\Time::initTimezone(); +$timeUtils = new \SimpleSAML\Utils\Time(); +$timeUtils->initTimezone(); + +// set the SAML2 container +$container = new \SimpleSAML\Compat\SspContainer(); +\SAML2\Compat\ContainerSingleton::setContainer($container); diff --git a/www/admin/index.php b/www/admin/index.php index 931de593df537e9b1aa974a681648b53873b0083..b77e3e4097cb7b21942baf87d1ef2693aa7c77da 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -2,4 +2,5 @@ require_once('../_include.php'); -\SimpleSAML\Utils\HTTP::redirectTrustedURL(\SimpleSAML\Module::getModuleURL('admin/')); +$httpUtils = new \SimpleSAML\Utils\HTTP(); +$httpUtils->redirectTrustedURL(\SimpleSAML\Module::getModuleURL('admin/')); diff --git a/www/errorreport.php b/www/errorreport.php index 0983cafef656ea523c6a89024ae0ad5dcadcd430..50c33e741ce97beadc835e6ec3ac43e68feafdab 100644 --- a/www/errorreport.php +++ b/www/errorreport.php @@ -58,4 +58,5 @@ if ($config->getBoolean('errorreporting', true)) { } // redirect the user back to this page to clear the POST request -\SimpleSAML\Utils\HTTP::redirectTrustedURL(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery()); +$httpUtils = new \SimpleSAML\Utils\HTTP(); +$httpUtils->redirectTrustedURL($httpUtils->getSelfURLNoQuery()); diff --git a/www/index.php b/www/index.php index 6f88d155f49d1fcb79a2b98bded6f4c5fa6ae890..b5933a4f1b3baa81cf24ed4536bd5017ed6da490 100644 --- a/www/index.php +++ b/www/index.php @@ -3,5 +3,6 @@ require_once('_include.php'); $config = \SimpleSAML\Configuration::getInstance(); +$httpUtils = new \SimpleSAML\Utils\HTTP(); -\SimpleSAML\Utils\HTTP::redirectTrustedURL(SimpleSAML\Module::getModuleURL('core/login')); +$httpUtils->redirectTrustedURL(SimpleSAML\Module::getModuleURL('core/login')); diff --git a/www/logout.php b/www/logout.php index 56d0401c758ea2cb42c46ac0ac04173540099c68..5be736361740a34afe287d5575acaff1cb484b9a 100644 --- a/www/logout.php +++ b/www/logout.php @@ -3,9 +3,10 @@ require_once('_include.php'); $config = \SimpleSAML\Configuration::getInstance(); +$httpUtils = new \SimpleSAML\Utils\HTTP(); if (array_key_exists('link_href', $_REQUEST)) { - $link = \SimpleSAML\Utils\HTTP::checkURLAllowed($_REQUEST['link_href']); + $link = $httpUtils->checkURLAllowed($_REQUEST['link_href']); } else { $link = 'index.php'; } diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php index ab41bab98fffe49ef7901e417171a8a2c5244eac..648320fa82fe05f87817cbd8c976b3e71f5d4214 100644 --- a/www/saml2/idp/SingleLogoutService.php +++ b/www/saml2/idp/SingleLogoutService.php @@ -26,12 +26,13 @@ if (!$config->getBoolean('enable.saml20-idp', false) || !Module::isModuleEnabled throw new Error\Error('NOACCESS', null, 403); } +$httpUtils = new Utils\HTTP(); $metadata = Metadata\MetaDataStorageHandler::getMetadataHandler(); $idpEntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idp = IdP::getById('saml2:' . $idpEntityId); if (isset($_REQUEST['ReturnTo'])) { - $idp->doLogoutRedirect(Utils\HTTP::checkURLAllowed((string) $_REQUEST['ReturnTo'])); + $idp->doLogoutRedirect($httpUtils->checkURLAllowed((string) $_REQUEST['ReturnTo'])); } else { try { Module\saml\IdP\SAML2::receiveLogoutMessage($idp); diff --git a/www/saml2/idp/initSLO.php b/www/saml2/idp/initSLO.php index f86b49488ba98645997f81ba1ef8a46484aa4eb8..11961711827be96764b280b0a47d9e686c5d8f3d 100644 --- a/www/saml2/idp/initSLO.php +++ b/www/saml2/idp/initSLO.php @@ -26,5 +26,6 @@ if (!isset($_GET['RelayState'])) { throw new Error\Error('NORELAYSTATE'); } -$idp->doLogoutRedirect(Utils\HTTP::checkURLAllowed((string) $_GET['RelayState'])); +$httpUtils = new Utils\HTTP(); +$idp->doLogoutRedirect($httpUtils->checkURLAllowed((string) $_GET['RelayState'])); Assert::true(false); diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php index 7bf640076a46786ea18b9e4135af9756ae115881..d6511a4d98518941ee8732d360b01e3d75ac605a 100644 --- a/www/saml2/idp/metadata.php +++ b/www/saml2/idp/metadata.php @@ -9,9 +9,7 @@ use SimpleSAML\Assert\Assert; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\Module; -use SimpleSAML\Utils\Auth as Auth; -use SimpleSAML\Utils\Crypto as Crypto; -use SimpleSAML\Utils\HTTP as HTTP; +use SimpleSAML\Utils; use SimpleSAML\Utils\Config\Metadata as Metadata; $config = Configuration::getInstance(); @@ -21,9 +19,11 @@ if (!$config->getBoolean('enable.saml20-idp', false) || !Module::isModuleEnabled // check if valid local session exists if ($config->getBoolean('admin.protectmetadata', false)) { - Auth::requireAdmin(); + $authUtils = new Utils\Auth(); + $authUtils->requireAdmin(); } +$httpUtils = new Utils\HTTP(); $metadata = \SimpleSAML\Metadata\MetaDataStorageHandler::getMetadataHandler(); try { @@ -31,10 +31,11 @@ try { $_GET['idpentityid'] : $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted'); + $cryptoUtils = new Utils\Crypto(); $availableCerts = []; - $keys = []; - $certInfo = Crypto::loadPublicKey($idpmeta, false, 'new_'); + + $certInfo = $cryptoUtils->loadPublicKey($idpmeta, false, 'new_'); if ($certInfo !== null) { $availableCerts['new_idp.crt'] = $certInfo; $keys[] = [ @@ -48,7 +49,7 @@ try { $hasNewCert = false; } - $certInfo = Crypto::loadPublicKey($idpmeta, true); + $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true); $availableCerts['idp.crt'] = $certInfo; $keys[] = [ 'type' => 'X509Certificate', @@ -58,7 +59,7 @@ try { ]; if ($idpmeta->hasValue('https.certificate')) { - $httpsCert = Crypto::loadPublicKey($idpmeta, true, 'https.'); + $httpsCert = $cryptoUtils->loadPublicKey($idpmeta, true, 'https.'); Assert::notNull($httpsCert['certData']); $availableCerts['https.crt'] = $httpsCert; $keys[] = [ @@ -117,7 +118,7 @@ try { // Artifact sending enabled $metaArray['ArtifactResolutionService'][] = [ 'index' => 0, - 'Location' => HTTP::getBaseURL() . 'saml2/idp/ArtifactResolutionService.php', + 'Location' => $httpUtils->getBaseURL() . 'saml2/idp/ArtifactResolutionService.php', 'Binding' => Constants::BINDING_SOAP, ]; } @@ -127,7 +128,7 @@ try { array_unshift($metaArray['SingleSignOnService'], [ 'hoksso:ProtocolBinding' => Constants::BINDING_HTTP_REDIRECT, 'Binding' => Constants::BINDING_HOK_SSO, - 'Location' => HTTP::getBaseURL() . 'saml2/idp/SSOService.php' + 'Location' => $httpUtils->getBaseURL() . 'saml2/idp/SSOService.php' ]); } @@ -135,7 +136,7 @@ try { $metaArray['SingleSignOnService'][] = [ 'index' => 0, 'Binding' => Constants::BINDING_SOAP, - 'Location' => HTTP::getBaseURL() . 'saml2/idp/SSOService.php', + 'Location' => $httpUtils->getBaseURL() . 'saml2/idp/SSOService.php', ]; } @@ -238,7 +239,7 @@ try { $t->data['certdata'] = $certdata; $t->data['header'] = 'saml20-idp'; // TODO: Replace with headerString in 2.0 $t->data['headerString'] = \SimpleSAML\Locale\Translate::noop('metadata_saml20-idp'); - $t->data['metaurl'] = HTTP::getSelfURLNoQuery(); + $t->data['metaurl'] = $httpUtils->getSelfURLNoQuery(); $t->data['metadata'] = htmlspecialchars($metaxml); $t->data['metadataflat'] = htmlspecialchars($metaflat); $t->send();