From edb1bbc59734875e33990c976917594b55fa412d Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 24 Sep 2019 14:58:53 +0200 Subject: [PATCH] Create controller, cleanup old www-files (#2) * Create controller, cleanup old www-files --- lib/AdfsController.php | 287 +++++++++++++++++++++++++++++++++++++++++ lib/IdP/ADFS.php | 1 + www/idp/metadata.php | 191 ++------------------------- www/idp/prp.php | 29 ++--- 4 files changed, 309 insertions(+), 199 deletions(-) create mode 100644 lib/AdfsController.php diff --git a/lib/AdfsController.php b/lib/AdfsController.php new file mode 100644 index 0000000..47eeb88 --- /dev/null +++ b/lib/AdfsController.php @@ -0,0 +1,287 @@ +config = $config; + $this->metadata = Metadata\MetaDataStorageHandler::getMetadataHandler(); + $this->session = $session; + } + + + /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response|\SimpleSAML\XHTML\Template + */ + public function metadata(Request $request) + { + if (!$this->config->getBoolean('enable.adfs-idp', false)) { + throw new SspError\Error('NOACCESS'); + } + + // check if valid local session exists + if ($this->config->getBoolean('admin.protectmetadata', false)) { + Utils\Auth::requireAdmin(); + } + + try { + $idpentityid = isset($_GET['idpentityid']) ? + $_GET['idpentityid'] : $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); + $idpmeta = $this->metadata->getMetaDataConfig($idpentityid, 'adfs-idp-hosted'); + + $availableCerts = []; + $keys = []; + $certInfo = Utils\Crypto::loadPublicKey($idpmeta, false, 'new_'); + + if ($certInfo !== null) { + $availableCerts['new_idp.crt'] = $certInfo; + $keys[] = [ + 'type' => 'X509Certificate', + 'signing' => true, + 'encryption' => true, + 'X509Certificate' => $certInfo['certData'], + ]; + $hasNewCert = true; + } else { + $hasNewCert = false; + } + + /** @var array $certInfo */ + $certInfo = Utils\Crypto::loadPublicKey($idpmeta, true); + $availableCerts['idp.crt'] = $certInfo; + $keys[] = [ + 'type' => 'X509Certificate', + 'signing' => true, + 'encryption' => ($hasNewCert ? false : true), + 'X509Certificate' => $certInfo['certData'], + ]; + + if ($idpmeta->hasValue('https.certificate')) { + /** @var array $httpsCert */ + $httpsCert = Utils\Crypto::loadPublicKey($idpmeta, true, 'https.'); + Assert::keyExists($httpsCert, 'certData'); + $availableCerts['https.crt'] = $httpsCert; + $keys[] = [ + 'type' => 'X509Certificate', + 'signing' => true, + 'encryption' => false, + 'X509Certificate' => $httpsCert['certData'], + ]; + } + + $adfs_service_location = Module::getModuleURL('adfs').'/idp/prp.php'; + $metaArray = [ + 'metadata-set' => 'adfs-idp-remote', + 'entityid' => $idpentityid, + 'SingleSignOnService' => [ + 0 => [ + 'Binding' => Constants::BINDING_HTTP_REDIRECT, + 'Location' => $adfs_service_location + ] + ], + 'SingleLogoutService' => [ + 0 => [ + 'Binding' => Constants::BINDING_HTTP_REDIRECT, + 'Location' => $adfs_service_location + ] + ], + ]; + + if (count($keys) === 1) { + $metaArray['certData'] = $keys[0]['X509Certificate']; + } else { + $metaArray['keys'] = $keys; + } + + $metaArray['NameIDFormat'] = $idpmeta->getString( + 'NameIDFormat', + Constants::NAMEID_TRANSIENT + ); + + if ($idpmeta->hasValue('OrganizationName')) { + $metaArray['OrganizationName'] = $idpmeta->getLocalizedString('OrganizationName'); + $metaArray['OrganizationDisplayName'] = $idpmeta->getLocalizedString( + 'OrganizationDisplayName', + $metaArray['OrganizationName'] + ); + + if (!$idpmeta->hasValue('OrganizationURL')) { + throw new SspError\Exception('If OrganizationName is set, OrganizationURL must also be set.'); + } + $metaArray['OrganizationURL'] = $idpmeta->getLocalizedString('OrganizationURL'); + } + + if ($idpmeta->hasValue('scope')) { + $metaArray['scope'] = $idpmeta->getArray('scope'); + } + + if ($idpmeta->hasValue('EntityAttributes')) { + $metaArray['EntityAttributes'] = $idpmeta->getArray('EntityAttributes'); + } + + if ($idpmeta->hasValue('UIInfo')) { + $metaArray['UIInfo'] = $idpmeta->getArray('UIInfo'); + } + + if ($idpmeta->hasValue('DiscoHints')) { + $metaArray['DiscoHints'] = $idpmeta->getArray('DiscoHints'); + } + + if ($idpmeta->hasValue('RegistrationInfo')) { + $metaArray['RegistrationInfo'] = $idpmeta->getArray('RegistrationInfo'); + } + + $metaflat = '$metadata['.var_export($idpentityid, true).'] = '.var_export($metaArray, true).';'; + + $metaBuilder = new Metadata\SAMLBuilder($idpentityid); + $metaBuilder->addSecurityTokenServiceType($metaArray); + $metaBuilder->addOrganizationInfo($metaArray); + $technicalContactEmail = $this->config->getString('technicalcontact_email', null); + if ($technicalContactEmail && $technicalContactEmail !== 'na@example.org') { + $metaBuilder->addContact('technical', Utils\Config\Metadata::getContact([ + 'emailAddress' => $technicalContactEmail, + 'name' => $this->config->getString('technicalcontact_name', null), + 'contactType' => 'technical', + ])); + } + $output_xhtml = array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml'; + $metaxml = $metaBuilder->getEntityDescriptorText($output_xhtml); + if (!$output_xhtml) { + $metaxml = str_replace("\n", '', $metaxml); + } + + // sign the metadata if enabled + $metaxml = Metadata\Signer::sign($metaxml, $idpmeta->toArray(), 'ADFS IdP'); + + if ($output_xhtml) { + $t = new Template($this->config, 'metadata.php', 'admin'); + + $t->data['clipboard.js'] = true; + $t->data['available_certs'] = $availableCerts; + $certdata = []; + foreach (array_keys($availableCerts) as $availableCert) { + $certdata[$availableCert]['name'] = $availableCert; + $certdata[$availableCert]['url'] = Module::getModuleURL('saml/idp/certs.php'). + '/'.$availableCert; + + $certdata[$availableCert]['comment'] = ''; + if ($availableCerts[$availableCert]['certFingerprint'][0] === 'afe71c28ef740bc87425be13a2263d37971da1f9') { + $certdata[$availableCert]['comment'] = 'This is the default certificate.'. + ' Generate a new certificate if this is a production system.'; + } + } + $t->data['certdata'] = $certdata; + $t->data['header'] = 'adfs-idp'; // TODO: Replace with headerString in 2.0 + $t->data['headerString'] = Translate::noop('metadata_adfs-idp'); + $t->data['metaurl'] = Utils\HTTP::getSelfURLNoQuery(); + $t->data['metadata'] = htmlspecialchars($metaxml); + $t->data['metadataflat'] = htmlspecialchars($metaflat); + + return $t; + } else { + // make sure to export only the md:EntityDescriptor + $i = strpos($metaxml, '') + $i = strrpos($metaxml, ''); + $metaxml = substr($metaxml, 0, $i ? $i + 22 : 0); + + $response = new Response(); + $response->headers->set('Content-Type', 'application/xml'); + $response->setContent($metaxml); + + return $response; + } + } catch (\Exception $exception) { + throw new SspError\Error('METADATA', $exception); + } + } + + + /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response + */ + public function prp(Request $request) + { + Logger::info('ADFS - IdP.prp: Accessing ADFS IdP endpoint prp'); + + $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); + $idp = IdP::getById('adfs:'.$idpEntityId); + + if (isset($_GET['wa'])) { + if ($_GET['wa'] === 'wsignout1.0') { + return new StreamedResponse( + /** @return void */ + function () use ($idp) { + ADFS::receiveLogoutMessage($idp); + } + ); + } elseif ($_GET['wa'] === 'wsignin1.0') { + return new StreamedResponse( + /** @return void */ + function () use ($idp) { + ADFS::receiveAuthnRequest($idp); + } + ); + } + throw new SspError\BadRequest("Unsupported value for 'wa' specified in request."); + } elseif (isset($_GET['assocId'])) { + // logout response from ADFS SP + $assocId = $_GET['assocId']; // Association ID of the SP that sent the logout response + $relayState = $_GET['relayState']; // Data that was sent in the logout request to the SP. Can be null + $logoutError = null; // null on success, or an instance of a \SimpleSAML\Error\Exception on failure. + + return new StreamedResponse( + /** @return void */ + function () use ($idp, $assocId, $relayState, $logoutError) { + $idp->handleLogoutResponse($assocId, $relayState, $logoutError); + } + ); + } + throw new SspError\BadRequest("Missing parameter 'wa' or 'assocId' in request."); + } +} diff --git a/lib/IdP/ADFS.php b/lib/IdP/ADFS.php index 072a668..2022f15 100644 --- a/lib/IdP/ADFS.php +++ b/lib/IdP/ADFS.php @@ -138,6 +138,7 @@ private static function generateResponse($issuer, $target, $nameid, $attributes, * @param string $key * @param string $cert * @param string $algo + * @param string|null $passphrase * @return string */ private static function signResponse($response, $key, $cert, $algo, $passphrase) diff --git a/www/idp/metadata.php b/www/idp/metadata.php index 463c38f..7d04309 100644 --- a/www/idp/metadata.php +++ b/www/idp/metadata.php @@ -1,186 +1,15 @@ getBoolean('enable.adfs-idp', false)) { - throw new \SimpleSAML\Error\Error('NOACCESS'); -} +$config = Configuration::getInstance(); +$session = Session::getSessionFromRequest(); +$request = Request::createFromGlobals(); -// check if valid local session exists -if ($config->getBoolean('admin.protectmetadata', false)) { - \SimpleSAML\Utils\Auth::requireAdmin(); -} - -try { - $idpentityid = isset($_GET['idpentityid']) ? - $_GET['idpentityid'] : $metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); - $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'adfs-idp-hosted'); - - $availableCerts = []; - - $keys = []; - $certInfo = \SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, false, 'new_'); - if ($certInfo !== null) { - $availableCerts['new_idp.crt'] = $certInfo; - $keys[] = [ - 'type' => 'X509Certificate', - 'signing' => true, - 'encryption' => true, - 'X509Certificate' => $certInfo['certData'], - ]; - $hasNewCert = true; - } else { - $hasNewCert = false; - } - - /** @var array $certInfo */ - $certInfo = \SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true); - $availableCerts['idp.crt'] = $certInfo; - $keys[] = [ - 'type' => 'X509Certificate', - 'signing' => true, - 'encryption' => ($hasNewCert ? false : true), - 'X509Certificate' => $certInfo['certData'], - ]; - - if ($idpmeta->hasValue('https.certificate')) { - /** @var array $httpsCert */ - $httpsCert = \SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'https.'); - Assert::keyExists($httpsCert, 'certData'); - $availableCerts['https.crt'] = $httpsCert; - $keys[] = [ - 'type' => 'X509Certificate', - 'signing' => true, - 'encryption' => false, - 'X509Certificate' => $httpsCert['certData'], - ]; - } - - $adfs_service_location = \SimpleSAML\Module::getModuleURL('adfs').'/idp/prp.php'; - $metaArray = [ - 'metadata-set' => 'adfs-idp-remote', - 'entityid' => $idpentityid, - 'SingleSignOnService' => [ - 0 => [ - 'Binding' => \SAML2\Constants::BINDING_HTTP_REDIRECT, - 'Location' => $adfs_service_location - ] - ], - 'SingleLogoutService' => [ - 0 => [ - 'Binding' => \SAML2\Constants::BINDING_HTTP_REDIRECT, - 'Location' => $adfs_service_location - ] - ], - ]; - - if (count($keys) === 1) { - $metaArray['certData'] = $keys[0]['X509Certificate']; - } else { - $metaArray['keys'] = $keys; - } - - $metaArray['NameIDFormat'] = $idpmeta->getString( - 'NameIDFormat', - 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - ); - - if ($idpmeta->hasValue('OrganizationName')) { - $metaArray['OrganizationName'] = $idpmeta->getLocalizedString('OrganizationName'); - $metaArray['OrganizationDisplayName'] = $idpmeta->getLocalizedString( - 'OrganizationDisplayName', - $metaArray['OrganizationName'] - ); - - if (!$idpmeta->hasValue('OrganizationURL')) { - throw new \SimpleSAML\Error\Exception('If OrganizationName is set, OrganizationURL must also be set.'); - } - $metaArray['OrganizationURL'] = $idpmeta->getLocalizedString('OrganizationURL'); - } - - if ($idpmeta->hasValue('scope')) { - $metaArray['scope'] = $idpmeta->getArray('scope'); - } - - if ($idpmeta->hasValue('EntityAttributes')) { - $metaArray['EntityAttributes'] = $idpmeta->getArray('EntityAttributes'); - } - - if ($idpmeta->hasValue('UIInfo')) { - $metaArray['UIInfo'] = $idpmeta->getArray('UIInfo'); - } - - if ($idpmeta->hasValue('DiscoHints')) { - $metaArray['DiscoHints'] = $idpmeta->getArray('DiscoHints'); - } - - if ($idpmeta->hasValue('RegistrationInfo')) { - $metaArray['RegistrationInfo'] = $idpmeta->getArray('RegistrationInfo'); - } - - $metaflat = '$metadata['.var_export($idpentityid, true).'] = '.var_export($metaArray, true).';'; - - $metaBuilder = new \SimpleSAML\Metadata\SAMLBuilder($idpentityid); - $metaBuilder->addSecurityTokenServiceType($metaArray); - $metaBuilder->addOrganizationInfo($metaArray); - $technicalContactEmail = $config->getString('technicalcontact_email', null); - if ($technicalContactEmail && $technicalContactEmail !== 'na@example.org') { - $metaBuilder->addContact('technical', \SimpleSAML\Utils\Config\Metadata::getContact([ - 'emailAddress' => $technicalContactEmail, - 'name' => $config->getString('technicalcontact_name', null), - 'contactType' => 'technical', - ])); - } - $output_xhtml = array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml'; - $metaxml = $metaBuilder->getEntityDescriptorText($output_xhtml); - if (!$output_xhtml) { - $metaxml = str_replace("\n", '', $metaxml); - } - - // sign the metadata if enabled - $metaxml = \SimpleSAML\Metadata\Signer::sign($metaxml, $idpmeta->toArray(), 'ADFS IdP'); - - if ($output_xhtml) { - $t = new \SimpleSAML\XHTML\Template($config, 'metadata.php', 'admin'); - - $t->data['clipboard.js'] = true; - $t->data['available_certs'] = $availableCerts; - $certdata = []; - foreach (array_keys($availableCerts) as $availableCert) { - $certdata[$availableCert]['name'] = $availableCert; - $certdata[$availableCert]['url'] = \SimpleSAML\Module::getModuleURL('saml/idp/certs.php'). - '/'.$availableCert; - - $certdata[$availableCert]['comment'] = ''; - if ($availableCerts[$availableCert]['certFingerprint'][0] === 'afe71c28ef740bc87425be13a2263d37971da1f9') { - $certdata[$availableCert]['comment'] = 'This is the default certificate.'. - ' Generate a new certificate if this is a production system.'; - } - } - $t->data['certdata'] = $certdata; - $t->data['header'] = 'adfs-idp'; // TODO: Replace with headerString in 2.0 - $t->data['headerString'] = \SimpleSAML\Locale\Translate::noop('metadata_adfs-idp'); - $t->data['metaurl'] = \SimpleSAML\Utils\HTTP::getSelfURLNoQuery(); - $t->data['metadata'] = htmlspecialchars($metaxml); - $t->data['metadataflat'] = htmlspecialchars($metaflat); - $t->show(); - } else { - header('Content-Type: application/xml'); - - // make sure to export only the md:EntityDescriptor - $i = strpos($metaxml, '') - $i = strrpos($metaxml, ''); - $metaxml = substr($metaxml, 0, $i ? $i + 22 : 0); - echo $metaxml; - - exit(0); - } -} catch (\Exception $exception) { - throw new \SimpleSAML\Error\Error('METADATA', $exception); -} +$controller = new AdfsController($config, $session); +$t = $controller->metadata($request); +$t->send(); diff --git a/www/idp/prp.php b/www/idp/prp.php index a94d328..0ea8438 100644 --- a/www/idp/prp.php +++ b/www/idp/prp.php @@ -7,23 +7,16 @@ * @package SimpleSAMLphp */ -\SimpleSAML\Logger::info('ADFS - IdP.prp: Accessing ADFS IdP endpoint prp'); +namespace SimpleSAML\Module\adfs; -$metadata = \SimpleSAML\Metadata\MetaDataStorageHandler::getMetadataHandler(); -$idpEntityId = $metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); -$idp = \SimpleSAML\IdP::getById('adfs:'.$idpEntityId); +use SimpleSAML\Configuration; +use SimpleSAML\Session; +use Symfony\Component\HttpFoundation\Request; -if (isset($_GET['wa'])) { - if ($_GET['wa'] === 'wsignout1.0') { - \SimpleSAML\Module\adfs\IdP\ADFS::receiveLogoutMessage($idp); - } elseif ($_GET['wa'] === 'wsignin1.0') { - \SimpleSAML\Module\adfs\IdP\ADFS::receiveAuthnRequest($idp); - } - throw new \Exception("Code should never be reached"); -} elseif (isset($_GET['assocId'])) { - // logout response from ADFS SP - $assocId = $_GET['assocId']; // Association ID of the SP that sent the logout response - $relayState = $_GET['relayState']; // Data that was sent in the logout request to the SP. Can be null - $logoutError = null; // null on success, or an instance of a \SimpleSAML\Error\Exception on failure. - $idp->handleLogoutResponse($assocId, $relayState, $logoutError); -} +$config = Configuration::getInstance(); +$session = Session::getSessionFromRequest(); +$request = Request::createFromGlobals(); + +$controller = new AdfsController($config, $session); +$t = $controller->prp($request); +$t->send();