From ec1798f88a79194c363e835c57f82a0c3c9514d3 Mon Sep 17 00:00:00 2001 From: Tim van Dijen <tim.dijen@minbzk.nl> Date: Fri, 19 Jun 2020 17:52:10 +0200 Subject: [PATCH] Admin controller tests (#1329) Start testing admin-module --- modules/admin/lib/Controller/Config.php | 36 +- modules/admin/lib/Controller/Federation.php | 76 +++- modules/admin/lib/Controller/Test.php | 64 ++- phpunit.xml | 1 + .../admin/lib/Controller/ConfigTest.php | 131 ++++++ .../admin/lib/Controller/FederationTest.php | 402 ++++++++++++++++++ .../modules/admin/lib/Controller/TestTest.php | 260 +++++++++++ 7 files changed, 941 insertions(+), 29 deletions(-) create mode 100644 tests/modules/admin/lib/Controller/ConfigTest.php create mode 100644 tests/modules/admin/lib/Controller/FederationTest.php create mode 100644 tests/modules/admin/lib/Controller/TestTest.php diff --git a/modules/admin/lib/Controller/Config.php b/modules/admin/lib/Controller/Config.php index b9ef5145c..3c4062e59 100644 --- a/modules/admin/lib/Controller/Config.php +++ b/modules/admin/lib/Controller/Config.php @@ -29,6 +29,12 @@ class Config /** @var \SimpleSAML\Configuration */ protected $config; + /** + * @var \SimpleSAML\Utils\Auth|string + * @psalm-var \SimpleSAML\Utils\Auth|class-string + */ + protected $authUtils = Utils\Auth::class; + /** @var Menu */ protected $menu; @@ -50,16 +56,27 @@ class Config } + /** + * Inject the \SimpleSAML\Utils\Auth dependency. + * + * @param \SimpleSAML\Utils\Auth $authUtils + */ + public function setAuthUtils(Utils\Auth $authUtils): void + { + $this->authUtils = $authUtils; + } + + /** * Display basic diagnostic information on hostname, port and protocol. * - * @param Request $request The current request. + * @param \Symfony\Component\HttpFoundation\Request $request The current request. * * @return \SimpleSAML\XHTML\Template */ public function diagnostics(Request $request): Template { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); $t = new Template($this->config, 'admin:diagnostics.twig'); $t->data = [ @@ -88,11 +105,13 @@ class Config /** * Display the main admin page. * + * @param \Symfony\Component\HttpFoundation\Request $request The current request. + * * @return \SimpleSAML\XHTML\Template */ - public function main(): Template + public function main(/** @scrutinizer ignore-unused */ Request $request): Template { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); $t = new Template($this->config, 'admin:config.twig'); $t->data = [ @@ -125,11 +144,13 @@ class Config /** * Display the output of phpinfo(). * - * @return RunnableResponse + * @param \Symfony\Component\HttpFoundation\Request $request The current request. + * + * @return \SimpleSAML\HTTP\RunnableResponse */ - public function phpinfo(): RunnableResponse + public function phpinfo(/** @scrutinizer ignore-unused */ Request $request): RunnableResponse { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); return new RunnableResponse('phpinfo'); } @@ -397,7 +418,6 @@ class Config curl_close($ch); } - if ($latest && version_compare($this->config->getVersion(), ltrim($latest['tag_name'], 'v'), 'lt')) { $warnings[] = [ Translate::noop( diff --git a/modules/admin/lib/Controller/Federation.php b/modules/admin/lib/Controller/Federation.php index d0e1ead67..b0cdef7bd 100644 --- a/modules/admin/lib/Controller/Federation.php +++ b/modules/admin/lib/Controller/Federation.php @@ -38,7 +38,19 @@ class Federation /** @var \SimpleSAML\Configuration */ protected $config; - /** @var MetaDataStorageHandler */ + /** + * @var \SimpleSAML\Auth\Source|string + * @psalm-var \SimpleSAML\Auth\Source|class-string + */ + protected $authSource = Auth\Source::class; + + /** + * @var \SimpleSAML\Utils\Auth|string + * @psalm-var \SimpleSAML\Utils\Auth|class-string + */ + protected $authUtils = Utils\Auth::class; + + /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */ protected $mdHandler; /** @var Menu */ @@ -58,16 +70,50 @@ class Federation } + /** + * Inject the \SimpleSAML\Auth\Source dependency. + * + * @param \SimpleSAML\Auth\Source $authSource + */ + public function setAuthSource(Auth\Source $authSource): void + { + $this->authSource = $authSource; + } + + + /** + * Inject the \SimpleSAML\Utils\Auth dependency. + * + * @param \SimpleSAML\Utils\Auth $authUtils + */ + public function setAuthUtils(Utils\Auth $authUtils): void + { + $this->authUtils = $authUtils; + } + + + /** + * Inject the \SimpleSAML\Metadata\MetadataStorageHandler dependency. + * + * @param \SimpleSAML\Metadata\MetaDataStorageHandler $mdHandler + */ + public function setMetadataStorageHandler(MetadataStorageHandler $mdHandler): void + { + $this->mdHandler = $mdHandler; + } + + /** * Display the federation page. * + * @param \Symfony\Component\HttpFoundation\Request $request * @return \SimpleSAML\XHTML\Template * @throws \SimpleSAML\Error\Exception * @throws \SimpleSAML\Error\Exception */ - public function main(): Template + public function main(/** @scrutinizer ignore-unused */ Request $request): Template { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); // initialize basic metadata array $hostedSPs = $this->getHostedSP(); @@ -289,7 +335,7 @@ class Federation $entities = []; /** @var \SimpleSAML\Module\saml\Auth\Source\SP $source */ - foreach (Auth\Source::getSourcesOfType('saml:SP') as $source) { + foreach ($this->authSource::getSourcesOfType('saml:SP') as $source) { $metadata = $source->getHostedMetadata(); if (isset($metadata['keys'])) { $certificates = $metadata['keys']; @@ -345,13 +391,13 @@ class Federation /** * Metadata converter * - * @param Request $request The current request. + * @param \Symfony\Component\HttpFoundation\Request $request The current request. * * @return \SimpleSAML\XHTML\Template */ public function metadataConverter(Request $request): Template { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); if ($xmlfile = $request->files->get('xmlfile')) { $xmldata = trim(file_get_contents($xmlfile->getPathname())); } elseif ($xmldata = $request->request->get('xmldata')) { @@ -422,16 +468,16 @@ class Federation /** * Download a certificate for a given entity. * - * @param Request $request The current request. + * @param \Symfony\Component\HttpFoundation\Request $request The current request. * - * @return Response PEM-encoded certificate. + * @return \Symfony\Component\HttpFoundation\Response PEM-encoded certificate. */ public function downloadCert(Request $request): Response { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); $set = $request->get('set'); - $prefix = $request->get('prefix'); + $prefix = $request->get('prefix', ''); if ($set === 'saml20-sp-hosted') { $sourceID = $request->get('source'); @@ -439,7 +485,7 @@ class Federation * The second argument ensures non-nullable return-value * @var \SimpleSAML\Module\saml\Auth\Source\SP $source */ - $source = \SimpleSAML\Auth\Source::getById($sourceID, Module\saml\Auth\Source\SP::class); + $source = $this->authSource::getById($sourceID, Module\saml\Auth\Source\SP::class); $mdconfig = $source->getMetadata(); } else { $entityID = $request->get('entity'); @@ -465,13 +511,13 @@ class Federation /** * Show remote entity metadata * - * @param Request $request The current request. + * @param \Symfony\Component\HttpFoundation\Request $request The current request. * - * @return Response + * @return \SimpleSAML\XHTML\Template */ - public function showRemoteEntity(Request $request): Response + public function showRemoteEntity(Request $request): Template { - Utils\Auth::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 16ac22719..5d23f41eb 100644 --- a/modules/admin/lib/Controller/Test.php +++ b/modules/admin/lib/Controller/Test.php @@ -30,6 +30,24 @@ class Test /** @var \SimpleSAML\Configuration */ protected $config; + /** + * @var \SimpleSAML\Utils\Auth|string + * @psalm-var \SimpleSAML\Utils\Auth|class-string + */ + protected $authUtils = Utils\Auth::class; + + /** + * @var \SimpleSAML\Auth\Simple|string + * @psalm-var \SimpleSAML\Auth\Simple|class-string + */ + protected $authSimple = Auth\Simple::class; + + /** + * @var \SimpleSAML\Auth\State|string + * @psalm-var \SimpleSAML\Auth\State|class-string + */ + protected $authState = Auth\State::class; + /** @var Menu */ protected $menu; @@ -51,29 +69,63 @@ class Test } + /** + * Inject the \SimpleSAML\Utils\Auth dependency. + * + * @param \SimpleSAML\Utils\Auth $authUtils + */ + public function setAuthUtils(Utils\Auth $authUtils): void + { + $this->authUtils = $authUtils; + } + + + /** + * Inject the \SimpleSAML\Auth\Simple dependency. + * + * @param \SimpleSAML\Auth\Simple $authSimple + */ + public function setAuthSimple(Auth\Simple $authSimple): void + { + $this->authSimple = $authSimple; + } + + + /** + * Inject the \SimpleSAML\Auth\State dependency. + * + * @param \SimpleSAML\Auth\State $authState + */ + public function setAuthState(Auth\State $authState): void + { + $this->authState = $authState; + } + + /** * Display the list of available authsources. * * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $as - * @return \SimpleSAML\XHTML\Template + * @return \SimpleSAML\XHTML\Template|\SimpleSAML\HTTP\RunnableResponse */ public function main(Request $request, string $as = null) { - Utils\Auth::requireAdmin(); + $this->authUtils::requireAdmin(); if (is_null($as)) { $t = new Template($this->config, 'admin:authsource_list.twig'); $t->data = [ 'sources' => Auth\Source::getSources(), ]; } else { - $authsource = new Auth\Simple($as); + $simple = $this->authSimple; + $authsource = new $simple($as); if (!is_null($request->query->get('logout'))) { - $authsource->logout($this->config->getBasePath() . 'logout.php'); + return new RunnableResponse([$authsource, 'logout'], [$this->config->getBasePath() . 'logout.php']); } elseif (!is_null($request->query->get(Auth\State::EXCEPTION_PARAM))) { // This is just a simple example of an error /** @var array $state */ - $state = Auth\State::loadExceptionState(); + $state = $this->authState::loadExceptionState(); Assert::keyExists($state, Auth\State::EXCEPTION_DATA); throw $state[Auth\State::EXCEPTION_DATA]; } @@ -84,7 +136,7 @@ class Test 'ErrorURL' => $url, 'ReturnTo' => $url, ]; - $authsource->login($params); + return new RunnableResponse([$authsource, 'login'], [$params]); } $attributes = $authsource->getAttributes(); diff --git a/phpunit.xml b/phpunit.xml index 28fd6ba44..d00aef1cc 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -17,6 +17,7 @@ <filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">./lib/</directory> + <directory suffix=".php">./modules/admin/lib/</directory> <directory suffix=".php">./modules/core/lib/</directory> <directory suffix=".php">./modules/saml/lib/</directory> <exclude> diff --git a/tests/modules/admin/lib/Controller/ConfigTest.php b/tests/modules/admin/lib/Controller/ConfigTest.php new file mode 100644 index 000000000..8685e4c9c --- /dev/null +++ b/tests/modules/admin/lib/Controller/ConfigTest.php @@ -0,0 +1,131 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Module\admin\Controller; + +use PHPUnit\Framework\TestCase; +use SimpleSAML\Configuration; +use SimpleSAML\HTTP\RunnableResponse; +use SimpleSAML\Module\admin\Controller; +use SimpleSAML\Session; +use SimpleSAML\Utils; +use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Set of tests for the controllers in the "admin" module. + * + * @package SimpleSAML\Test + */ +class ConfigTest extends TestCase +{ + /** @var \SimpleSAML\Configuration */ + protected $config; + + /** @var \SimpleSAML\Utils\Auth */ + protected $authUtils; + + /** @var \SimpleSAML\Session */ + protected $session; + + + /** + * Set up for each test. + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + + $this->config = new class ( + [ + 'module.enable' => ['admin' => true], + 'secretsalt' => 'defaultsecretsalt', + 'admin.checkforupdates' => true + ], + '[ARRAY]' + ) extends Configuration + { + public function getVersion(): string + { + return '1.14.7'; + } + }; + + // Dirty hack, but Session relies on config being actually loaded + $this->config::setPreloadedConfig( + Configuration::loadFromArray([], '[ARRAY]', 'simplesaml'), + 'config.php', + 'simplesaml' + ); + + $this->authUtils = new class () extends Utils\Auth { + public static function requireAdmin(): void + { + // stub + } + }; + + $session = $this->createMock(Session::class); + $session->method('getData')->willReturn(['tag_name' => 'v1.18.7', 'html_url' => 'https://example.org']); + + /** @var \SimpleSAML\Session $session */ + $this->session = $session; + } + + + /** + * @return void + */ + public function testDiagnostics(): void + { + $request = Request::create( + '/diagnostics', + 'GET' + ); + + $c = new Controller\Config($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $response = $c->diagnostics($request); + + $this->assertTrue($response->isSuccessful()); + } + + + /** + * @return void + */ + public function testMain(): void + { + $request = Request::create( + '/', + 'GET' + ); + + $c = new Controller\Config($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $response = $c->main($request); + + $this->assertTrue($response->isSuccessful()); + } + + + /** + * @return void + */ + public function testPhpinfo(): void + { + $request = Request::create( + '/phpinfo', + 'GET' + ); + + $c = new Controller\Config($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $response = $c->phpinfo($request); + + $this->assertTrue($response->isSuccessful()); + } +} diff --git a/tests/modules/admin/lib/Controller/FederationTest.php b/tests/modules/admin/lib/Controller/FederationTest.php new file mode 100644 index 000000000..efb007116 --- /dev/null +++ b/tests/modules/admin/lib/Controller/FederationTest.php @@ -0,0 +1,402 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Module\admin\Controller; + +use PHPUnit\Framework\TestCase; +use SimpleSAML\Auth; +use SimpleSAML\Configuration; +use SimpleSAML\Metadata\MetaDataStorageHandler; +use SimpleSAML\Module; +use SimpleSAML\Module\admin\Controller; +use SimpleSAML\Session; +use SimpleSAML\Utils; +use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * Set of tests for the controllers in the "admin" module. + * + * @package SimpleSAML\Test + */ +class FederationTest extends TestCase +{ + /** @var string */ + private const FRAMEWORK = 'vendor/simplesamlphp/simplesamlphp-test-framework'; + + /** @var string */ + public const CERT_KEY = '../' . self::FRAMEWORK . '/certificates/pem/selfsigned.example.org.key'; + + /** @var string */ + public const CERT_PUBLIC = '../' . self::FRAMEWORK . '/certificates/pem/selfsigned.example.org.crt'; + + /** @var \SimpleSAML\Configuration */ + protected $config; + + /** @var \SimpleSAML\Utils\Auth */ + protected $authUtils; + + /** @var string */ + private $metadata_xml = self::FRAMEWORK . '/metadata/xml/valid-metadata-selfsigned.xml'; + + /** @var string */ + private $broken_metadata_xml = self::FRAMEWORK . '/metadata/xml/corrupted-metadata-selfsigned.xml'; + + /** @var string */ + private $ssp_metadata = self::FRAMEWORK . '/metadata/simplesamlphp/saml20-idp-remote_cert_selfsigned.php'; + + /** + * Set up for each test. + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + + $this->config = Configuration::loadFromArray( + [ + 'module.enable' => ['adfs' => true, 'admin' => true], + 'enable.saml20-idp' => true, + 'enable.adfs-idp' => true, + 'language.default' => 'fr', + 'language.get_language_function' => [$this, 'getLanguage'] + ], + '[ARRAY]', + 'simplesaml' + ); + + $this->authUtils = new class () extends Utils\Auth { + public static function requireAdmin(): void + { + // stub + } + }; + + Configuration::setPreLoadedConfig( + Configuration::loadFromArray( + [ + 'admin' => ['core:AdminPassword'], + ], + '[ARRAY]', + 'simplesaml' + ), + 'authsources.php', + 'simplesaml' + ); + } + + + /** + * @return void + */ + public function testMain(): void + { + $request = Request::create( + '/federation', + 'GET' + ); + + $mdh = new class () extends MetaDataStorageHandler { + public function __construct() + { + } + + public function getList(string $set = 'saml20-idp-remote', bool $showExpired = false): array + { + if ($set === 'saml20-idp-hosted') { + return [ + 0 => [ + 'name' => 'SimpleSAMLphp Hosted IDP', + 'descr' => 'The local IDP', + 'OrganizationDisplayName' => ['en' => 'My IDP', 'nl' => 'Mijn IDP'] + ] + ]; + } elseif ($set === 'saml20-sp-remote') { + return [ + 0 => [ + 'name' => ['nl' => 'SimpleSAMLphp Remote SP'], + 'descr' => 'The remote SP', + 'OrganizationDisplayName' => ['en' => 'His SP', 'nl' => 'Zijn SP', 'fr' => 'Son SP'] + ], + 1 => [ + 'name' => ['fr' => 'SimpleSAMLphp Remote SP'], + 'descr' => 'The remote SP', + 'OrganizationDisplayName' => ['en' => 'Her SP'] + ] + ]; + } + return []; + } + }; + + $authSource = new class () extends Auth\Source { + public function __construct() + { + // stub + } + + public function authenticate(array &$state): void + { + // stub + } + + public static function getSourcesOfType(string $type): array + { + return [ + new \SimpleSAML\Module\saml\Auth\Source\SP( + ['AuthId' => 'AuthId'], + [ + 'saml:SP', + + 'name' => [ + 'en' => 'A service', + ], + 'entityID' => null, + 'privatekey' => FederationTest::CERT_KEY, + 'certificate' => FederationTest::CERT_PUBLIC, + 'attributes' => ['uid', 'mail'] + ] + ) + ]; + } + }; + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + $c->setAuthSource($authSource); + $c->setMetadataStorageHandler($mdh); + $response = $c->main($request); + + $this->assertTrue($response->isSuccessful()); + } + + + /** + * @return void + */ + public function testMetadataConverterFileUpload(): void + { + $request = Request::create( + '/federation/metadata-converter', + 'POST' + ); + $request->files->add( + [ + 'xmlfile' => new UploadedFile( + $this->metadata_xml, + 'valid-metadata-selfsigned.xml', + 'application/xml', + null, + true + ) + ] + ); + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + $response = $c->metadataConverter($request); + + $this->assertTrue($response->isSuccessful()); + $this->assertNull($response->data['error']); + } + + + /** + * @return void + */ + public function testMetadataConverterData(): void + { + $request = Request::create( + '/federation/metadata-converter', + 'POST', + ['xmldata' => file_get_contents($this->metadata_xml)] + ); + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + $response = $c->metadataConverter($request); + + $this->assertTrue($response->isSuccessful()); + $this->assertNull($response->data['error']); + } + + + /** + * @return void + */ + public function testMetadataConverterInvalidMetadataShowsError(): void + { + $request = Request::create( + '/federation/metadata-converter', + 'POST', + ['xmldata' => file_get_contents($this->broken_metadata_xml)] + ); + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + + $response = $c->metadataConverter($request); + + $this->assertTrue($response->isSuccessful()); + $this->assertNotNull($response->data['error']); + } + + + /** + * @return void + */ + public function testMetadataConverterEmptyInput(): void + { + $request = Request::create( + '/federation/metadata-converter', + 'POST', + ['xmldata' => ''] + ); + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + + $response = $c->metadataConverter($request); + + $this->assertTrue($response->isSuccessful()); + $this->assertEquals([], $response->data['output']); + $this->assertEquals('', $response->data['xmldata']); + } + + + /** + * @return void + */ + public function testDownloadCertSP(): void + { + $request = Request::create( + '/federation/cert', + 'GET', + [ + 'set' => 'saml20-sp-hosted', + 'source' => 'default-sp' + ] + ); + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + $authSource = new class () extends Auth\Source { + public function __construct() + { + // stub + } + + public function authenticate(array &$state): void + { + // stub + } + + public function getMetadata(): Configuration + { + return Configuration::loadFromArray( + ['certData' => 'abc123'], + '[ARRAY]', + 'simplesaml' + ); + } + + public static function getById(string $authId, ?string $type = null): ?Auth\Source + { + return new static(); + } + }; + + $c->setAuthSource($authSource); + + $response = $c->downloadCert($request); + + $this->assertTrue($response->isSuccessful()); + $this->assertNotNull($response->headers->get('Content-Disposition')); + $this->assertEquals('application/x-pem-file', $response->headers->get('Content-Type')); + } + + + /** + * @return void + */ + public function testDownloadCertFile(): void + { + $request = Request::create( + '/federation/cert', + 'GET', + [ + 'set' => 'saml20-idp-hosted', + 'entity' => 'some entity' + ] + ); + + $mdh = new class () extends MetaDataStorageHandler { + public function __construct() + { + } + + public function getMetaDataConfig(string $entityId, string $set): Configuration + { + return Configuration::loadFromArray([ + 'AssertionConsumerService' => 'https://example.org/acs/or/something', + 'certData' => 'some cert', + ]); + } + }; + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + $c->setMetaDataStorageHandler($mdh); + + $response = $c->downloadCert($request); + + $this->assertTrue($response->isSuccessful()); + $this->assertNotNull($response->headers->get('Content-Disposition')); + $this->assertEquals('application/x-pem-file', $response->headers->get('Content-Type')); + } + + + /** + * @return void + */ + public function testShowRemoteEntity(): void + { + $request = Request::create( + '/federation/show', + 'GET', + ['set' => 'saml20-sp-hosted', 'entityid' => 'some entity'] + ); + + $mdh = new class () extends MetaDataStorageHandler { + public function __construct() + { + } + + public function getMetaData(?string $entityId, string $set): array + { + return []; + } + }; + + $c = new Controller\Federation($this->config); + $c->setAuthUtils($this->authUtils); + $c->setMetaDataStorageHandler($mdh); + $response = $c->showRemoteEntity($request); + + $this->assertTrue($response->isSuccessful()); + } + + + /** + * Helper method for the main-controller + * @return string + */ + public function getLanguage(): string + { + return 'nl'; + } +} diff --git a/tests/modules/admin/lib/Controller/TestTest.php b/tests/modules/admin/lib/Controller/TestTest.php new file mode 100644 index 000000000..a74fa8319 --- /dev/null +++ b/tests/modules/admin/lib/Controller/TestTest.php @@ -0,0 +1,260 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Module\admin\Controller; + +use PHPUnit\Framework\TestCase; +use SAML2\XML\saml\NameID; +use SimpleSAML\Auth; +use SimpleSAML\Configuration; +use SimpleSAML\Error; +use SimpleSAML\HTTP\RunnableResponse; +use SimpleSAML\Module\admin\Controller; +use SimpleSAML\Session; +use SimpleSAML\Utils; +use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Set of tests for the controllers in the "admin" module. + * + * @package SimpleSAML\Test + */ +class TestTest extends TestCase +{ + /** @var \SimpleSAML\Configuration */ + protected $config; + + /** @var \SimpleSAML\Utils\Auth */ + protected $authUtils; + + /** @var \SimpleSAML\Session */ + protected $session; + + + /** + * Set up for each test. + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + + $this->config = Configuration::loadFromArray( + [ + 'module.enable' => ['admin' => true], + ], + '[ARRAY]', + 'simplesaml' + ); + + $this->authUtils = new class () extends Utils\Auth { + public static function requireAdmin(): void + { + // stub + } + }; + + $this->session = Session::getSessionFromRequest(); + + Configuration::setPreLoadedConfig( + Configuration::loadFromArray( + [ + 'admin' => ['core:AdminPassword'], + ], + '[ARRAY]', + 'simplesaml' + ), + 'authsources.php', + 'simplesaml' + ); + } + + + /** + * @return void + */ + public function testMainWithoutAuthSource(): void + { + $request = Request::create( + '/test', + 'GET' + ); + + $c = new Controller\Test($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $response = $c->main($request); + + $this->assertInstanceOf(Template::class, $response); + $this->assertTrue($response->isSuccessful()); + } + + + /** + * @return void + */ + public function testMainWithAuthSourceAndLogout(): void + { + $request = Request::create( + '/test', + 'GET', + ['logout' => 'notnull'] + ); + + $c = new Controller\Test($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $c->setAuthSimple(new class ('admin') extends Auth\Simple { + public function logout($params = null): void + { + // stub + } + }); + + $response = $c->main($request, 'admin'); + + $this->assertInstanceOf(RunnableResponse::class, $response); + $this->assertTrue($response->isSuccessful()); + } + + + /** + * @return void + */ + public function testMainWithAuthSourceAndException(): void + { + $request = Request::create( + '/test', + 'GET', + [Auth\State::EXCEPTION_PARAM => 'someException'] + ); + + $c = new Controller\Test($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $c->setAuthState(new class () extends Auth\State { + public static function loadExceptionState(?string $id = null): ?array + { + return [Auth\State::EXCEPTION_DATA => new Error\NoState()]; + } + }); + + $this->expectException(Error\NoState::class); + $this->expectExceptionMessage('NOSTATE'); + $c->main($request, 'admin'); + } + + + /** + * @return void + */ + public function testMainWithAuthSourceNotAuthenticated(): void + { + $request = Request::create( + '/test', + 'GET', + ['as' => 'admin'] + ); + + $c = new Controller\Test($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $c->setAuthSimple(new class ('admin') extends Auth\Simple { + public function isAuthenticated(): bool + { + return false; + } + + public function login(array $params = []): void + { + // stub + } + }); + + $response = $c->main($request, 'admin'); + + $this->assertInstanceOf(RunnableResponse::class, $response); + $this->assertTrue($response->isSuccessful()); + } + + + /** + * @return void + */ + public function testMainWithAuthSourceAuthenticated(): void + { + $request = Request::create( + '/test', + 'GET' + ); + + $c = new Controller\Test($this->config, $this->session); + $c->setAuthUtils($this->authUtils); + $c->setAuthSimple(new class ('admin') extends Auth\Simple { + public function isAuthenticated(): bool + { + return true; + } + + public function getAttributes(): array + { + $nameId = new NameID(); + $nameId->setValue('_b806c4f98188b42e48d3eb5444db613dbde463e2e8'); + $nameId->setSPProvidedID('some:entity'); + $nameId->setNameQualifier('some name qualifier'); + $nameId->setSPNameQualifier('some SP name qualifier'); + $nameId->setFormat('urn:oasis:names:tc:SAML:2.0:nameid-format:transient'); + + /** @psalm-suppress PossiblyNullPropertyFetch */ + return [ + 'urn:mace:dir:attribute-def:cn' => [ + 'Tim van Dijen' + ], + 'urn:mace:dir:attribute-def:givenName' => [ + 'Tim' + ], + 'urn:mace:dir:attribute-def:sn' => [ + 'van Dijen' + ], + 'urn:mace:dir:attribute-def:displayName' => [ + 'Mr. T. van Dijen BSc' + ], + 'urn:mace:dir:attribute-def:mail' => [ + 'tvdijen@hotmail.com', + 'tvdijen@gmail.com' + ], + 'urn:mace:dir:attribute-def:eduPersonTargetedID' => [ + $nameId->toXML()->ownerDocument->childNodes + ], + 'jpegPhoto' => [ + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' + ], + 'nameId' => [ + $nameId + ] + ]; + } + + public function getAuthDataArray(): ?array + { + return []; + } + + public function getAuthData(string $name) + { + $nameId = new NameID(); + $nameId->setValue('_b806c4f98188b42e48d3eb5444db613dbde463e2e8'); + $nameId->setSPProvidedID('some:entity'); + $nameId->setNameQualifier('some name qualifier'); + $nameId->setSPNameQualifier('some SP name qualifier'); + $nameId->setFormat('urn:oasis:names:tc:SAML:2.0:nameid-format:transient'); + + return $nameId; + } + }); + + $response = $c->main($request, 'admin'); + + $this->assertInstanceOf(Template::class, $response); + $this->assertTrue($response->isSuccessful()); + } +} -- GitLab