diff --git a/docs/simplesamlphp-changelog.txt b/docs/simplesamlphp-changelog.txt index 81c11793ad05f3c050d71ef5e79d805f5018907a..0aa49e8f644123041a281d7b9d0323ad53a65d1d 100644 --- a/docs/simplesamlphp-changelog.txt +++ b/docs/simplesamlphp-changelog.txt @@ -1,11 +1,92 @@ -simpleSAMLphp changelog +SimpleSAMLphp changelog ======================= <!-- {{TOC}} --> -This document lists the changes between versions of simpleSAMLphp. +This document lists the changes between versions of SimpleSAMLphp. See the upgrade notes for specific information about upgrading. +## Version 1.14.0 + +Released TBD + +### Security + + * Resolved a security issue with multiple modules that were not validating the URLs they were redirecting to. + * Added a security check to disable loading external entities in XML documents. + * Enforced admin access to the metadata converter tool. + * Changed `xmlseclibs` dependency to point to `robrichards/xmlseclibs` version 1.4.1. + +### New features + + * Allow setting the location of the configuration directory with an environment variable. + * Added support for the Metadata Query Protocol by means of the new MDX metadata storage handler. + * Added support for the Sender-Vouches method. + * Added support for WantAssertionsSigned and AuthnRequestsSigned in SAML 2.0 SP metadata. + * Added support for file uploads in the metadata converter. + * Added support for the Hide From Discovery REFEDS Entity Category. + * Added the SAML NameID to the attributes status page, when available. + * Added attribute definitions for schacGender (schac), sisSchoolGrade and sisLegalGuardianFor (skolfederation.se). + * Attributes required in metadata are now taken into account when parsing. + +### Bug fixes + + * Fixed an issue with friendly names in the attributes released. + * Fixed an issue with memcache that would result in a push for every fetch, when several servers configured. + * Fixed an issue with HTML escaping in error reports. + * Fixed an issue with the 'admin.protectmetadata' option not being enforced for SP metadata. + * Fixed an issue with SAML 1.X SSO authentications that removed the NameID of the subject from available data. + * Fixed an issue with the login form that resulted in a `NOSTATE` error if the user clicked the login button twice. + * Fixed an issue with replay detection in IdP-initiated flows. + * Fixed an issue that prevented the SAML 1.X IdP to restart when the session is lost. + * Fixed an issue that prevented classes using namespaces to be loaded automatically. + * Fixed an issue that prevented certain metadata signatures to be verified (fixed upstream in `xmlseclibs`). + * Other bug fixes and numerous documentation enhancements. + +### API and user interface + + * Added a new and simple database class to serve as PDO interface for all the database needs. + * Removed the old, unused `pack` installer tool. + * Improved usability by telling users the endpoints are not to be accessed directly. + * Moved the hostname, port and protocol diagnostics tool to the admin directory. + * Several classes and functions deprecated. + * Changed the signature of several functions. + * Deleted old and deprecated code, interfaces and endpoints. + * Deleted old jQuery remnants. + * Deleted the undocumented dynamic XML metadata storage handler. + * Deleted the backwards-compatible authentication source. + +### `authcrypt` + + * Added whitehat101/apr1-md5 as a dependency for Apache htpasswd. + +### `authX509` + + * Added an authentication processing filter to warn about certificate expiration. + +### `core` + + * The PHP authentication processing filter now accepts a new option called `function` to define an anonymous function. + +### `ldap` + + * Added a new `port` configuration option. + * Better error reporting. + +### `metaedit` + + * Removed the `admins` configuration option. + +### `metarefresh` + + * Added the possibility to specify which types of entities to load. + * Added the possibility to verify metadata signatures by using the public key present in a certificate. + * Fix `certificate` precedence over `fingerprint` in the configuration options when verifying metadata signatures. + +### `smartnameattribute` + + * This module was deprecated long time ago and has now been removed. Use the `smartattributes` module instead. + ## Version 1.13.2 Released 2014-11-04 @@ -846,7 +927,7 @@ Released 2010-01-08. * Fix security vulnerability due to insecure temp file creation: * statistics: The logcleaner script outputs to a file in /tmp. - * InfoCard: Saves state directly in /tmp. Changed to the simpleSAMLphp temp directory. + * InfoCard: Saves state directly in /tmp. Changed to the SimpleSAMLphp temp directory. * openidProvider: Default configuration saves state information in /tmp. Changed to '/var/lib/simplesamlphp-openid-provider'. * SAML 1 artifact support: Saves certificates temporarily in '/tmp/simplesaml', but directory creation was insecure. @@ -872,7 +953,7 @@ Released 2009-11-05. Revision 1937. * Make use of the portal module on the frontpage. * SQL datastore. * Support for setting timezone in config (instead of php.ini). - * Logging of PHP errors and notices to simpleSAMLphp log file. + * Logging of PHP errors and notices to SimpleSAMLphp log file. * Improve handling of unhandled errors and exceptions. * Admin authentication through authentication sources. * Various bugfixes & cleanups. @@ -1002,12 +1083,12 @@ Updates to `config.php`. Please check for updates in your local modified configu * AttributeMap * Smartname. does it best to guess the full name of the user based on several attributes. * Language adaptor: allow adopting UI by preferredLanguage SAML 2.0 Attribute both on the IdP and the SP. And if the user selects a lanauge, this can be sent to the SP as an attribute. - * New module: portal, allows you to created tabbed interface for custom pages within simpleSAMLphp. In example user consent management and attribute viewer. + * New module: portal, allows you to created tabbed interface for custom pages within SimpleSAMLphp. In example user consent management and attribute viewer. * New module: ldapstatus. Used by Feide to monitor connections to a large list of LDAP connections. Contact Feide on details on how to use. * ldapstatus also got certificate check capabilities. * New module: MemcacheMonitor: Show statistics for memcache servers. * New module: DiscoPower. A tabbed discovery service module with alot of functionality. - * New module: SAML 2.0 Debugginer. An improved version of the one found on rnd.feide.no earlier is not included in simpleSAMLphp allowing you to run it locally. + * New module: SAML 2.0 Debugginer. An improved version of the one found on rnd.feide.no earlier is not included in SimpleSAMLphp allowing you to run it locally. * New module: Simple Consent Amdin module that have one button to remove all consent for one user. * New module: Consent Administration. Contribution from Wayf. * We also have a consent adminstration module that we use in Feide that is not checked in to subversion. @@ -1030,7 +1111,7 @@ Updates to `config.php`. Please check for updates in your local modified configu * More localized UI. * New login as administrator link on frontpage. * Tabbed frontpage. Restructured. - * Simplifications to the theming and updated documentation on theming simpleSAMLphp. + * Simplifications to the theming and updated documentation on theming SimpleSAMLphp. * Attribute presentation hook allows you to tweak attributes before presentation in the attribute viewers. Used by Feide to group orgUnit information in a hieararchy. * Verification of the Receipient attribute in the response. Will improve security if for some reason an IdP is not includeding sufficient Audience restrictions. * Added hook to let modules tell about themself moduleinfo hook. @@ -1174,7 +1255,7 @@ New localizations in version 1.1: Sami, Svenska (swedish), Suomeksi (finnish), N * Add support for external IdP discovery services. * Support password encrypted private keys. * Added PHP autoloading as the preferred way of loading the - simpleSAMLphp library. + SimpleSAMLphp library. * New error report script which will report errors to the `technicalcontact_email` address. * Support lookup of the DN of the user who is logging in by searching diff --git a/docs/simplesamlphp-upgrade-notes-1.14.txt b/docs/simplesamlphp-upgrade-notes-1.14.txt new file mode 100644 index 0000000000000000000000000000000000000000..aa6e90fe648e0d2d2e63b8aa64d5eb380123977d --- /dev/null +++ b/docs/simplesamlphp-upgrade-notes-1.14.txt @@ -0,0 +1,176 @@ +Upgrade notes for simpleSAMLphp 1.14 +==================================== + +The `mcrypt` extension is no longer required by SimpleSAMLphp, so if no signatures or encryption are being used, it +can be skipped. It is still a requirement for `xmlseclibs` though, so for those verifying or creating signed +documents, or using encryption, is is still needed. + +PHP session cookies are now set to HTTP-only by default. This relates to the `session.phpsession.httponly` +configuration option. + +The following deprecated files, directories and endpoints have been removed: + + * `bin/pack.php` + * `docs/pack.txt` + * `docs/simplesamlphp-features.txt` + * `docs/simplesamlphp-reference-sp-hosted.txt` + * `docs/simplesamlphp-subversion.txt` + * `lib/SimpleSAML/Auth/BWC.php` (`SimpleSAML_Auth_BWC`) + * `lib/SimpleSAML/MemcacheStore.php` (`SimpleSAML_MemcacheStore`) + * `lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php` (`SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML`) + * `modules/aselect/www/linkback.php` + * `modules/core/lib/ModuleDefinition.php` (`sspmod_core_ModuleDefinition`) + * `modules/core/lib/ModuleInstaller.php` (`sspmod_core_ModuleInstaller`) + * `modules/core/www/bwc_resumeauth.php` + * `modules/core/www/idp/resumeauth.php` + * `modules/oauth/lib/OauthSignatureMethodRSASHA1.php` (`sspmod_oauth_OauthSignatureMethodRSASHA1`) + * `modules/oauth/www/accessToken.php` + * `modules/oauth/www/authorize.php` + * `modules/oauth/www/requestToken.php` + * `modules/smartnameattribute/` + * `www/resources/jquery.js` + * `www/resources/jquery-ui.js` + * `www/resources/uitheme/` + * `www/shib13/sp/` + * `www/saml2/idp/idpInitSingleLogoutServiceiFrame.php` + * `www/saml2/idp/SingleLogoutServiceiFrame.php` + * `www/saml2/idp/SingleLogoutServiceiFrameResponse.php` + * `www/saml2/sp/` + * `www/wsfed/` + * `www/example-simple/` + * `www/auth/` + +The following deprecated methods and constants have been removed: + + * `SimpleSAML_AuthMemCookie::getLoginMethod()` + * `SimpleSAML_Session::DATA_TIMEOUT_LOGOUT` + * `SimpleSAML_Session::expireDataLogout()` + * `SimpleSAML_Session::get_sp_list()` + * `SimpleSAML_Session::getAttribute()` + * `SimpleSAML_Session::getAttributes()` + * `SimpleSAML_Session::getAuthnInstant()` + * `SimpleSAML_Session::getAuthnRequest()` + * `SimpleSAML_Session::getAuthority()` + * `SimpleSAML_Session::getIdP()` + * `SimpleSAML_Session::getInstance()` + * `SimpleSAML_Session::getLogoutState()` + * `SimpleSAML_Session::getNameID()` + * `SimpleSAML_Session::getSessionIndex()` + * `SimpleSAML_Session::getSize()` + * `SimpleSAML_Session::isAuthenticated()` + * `SimpleSAML_Session::remainingTime()` + * `SimpleSAML_Session::setAttribute()` + * `SimpleSAML_Session::setAttributes()` + * `SimpleSAML_Session::setAuthnRequest()` + * `SimpleSAML_Session::setIdP()` + * `SimpleSAML_Session::setLogoutState()` + * `SimpleSAML_Session::setNameID()` + * `SimpleSAML_Session::setSessionDuration()` + * `SimpleSAML_Session::setSessionIndex()` + * `SimpleSAML_Utilities::generateRandomBytesMTrand()` + +The following methods have changed their signature. Refer to the code for the updated signatures: + + * `SimpleSAML_Auth_Default::initLogout()` + * `SimpleSAML_Auth_Default::initLogoutReturn()` + * `SimpleSAML_Metadata_MetaDataStorageHandler::getGenerated()` + * `SimpleSAML_Metadata_MetaDataStorageHandler::getMetaData()` + * `SimpleSAML_Metadata_MetaDataStorageHandler::getMetaDataCurrent()` + * `SimpleSAML_Metadata_MetaDataStorageHandler::getMetaDataCurrentEntityID()` + * `SimpleSAML_Session::doLogout()` + * `SimpleSAML_Session::getAuthState()` + * `SimpleSAML_Session::registerLogoutHandler()` + * `SimpleSAML_Utilities::generateRandomBytes()` + * `SimpleSAML_XML_Shib13_AuthnRequest::createRedirect()` + +The following methods and classes have been deprecated. Refer to the code for alternatives: + + * `SimpleSAML_Auth_Default` + * `SimpleSAML_Auth_Default::extractPersistentAuthState()` + * `SimpleSAML_Auth_Default::handleUnsolicitedAuth()` + * `SimpleSAML_Auth_Default::initLogin()` + * `SimpleSAML_Auth_Default::loginCompleted()` + * `SimpleSAML_Utilities` + * `SimpleSAML_Utilities::addURLParameter()` + * `SimpleSAML_Utilities::aesDecrypt()` + * `SimpleSAML_Utilities::aesEncrypt()` + * `SimpleSAML_Utilities::arrayize()` + * `SimpleSAML_Utilities::checkCookie()` + * `SimpleSAML_Utilities::checkDateConditions()` + * `SimpleSAML_Utilities::checkURLAllowed()` + * `SimpleSAML_Utilities::createHttpPostRedirectLink()` + * `SimpleSAML_Utilities::createPostRedirectLink()` + * `SimpleSAML_Utilities::debugMessage()` + * `SimpleSAML_Utilities::doRedirect()` + * `SimpleSAML_Utilities::fatalError()` + * `SimpleSAML_Utilities::fetch()` + * `SimpleSAML_Utilities::formatDOMElement()` + * `SimpleSAML_Utilities::formatXMLString()` + * `SimpleSAML_Utilities::generateID()` + * `SimpleSAML_Utilities::generateRandomBytes()` + * `SimpleSAML_Utilities::generateTimestamp()` + * `SimpleSAML_Utilities::getAcceptLanguage()` + * `SimpleSAML_Utilities::getAdminLogoutURL()` + * `SimpleSAML_Utilities::getBaseURL()` + * `SimpleSAML_Utilities::getDefaultEndpoint()` + * `SimpleSAML_Utilities::getDOMChildren()` + * `SimpleSAML_Utilities::getDOMText()` + * `SimpleSAML_Utilities::getFirstPathElement()` + * `SimpleSAML_Utilities::getLastError()` + * `SimpleSAML_Utilities::getSecretSalt()` + * `SimpleSAML_Utilities::getSelfHost()` + * `SimpleSAML_Utilities::getSelfHostWithPath()` + * `SimpleSAML_Utilities::getTempDir()` + * `SimpleSAML_Utilities::initTimezone()` + * `SimpleSAML_Utilities::ipCIDRcheck()` + * `SimpleSAML_Utilities::isAdmin()` + * `SimpleSAML_Utilities::isDOMElementOfType()` + * `SimpleSAML_Utilities::isHTTPS()` + * `SimpleSAML_Utilities::isWindowsOS()` + * `SimpleSAML_Utilities::loadPrivateKey()` + * `SimpleSAML_Utilities::loadPublicKey()` + * `SimpleSAML_Utilities::maskErrors()` + * `SimpleSAML_Utilities::normalizeURL()` + * `SimpleSAML_Utilities::parseAttributes()` + * `SimpleSAML_Utilities::parseDuration()` + * `SimpleSAML_Utilities::parseQueryString()` + * `SimpleSAML_Utilities::parseStateID()` + * `SimpleSAML_Utilities::popErrorMask()` + * `SimpleSAML_Utilities::postRedirect()` + * `SimpleSAML_Utilities::redirect()` + * `SimpleSAML_Utilities::redirectTrustedURL()` + * `SimpleSAML_Utilities::redirectUntrustedURL()` + * `SimpleSAML_Utilities::requireAdmin()` + * `SimpleSAML_Utilities::resolveCert()` + * `SimpleSAML_Utilities::resolvePath()` + * `SimpleSAML_Utilities::resolveURL()` + * `SimpleSAML_Utilities::selfURL()` + * `SimpleSAML_Utilities::selfURLHost()` + * `SimpleSAML_Utilities::selfURLNoQuery()` + * `SimpleSAML_Utilities::setCookie()` + * `SimpleSAML_Utilities::stringToHex()` + * `SimpleSAML_Utilities::transposeArray()` + * `SimpleSAML_Utilities::validateCA()` + * `SimpleSAML_Utilities::validateXML()` + * `SimpleSAML_Utilities::validateXMLDocument()` + * `SimpleSAML_Utilities::writeFile()` + +The following modules will no longer be shipped with the next version of SimpleSAMLphp: + + * `aggregator` + * `aggregator2` + * `aselect` + * `autotest` + * `casserver` + * `consentSimpleAdmin` + * `discojuice` + * `InfoCard` + * `logpeek` + * `metaedit` + * `modinfo` + * `papi` + * `oauth` + * `openid` + * `openidProvider` + * `saml2debug` + * `themefeidernd` diff --git a/lib/SimpleSAML/Auth/Default.php b/lib/SimpleSAML/Auth/Default.php index 049855432538f4b7d9d2cd231e9f5c6106ed03cf..5f2a6fe4b1a3f2386c06c5b10b1eb043d4dc16a7 100644 --- a/lib/SimpleSAML/Auth/Default.php +++ b/lib/SimpleSAML/Auth/Default.php @@ -8,110 +8,36 @@ * * @author Olav Morken, UNINETT AS. * @package simpleSAMLphp + * + * @deprecated This class will be removed in SSP 2.0. */ class SimpleSAML_Auth_Default { /** - * Start authentication. - * - * This function never returns. - * - * @param string $authId The identifier of the authentication source. - * @param string|array $return The URL or function we should direct the - * user to after authentication. If using a URL obtained from user input, - * please make sure to check it by calling - * \SimpleSAML\Utils\HTTP::checkURLAllowed(). - * @param string|NULL $errorURL The URL we should direct the user to after - * failed authentication. Can be NULL, in which case a standard error page - * will be shown. If using a URL obtained from user input, please make sure - * to check it by calling \SimpleSAML\Utils\HTTP::checkURLAllowed(). - * @param array $params Extra information about the login. Different - * authentication requestors may provide different information. Optional, - * will default to an empty array. + * @deprecated This method will be removed in SSP 2.0. */ public static function initLogin($authId, $return, $errorURL = NULL, array $params = array()) { - assert('is_string($authId)'); - assert('is_string($return) || is_array($return)'); - assert('is_string($errorURL) || is_null($errorURL)'); - - $state = array_merge($params, array( - 'SimpleSAML_Auth_Default.id' => $authId, - 'SimpleSAML_Auth_Default.Return' => $return, - 'SimpleSAML_Auth_Default.ErrorURL' => $errorURL, - 'LoginCompletedHandler' => array(get_class(), 'loginCompleted'), - 'LogoutCallback' => array(get_class(), 'logoutCallback'), - 'LogoutCallbackState' => array( - 'SimpleSAML_Auth_Default.logoutSource' => $authId, - ), - )); - - if (is_string($return)) { - $state['SimpleSAML_Auth_Default.ReturnURL'] = $return; - } - - if ($errorURL !== NULL) { - $state[SimpleSAML_Auth_State::EXCEPTION_HANDLER_URL] = $errorURL; - } - $as = SimpleSAML_Auth_Source::getById($authId); - if ($as === NULL) { - throw new Exception('Invalid authentication source: ' . $authId); - } - - try { - $as->authenticate($state); - } catch (SimpleSAML_Error_Exception $e) { - SimpleSAML_Auth_State::throwException($state, $e); - } catch (Exception $e) { - $e = new SimpleSAML_Error_UnserializableException($e); - SimpleSAML_Auth_State::throwException($state, $e); - } - self::loginCompleted($state); + $as->initLogin($return, $errorURL, $params); } /** - * Extract the persistent authentication state from the state array. - * - * @param array $state The state after the login. - * @return array The persistent authentication state. + * @deprecated This method will be removed in SSP 2.0. Please use + * SimpleSAML_Auth_State::extractPersistentAuthState() instead. */ public static function extractPersistentAuthState(array &$state) { - /* Save persistent authentication data. */ - $persistentAuthState = array(); - - if (isset($state['IdP'])) { - /* For backwards compatibility. */ - $persistentAuthState['saml:sp:IdP'] = $state['IdP']; - } - - if (isset($state['PersistentAuthData'])) { - foreach ($state['PersistentAuthData'] as $key) { - if (isset($state[$key])) { - $persistentAuthState[$key] = $state[$key]; - } - } - } - - /* Add those that should always be included. */ - foreach (array('Attributes', 'Expire', 'LogoutState', 'AuthnInstant', 'RememberMe', 'saml:sp:NameID') as $a) { - if (isset($state[$a])) { - $persistentAuthState[$a] = $state[$a]; - } - } - - return $persistentAuthState; + $state = SimpleSAML_Auth_State::extractPersistentAuthState($state); + return $state; } /** - * Called when a login operation has finished. - * - * @param array $state The state after the login. + * @deprecated This method will be removed in SSP 2.0. */ public static function loginCompleted($state) { assert('is_array($state)'); @@ -124,7 +50,9 @@ class SimpleSAML_Auth_Default { /* Save session state. */ $session = SimpleSAML_Session::getSessionFromRequest(); - $session->doLogin($state['SimpleSAML_Auth_Default.id'], self::extractPersistentAuthState($state)); + $authId = $state['SimpleSAML_Auth_Default.id']; + $state = SimpleSAML_Auth_State::extractPersistentAuthState($state); + $session->doLogin($authId, $state); if (is_string($return)) { /* Redirect... */ @@ -242,30 +170,11 @@ class SimpleSAML_Auth_Default { /** - * Handle a unsolicited login operations. - * - * This function creates a session from the received information. It - * will then redirect to the given URL. - * - * This is used to handle IdP initiated SSO. - * - * @param string $authId The id of the authentication source that received - * the request. - * @param array $state A state array. - * @param string $redirectTo The URL we should redirect the user to after - * updating the session. The function will check if the URL is allowed, so - * there is no need to manually check the URL on beforehand. Please refer - * to the 'trusted.url.domains' configuration directive for more - * information about allowing (or disallowing) URLs. + * @deprecated This method will be removed in SSP 2.0. Please use + * sspmod_saml_Auth_Source_SP::handleUnsolicitedAuth() instead. */ public static function handleUnsolicitedAuth($authId, array $state, $redirectTo) { - assert('is_string($authId)'); - assert('is_string($redirectTo)'); - - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->doLogin($authId, self::extractPersistentAuthState($state)); - - \SimpleSAML\Utils\HTTP::redirectUntrustedURL($redirectTo); + sspmod_saml_Auth_Source_SP::handleUnsolicitedAuth($authId, $state, $redirectTo); } } diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php index f9d543905001a6bd02fe1c0fc4efabbf01dd53a2..933b2eac7cf101b14014707634dcad6cbf641d70 100644 --- a/lib/SimpleSAML/Auth/LDAP.php +++ b/lib/SimpleSAML/Auth/LDAP.php @@ -150,7 +150,7 @@ class SimpleSAML_Auth_LDAP { }else{ if ($errNo !== 0) { $description .= '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')'; - if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) and !empty($extendedError)) { + if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) && !empty($extendedError)) { $description .= '; additional: \'' . $extendedError . '\''; } } diff --git a/lib/SimpleSAML/Auth/Simple.php b/lib/SimpleSAML/Auth/Simple.php index a82419f2ffbed1015c19b9026ad06b8e5823e09e..e1ece69972f76b46363ca5b36289df6dc71ae6ba 100644 --- a/lib/SimpleSAML/Auth/Simple.php +++ b/lib/SimpleSAML/Auth/Simple.php @@ -133,7 +133,8 @@ class SimpleSAML_Auth_Simple { $params[SimpleSAML_Auth_State::RESTART] = $restartURL; } - SimpleSAML_Auth_Default::initLogin($this->authSource, $returnTo, $errorURL, $params); + $as = $this->getAuthSource(); + $as->initLogin($returnTo, $errorURL, $params); assert('FALSE'); } diff --git a/lib/SimpleSAML/Auth/Source.php b/lib/SimpleSAML/Auth/Source.php index 4f071fa71e0a4a5444218ef3a437736f637017ab..b47f53aff44586c2d93d8aedf01f6f3f7ec1a7db 100644 --- a/lib/SimpleSAML/Auth/Source.php +++ b/lib/SimpleSAML/Auth/Source.php @@ -147,6 +147,88 @@ abstract class SimpleSAML_Auth_Source } + /** + * Start authentication. + * + * This method never returns. + * + * @param string|array $return The URL or function we should direct the user to after authentication. If using a + * URL obtained from user input, please make sure to check it by calling \SimpleSAML\Utils\HTTP::checkURLAllowed(). + * @param string|null $errorURL The URL we should direct the user to after failed authentication. Can be null, in + * which case a standard error page will be shown. If using a URL obtained from user input, please make sure to + * check it by calling \SimpleSAML\Utils\HTTP::checkURLAllowed(). + * @param array $params Extra information about the login. Different authentication requestors may provide different + * information. Optional, will default to an empty array. + */ + public function initLogin($return, $errorURL = null, array $params = array()) + { + assert('is_string($authId)'); + assert('is_string($return) || is_array($return)'); + assert('is_string($errorURL) || is_null($errorURL)'); + + $state = array_merge($params, array( + 'SimpleSAML_Auth_Default.id' => $this->authId, + 'SimpleSAML_Auth_Default.Return' => $return, + 'SimpleSAML_Auth_Default.ErrorURL' => $errorURL, + 'LoginCompletedHandler' => array(get_class(), 'loginCompleted'), + 'LogoutCallback' => array(get_class(), 'logoutCallback'), + 'LogoutCallbackState' => array( + 'SimpleSAML_Auth_Default.logoutSource' => $this->authId, + ), + )); + + if (is_string($return)) { + $state['SimpleSAML_Auth_Default.ReturnURL'] = $return; + } + + if ($errorURL !== null) { + $state[SimpleSAML_Auth_State::EXCEPTION_HANDLER_URL] = $errorURL; + } + + try { + $this->authenticate($state); + } catch (SimpleSAML_Error_Exception $e) { + SimpleSAML_Auth_State::throwException($state, $e); + } catch (Exception $e) { + $e = new SimpleSAML_Error_UnserializableException($e); + SimpleSAML_Auth_State::throwException($state, $e); + } + self::loginCompleted($state); + } + + + /** + * Called when a login operation has finished. + * + * This method never returns. + * + * @param array $state The state after the login has completed. + */ + protected static function loginCompleted($state) + { + assert('is_array($state)'); + assert('array_key_exists("SimpleSAML_Auth_Default.Return", $state)'); + assert('array_key_exists("SimpleSAML_Auth_Default.id", $state)'); + assert('array_key_exists("Attributes", $state)'); + assert('!array_key_exists("LogoutState", $state) || is_array($state["LogoutState"])'); + + $return = $state['SimpleSAML_Auth_Default.Return']; + + // save session state + $session = SimpleSAML_Session::getSessionFromRequest(); + $authId = $state['SimpleSAML_Auth_Default.id']; + $state = SimpleSAML_Auth_State::extractPersistentAuthState($state); + $session->doLogin($authId, $state); + + if (is_string($return)) { // redirect... + \SimpleSAML\Utils\HTTP::redirectTrustedURL($return); + } else { + call_user_func($return, $state); + } + assert('false'); + } + + /** * Log out from this authentication source. * diff --git a/lib/SimpleSAML/Auth/State.php b/lib/SimpleSAML/Auth/State.php index 4f5e263be43e255900a6f1d7f87970435e3a58cf..5d6ebc6148e7d094c1332a54a149522217786962 100644 --- a/lib/SimpleSAML/Auth/State.php +++ b/lib/SimpleSAML/Auth/State.php @@ -91,6 +91,44 @@ class SimpleSAML_Auth_State { private static $stateTimeout = NULL; + /** + * Extract the persistent authentication state from the state array. + * + * @param array $state The state array to analyze. + * @return array The persistent authentication state. + */ + public static function extractPersistentAuthState(array $state) + { + // save persistent authentication data + $persistent = array(); + + if (array_key_exists('PersistentAuthData', $state)) { + foreach ($state['PersistentAuthData'] as $key) { + if (isset($state[$key])) { + $persistent[$key] = $state[$key]; + } + } + } + + // add those that should always be included + $mandatory = array( + 'Attributes', + 'Expire', + 'LogoutState', + 'AuthInstant', + 'RememberMe', + 'saml:sp:NameID' + ); + foreach ($mandatory as $key) { + if (isset($state[$key])) { + $persistent[$key] = $state[$key]; + } + } + + return $persistent; + } + + /** * Retrieve the ID of a state array. * diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index 0313330efae0c8beaa771ebe56a50e53c60fa64a..c7eb315b1212729082dab7ab2269fe51284379af 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -6,6 +6,8 @@ * * @author Andreas Ã…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no> * @package simpleSAMLphp + * + * @deprecated This entire class will be removed in SimpleSAMLphp 2.0. */ class SimpleSAML_Utilities { diff --git a/modules/oauth/www/registry.edit.php b/modules/oauth/www/registry.edit.php index 555e77b678e43a55ba6e96765f27bf68de372158..6efd59beb6937e16fd1611bb88f7665967068111 100644 --- a/modules/oauth/www/registry.edit.php +++ b/modules/oauth/www/registry.edit.php @@ -17,7 +17,8 @@ if ($session->isValid($authsource)) { throw new Exception('User ID is missing'); $userid = $attributes[$useridattr][0]; } else { - SimpleSAML_Auth_Default::initLogin($authsource, \SimpleSAML\Utils\HTTP::getSelfURL()); + $as = SimpleSAML_Auth_Source::getById($authsource); + $as->initLogin(\SimpleSAML\Utils\HTTP::getSelfURL()); } function requireOwnership($entry, $userid) { diff --git a/modules/oauth/www/registry.php b/modules/oauth/www/registry.php index bd36ac448ee17454e8c68f94dfd1b86f3f1c59b6..d611c3e052bd7ff4ab44c59352fddea827dac08d 100644 --- a/modules/oauth/www/registry.php +++ b/modules/oauth/www/registry.php @@ -17,7 +17,8 @@ if ($session->isValid($authsource)) { throw new Exception('User ID is missing'); $userid = $attributes[$useridattr][0]; } else { - SimpleSAML_Auth_Default::initLogin($authsource, \SimpleSAML\Utils\HTTP::getSelfURL()); + $as = SimpleSAML_Auth_Source::getById($authsource); + $as->initLogin(\SimpleSAML\Utils\HTTP::getSelfURL()); } function requireOwnership($entry, $userid) { diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php index e5140681ef8b4bf475e17aa2c1364abb6d4025bc..cb926f6281e960cb1290e2611fea25171e2a3f7b 100644 --- a/modules/saml/lib/Auth/Source/SP.php +++ b/modules/saml/lib/Auth/Source/SP.php @@ -439,7 +439,9 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { // Update session state $session = SimpleSAML_Session::getSessionFromRequest(); - $session->doLogin($state['saml:sp:AuthId'], SimpleSAML_Auth_Default::extractPersistentAuthState($state)); + $authId = $state['saml:sp:AuthId']; + $state = SimpleSAML_Auth_State::extractPersistentAuthState($state); + $session->doLogin($authId, $state); // resume the login process call_user_func($state['ReturnCallback'], $state); @@ -577,6 +579,29 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { } + /** + * Handle an unsolicited login operations. + * + * This method creates a session from the information received. It will then redirect to the given URL. This is used + * to handle IdP initiated SSO. This method will never return. + * + * @param string $authId The id of the authentication source that received the request. + * @param array $state A state array. + * @param string $redirectTo The URL we should redirect the user to after updating the session. The function will + * check if the URL is allowed, so there is no need to manually check the URL on beforehand. Please refer to the + * 'trusted.url.domains' configuration directive for more information about allowing (or disallowing) URLs. + */ + public static function handleUnsolicitedAuth($authId, array $state, $redirectTo) { + assert('is_string($authId)'); + assert('is_string($redirectTo)'); + + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->doLogin($authId, SimpleSAML_Auth_State::extractPersistentAuthState($state)); + + \SimpleSAML\Utils\HTTP::redirectUntrustedURL($redirectTo); + } + + /** * Called when we have completed the procssing chain. * @@ -607,7 +632,7 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { } else { $redirectTo = $source->getMetadata()->getString('RelayState', '/'); } - SimpleSAML_Auth_Default::handleUnsolicitedAuth($sourceId, $state, $redirectTo); + self::handleUnsolicitedAuth($sourceId, $state, $redirectTo); } SimpleSAML_Auth_Source::completeAuth($state); diff --git a/tests/lib/SimpleSAML/Auth/StateTest.php b/tests/lib/SimpleSAML/Auth/StateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..741acdaaba5922b1cbd70ae229a9fe6b5825cc07 --- /dev/null +++ b/tests/lib/SimpleSAML/Auth/StateTest.php @@ -0,0 +1,81 @@ +<?php + + +/** + * Tests for SimpleSAML_Auth_State + */ +class Auth_StateTest extends PHPUnit_Framework_TestCase +{ + + + /** + * Test the extractPersistentAuthState() function. + */ + public function testExtractPersistentAuthState() + { + + $mandatory = array( + 'Attributes' => array(), + 'Expire' => 1234, + 'LogoutState' => 'logoutState', + 'AuthInstant' => 123456, + 'RememberMe' => true, + 'saml:sp:NameID' => 'nameID', + ); + + // check just mandatory parameters + $state = $mandatory; + $expected = $mandatory; + $this->assertEquals( + $expected, + SimpleSAML_Auth_State::extractPersistentAuthState($state), + 'Mandatory state attributes did not survive as expected'.print_r($expected, true) + ); + + // check missing mandatory parameters + unset($state['LogoutState']); + unset($state['RememberMe']); + $expected = $state; + $this->assertEquals( + $expected, + SimpleSAML_Auth_State::extractPersistentAuthState($state), + 'Some error occurred with missing mandatory parameters' + ); + + // check additional non-persistent parameters + $additional = array( + 'additional1' => 1, + 'additional2' => 2, + ); + $state = array_merge($mandatory, $additional); + $expected = $mandatory; + $this->assertEquals( + $expected, + SimpleSAML_Auth_State::extractPersistentAuthState($state), + 'Additional parameters survived' + ); + + // check additional persistent parameters + $additional['PersistentAuthData'] = array('additional1'); + $state = array_merge($mandatory, $additional); + $expected = $state; + unset($expected['additional2']); + unset($expected['PersistentAuthData']); + $this->assertEquals( + $expected, + SimpleSAML_Auth_State::extractPersistentAuthState($state), + 'Some error occurred with additional, persistent parameters' + ); + + // check only additional persistent parameters + $state = $additional; + $expected = $state; + unset($expected['additional2']); + unset($expected['PersistentAuthData']); + $this->assertEquals( + $expected, + SimpleSAML_Auth_State::extractPersistentAuthState($state), + 'Some error occurred with additional, persistent parameters, and no mandatory ones' + ); + } +} diff --git a/tests/modules/core/lib/Auth/Process/AttributeAddTest.php b/tests/modules/core/lib/Auth/Process/AttributeAddTest.php index 20748fafe0116b0e5c047a28aae45978d31241eb..3db1f388d3fac58df6c3165c538b5724de355f5a 100644 --- a/tests/modules/core/lib/Auth/Process/AttributeAddTest.php +++ b/tests/modules/core/lib/Auth/Process/AttributeAddTest.php @@ -6,7 +6,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase { - /* + /** * Helper function to run the filter with a given configuration. * * @param array $config The filter configuration. @@ -20,7 +20,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase return $request; } - /* + /** * Test the most basic functionality. */ public function testBasic() @@ -37,7 +37,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase $this->assertEquals($attributes['test'], array('value1', 'value2')); } - /* + /** * Test that existing attributes are left unmodified. */ public function testExistingNotModified() @@ -61,7 +61,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase $this->assertEquals($attributes['original2'], array('original_value2')); } - /* + /** * Test single string as attribute value. */ public function testStringValue() @@ -78,8 +78,8 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase $this->assertEquals($attributes['test'], array('value')); } - /* - * Test the most basic functionality. + /** + * Test adding multiple attributes in one config. */ public function testAddMultiple() { @@ -98,7 +98,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase $this->assertEquals($attributes['test2'], array('value2')); } - /* + /** * Test behavior when appending attribute values. */ public function testAppend() @@ -116,7 +116,7 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase $this->assertEquals($attributes['test'], array('value1', 'value2')); } - /* + /** * Test replacing attribute values. */ public function testReplace() @@ -135,4 +135,60 @@ class Test_Core_Auth_Process_AttributeAdd extends PHPUnit_Framework_TestCase $this->assertEquals($attributes['test'], array('value2')); } + /** + * Test wrong usage generates exceptions + * + * @expectedException Exception + */ + public function testWrongFlag() + { + $config = array( + '%nonsense', + 'test' => array('value2'), + ); + $request = array( + 'Attributes' => array( + 'test' => array('value1'), + ), + ); + $result = self::processFilter($config, $request); + } + + /** + * Test wrong attribute name + * + * @expectedException Exception + */ + public function testWrongAttributeName() + { + $config = array( + '%replace', + true => array('value2'), + ); + $request = array( + 'Attributes' => array( + 'test' => array('value1'), + ), + ); + $result = self::processFilter($config, $request); + } + + /** + * Test wrong attribute value + * + * @expectedException Exception + */ + public function testWrongAttributeValue() + { + $config = array( + '%replace', + 'test' => array(true), + ); + $request = array( + 'Attributes' => array( + 'test' => array('value1'), + ), + ); + $result = self::processFilter($config, $request); + } } diff --git a/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php b/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..29e696400417a31d7e0b4631e3fb946c9db80ce8 --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php @@ -0,0 +1,140 @@ +<?php + +/** + * Test for the core:AttributeCopy filter. + */ +class Test_Core_Auth_Process_AttributeCopy extends PHPUnit_Framework_TestCase +{ + + /** + * 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) + { + $filter = new sspmod_core_Auth_Process_AttributeCopy($config, NULL); + $filter->process($request); + return $request; + } + + /** + * Test the most basic functionality. + */ + public function testBasic() + { + $config = array( + 'test' => 'testnew', + ); + $request = array( + 'Attributes' => array('test' => array('AAP')), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey('test', $attributes); + $this->assertArrayHasKey('testnew', $attributes); + $this->assertEquals($attributes['testnew'], array('AAP')); + } + + /** + * Test that existing attributes are left unmodified. + */ + public function testExistingNotModified() + { + $config = array( + 'test' => 'testnew', + ); + $request = array( + 'Attributes' => array( + 'test' => array('AAP'), + 'original1' => array('original_value1'), + 'original2' => array('original_value2'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey('testnew', $attributes); + $this->assertEquals($attributes['test'], array('AAP')); + $this->assertArrayHasKey('original1', $attributes); + $this->assertEquals($attributes['original1'], array('original_value1')); + $this->assertArrayHasKey('original2', $attributes); + $this->assertEquals($attributes['original2'], array('original_value2')); + } + + /** + * Test copying multiple attributes + */ + public function testCopyMultiple() + { + $config = array( + 'test1' => 'new1', + 'test2' => 'new2', + ); + $request = array( + 'Attributes' => array('test1' => array('val1'), 'test2' => array('val2.1','val2.2')), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey('new1', $attributes); + $this->assertEquals($attributes['new1'], array('val1')); + $this->assertArrayHasKey('new2', $attributes); + $this->assertEquals($attributes['new2'], array('val2.1','val2.2')); + } + + /** + * Test behaviour when target attribute exists (should be replaced). + */ + public function testCopyClash() + { + $config = array( + 'test' => 'new1', + ); + $request = array( + 'Attributes' => array( + 'test' => array('testvalue1'), + 'new1' => array('newvalue1'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertEquals($attributes['new1'], array('testvalue1')); + } + + /** + * Test wrong attribute name + * + * @expectedException Exception + */ + public function testWrongAttributeName() + { + $config = array( + array('value2'), + ); + $request = array( + 'Attributes' => array( + 'test' => array('value1'), + ), + ); + $result = self::processFilter($config, $request); + } + + /** + * Test wrong attribute value + * + * @expectedException Exception + */ + public function testWrongAttributeValue() + { + $config = array( + 'test' => array('test2'), + ); + $request = array( + 'Attributes' => array( + 'test' => array('value1'), + ), + ); + $result = self::processFilter($config, $request); + } +} diff --git a/tests/modules/core/lib/Auth/Process/ScopeAttributeTest.php b/tests/modules/core/lib/Auth/Process/ScopeAttributeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..31e151636028a1901b710bd97011c0fbbbc4ff6a --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/ScopeAttributeTest.php @@ -0,0 +1,194 @@ +<?php + +/** + * Test for the core:ScopeAttribute filter. + */ +class Test_Core_Auth_Process_ScopeAttribute extends PHPUnit_Framework_TestCase +{ + + /* + * 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) + { + $filter = new sspmod_core_Auth_Process_ScopeAttribute($config, NULL); + $filter->process($request); + return $request; + } + + /* + * Test the most basic functionality. + */ + public function testBasic() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('jdoe@example.com'), + 'eduPersonAffiliation' => array('member'), + ) + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertArrayHasKey('eduPersonScopedAffiliation', $attributes); + $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@example.com')); + } + + /* + * If target attribute already set, module must add, not overwrite. + */ + public function testNoOverwrite() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('jdoe@example.com'), + 'eduPersonAffiliation' => array('member'), + 'eduPersonScopedAffiliation' => array('library-walk-in@example.edu'), + ) + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('library-walk-in@example.edu', 'member@example.com')); + } + + /* + * If same scope already set, module must do nothing, not duplicate value. + */ + public function testNoDuplication() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('jdoe@example.com'), + 'eduPersonAffiliation' => array('member'), + 'eduPersonScopedAffiliation' => array('member@example.com'), + ) + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@example.com')); + } + + + /* + * If source attribute not set, nothing happens + */ + public function testNoSourceAttribute() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'mail' => array('j.doe@example.edu', 'john@example.org'), + 'eduPersonAffiliation' => array('member'), + 'eduPersonScopedAffiliation' => array('library-walk-in@example.edu'), + ) + ); + $result = self::processFilter($config, $request); + $this->assertEquals($request['Attributes'], $result['Attributes']); + } + + /* + * If scope attribute not set, nothing happens + */ + public function testNoScopeAttribute() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'mail' => array('j.doe@example.edu', 'john@example.org'), + 'eduPersonScopedAffiliation' => array('library-walk-in@example.edu'), + 'eduPersonPrincipalName' => array('jdoe@example.com'), + ) + ); + $result = self::processFilter($config, $request); + $this->assertEquals($request['Attributes'], $result['Attributes']); + } + + /* + * When multiple @ signs in attribute, will use the first one. + */ + public function testMultiAt() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('john@doe@example.com'), + 'eduPersonAffiliation' => array('member'), + ) + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@doe@example.com')); + } + + /* + * When multiple values in source attribute, should render multiple targets. + */ + public function testMultivaluedSource() + { + $config = array( + 'scopeAttribute' => 'eduPersonPrincipalName', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('jdoe@example.com'), + 'eduPersonAffiliation' => array('member','staff','faculty'), + ) + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('member@example.com','staff@example.com','faculty@example.com')); + } + + /* + * When the source attribute doesn't have a scope, the entire value is used. + */ + public function testNoAt() + { + $config = array( + 'scopeAttribute' => 'schacHomeOrganization', + 'sourceAttribute' => 'eduPersonAffiliation', + 'targetAttribute' => 'eduPersonScopedAffiliation', + ); + $request = array( + 'Attributes' => array( + 'schacHomeOrganization' => array('example.org'), + 'eduPersonAffiliation' => array('student'), + ) + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $this->assertEquals($attributes['eduPersonScopedAffiliation'], array('student@example.org')); + } +} diff --git a/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php b/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php index 955d908c10ecd1b61d54b4fb8b0aef9a1be9f27f..f6ef1bf90414abb094f6e92db22c2d75fda23fd1 100644 --- a/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php +++ b/tests/modules/core/lib/Auth/Process/ScopeFromAttributeTest.php @@ -103,7 +103,7 @@ class Test_Core_Auth_Process_ScopeFromAttribute extends PHPUnit_Framework_TestCa * NOTE: currently disabled: this triggers a warning and a warning * wants to start a session which we cannot do in phpunit. How to fix? */ -/* public function testNoAt() + public function testNoAt() { $config = array( 'sourceAttribute' => 'eduPersonPrincipalName', @@ -118,5 +118,5 @@ class Test_Core_Auth_Process_ScopeFromAttribute extends PHPUnit_Framework_TestCa $attributes = $result['Attributes']; $this->assertArrayNotHasKey('scope', $attributes); - } */ + } }