summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-templates/config.php13
-rw-r--r--docs/simplesamlphp-authproc.md7
-rw-r--r--docs/simplesamlphp-install.md18
-rw-r--r--docs/simplesamlphp-maintenance.md11
-rw-r--r--docs/simplesamlphp-reference-idp-remote.md8
-rw-r--r--lib/SimpleSAML/Auth/LDAP.php115
-rw-r--r--modules/authX509/lib/Auth/Process/ExpiryWarning.php9
-rw-r--r--modules/authX509/lib/Auth/Source/X509userCert.php27
-rw-r--r--modules/core/docs/authproc_attributerealm.md3
-rw-r--r--modules/core/lib/Auth/Process/AttributeRealm.php83
-rw-r--r--modules/ldap/docs/ldap.md15
-rw-r--r--modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php16
-rw-r--r--modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php591
-rw-r--r--modules/ldap/lib/Auth/Process/BaseFilter.php533
-rw-r--r--modules/ldap/lib/Auth/Source/LDAP.php83
-rw-r--r--modules/ldap/lib/Auth/Source/LDAPMulti.php219
-rw-r--r--modules/ldap/lib/ConfigHelper.php543
-rw-r--r--modules/saml/lib/IdP/SAML2.php2173
-rw-r--r--modules/sanitycheck/config-templates/config-sanitycheck.php14
-rw-r--r--modules/sanitycheck/hooks/hook_cron.php59
-rw-r--r--modules/sanitycheck/hooks/hook_frontpage.php18
-rw-r--r--modules/sanitycheck/hooks/hook_moduleinfo.php20
-rw-r--r--modules/sanitycheck/hooks/hook_sanitycheck.php12
-rw-r--r--modules/sanitycheck/www/index.php20
-rw-r--r--tests/lib/SimpleSAML/Locale/LanguageTest.php62
25 files changed, 2441 insertions, 2231 deletions
diff --git a/config-templates/config.php b/config-templates/config.php
index 6d97301..db4ec3b 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -649,9 +649,15 @@ $config = array(
'language.cookie.lifetime' => (60 * 60 * 24 * 900),
/*
- * Which i18n backend to use
+ * Which i18n backend to use.
+ *
+ * "SimpleSAMLphp" is the home made system, valid for 1.x.
+ * For 2.x, only "gettext/gettext" will be possible.
+ *
+ * Home-made templates will always use "SimpleSAMLphp".
+ * To use twig (where avaliable), select "gettext/gettext".
*/
- 'language.i18n.backend' => 'twig.i18n',
+ 'language.i18n.backend' => 'SimpleSAMLphp',
/**
* Custom getLanguage function called from SimpleSAML\Locale\Language::getLanguage().
@@ -773,9 +779,6 @@ $config = array(
// Adopts language from attribute to use in UI
30 => 'core:LanguageAdaptor',
- /* Add a realm attribute from edupersonprincipalname
- 40 => 'core:AttributeRealm',
- */
45 => array(
'class' => 'core:StatisticsWithAttribute',
'attributename' => 'realm',
diff --git a/docs/simplesamlphp-authproc.md b/docs/simplesamlphp-authproc.md
index 784ae9e..d8523d1 100644
--- a/docs/simplesamlphp-authproc.md
+++ b/docs/simplesamlphp-authproc.md
@@ -50,7 +50,6 @@ The configuration of *Auth Proc Filters* is a list of filters with priority as *
'addurnprefix'
),
20 => 'core:TargetedID',
- 40 => 'core:AttributeRealm',
50 => 'core:AttributeLimit',
90 => array(
'class' => 'consent:Consent',
@@ -112,7 +111,7 @@ Filters can be added both in `hosted` and `remote` metadata. Here is an example
'certificate' => 'example.org.crt',
'auth' => 'feide',
'authproc' => array(
- 40 => 'core:AttributeRealm',
+ 40 => 'preprodwarning:Warning',
),
)
@@ -132,7 +131,7 @@ The following filters are included in the SimpleSAMLphp distribution:
- [`core:AttributeAlter`](./core:authproc_attributealter): Do search-and-replace on attributevalues.
- [`core:AttributeLimit`](./core:authproc_attributelimit): Limit the attributes in the response.
- [`core:AttributeMap`](./core:authproc_attributemap): Change the name of the attributes.
-- [`core:AttributeRealm`](./core:authproc_attributerealm): Create an attribute with the realm of the user.
+- [`core:AttributeRealm`](./core:authproc_attributerealm): (deprecated) Create an attribute with the realm of the user.
- [`core:GenerateGroups`](./core:authproc_generategroups): Generate a `group` attribute for the user.
- [`core:LanguageAdaptor`](./core:authproc_languageadaptor): Transfering language setting from IdP to SP.
- [`core:PHP`](./core:authproc_php): Modify attributes with custom PHP code.
@@ -167,7 +166,7 @@ Requirements for authentication processing filters:
- Must be derived from the `SimpleSAML_Auth_ProcessingFilter`-class.
- If a constructor is implemented, it must first call the parent constructor, passing along all parameters, before accessing any of the parameters. In general, only the $config parameter should be accessed.
- - The `process(&$state)`-function must be implemented. If this function completes, it is assumed that processing is completed, and that the $request array has been updated.
+ - The `process(&$request)`-function must be implemented. If this function completes, it is assumed that processing is completed, and that the $request array has been updated.
- If the `process`-function does not return, it must at a later time call `SimpleSAML_Auth_ProcessingChain::resumeProcessing` with the new request state. The request state must be an update of the array passed to the `process`-function.
- No pages may be shown to the user from the `process`-function. Instead, the request state should be saved, and the user should be redirected to a new page. This must be done to prevent unpredictable events if the user for example reloads the page.
- No state information should be stored in the filter object. It must instead be stored in the request state array. Any changes to variables in the filter object may be lost.
diff --git a/docs/simplesamlphp-install.md b/docs/simplesamlphp-install.md
index 7ebd15c..7e02d93 100644
--- a/docs/simplesamlphp-install.md
+++ b/docs/simplesamlphp-install.md
@@ -16,7 +16,7 @@ SimpleSAMLphp news and documentation
This document is part of the SimpleSAMLphp documentation suite.
- * [List of all SimpleSAMLphp documentation](http://simplesamlphp.org/docs)
+ * [List of all SimpleSAMLphp documentation](https://simplesamlphp.org/docs)
* [SimpleSAMLphp homepage](https://simplesamlphp.org)
@@ -110,7 +110,7 @@ Configuring Apache
Examples below assume that SimpleSAMLphp is installed in the default location, `/var/simplesamlphp`. You may choose another location, but this requires a path update in a few files. See Appendix for details ‹Installing SimpleSAMLphp in alternative locations›.
-The only subdirectories of `SimpleSAMLphp` that needs to be accessible from the web is `www`. There are several ways of putting the SimpleSAMLphp depending on the way web sites are structured on your apache web server. Here is what I believe is the best configuration.
+The only subdirectory of `SimpleSAMLphp` that needs to be accessible from the web is `www`. There are several ways of exposing SimpleSAMLphp depending on the way web sites are structured on your Apache web server. The following is just one possible configuration.
Find the Apache configuration file for the virtual hosts where you want to run SimpleSAMLphp. The configuration may look like this:
@@ -135,19 +135,24 @@ Find the Apache configuration file for the virtual hosts where you want to run S
</Directory>
</VirtualHost>
-Note the `Alias` directive, which gives control to SimpleSAMLphp for all urls matching `http(s)://service.example.com/simplesaml/*`. SimpleSAMLphp makes several SAML interfaces available on the web; all of them are included in the `www` subdirectory of your SimpleSAMLphp installation. You can name the alias whatever you want, but the name must be specified in the `config.php` file of simpleSAML as described in [the section called “SimpleSAMLphp configuration: config.php”](#sect.config "SimpleSAMLphp configuration: config.php"). Here is an example of how this configuration may look like in `config.php`:
+Note the `Alias` directive, which gives control to SimpleSAMLphp for all urls matching `http(s)://service.example.com/simplesaml/*`. SimpleSAMLphp makes several SAML interfaces available on the web; all of them are included in the `www` subdirectory of your SimpleSAMLphp installation. You can name the alias whatever you want, but the name must be specified in the `config.php` file of SimpleSAMLphp as described in [the section called “SimpleSAMLphp configuration: config.php”](#sect.config "SimpleSAMLphp configuration: config.php"). Here is an example of how this configuration may look like in `config.php`:
$config = array (
[...]
'baseurlpath' => 'simplesaml/',
Note also the `SetEnv` directive. It sets the `SIMPLESAMLPHP_CONFIG_DIR` environment variable, in this case, to the
-default location for the configuration directory. You can always omit this environment variable, and SimpleSAMLphp will
+default location for the configuration directory. You can omit this environment variable, and SimpleSAMLphp will
then look for the `config` directory inside its own directory. If you need to move your configuration to a different
-location, you can use this environment variable to tell SimpleSAMLphp where to look for configuration files. Remember
-this works only for the `config` directory. If you need your metadata to be in a different directory too, use the
+location, you can use this environment variable to tell SimpleSAMLphp where to look for configuration files.
+This works only for the `config` directory. If you need your metadata to be in a different directory too, use the
`metadatadir` configuration option to specify the location.
+This is just the basic configuration to get things working. For a checklist
+further completing your documentation, please see
+[Maintenance and configuration: Apache](simplesamlphp-maintenance#section_4).
+
+
SimpleSAMLphp configuration: config.php
---------------------------------------
@@ -258,6 +263,7 @@ You have now successfully installed SimpleSAMLphp, and the next steps depends on
* [Use case: Setting up an IdP for Google Apps](simplesamlphp-googleapps)
* [Identity Provider Advanced Topics](simplesamlphp-idp-more)
* [Automated Metadata Management](simplesamlphp-automated_metadata)
+ * [Maintenance and configuration](simplesamlphp-maintenance)
Support
diff --git a/docs/simplesamlphp-maintenance.md b/docs/simplesamlphp-maintenance.md
index 421377a..c533e2c 100644
--- a/docs/simplesamlphp-maintenance.md
+++ b/docs/simplesamlphp-maintenance.md
@@ -164,7 +164,16 @@ alternative, you may log to flat files.
## Apache configuration
-
+Basic Apache configruation is described in [SimpleSAMLphp Installation](simplesamlphp-install#section_6).
+However, your IdP or SP is most likely a valuable website that you want to configure securely. Here are some checks.
+
+* Make sure you use HTTPS with a proper certificate. The best way is to not
+ serve anything over plain HTTP, except for a possible redirect to https.
+* Configure your TLS/SSL to be secure. Mozilla has an easy way to generate
+ [Recommended Server Configurations](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_Server_Configurations).
+ Verify your SSL settings, e.g. with the [SSLLabs SSLtest](https://www.ssllabs.com/ssltest/).
+* In your Apache configuration, add headers that further secure your site.
+ A good check with hints on what to add is [Mozilla Observatory](https://observatory.mozilla.org/).
## PHP configuration
diff --git a/docs/simplesamlphp-reference-idp-remote.md b/docs/simplesamlphp-reference-idp-remote.md
index a8520d4..89526fd 100644
--- a/docs/simplesamlphp-reference-idp-remote.md
+++ b/docs/simplesamlphp-reference-idp-remote.md
@@ -85,6 +85,14 @@ The following options are common between both the SAML 2.0 protocol and Shibbole
'no' => 'En tjeneste',
),
+`scope`
+: An array with scopes valid for this IdP.
+ The IdP will send scopes in scoped attributes, that is, attributes containing a value with an `@` sign and a domain name
+ after it.
+
+: When the `saml:FilterScopes` authentication processing filter is used, this list of scopes will determine the valid
+ scopes for attributes.
+
`SingleSignOnService`
: Endpoint URL for sign on. You should obtain this from the IdP. For SAML 2.0, SimpleSAMLphp will use the HTTP-Redirect binding when contacting this endpoint.
diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php
index 3a3679d..f8a56eb 100644
--- a/lib/SimpleSAML/Auth/LDAP.php
+++ b/lib/SimpleSAML/Auth/LDAP.php
@@ -22,9 +22,8 @@ if (!defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) {
* @author Anders Lund, UNINETT AS. <anders.lund@uninett.no>
* @package SimpleSAMLphp
*/
-class SimpleSAML_Auth_LDAP {
-
-
+class SimpleSAML_Auth_LDAP
+{
/**
* LDAP link identifier.
*
@@ -55,7 +54,8 @@ class SimpleSAML_Auth_LDAP {
* @param bool $referrals
*/
// TODO: Flesh out documentation
- public function __construct($hostname, $enable_tls = TRUE, $debug = FALSE, $timeout = 0, $port = 389, $referrals = TRUE) {
+ public function __construct($hostname, $enable_tls = true, $debug = false, $timeout = 0, $port = 389, $referrals = true)
+ {
// Debug
SimpleSAML\Logger::debug('Library - LDAP __construct(): Setup LDAP with ' .
@@ -71,7 +71,7 @@ class SimpleSAML_Auth_LDAP {
*
* OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
*/
- if ($debug && !ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7)) {
+ if ($debug && !ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7)) {
SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7');
}
@@ -80,7 +80,7 @@ class SimpleSAML_Auth_LDAP {
* doesn't actually connect to the server.
*/
$this->ldap = @ldap_connect($hostname, $port);
- if ($this->ldap === FALSE) {
+ if ($this->ldap === false) {
throw $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL);
}
@@ -107,7 +107,7 @@ class SimpleSAML_Auth_LDAP {
}
// Enable TLS, if needed
- if (stripos($hostname, "ldaps:") === FALSE and $enable_tls) {
+ if (stripos($hostname, "ldaps:") === false and $enable_tls) {
if (!@ldap_start_tls($this->ldap)) {
throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL);
}
@@ -123,7 +123,8 @@ class SimpleSAML_Auth_LDAP {
* The exception's description
* @return Exception
*/
- private function makeException($description, $type = NULL) {
+ private function makeException($description, $type = null)
+ {
$errNo = 0x00;
// Log LDAP code and description, if possible
@@ -200,21 +201,21 @@ class SimpleSAML_Auth_LDAP {
* - Failed to get first entry from result
* - Failed to get DN for entry
* @throws SimpleSAML_Error_UserNotFound if:
- * - Zero entries was found
+ * - Zero entries were found
*/
- private function search($base, $attribute, $value, $searchFilter=NULL) {
-
+ private function search($base, $attribute, $value, $searchFilter = null)
+ {
// Create the search filter
- $attribute = self::escape_filter_value($attribute, FALSE);
+ $attribute = self::escape_filter_value($attribute, false);
$value = self::escape_filter_value($value);
$filter = '';
- foreach ($attribute AS $attr) {
+ foreach ($attribute as $attr) {
$filter .= '(' . $attr . '=' . $value. ')';
}
$filter = '(|' . $filter . ')';
// Append LDAP filters if defined
- if ($searchFilter!=NULL) {
+ if ($searchFilter != null) {
$filter = "(&".$filter."".$searchFilter.")";
}
@@ -222,13 +223,13 @@ class SimpleSAML_Auth_LDAP {
SimpleSAML\Logger::debug('Library - LDAP search(): Searching base \'' . $base . '\' for \'' . $filter . '\'');
// TODO: Should aliases be dereferenced?
$result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout);
- if ($result === FALSE) {
+ if ($result === false) {
throw $this->makeException('Library - LDAP search(): Failed search on base \'' . $base . '\' for \'' . $filter . '\'');
}
// Sanity checks on search results
$count = @ldap_count_entries($this->ldap, $result);
- if ($count === FALSE) {
+ if ($count === false) {
throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned');
} elseif ($count > 1) {
// More than one entry is found. External error
@@ -241,11 +242,11 @@ class SimpleSAML_Auth_LDAP {
// Resolve the DN from the search result
$entry = @ldap_first_entry($this->ldap, $result);
- if ($entry === FALSE) {
+ if ($entry === false) {
throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \'' . $base . '\' for \'' . $filter . '\'');
}
$dn = @ldap_get_dn($this->ldap, $entry);
- if ($dn === FALSE) {
+ if ($dn === false) {
throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \'' . $base . '\' for \'' . $filter . '\'');
}
// FIXME: Are we now sure, if no excepton has been thrown, that we are returning a DN?
@@ -273,15 +274,15 @@ class SimpleSAML_Auth_LDAP {
* - LDAP search encounter some problems when searching cataloge
* - Not able to connect to LDAP server
* @throws SimpleSAML_Error_UserNotFound if:
- * - $allowZeroHits er TRUE and no result is found
+ * - $allowZeroHits is FALSE and no result is found
*
*/
- public function searchfordn($base, $attribute, $value, $allowZeroHits = FALSE, $searchFilter = NULL) {
-
+ public function searchfordn($base, $attribute, $value, $allowZeroHits = false, $searchFilter = null)
+ {
// Traverse all search bases, returning DN if found
$bases = SimpleSAML\Utils\Arrays::arrayize($base);
- $result = NULL;
- foreach ($bases AS $current) {
+ $result = null;
+ foreach ($bases as $current) {
try {
// Single base search
$result = $this->search($current, $attribute, $value, $searchFilter);
@@ -299,7 +300,7 @@ class SimpleSAML_Auth_LDAP {
SimpleSAML\Logger::debug('Library - LDAP searchfordn(): No entries found');
if ($allowZeroHits) {
// Zero hits allowed
- return NULL;
+ return null;
} else {
// Zero hits not allowed
throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'(' .
@@ -320,11 +321,11 @@ class SimpleSAML_Auth_LDAP {
* @param bool $escape Weather to escape the filter values or not
* @return array
*/
- public function searchformultiple($bases, $filters, $attributes = array(), $and = TRUE, $escape = TRUE) {
-
+ public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true)
+ {
// Escape the filter values, if requested
if ($escape) {
- $filters = $this->escape_filter_value($filters, FALSE);
+ $filters = $this->escape_filter_value($filters, false);
}
// Build search filter
@@ -352,16 +353,16 @@ class SimpleSAML_Auth_LDAP {
}
// Search each base until result is found
- $result = FALSE;
+ $result = false;
foreach ($bases as $base) {
$result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout);
- if ($result !== FALSE) {
- break;
+ if ($result !== false) {
+ break;
}
}
// Verify that a result was found in one of the bases
- if ($result === FALSE) {
+ if ($result === false) {
throw $this->makeException(
'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) [' .
implode('; ', $bases) . '] with filter [' . $filter . ']. LDAP error [' .
@@ -377,7 +378,7 @@ class SimpleSAML_Auth_LDAP {
// Get all results
$results = ldap_get_entries($this->ldap, $result);
- if ($results === FALSE) {
+ if ($results === false) {
throw $this->makeException(
'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results'
);
@@ -424,10 +425,11 @@ class SimpleSAML_Auth_LDAP {
* LDAP_INAPPROPRIATE_AUTH, LDAP_INSUFFICIENT_ACCESS
* @throws SimpleSAML_Error_Exception on other errors
*/
- public function bind($dn, $password, array $sasl_args = NULL) {
+ public function bind($dn, $password, array $sasl_args = null)
+ {
$authz_id = null;
- if ($sasl_args != NULL) {
+ if ($sasl_args != null) {
if (!function_exists('ldap_sasl_bind')) {
$ex_msg = 'Library - missing SASL support';
throw $this->makeException($ex_msg);
@@ -447,26 +449,26 @@ class SimpleSAML_Auth_LDAP {
$error = @ldap_bind($this->ldap, $dn, $password);
}
- if ($error === TRUE) {
+ if ($error === true) {
// Good
$this->authz_id = $authz_id;
SimpleSAML\Logger::debug('Library - LDAP bind(): Bind successful with DN \'' . $dn . '\'');
- return TRUE;
+ return true;
}
/* Handle errors
* LDAP_INVALID_CREDENTIALS
* LDAP_INSUFFICIENT_ACCESS */
- switch(ldap_errno($this->ldap)) {
- case 32: // LDAP_NO_SUCH_OBJECT
+ switch (ldap_errno($this->ldap)) {
+ case 32: // LDAP_NO_SUCH_OBJECT
// no break
- case 47: // LDAP_X_PROXY_AUTHZ_FAILURE
+ case 47: // LDAP_X_PROXY_AUTHZ_FAILURE
// no break
- case 48: // LDAP_INAPPROPRIATE_AUTH
+ case 48: // LDAP_INAPPROPRIATE_AUTH
// no break
- case 49: // LDAP_INVALID_CREDENTIALS
+ case 49: // LDAP_INVALID_CREDENTIALS
// no break
- case 50: // LDAP_INSUFFICIENT_ACCESS
+ case 50: // LDAP_INSUFFICIENT_ACCESS
return false;
default:
break;
@@ -485,12 +487,12 @@ class SimpleSAML_Auth_LDAP {
* @param $value
* @return void
*/
- public function setOption($option, $value) {
-
+ public function setOption($option, $value)
+ {
// Attempt to set the LDAP option
if (!@ldap_set_option($this->ldap, $option, $value)) {
throw $this->makeException(
- 'ldap:LdapConnection->setOption : Failed to set LDAP option [' .
+ 'ldap:LdapConnection->setOption : Failed to set LDAP option [' .
$option . '] with the value [' . $value . '] error: ' . ldap_error($this->ldap),
ERR_INTERNAL
);
@@ -520,8 +522,8 @@ class SimpleSAML_Auth_LDAP {
* The array of attributes and their values.
* @see http://no.php.net/manual/en/function.ldap-read.php
*/
- public function getAttributes($dn, $attributes = NULL, $maxsize = NULL) {
-
+ public function getAttributes($dn, $attributes = null, $maxsize = null)
+ {
// Preparations, including a pretty debug message...
$description = 'all attributes';
if (is_array($attributes)) {
@@ -597,8 +599,8 @@ class SimpleSAML_Auth_LDAP {
* @return array|bool
*/
// TODO: Documentation; only cleared up exception/log messages
- public function validate($config, $username, $password = null) {
-
+ public function validate($config, $username, $password = null)
+ {
/* Escape any characters with a special meaning in LDAP. The following
* characters have a special meaning (according to RFC 2253):
* ',', '+', '"', '\', '<', '>', ';', '*'
@@ -620,7 +622,7 @@ class SimpleSAML_Auth_LDAP {
$password = addcslashes($password, ',+"\\<>;*');
if (!$this->bind($dn, $password)) {
SimpleSAML\Logger::info('Library - LDAP validate(): Failed to authenticate \''. $username . '\' using DN \'' . $dn . '\'');
- return FALSE;
+ return false;
}
}
@@ -646,7 +648,8 @@ class SimpleSAML_Auth_LDAP {
* @param array $values Array of values to escape
* @return array Array $values, but escaped
*/
- public static function escape_filter_value($values = array(), $singleValue = TRUE) {
+ public static function escape_filter_value($values = array(), $singleValue = true)
+ {
// Parameter validation
if (!is_array($values)) {
$values = array($values);
@@ -685,7 +688,8 @@ class SimpleSAML_Auth_LDAP {
* @static
* @return string
*/
- public static function asc2hex32($string) {
+ public static function asc2hex32($string)
+ {
for ($i = 0; $i < strlen($string); $i++) {
$char = substr($string, $i, 1);
if (ord($char) < 32) {
@@ -702,7 +706,8 @@ class SimpleSAML_Auth_LDAP {
/**
* Convert SASL authz_id into a DN
*/
- private function authzid_to_dn($searchBase, $searchAttributes, $authz_id) {
+ private function authzid_to_dn($searchBase, $searchAttributes, $authz_id)
+ {
if (preg_match("/^dn:/", $authz_id)) {
return preg_replace("/^dn:/", "", $authz_id);
}
@@ -723,7 +728,8 @@ class SimpleSAML_Auth_LDAP {
* And the patch against lastest PHP release:
* http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/databases/php-ldap/files/ldap-ctrl-exop.patch
*/
- public function whoami($searchBase, $searchAttributes) {
+ public function whoami($searchBase, $searchAttributes)
+ {
$authz_id = '';
if (function_exists('ldap_exop_whoami')) {
@@ -742,5 +748,4 @@ class SimpleSAML_Auth_LDAP {
return $dn;
}
-
}
diff --git a/modules/authX509/lib/Auth/Process/ExpiryWarning.php b/modules/authX509/lib/Auth/Process/ExpiryWarning.php
index a108d95..7a8841e 100644
--- a/modules/authX509/lib/Auth/Process/ExpiryWarning.php
+++ b/modules/authX509/lib/Auth/Process/ExpiryWarning.php
@@ -14,7 +14,8 @@
* @author Joost van Dijk, SURFnet. <Joost.vanDijk@surfnet.nl>
* @package SimpleSAMLphp
*/
-class sspmod_authX509_Auth_Process_ExpiryWarning extends SimpleSAML_Auth_ProcessingFilter {
+class sspmod_authX509_Auth_Process_ExpiryWarning extends SimpleSAML_Auth_ProcessingFilter
+{
private $warndaysbefore = 30;
private $renewurl = null;
@@ -25,7 +26,8 @@ class sspmod_authX509_Auth_Process_ExpiryWarning extends SimpleSAML_Auth_Process
* @param array $config Configuration information about this filter.
* @param mixed $reserved For future use.
*/
- public function __construct($config, $reserved) {
+ public function __construct($config, $reserved)
+ {
parent::__construct($config, $reserved);
assert('is_array($config)');
@@ -53,7 +55,8 @@ class sspmod_authX509_Auth_Process_ExpiryWarning extends SimpleSAML_Auth_Process
*
* @param array $state The state of the response.
*/
- public function process(&$state) {
+ public function process(&$state)
+ {
assert('is_array($state)');
if (isset($state['isPassive']) && $state['isPassive'] === true) {
diff --git a/modules/authX509/lib/Auth/Source/X509userCert.php b/modules/authX509/lib/Auth/Source/X509userCert.php
index e9bf052..74729dd 100644
--- a/modules/authX509/lib/Auth/Source/X509userCert.php
+++ b/modules/authX509/lib/Auth/Source/X509userCert.php
@@ -7,7 +7,8 @@
* @author Emmanuel Dreyfus <manu@netbsd.org>
* @package SimpleSAMLphp
*/
-class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
+class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source
+{
/**
* x509 attributes to use from the certificate
@@ -37,17 +38,20 @@ class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
* @param array $info Information about this authentication source.
* @param array &$config Configuration for this authentication source.
*/
- public function __construct($info, &$config) {
+ public function __construct($info, &$config)
+ {
assert('is_array($info)');
assert('is_array($config)');
- if (isset($config['authX509:x509attributes']))
+ if (isset($config['authX509:x509attributes'])) {
$this->x509attributes =
$config['authX509:x509attributes'];
+ }
- if (array_key_exists('authX509:ldapusercert', $config))
+ if (array_key_exists('authX509:ldapusercert', $config)) {
$this->ldapusercert =
$config['authX509:ldapusercert'];
+ }
parent::__construct($info, $config);
@@ -63,7 +67,8 @@ class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
*
* @param array $pem_data PEM-encoded certificate
*/
- private function pem2der($pem_data) {
+ private function pem2der($pem_data)
+ {
$begin = "CERTIFICATE-----";
$end = "-----END";
$pem_data = substr($pem_data,
@@ -79,7 +84,8 @@ class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
*
* @param array $der_data DER-encoded certificate
*/
- private function der2pem($der_data) {
+ private function der2pem($der_data)
+ {
$pem = chunk_split(base64_encode($der_data), 64, "\n");
$pem = "-----BEGIN CERTIFICATE-----\n".$pem.
"-----END CERTIFICATE-----\n";
@@ -95,7 +101,8 @@ class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
*
* @param array &$state Information about the current authentication.
*/
- public function authFailed(&$state) {
+ public function authFailed(&$state)
+ {
$config = SimpleSAML_Configuration::getInstance();
$t = new SimpleSAML_XHTML_Template($config,
@@ -118,7 +125,8 @@ class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
*
* @param array &$state Information about the current authentication.
*/
- public function authenticate(&$state) {
+ public function authenticate(&$state)
+ {
assert('is_array($state)');
$ldapcf = $this->ldapcf;
@@ -232,7 +240,8 @@ class sspmod_authX509_Auth_Source_X509userCert extends SimpleSAML_Auth_Source {
*
* @param array &$state Information about the current authentication.
*/
- public function authSuccesful(&$state) {
+ public function authSuccesful(&$state)
+ {
SimpleSAML_Auth_Source::completeAuth($state);
assert('false'); /* NOTREACHED */
diff --git a/modules/core/docs/authproc_attributerealm.md b/modules/core/docs/authproc_attributerealm.md
index 77b0bb3..cf51177 100644
--- a/modules/core/docs/authproc_attributerealm.md
+++ b/modules/core/docs/authproc_attributerealm.md
@@ -1,6 +1,9 @@
`core:AttributeRealm`
=====================
+*NOTE:* This filter has been deprecated and will be removed in a future release. Please use
+`core:ScopeFromAttribute` instead.
+
This filter creates a new attribute with the realm of the user.
The new attribute is names `realm` by default, but can be controlled by the `attributename` option.
diff --git a/modules/core/lib/Auth/Process/AttributeRealm.php b/modules/core/lib/Auth/Process/AttributeRealm.php
index a4755a0..9e50d78 100644
--- a/modules/core/lib/Auth/Process/AttributeRealm.php
+++ b/modules/core/lib/Auth/Process/AttributeRealm.php
@@ -6,49 +6,48 @@
*
* @author Andreas Åkre Solberg, UNINETT AS.
* @package SimpleSAMLphp
+ * @deprecated Use ScopeFromAttribute instead.
*/
class sspmod_core_Auth_Process_AttributeRealm extends SimpleSAML_Auth_ProcessingFilter {
- private $attributename = 'realm';
-
- /**
- * Initialize this filter.
- *
- * @param array $config Configuration information about this filter.
- * @param mixed $reserved For future use.
- */
- public function __construct($config, $reserved) {
- parent::__construct($config, $reserved);
- assert('is_array($config)');
-
- if (array_key_exists('attributename', $config))
- $this->attributename = $config['attributename'];
-
- }
-
-
- /**
- * Apply filter to add or replace attributes.
- *
- * Add or replace existing attributes with the configured values.
- *
- * @param array &$request The current request
- */
- public function process(&$request) {
- assert('is_array($request)');
- assert('array_key_exists("Attributes", $request)');
-
- $attributes =& $request['Attributes'];
-
- if (!array_key_exists('UserID', $request)) {
- throw new Exception('core:AttributeRealm: Missing UserID for this user. Please' .
- ' check the \'userid.attribute\' option in the metadata against the' .
- ' attributes provided by the authentication source.');
- }
- $userID = $request['UserID'];
- $decomposed = explode('@', $userID);
- if (count($decomposed) !== 2) return;
- $request['Attributes'][$this->attributename] = array($decomposed[1]);
- }
-
+ private $attributename = 'realm';
+
+ /**
+ * Initialize this filter.
+ *
+ * @param array $config Configuration information about this filter.
+ * @param mixed $reserved For future use.
+ */
+ public function __construct($config, $reserved) {
+ parent::__construct($config, $reserved);
+ assert('is_array($config)');
+
+ if (array_key_exists('attributename', $config))
+ $this->attributename = $config['attributename'];
+
+ }
+
+ /**
+ * Apply filter to add or replace attributes.
+ *
+ * Add or replace existing attributes with the configured values.
+ *
+ * @param array &$request The current request
+ */
+ public function process(&$request) {
+ assert('is_array($request)');
+ assert('array_key_exists("Attributes", $request)');
+
+ $attributes =& $request['Attributes'];
+
+ if (!array_key_exists('UserID', $request)) {
+ throw new Exception('core:AttributeRealm: Missing UserID for this user. Please' .
+ ' check the \'userid.attribute\' option in the metadata against the' .
+ ' attributes provided by the authentication source.');
+ }
+ $userID = $request['UserID'];
+ $decomposed = explode('@', $userID);
+ if (count($decomposed) !== 2) return;
+ $request['Attributes'][$this->attributename] = array($decomposed[1]);
+ }
}
diff --git a/modules/ldap/docs/ldap.md b/modules/ldap/docs/ldap.md
index cbc9e6f..098e404 100644
--- a/modules/ldap/docs/ldap.md
+++ b/modules/ldap/docs/ldap.md
@@ -462,13 +462,15 @@ a listing of all configuration options and their details.
* that most products have a special query to recursively search
* group membership.
*
- * Note: Only ActiveDirectory is currently supported.
+ * Note: Only ActiveDirectory is currently supported
+ * (OpenLDAP is implemented but not supported, see example below).
*
* Default: ''
* Required: No
*/
'ldap.product' => '',
'ldap.product' => 'ActiveDirectory',
+ 'ldap.product' => 'OpenLDAP',
/**
@@ -559,3 +561,14 @@ required, see the config info above for details.
'ldap.basedn' => 'DC=example,DC=org'
)
+Example for unsupported OpenLDAP usage.
+Intention is to filter in 'ou=groups,dc=example,dc=com' for
+'(memberUid = <UID>)' and take only the attributes 'cn' (=name of the group).
+
+ 50 => array(
+ 'class' => 'ldap:AttributeAddUsersGroups',
+ 'ldap.product' => 'OpenLDAP',
+ 'ldap.basedn' => 'ou=groups,dc=example,dc=org',
+ 'attribute.member' => 'cn',
+ 'attribute.memberof' => 'memberUid',
+ ),
diff --git a/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php b/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php
index 38c85c0..b1c5910 100644
--- a/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php
+++ b/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php
@@ -29,7 +29,8 @@
* @author Ryan Panning
* @package SimpleSAMLphp
*/
-class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Process_BaseFilter {
+class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Process_BaseFilter
+{
/**
* LDAP attribute to add to the request attributes
@@ -60,8 +61,8 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro
* @param array $config Configuration information about this filter.
* @param mixed $reserved For future use.
*/
- public function __construct($config, $reserved) {
-
+ public function __construct($config, $reserved)
+ {
/*
* For backwards compatibility, check for old config names
* @TODO Remove after 2.0
@@ -119,7 +120,7 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro
$new_attribute = $this->config->getString('attribute.new', '');
$this->search_attributes[$new_attribute] = $this->config->getString('search.attribute');
}
- $this->search_filter = $this->config->getString('search.filter');
+ $this->search_filter = $this->config->getString('search.filter');
// get the attribute policy
$this->attr_policy = $this->config->getString('attribute.policy', 'merge');
@@ -131,7 +132,8 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro
*
* @param array &$request The current request
*/
- public function process(&$request) {
+ public function process(&$request)
+ {
assert('is_array($request)');
assert('array_key_exists("Attributes", $request)');
@@ -153,7 +155,7 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro
// merge the attributes into the ldap_search_filter
$filter = str_replace($arrSearch, $arrReplace, $this->search_filter);
- if (strpos($filter, '%') !== FALSE) {
+ if (strpos($filter, '%') !== false) {
SimpleSAML\Logger::info('AttributeAddFromLDAP: There are non-existing attributes in the search filter. ('.
$this->search_filter.')');
return;
@@ -168,7 +170,7 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro
// search for matching entries
try {
$entries = $this->getLdap()->searchformultiple($this->base_dn, $filter,
- array_values($this->search_attributes), TRUE, FALSE);
+ array_values($this->search_attributes), true, false);
} catch (Exception $e) {
return; // silent fail, error is still logged by LDAP search
}
diff --git a/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php b/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php
index 6364efe..ada4932 100644
--- a/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php
+++ b/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php
@@ -8,287 +8,312 @@
* @author Ryan Panning <panman@traileyes.com>
* @package SimpleSAMLphp
*/
-class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_Process_BaseFilter {
-
-
- /**
- * This is run when the filter is processed by SimpleSAML.
- * It will attempt to find the current users groups using
- * the best method possible for the LDAP product. The groups
- * are then added to the request attributes.
- *
- * @throws SimpleSAML_Error_Exception
- * @param $request
- */
- public function process(&$request) {
- assert('is_array($request)');
- assert('array_key_exists("Attributes", $request)');
-
- // Log the process
- SimpleSAML\Logger::debug(
- $this->title . 'Attempting to get the users groups...'
- );
-
- // Reference the attributes, just to make the names shorter
- $attributes =& $request['Attributes'];
- $map =& $this->attribute_map;
-
- // Get the users groups from LDAP
- $groups = $this->getGroups($attributes);
-
- // Make the array if it is not set already
- if (!isset($attributes[$map['groups']])) {
- $attributes[$map['groups']] = array();
- }
-
- // Must be an array, else cannot merge groups
- if (!is_array($attributes[$map['groups']])) {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'The group attribute [' . $map['groups'] .
- '] is not an array of group DNs. ' . $this->var_export($attributes[$map['groups']])
- );
- }
-
- // Add the users group(s)
- $group_attribute =& $attributes[$map['groups']];
- $group_attribute = array_merge($group_attribute, $groups);
- $group_attribute = array_unique($group_attribute);
-
- // All done
- SimpleSAML\Logger::debug(
- $this->title . 'Added users groups to the group attribute [' .
- $map['groups'] . ']: ' . implode('; ', $groups)
- );
- }
-
-
- /**
- * This section of code was broken out because the child
- * filter AuthorizeByGroup can use this method as well.
- * Based on the LDAP product, it will do an optimized search
- * using the required attribute values from the user to
- * get their group membership, recursively.
- *
- * @throws SimpleSAML_Error_Exception
- * @param array $attributes
- * @return array
- */
- protected function getGroups(array $attributes) {
-
- // Reference the map, just to make the name shorter
- $map =& $this->attribute_map;
-
- // Log the request
- SimpleSAML\Logger::debug(
- $this->title . 'Checking for groups based on the best method for the LDAP product.'
- );
-
- // Based on the directory service, search LDAP for groups
- // If any attributes are needed, prepare them before calling search method
- switch ($this->product) {
-
- case 'ACTIVEDIRECTORY':
-
- // Log the AD specific search
- SimpleSAML\Logger::debug(
- $this->title . 'Searching LDAP using ActiveDirectory specific method.'
- );
-
- // Make sure the defined dn attribute exists
- if (!isset($attributes[$map['dn']])) {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'The DN attribute [' . $map['dn'] .
- '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes))
- );
- }
-
- // DN attribute must have a value
- if (!isset($attributes[$map['dn']][0]) || !$attributes[$map['dn']][0]) {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'The DN attribute [' . $map['dn'] .
- '] does not have a [0] value defined. ' . $this->var_export($attributes[$map['dn']])
- );
- }
-
- // Pass to the AD specific search
- $groups = $this->searchActiveDirectory($attributes[$map['dn']][0]);
- break;
-
- default:
-
- // Log the general search
- SimpleSAML\Logger::debug(
- $this->title . 'Searching LDAP using the default search method.'
- );
-
- // Make sure the defined memberOf attribute exists
- if (!isset($attributes[$map['memberof']])) {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'The memberof attribute [' . $map['memberof'] .
- '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes))
- );
- }
-
- // MemberOf must be an array of group DN's
- if (!is_array($attributes[$map['memberof']])) {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'The memberof attribute [' . $map['memberof'] .
- '] is not an array of group DNs. ' . $this->var_export($attributes[$map['memberof']])
- );
- }
-
- // Search for the users group membership, recursively
- $groups = $this->search($attributes[$map['memberof']]);
- }
-
- // All done
- SimpleSAML\Logger::debug(
- $this->title . 'User found to be a member of the groups:' . implode('; ', $groups)
- );
- return $groups;
- }
-
-
- /**
- * Looks for groups from the list of DN's passed. Also
- * recursively searches groups for further membership.
- * Avoids loops by only searching a DN once. Returns
- * the list of groups found.
- *
- * @param array $memberof
- * @return array
- */
- protected function search($memberof) {
- assert('is_array($memberof)');
-
- // Used to determine what DN's have already been searched
- static $searched = array();
-
- // Init the groups variable
- $groups = array();
-
- // Shorten the variable name
- $map =& $this->attribute_map;
-
- // Log the search
- SimpleSAML\Logger::debug(
- $this->title . 'Checking DNs for groups.' .
- ' DNs: '. implode('; ', $memberof) .
- ' Attributes: ' . $map['memberof'] . ', ' . $map['type'] .
- ' Group Type: ' . $this->type_map['group']
- );
-
- // Check each DN of the passed memberOf
- foreach ($memberof as $dn) {
-
- // Avoid infinite loops, only need to check a DN once
- if (isset($searched[$dn])) {
- continue;
- }
-
- // Track all DN's that are searched
- // Use DN for key as well, isset() is faster than in_array()
- $searched[$dn] = $dn;
-
- // Query LDAP for the attribute values for the DN
- try {
- $attributes = $this->getLdap()->getAttributes($dn, array($map['memberof'], $map['type']));
- } catch (SimpleSAML_Error_AuthSource $e) {
- continue; // DN must not exist, just continue. Logged by the LDAP object
- }
-
- // Only look for groups
- if (!in_array($this->type_map['group'], $attributes[$map['type']])) {
- continue;
- }
-
- // Add to found groups array
- $groups[] = $dn;
-
- // Recursively search "sub" groups
- $groups = array_merge($groups, $this->search($attributes[$map['memberof']]));
- }
-
- // Return only the unique group names
- return array_unique($groups);
- }
-
-
- /**
- * Searches LDAP using a ActiveDirectory specific filter,
- * looking for group membership for the users DN. Returns
- * the list of group DNs retrieved.
- *
- * @param string $dn
- * @return array
- */
- protected function searchActiveDirectory($dn) {
- assert('is_string($dn) && $dn != ""');
-
- // Shorten the variable name
- $map =& $this->attribute_map;
-
- // Log the search
- SimpleSAML\Logger::debug(
- $this->title . 'Searching ActiveDirectory group membership.' .
- ' DN: ' . $dn .
- ' DN Attribute: ' . $map['dn'] .
- ' Member Attribute: ' . $map['member'] .
- ' Type Attribute: ' . $map['type'] .
- ' Type Value: ' . $this->type_map['group'] .
- ' Base: ' . implode('; ', $this->base_dn)
- );
-
- // AD connections should have this set
- $this->getLdap()->setOption(LDAP_OPT_REFERRALS, 0);
-
- // Search AD with the specific recursive flag
- try {
- $entries = $this->getLdap()->searchformultiple(
- $this->base_dn,
- array($map['type'] => $this->type_map['group'], $map['member'] . ':1.2.840.113556.1.4.1941:' => $dn),
- array($map['dn'])
- );
-
- // The search may throw an exception if no entries
- // are found, unlikely but possible.
- } catch (SimpleSAML_Error_UserNotFound $e) {
- return array();
- }
-
- //Init the groups
- $groups = array();
-
- // Check each entry..
- foreach ($entries as $entry) {
-
- // Check for the DN using the original attribute name
- if (isset($entry[$map['dn']][0])) {
- $groups[] = $entry[$map['dn']][0];
- continue;
- }
-
- // Sometimes the returned attribute names are lowercase
- if (isset($entry[strtolower($map['dn'])][0])) {
- $groups[] = $entry[strtolower($map['dn'])][0];
- continue;
- }
-
- // AD queries also seem to return the objects dn by default
- if (isset($entry['dn'])) {
- $groups[] = $entry['dn'];
- continue;
- }
-
- // Could not find DN, log and continue
- SimpleSAML\Logger::notice(
- $this->title . 'The DN attribute [' .
- implode(', ', array($map['dn'], strtolower($map['dn']), 'dn')) .
- '] could not be found in the entry. ' . $this->var_export($entry)
- );
- }
-
- // All done
- return $groups;
- }
+class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_Process_BaseFilter
+{
+ /**
+ * This is run when the filter is processed by SimpleSAML.
+ * It will attempt to find the current users groups using
+ * the best method possible for the LDAP product. The groups
+ * are then added to the request attributes.
+ *
+ * @throws SimpleSAML_Error_Exception
+ * @param $request
+ */
+ public function process(&$request)
+ {
+ assert('is_array($request)');
+ assert('array_key_exists("Attributes", $request)');
+
+ // Log the process
+ SimpleSAML\Logger::debug(
+ $this->title . 'Attempting to get the users groups...'
+ );
+
+ // Reference the attributes, just to make the names shorter
+ $attributes =& $request['Attributes'];
+ $map =& $this->attribute_map;
+
+ // Get the users groups from LDAP
+ $groups = $this->getGroups($attributes);
+
+ // Make the array if it is not set already
+ if (!isset($attributes[$map['groups']])) {
+ $attributes[$map['groups']] = array();
+ }
+
+ // Must be an array, else cannot merge groups
+ if (!is_array($attributes[$map['groups']])) {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'The group attribute [' . $map['groups'] .
+ '] is not an array of group DNs. ' . $this->var_export($attributes[$map['groups']])
+ );
+ }
+
+ // Add the users group(s)
+ $group_attribute =& $attributes[$map['groups']];
+ $group_attribute = array_merge($group_attribute, $groups);
+ $group_attribute = array_unique($group_attribute);
+
+ // All done
+ SimpleSAML\Logger::debug(
+ $this->title . 'Added users groups to the group attribute [' .
+ $map['groups'] . ']: ' . implode('; ', $groups)
+ );
+ }
+
+
+ /**
+ * This section of code was broken out because the child
+ * filter AuthorizeByGroup can use this method as well.
+ * Based on the LDAP product, it will do an optimized search
+ * using the required attribute values from the user to
+ * get their group membership, recursively.
+ *
+ * @throws SimpleSAML_Error_Exception
+ * @param array $attributes
+ * @return array
+ */
+ protected function getGroups(array $attributes)
+ {
+ // Reference the map, just to make the name shorter
+ $map =& $this->attribute_map;
+
+ // Log the request
+ SimpleSAML\Logger::debug(
+ $this->title . 'Checking for groups based on the best method for the LDAP product.'
+ );
+
+ // Based on the directory service, search LDAP for groups
+ // If any attributes are needed, prepare them before calling search method
+ switch ($this->product) {
+
+ case 'ACTIVEDIRECTORY':
+
+ // Log the AD specific search
+ SimpleSAML\Logger::debug(
+ $this->title . 'Searching LDAP using ActiveDirectory specific method.'
+ );
+
+ // Make sure the defined dn attribute exists
+ if (!isset($attributes[$map['dn']])) {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'The DN attribute [' . $map['dn'] .
+ '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes))
+ );
+ }
+
+ // DN attribute must have a value
+ if (!isset($attributes[$map['dn']][0]) || !$attributes[$map['dn']][0]) {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'The DN attribute [' . $map['dn'] .
+ '] does not have a [0] value defined. ' . $this->var_export($attributes[$map['dn']])
+ );
+ }
+
+ // Pass to the AD specific search
+ $groups = $this->searchActiveDirectory($attributes[$map['dn']][0]);
+ break;
+
+ case 'OPENLDAP':
+ // Log the OpenLDAP specific search
+ SimpleSAML\Logger::debug(
+ $this->title . 'Searching LDAP using OpenLDAP specific method.'
+ );
+ // Print group search string and search for all group names
+ $openldap_base = $this->config->getString('ldap.basedn','ou=groups,dc=example,dc=com');
+ SimpleSAML\Logger::debug(
+ $this->title . "Searching for groups in ldap.basedn ".$openldap_base." with filter (".$map['memberof']."=".$attributes['uid'][0].") and attributes ".$map['member']
+ );
+ $groups = array();
+ try {
+ // Intention is to filter in 'ou=groups,dc=example,dc=com' for '(memberUid = <UID>)' and take only the attributes 'cn' (=name of the group)
+ $all_groups = $this->getLdap()->searchformultiple( $openldap_base, array($map['memberof'] => $attributes['uid'][0]) , array($map['member']));
+ } catch (SimpleSAML_Error_UserNotFound $e) {
+ break; // if no groups found return with empty (still just initialized) groups array
+ }
+ // run through all groups and add each to our groups array
+ foreach ($all_groups as $group_entry) {
+ $groups[] .= $group_entry[$map['member']][0];
+ }
+ break;
+
+ default:
+
+ // Log the general search
+ SimpleSAML\Logger::debug(
+ $this->title . 'Searching LDAP using the default search method.'
+ );
+
+ // Make sure the defined memberOf attribute exists
+ if (!isset($attributes[$map['memberof']])) {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'The memberof attribute [' . $map['memberof'] .
+ '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes))
+ );
+ }
+
+ // MemberOf must be an array of group DN's
+ if (!is_array($attributes[$map['memberof']])) {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'The memberof attribute [' . $map['memberof'] .
+ '] is not an array of group DNs. ' . $this->var_export($attributes[$map['memberof']])
+ );
+ }
+
+ // Search for the users group membership, recursively
+ $groups = $this->search($attributes[$map['memberof']]);
+ }
+
+ // All done
+ SimpleSAML\Logger::debug(
+ $this->title . 'User found to be a member of the groups:' . implode('; ', $groups)
+ );
+ return $groups;
+ }
+
+
+ /**
+ * Looks for groups from the list of DN's passed. Also
+ * recursively searches groups for further membership.
+ * Avoids loops by only searching a DN once. Returns
+ * the list of groups found.
+ *
+ * @param array $memberof
+ * @return array
+ */
+ protected function search($memberof)
+ {
+ assert('is_array($memberof)');
+
+ // Used to determine what DN's have already been searched
+ static $searched = array();
+
+ // Init the groups variable
+ $groups = array();
+
+ // Shorten the variable name
+ $map =& $this->attribute_map;
+
+ // Log the search
+ SimpleSAML\Logger::debug(
+ $this->title . 'Checking DNs for groups.' .
+ ' DNs: '. implode('; ', $memberof) .
+ ' Attributes: ' . $map['memberof'] . ', ' . $map['type'] .
+ ' Group Type: ' . $this->type_map['group']
+ );
+
+ // Check each DN of the passed memberOf
+ foreach ($memberof as $dn) {
+
+ // Avoid infinite loops, only need to check a DN once
+ if (isset($searched[$dn])) {
+ continue;
+ }
+
+ // Track all DN's that are searched
+ // Use DN for key as well, isset() is faster than in_array()
+ $searched[$dn] = $dn;
+
+ // Query LDAP for the attribute values for the DN
+ try {
+ $attributes = $this->getLdap()->getAttributes($dn, array($map['memberof'], $map['type']));
+ } catch (SimpleSAML_Error_AuthSource $e) {
+ continue; // DN must not exist, just continue. Logged by the LDAP object
+ }
+
+ // Only look for groups
+ if (!in_array($this->type_map['group'], $attributes[$map['type']])) {
+ continue;
+ }
+
+ // Add to found groups array
+ $groups[] = $dn;
+
+ // Recursively search "sub" groups
+ $groups = array_merge($groups, $this->search($attributes[$map['memberof']]));
+ }
+
+ // Return only the unique group names
+ return array_unique($groups);
+ }
+
+
+ /**
+ * Searches LDAP using a ActiveDirectory specific filter,
+ * looking for group membership for the users DN. Returns
+ * the list of group DNs retrieved.
+ *
+ * @param string $dn
+ * @return array
+ */
+ protected function searchActiveDirectory($dn)
+ {
+ assert('is_string($dn) && $dn != ""');
+
+ // Shorten the variable name
+ $map =& $this->attribute_map;
+
+ // Log the search
+ SimpleSAML\Logger::debug(
+ $this->title . 'Searching ActiveDirectory group membership.' .
+ ' DN: ' . $dn .
+ ' DN Attribute: ' . $map['dn'] .
+ ' Member Attribute: ' . $map['member'] .
+ ' Type Attribute: ' . $map['type'] .
+ ' Type Value: ' . $this->type_map['group'] .
+ ' Base: ' . implode('; ', $this->base_dn)
+ );
+
+ // AD connections should have this set
+ $this->getLdap()->setOption(LDAP_OPT_REFERRALS, 0);
+
+ // Search AD with the specific recursive flag
+ try {
+ $entries = $this->getLdap()->searchformultiple(
+ $this->base_dn,
+ array($map['type'] => $this->type_map['group'], $map['member'] . ':1.2.840.113556.1.4.1941:' => $dn),
+ array($map['dn'])
+ );
+
+ // The search may throw an exception if no entries
+ // are found, unlikely but possible.
+ } catch (SimpleSAML_Error_UserNotFound $e) {
+ return array();
+ }
+
+ //Init the groups
+ $groups = array();
+
+ // Check each entry..
+ foreach ($entries as $entry) {
+
+ // Check for the DN using the original attribute name
+ if (isset($entry[$map['dn']][0])) {
+ $groups[] = $entry[$map['dn']][0];
+ continue;
+ }
+
+ // Sometimes the returned attribute names are lowercase
+ if (isset($entry[strtolower($map['dn'])][0])) {
+ $groups[] = $entry[strtolower($map['dn'])][0];
+ continue;
+ }
+
+ // AD queries also seem to return the objects dn by default
+ if (isset($entry['dn'])) {
+ $groups[] = $entry['dn'];
+ continue;
+ }
+
+ // Could not find DN, log and continue
+ SimpleSAML\Logger::notice(
+ $this->title . 'The DN attribute [' .
+ implode(', ', array($map['dn'], strtolower($map['dn']), 'dn')) .
+ '] could not be found in the entry. ' . $this->var_export($entry)
+ );
+ }
+
+ // All done
+ return $groups;
+ }
}
diff --git a/modules/ldap/lib/Auth/Process/BaseFilter.php b/modules/ldap/lib/Auth/Process/BaseFilter.php
index 3d328e4..22aa197 100644
--- a/modules/ldap/lib/Auth/Process/BaseFilter.php
+++ b/modules/ldap/lib/Auth/Process/BaseFilter.php
@@ -8,270 +8,271 @@
* @author Ryan Panning <panman@traileyes.com>
* @package SimpleSAMLphp
*/
-abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_ProcessingFilter {
-
- /**
- * List of attribute "alias's" linked to the real attribute
- * name. Used for abstraction / configuration of the LDAP
- * attribute names, which may change between dir service.
- *
- * @var array
- */
- protected $attribute_map;
-
-
- /**
- * The base DN of the LDAP connection. Used when searching
- * the LDAP server.
- *
- * @var string|array
- */
- protected $base_dn;
-
-
- /**
- * The construct method will change the filter config into
- * a SimpleSAML_Configuration object and store it here for
- * later use, if needed.
- *
- * @var SimpleSAML_Configuration
- */
- protected $config;
-
-
- /**
- * Instance, object of the ldap connection. Stored here to
- * be access later during processing.
- *
- * @var sspmod_ldap_LdapConnection
- */
- private $ldap;
-
-
- /**
- * Many times a LDAP product specific query can be used to
- * speed up or reduce the filter process. This helps the
- * child classes determine the product used to optimize
- * those queries.
- *
- * @var string
- */
- protected $product;
-
-
- /**
- * The class "title" used in logging and exception messages.
- * This should be prepended to the beginning of the message.
- *
- * @var string
- */
- protected $title = 'ldap:BaseFilter : ';
-
-
- /**
- * List of LDAP object types, used to determine the type of
- * object that a DN references.
- *
- * @var array
- */
- protected $type_map;
-
-
- /**
- * Checks the authsource, if defined, for configuration values
- * to the LDAP server. Then sets up the LDAP connection for the
- * instance/object and stores everything in class members.
- *
- * @throws SimpleSAML_Error_Exception
- * @param array $config
- * @param $reserved
- */
- public function __construct(&$config, $reserved) {
- parent::__construct($config, $reserved);
-
- // Change the class $title to match it's true name
- // This way if the class is extended the proper name is used
- $classname = get_class($this);
- $classname = explode('_', $classname);
- $this->title = 'ldap:' . end($classname) . ' : ';
-
- // Log the construction
- SimpleSAML\Logger::debug(
- $this->title . 'Creating and configuring the filter.'
- );
-
- // If an authsource was defined (an not empty string)...
- if (isset($config['authsource']) && $config['authsource']) {
-
- // Log the authsource request
- SimpleSAML\Logger::debug(
- $this->title . 'Attempting to get configuration values from authsource [' .
- $config['authsource'] . ']'
- );
-
- // Get the authsources file, which should contain the config
- $authsource = SimpleSAML_Configuration::getConfig('authsources.php');
-
- // Verify that the authsource config exists
- if (!$authsource->hasValue($config['authsource'])) {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'Authsource [' . $config['authsource'] .
- '] defined in filter parameters not found in authsources.php'
- );
- }
-
- // Get just the specified authsource config values
- $authsource = $authsource->getConfigItem($config['authsource']);
- $authsource = $authsource->toArray();
-
- // Make sure it is an ldap source
- // TODO: Support ldap:LDAPMulti, if possible
- if (@$authsource[0] != 'ldap:LDAP') {
- throw new SimpleSAML_Error_Exception(
- $this->title . 'Authsource [' . $config['authsource'] .
- '] specified in filter parameters is not an ldap:LDAP type'
- );
- }
-
- // Build the authsource config
- $authconfig = array();
- $authconfig['ldap.hostname'] = @$authsource['hostname'];
- $authconfig['ldap.enable_tls'] = @$authsource['enable_tls'];
- $authconfig['ldap.port'] = @$authsource['port'];
- $authconfig['ldap.timeout'] = @$authsource['timeout'];
- $authconfig['ldap.debug'] = @$authsource['debug'];
- $authconfig['ldap.basedn'] = (@$authsource['search.enable'] ? @$authsource['search.base'] : NULL);
- $authconfig['ldap.username'] = (@$authsource['search.enable'] ? @$authsource['search.username'] : NULL);
- $authconfig['ldap.password'] = (@$authsource['search.enable'] ? @$authsource['search.password'] : NULL);
- $authconfig['ldap.username'] = (@$authsource['priv.read'] ? @$authsource['priv.username'] : $authconfig['ldap.username']);
- $authconfig['ldap.password'] = (@$authsource['priv.read'] ? @$authsource['priv.password'] : $authconfig['ldap.password']);
-
- // Only set the username attribute if the authsource specifies one attribute
- if (@$authsource['search.enable'] && is_array(@$authsource['search.attributes'])
- && count($authsource['search.attributes']) == 1) {
- $authconfig['attribute.username'] = reset($authsource['search.attributes']);
- }
-
- // Merge the authsource config with the filter config,
- // but have the filter config override the authsource config
- $config = array_merge($authconfig, $config);
-
- // Authsource complete
- SimpleSAML\Logger::debug(
- $this->title . 'Retrieved authsource [' . $config['authsource'] .
- '] configuration values: ' . $this->var_export($authconfig)
- );
- }
-
- // Convert the config array to a config class,
- // that way we can verify type and define defaults.
- // Store in the instance in-case needed later, by a child class.
- $this->config = SimpleSAML_Configuration::loadFromArray($config, 'ldap:AuthProcess');
-
- // Set all the filter values, setting defaults if needed
- $this->base_dn = $this->config->getArrayizeString('ldap.basedn', '');
- $this->product = $this->config->getString('ldap.product', '');
-
- // Cleanup the directory service, so that it is easier for
- // child classes to determine service name consistently
- $this->product = trim($this->product);
- $this->product = strtoupper($this->product);
-
- // Log the member values retrieved above
- SimpleSAML\Logger::debug(
- $this->title . 'Configuration values retrieved;' .
- ' BaseDN: ' . $this->var_export($this->base_dn) .
- ' Product: ' . $this->var_export($this->product)
- );
-
- // Setup the attribute map which will be used to search LDAP
- $this->attribute_map = array(
- 'dn' => $this->config->getString('attribute.dn', 'distinguishedName'),
- 'groups' => $this->config->getString('attribute.groups', 'groups'),
- 'member' => $this->config->getString('attribute.member', 'member'),
- 'memberof' => $this->config->getString('attribute.memberof', 'memberOf'),
- 'name' => $this->config->getString('attribute.groupname', 'name'),
- 'type' => $this->config->getString('attribute.type', 'objectClass'),
- 'username' => $this->config->getString('attribute.username', 'sAMAccountName')
- );
-
- // Log the attribute map
- SimpleSAML\Logger::debug(
- $this->title . 'Attribute map created: ' . $this->var_export($this->attribute_map)
- );
-
- // Setup the object type map which is used to determine a DNs' type
- $this->type_map = array(
- 'group' => $this->config->getString('type.group', 'group'),
- 'user' => $this->config->getString('type.user', 'user')
- );
-
- // Log the type map
- SimpleSAML\Logger::debug(
- $this->title . 'Type map created: ' . $this->var_export($this->type_map)
- );
- }
-
-
- /**
- * Getter for the LDAP connection object. Created this getter
- * rather than setting in the constructor to avoid unnecessarily
- * connecting to LDAP when it might not be needed.
- *
- * @return sspmod_ldap_LdapConnection
- */
- protected function getLdap() {
-
- // Check if already connected
- if ($this->ldap) {
- return $this->ldap;
- }
-
- // Get the connection specific options
- $hostname = $this->config->getString('ldap.hostname');
- $port = $this->config->getInteger('ldap.port', 389);
- $enable_tls = $this->config->getBoolean('ldap.enable_tls', FALSE);
- $debug = $this->config->getBoolean('ldap.debug', FALSE);
- $timeout = $this->config->getInteger('ldap.timeout', 0);
- $username = $this->config->getString('ldap.username', NULL);
- $password = $this->config->getString('ldap.password', NULL);
-
- // Log the LDAP connection
- SimpleSAML\Logger::debug(
- $this->title . 'Connecting to LDAP server;' .
- ' Hostname: ' . $hostname .
- ' Port: ' . $port .
- ' Enable TLS: ' . ($enable_tls ? 'Yes' : 'No') .
- ' Debug: ' . ($debug ? 'Yes' : 'No') .
- ' Timeout: ' . $timeout .
- ' Username: ' . $username .
- ' Password: ' . str_repeat('*', strlen($password))
- );
-
- // Connect to the LDAP server to be queried during processing
- $this->ldap = new SimpleSAML_Auth_LDAP($hostname, $enable_tls, $debug, $timeout, $port);
- $this->ldap->bind($username, $password);
-
- // All done
- return $this->ldap;
- }
-
-
- /**
- * Local utility function to get details about a variable,
- * basically converting it to a string to be used in a log
- * message. The var_export() function returns several lines
- * so this will remove the new lines and trim each line.
- *
- * @param mixed $value
- * @return string
- */
- protected function var_export($value) {
- $export = var_export($value, TRUE);
- $lines = explode("\n", $export);
- foreach ($lines as &$line) {
- $line = trim($line);
- }
- return implode(' ', $lines);
- }
+abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_ProcessingFilter
+{
+
+ /**
+ * List of attribute "alias's" linked to the real attribute
+ * name. Used for abstraction / configuration of the LDAP
+ * attribute names, which may change between dir service.
+ *
+ * @var array
+ */
+ protected $attribute_map;
+
+
+ /**
+ * The base DN of the LDAP connection. Used when searching
+ * the LDAP server.
+ *
+ * @var string|array
+ */
+ protected $base_dn;
+
+
+ /**
+ * The construct method will change the filter config into
+ * a SimpleSAML_Configuration object and store it here for
+ * later use, if needed.
+ *
+ * @var SimpleSAML_Configuration
+ */
+ protected $config;
+
+
+ /**
+ * Instance, object of the ldap connection. Stored here to
+ * be access later during processing.
+ *
+ * @var sspmod_ldap_LdapConnection
+ */
+ private $ldap;
+
+
+ /**
+ * Many times a LDAP product specific query can be used to
+ * speed up or reduce the filter process. This helps the
+ * child classes determine the product used to optimize
+ * those queries.
+ *
+ * @var string
+ */
+ protected $product;
+
+
+ /**
+ * The class "title" used in logging and exception messages.
+ * This should be prepended to the beginning of the message.
+ *
+ * @var string
+ */
+ protected $title = 'ldap:BaseFilter : ';
+
+
+ /**
+ * List of LDAP object types, used to determine the type of
+ * object that a DN references.
+ *
+ * @var array
+ */
+ protected $type_map;
+
+
+ /**
+ * Checks the authsource, if defined, for configuration values
+ * to the LDAP server. Then sets up the LDAP connection for the
+ * instance/object and stores everything in class members.
+ *
+ * @throws SimpleSAML_Error_Exception
+ * @param array $config
+ * @param $reserved
+ */
+ public function __construct(&$config, $reserved)
+ {
+ parent::__construct($config, $reserved);
+
+ // Change the class $title to match it's true name
+ // This way if the class is extended the proper name is used
+ $classname = get_class($this);
+ $classname = explode('_', $classname);
+ $this->title = 'ldap:' . end($classname) . ' : ';
+
+ // Log the construction
+ SimpleSAML\Logger::debug(
+ $this->title . 'Creating and configuring the filter.'
+ );
+
+ // If an authsource was defined (an not empty string)...
+ if (isset($config['authsource']) && $config['authsource']) {
+
+ // Log the authsource request
+ SimpleSAML\Logger::debug(
+ $this->title . 'Attempting to get configuration values from authsource [' .
+ $config['authsource'] . ']'
+ );
+
+ // Get the authsources file, which should contain the config
+ $authsource = SimpleSAML_Configuration::getConfig('authsources.php');
+
+ // Verify that the authsource config exists
+ if (!$authsource->hasValue($config['authsource'])) {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'Authsource [' . $config['authsource'] .
+ '] defined in filter parameters not found in authsources.php'
+ );
+ }
+
+ // Get just the specified authsource config values
+ $authsource = $authsource->getConfigItem($config['authsource']);
+ $authsource = $authsource->toArray();
+
+ // Make sure it is an ldap source
+ // TODO: Support ldap:LDAPMulti, if possible
+ if (@$authsource[0] != 'ldap:LDAP') {
+ throw new SimpleSAML_Error_Exception(
+ $this->title . 'Authsource [' . $config['authsource'] .
+ '] specified in filter parameters is not an ldap:LDAP type'
+ );
+ }
+
+ // Build the authsource config
+ $authconfig = array();
+ $authconfig['ldap.hostname'] = @$authsource['hostname'];
+ $authconfig['ldap.enable_tls'] = @$authsource['enable_tls'];
+ $authconfig['ldap.port'] = @$authsource['port'];
+ $authconfig['ldap.timeout'] = @$authsource['timeout'];
+ $authconfig['ldap.debug'] = @$authsource['debug'];
+ $authconfig['ldap.basedn'] = (@$authsource['search.enable'] ? @$authsource['search.base'] : null);
+ $authconfig['ldap.username'] = (@$authsource['search.enable'] ? @$authsource['search.username'] : null);
+ $authconfig['ldap.password'] = (@$authsource['search.enable'] ? @$authsource['search.password'] : null);
+ $authconfig['ldap.username'] = (@$authsource['priv.read'] ? @$authsource['priv.username'] : $authconfig['ldap.username']);
+ $authconfig['ldap.password'] = (@$authsource['priv.read'] ? @$authsource['priv.password'] : $authconfig['ldap.password']);
+
+ // Only set the username attribute if the authsource specifies one attribute
+ if (@$authsource['search.enable'] && is_array(@$authsource['search.attributes'])
+ && count($authsource['search.attributes']) == 1) {
+ $authconfig['attribute.username'] = reset($authsource['search.attributes']);
+ }
+
+ // Merge the authsource config with the filter config,
+ // but have the filter config override the authsource config
+ $config = array_merge($authconfig, $config);
+
+ // Authsource complete
+ SimpleSAML\Logger::debug(
+ $this->title . 'Retrieved authsource [' . $config['authsource'] .
+ '] configuration values: ' . $this->var_export($authconfig)
+ );
+ }
+
+ // Convert the config array to a config class,
+ // that way we can verify type and define defaults.
+ // Store in the instance in-case needed later, by a child class.
+ $this->config = SimpleSAML_Configuration::loadFromArray($config, 'ldap:AuthProcess');
+
+ // Set all the filter values, setting defaults if needed
+ $this->base_dn = $this->config->getArrayizeString('ldap.basedn', '');
+ $this->product = $this->config->getString('ldap.product', '');
+
+ // Cleanup the directory service, so that it is easier for
+ // child classes to determine service name consistently
+ $this->product = trim($this->product);
+ $this->product = strtoupper($this->product);
+
+ // Log the member values retrieved above
+ SimpleSAML\Logger::debug(
+ $this->title . 'Configuration values retrieved;' .
+ ' BaseDN: ' . $this->var_export($this->base_dn) .
+ ' Product: ' . $this->var_export($this->product)
+ );
+
+ // Setup the attribute map which will be used to search LDAP
+ $this->attribute_map = array(
+ 'dn' => $this->config->getString('attribute.dn', 'distinguishedName'),
+ 'groups' => $this->config->getString('attribute.groups', 'groups'),
+ 'member' => $this->config->getString('attribute.member', 'member'),
+ 'memberof' => $this->config->getString('attribute.memberof', 'memberOf'),
+ 'name' => $this->config->getString('attribute.groupname', 'name'),
+ 'type' => $this->config->getString('attribute.type', 'objectClass'),
+ 'username' => $this->config->getString('attribute.username', 'sAMAccountName')
+ );
+
+ // Log the attribute map
+ SimpleSAML\Logger::debug(
+ $this->title . 'Attribute map created: ' . $this->var_export($this->attribute_map)
+ );
+
+ // Setup the object type map which is used to determine a DNs' type
+ $this->type_map = array(
+ 'group' => $this->config->getString('type.group', 'group'),
+ 'user' => $this->config->getString('type.user', 'user')
+ );
+
+ // Log the type map
+ SimpleSAML\Logger::debug(
+ $this->title . 'Type map created: ' . $this->var_export($this->type_map)
+ );
+ }
+
+ /**
+ * Getter for the LDAP connection object. Created this getter
+ * rather than setting in the constructor to avoid unnecessarily
+ * connecting to LDAP when it might not be needed.
+ *
+ * @return sspmod_ldap_LdapConnection
+ */
+ protected function getLdap()
+ {
+ // Check if already connected
+ if ($this->ldap) {
+ return $this->ldap;
+ }
+
+ // Get the connection specific options
+ $hostname = $this->config->getString('ldap.hostname');
+ $port = $this->config->getInteger('ldap.port', 389);
+ $enable_tls = $this->config->getBoolean('ldap.enable_tls', false);
+ $debug = $this->config->getBoolean('ldap.debug', false);
+ $timeout = $this->config->getInteger('ldap.timeout', 0);
+ $username = $this->config->getString('ldap.username', null);
+ $password = $this->config->getString('ldap.password', null);
+
+ // Log the LDAP connection
+ SimpleSAML\Logger::debug(
+ $this->title . 'Connecting to LDAP server;' .
+ ' Hostname: ' . $hostname .
+ ' Port: ' . $port .
+ ' Enable TLS: ' . ($enable_tls ? 'Yes' : 'No') .
+ ' Debug: ' . ($debug ? 'Yes' : 'No') .
+ ' Timeout: ' . $timeout .
+ ' Username: ' . $username .
+ ' Password: ' . str_repeat('*', strlen($password))
+ );
+
+ // Connect to the LDAP server to be queried during processing
+ $this->ldap = new SimpleSAML_Auth_LDAP($hostname, $enable_tls, $debug, $timeout, $port);
+ $this->ldap->bind($username, $password);
+
+ // All done
+ return $this->ldap;
+ }
+
+ /**
+ * Local utility function to get details about a variable,
+ * basically converting it to a string to be used in a log
+ * message. The var_export() function returns several lines
+ * so this will remove the new lines and trim each line.
+ *
+ * @param mixed $value
+ * @return string
+ */
+ protected function var_export($value)
+ {
+ $export = var_export($value, true);
+ $lines = explode("\n", $export);
+ foreach ($lines as &$line) {
+ $line = trim($line);
+ }
+ return implode(' ', $lines);
+ }
}
diff --git a/modules/ldap/lib/Auth/Source/LDAP.php b/modules/ldap/lib/Auth/Source/LDAP.php
index 83b35fa..7bf979a 100644
--- a/modules/ldap/lib/Auth/Source/LDAP.php
+++ b/modules/ldap/lib/Auth/Source/LDAP.php
@@ -10,45 +10,48 @@
*
* @package SimpleSAMLphp
*/
-class sspmod_ldap_Auth_Source_LDAP extends sspmod_core_Auth_UserPassBase {
-
- /**
- * A LDAP configuration object.
- */
- private $ldapConfig;
-
-
- /**
- * Constructor for this authentication source.
- *
- * @param array $info Information about this authentication source.
- * @param array $config Configuration.
- */
- public function __construct($info, $config) {
- assert('is_array($info)');
- assert('is_array($config)');
-
- // Call the parent constructor first, as required by the interface
- parent::__construct($info, $config);
-
- $this->ldapConfig = new sspmod_ldap_ConfigHelper($config,
- 'Authentication source ' . var_export($this->authId, TRUE));
- }
-
-
- /**
- * Attempt to log in using the given username and password.
- *
- * @param string $username The username the user wrote.
- * @param string $password The password the user wrote.
- * param array $sasl_arg Associative array of SASL options
- * @return array Associative array with the users attributes.
- */
- protected function login($username, $password, array $sasl_args = NULL) {
- assert('is_string($username)');
- assert('is_string($password)');
-
- return $this->ldapConfig->login($username, $password, $sasl_args);
- }
+class sspmod_ldap_Auth_Source_LDAP extends sspmod_core_Auth_UserPassBase
+{
+
+ /**
+ * A LDAP configuration object.
+ */
+ private $ldapConfig;
+
+
+ /**
+ * Constructor for this authentication source.
+ *
+ * @param array $info Information about this authentication source.
+ * @param array $config Configuration.
+ */
+ public function __construct($info, $config)
+ {
+ assert('is_array($info)');
+ assert('is_array($config)');
+
+ // Call the parent constructor first, as required by the interface
+ parent::__construct($info, $config);
+
+ $this->ldapConfig = new sspmod_ldap_ConfigHelper($config,
+ 'Authentication source ' . var_export($this->authId, true));
+ }
+
+
+ /**
+ * Attempt to log in using the given username and password.
+ *
+ * @param string $username The username the user wrote.
+ * @param string $password The password the user wrote.
+ * param array $sasl_arg Associative array of SASL options
+ * @return array Associative array with the users attributes.
+ */
+ protected function login($username, $password, array $sasl_args = null)
+ {
+ assert('is_string($username)');
+ assert('is_string($password)');
+
+ return $this->ldapConfig->login($username, $password, $sasl_args);
+ }
}
diff --git a/modules/ldap/lib/Auth/Source/LDAPMulti.php b/modules/ldap/lib/Auth/Source/LDAPMulti.php
index 4f5adaf..e38118e 100644
--- a/modules/ldap/lib/Auth/Source/LDAPMulti.php
+++ b/modules/ldap/lib/Auth/Source/LDAPMulti.php
@@ -10,112 +10,115 @@
*
* @package SimpleSAMLphp
*/
-class sspmod_ldap_Auth_Source_LDAPMulti extends sspmod_core_Auth_UserPassOrgBase {
-
- /**
- * An array with descriptions for organizations.
- */
- private $orgs;
-
- /**
- * An array of organization IDs to LDAP configuration objects.
- */
- private $ldapOrgs;
-
- /**
- * Whether we should include the organization as part of the username.
- */
- private $includeOrgInUsername;
-
-
- /**
- * Constructor for this authentication source.
- *
- * @param array $info Information about this authentication source.
- * @param array $config Configuration.
- */
- public function __construct($info, $config) {
- assert('is_array($info)');
- assert('is_array($config)');
-
- // Call the parent constructor first, as required by the interface
- parent::__construct($info, $config);
-
- $cfgHelper = SimpleSAML_Configuration::loadFromArray($config,
- 'Authentication source ' . var_export($this->authId, TRUE));
-
-
- $this->orgs = array();
- $this->ldapOrgs = array();
- foreach ($config as $name => $value) {
-
- if ($name === 'username_organization_method') {
- $usernameOrgMethod = $cfgHelper->getValueValidate(
- 'username_organization_method',
- array('none', 'allow', 'force'));
- $this->setUsernameOrgMethod($usernameOrgMethod);
- continue;
- }
-
- if ($name === 'include_organization_in_username') {
- $this->includeOrgInUsername = $cfgHelper->getBoolean(
- 'include_organization_in_username', FALSE);
- continue;
- }
-
- $orgCfg = $cfgHelper->getArray($name);
- $orgId = $name;
-
- if (array_key_exists('description', $orgCfg)) {
- $this->orgs[$orgId] = $orgCfg['description'];
- } else {
- $this->orgs[$orgId] = $orgId;
- }
-
- $orgCfg = new sspmod_ldap_ConfigHelper($orgCfg,
- 'Authentication source ' . var_export($this->authId, TRUE) .
- ', organization ' . var_export($orgId, TRUE));
- $this->ldapOrgs[$orgId] = $orgCfg;
- }
- }
-
-
- /**
- * Attempt to log in using the given username and password.
- *
- * @param string $username The username the user wrote.
- * @param string $password The password the user wrote.
- * @param string $org The organization the user chose.
- * @return array Associative array with the users attributes.
- */
- protected function login($username, $password, $org, array $sasl_args = NULL) {
- assert('is_string($username)');
- assert('is_string($password)');
- assert('is_string($org)');
-
- if (!array_key_exists($org, $this->ldapOrgs)) {
- // The user has selected an organization which doesn't exist anymore.
- SimpleSAML\Logger::warning('Authentication source ' . var_export($this->authId, TRUE) .
- ': Organization seems to have disappeared while the user logged in.' .
- ' Organization was ' . var_export($org, TRUE));
- throw new SimpleSAML_Error_Error('WRONGUSERPASS');
- }
-
- if ($this->includeOrgInUsername) {
- $username = $username . '@' . $org;
- }
-
- return $this->ldapOrgs[$org]->login($username, $password, $sasl_args);
- }
-
-
- /**
- * Retrieve list of organizations.
- *
- * @return array Associative array with the organizations.
- */
- protected function getOrganizations() {
- return $this->orgs;
- }
-
+class sspmod_ldap_Auth_Source_LDAPMulti extends sspmod_core_Auth_UserPassOrgBase
+{
+
+ /**
+ * An array with descriptions for organizations.
+ */
+ private $orgs;
+
+ /**
+ * An array of organization IDs to LDAP configuration objects.
+ */
+ private $ldapOrgs;
+
+ /**
+ * Whether we should include the organization as part of the username.
+ */
+ private $includeOrgInUsername;
+
+
+ /**
+ * Constructor for this authentication source.
+ *
+ * @param array $info Information about this authentication source.
+ * @param array $config Configuration.
+ */
+ public function __construct($info, $config)
+ {
+ assert('is_array($info)');
+ assert('is_array($config)');
+
+ // Call the parent constructor first, as required by the interface
+ parent::__construct($info, $config);
+
+ $cfgHelper = SimpleSAML_Configuration::loadFromArray($config,
+ 'Authentication source ' . var_export($this->authId, true));
+
+
+ $this->orgs = array();
+ $this->ldapOrgs = array();
+ foreach ($config as $name => $value) {
+
+ if ($name === 'username_organization_method') {
+ $usernameOrgMethod = $cfgHelper->getValueValidate(
+ 'username_organization_method',
+ array('none', 'allow', 'force'));
+ $this->setUsernameOrgMethod($usernameOrgMethod);
+ continue;
+ }
+
+ if ($name === 'include_organization_in_username') {
+ $this->includeOrgInUsername = $cfgHelper->getBoolean(
+ 'include_organization_in_username', false);
+ continue;
+ }
+
+ $orgCfg = $cfgHelper->getArray($name);
+ $orgId = $name;
+
+ if (array_key_exists('description', $orgCfg)) {
+ $this->orgs[$orgId] = $orgCfg['description'];
+ } else {
+ $this->orgs[$orgId] = $orgId;
+ }
+
+ $orgCfg = new sspmod_ldap_ConfigHelper($orgCfg,
+ 'Authentication source ' . var_export($this->authId, true) .
+ ', organization ' . var_export($orgId, true));
+ $this->ldapOrgs[$orgId] = $orgCfg;
+ }
+ }
+
+
+ /**
+ * Attempt to log in using the given username and password.
+ *
+ * @param string $username The username the user wrote.
+ * @param string $password The password the user wrote.
+ * @param string $org The organization the user chose.
+ * @return array Associative array with the users attributes.
+ */
+ protected function login($username, $password, $org, array $sasl_args = null)
+ {
+ assert('is_string($username)');
+ assert('is_string($password)');
+ assert('is_string($org)');
+
+ if (!array_key_exists($org, $this->ldapOrgs)) {
+ // The user has selected an organization which doesn't exist anymore.
+ SimpleSAML\Logger::warning('Authentication source ' . var_export($this->authId, true) .
+ ': Organization seems to have disappeared while the user logged in.' .
+ ' Organization was ' . var_export($org, true));
+ throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+ }
+
+ if ($this->includeOrgInUsername) {
+ $username = $username . '@' . $org;
+ }
+
+ return $this->ldapOrgs[$org]->login($username, $password, $sasl_args);
+ }
+
+
+ /**
+ * Retrieve list of organizations.
+ *
+ * @return array Associative array with the organizations.
+ */
+ protected function getOrganizations()
+ {
+ return $this->orgs;
+ }
}
diff --git a/modules/ldap/lib/ConfigHelper.php b/modules/ldap/lib/ConfigHelper.php
index 7c8802d..28e4151 100644
--- a/modules/ldap/lib/ConfigHelper.php
+++ b/modules/ldap/lib/ConfigHelper.php
@@ -8,292 +8,297 @@
*
* @package SimpleSAMLphp
*/
-class sspmod_ldap_ConfigHelper {
+class sspmod_ldap_ConfigHelper
+{
+ /**
+ * String with the location of this configuration.
+ * Used for error reporting.
+ */
+ private $location;
- /**
- * String with the location of this configuration.
- * Used for error reporting.
- */
- private $location;
+ /**
+ * The hostname of the LDAP server.
+ */
+ private $hostname;
- /**
- * The hostname of the LDAP server.
- */
- private $hostname;
+ /**
+ * Whether we should use TLS/SSL when contacting the LDAP server.
+ */
+ private $enableTLS;
- /**
- * Whether we should use TLS/SSL when contacting the LDAP server.
- */
- private $enableTLS;
+ /**
+ * Whether debug output is enabled.
+ *
+ * @var bool
+ */
+ private $debug;
- /**
- * Whether debug output is enabled.
- *
- * @var bool
- */
- private $debug;
+ /**
+ * The timeout for accessing the LDAP server.
+ *
+ * @var int
+ */
+ private $timeout;
+ /**
+ * The port used when accessing the LDAP server.
+ *
+ * @var int
+ */
+ private $port;
- /**
- * The timeout for accessing the LDAP server.
- *
- * @var int
- */
- private $timeout;
+ /**
+ * Whether to follow referrals
+ */
+ private $referrals;
- /**
- * The port used when accessing the LDAP server.
- *
- * @var int
- */
- private $port;
- /**
- * Whether to follow referrals
- */
- private $referrals;
+ /**
+ * Whether we need to search for the users DN.
+ */
+ private $searchEnable;
- /**
- * Whether we need to search for the users DN.
- */
- private $searchEnable;
+ /**
+ * The username we should bind with before we can search for the user.
+ */
+ private $searchUsername;
- /**
- * The username we should bind with before we can search for the user.
- */
- private $searchUsername;
+ /**
+ * The password we should bind with before we can search for the user.
+ */
+ private $searchPassword;
- /**
- * The password we should bind with before we can search for the user.
- */
- private $searchPassword;
-
-
- /**
- * Array with the base DN(s) for the search.
- */
- private $searchBase;
-
- /**
- * Additional LDAP filter fields for the search
- */
- private $searchFilter;
-
- /**
- * The attributes which should match the username.
- */
- private $searchAttributes;
-
-
- /**
- * The DN pattern we should use to create the DN from the username.
- */
- private $dnPattern;
-
-
- /**
- * The attributes we should fetch. Can be NULL in which case we will fetch all attributes.
- */
- private $attributes;
-
-
- /**
- * The user cannot get all attributes, privileged reader required
- */
- private $privRead;
-
-
- /**
- * The DN we should bind with before we can get the attributes.
- */
- private $privUsername;
-
-
- /**
- * The password we should bind with before we can get the attributes.
- */
- private $privPassword;
-
-
- /**
- * Constructor for this configuration parser.
- *
- * @param array $config Configuration.
- * @param string $location The location of this configuration. Used for error reporting.
- */
- public function __construct($config, $location) {
- assert('is_array($config)');
- assert('is_string($location)');
-
- $this->location = $location;
-
- // Parse configuration
- $config = SimpleSAML_Configuration::loadFromArray($config, $location);
-
- $this->hostname = $config->getString('hostname');
- $this->enableTLS = $config->getBoolean('enable_tls', FALSE);
- $this->debug = $config->getBoolean('debug', FALSE);
- $this->timeout = $config->getInteger('timeout', 0);
- $this->port = $config->getInteger('port', 389);
- $this->referrals = $config->getBoolean('referrals', TRUE);
- $this->searchEnable = $config->getBoolean('search.enable', FALSE);
- $this->privRead = $config->getBoolean('priv.read', FALSE);
-
- if ($this->searchEnable) {
- $this->searchUsername = $config->getString('search.username', NULL);
- if ($this->searchUsername !== NULL) {
- $this->searchPassword = $config->getString('search.password');
- }
-
- $this->searchBase = $config->getArrayizeString('search.base');
- $this->searchFilter = $config->getString('search.filter',NULL);
- $this->searchAttributes = $config->getArray('search.attributes');
-
- } else {
- $this->dnPattern = $config->getString('dnpattern');
- }
-
- // Are privs needed to get to the attributes?
- if ($this->privRead) {
- $this->privUsername = $config->getString('priv.username');
- $this->privPassword = $config->getString('priv.password');
- }
-
- $this->attributes = $config->getArray('attributes', NULL);
- }
-
-
- /**
- * Attempt to log in using the given username and password.
- *
- * Will throw a SimpleSAML_Error_Error('WRONGUSERPASS') if the username or password is wrong.
- * If there is a configuration problem, an Exception will be thrown.
- *
- * @param string $username The username the user wrote.
- * @param string $password The password the user wrote.
- * @param arrray $sasl_args Array of SASL options for LDAP bind.
- * @return array Associative array with the users attributes.
- */
- public function login($username, $password, array $sasl_args = NULL) {
- assert('is_string($username)');
- assert('is_string($password)');
-
- if (empty($password)) {
- SimpleSAML\Logger::info($this->location . ': Login with empty password disallowed.');
- throw new SimpleSAML_Error_Error('WRONGUSERPASS');
- }
-
- $ldap = new SimpleSAML_Auth_LDAP($this->hostname, $this->enableTLS, $this->debug, $this->timeout, $this->port, $this->referrals);
-
- if (!$this->searchEnable) {
- $ldapusername = addcslashes($username, ',+"\\<>;*');
- $dn = str_replace('%username%', $ldapusername, $this->dnPattern);
- } else {
- if ($this->searchUsername !== NULL) {
- if(!$ldap->bind($this->searchUsername, $this->searchPassword)) {
- throw new Exception('Error authenticating using search username & password.');
- }
- }
-
- $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, TRUE, $this->searchFilter);
- if ($dn === NULL) {
- /* User not found with search. */
- SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\'');
- throw new SimpleSAML_Error_Error('WRONGUSERPASS');
- }
- }
-
- if (!$ldap->bind($dn, $password, $sasl_args)) {
- SimpleSAML\Logger::info($this->location . ': '. $username . ' failed to authenticate. DN=' . $dn);
- throw new SimpleSAML_Error_Error('WRONGUSERPASS');
- }
-
- /* In case of SASL bind, authenticated and authorized DN may differ */
- if (isset($sasl_args))
- $dn = $ldap->whoami($this->searchBase, $this->searchAttributes);
-
- /* Are privs needed to get the attributes? */
- if ($this->privRead) {
- /* Yes, rebind with privs */
- if(!$ldap->bind($this->privUsername, $this->privPassword)) {
- throw new Exception('Error authenticating using privileged DN & password.');
- }
- }
-
- return $ldap->getAttributes($dn, $this->attributes);
- }
-
-
- /**
- * Search for a DN.
- *
- * @param string|array $attribute
- * The attribute name(s) searched for. If set to NULL, values from
- * configuration is used.
- * @param string $value
- * The attribute value searched for.
- * @param bool $allowZeroHits
- * Determines if the method will throw an exception if no
- * hits are found. Defaults to FALSE.
- * @return string
- * The DN of the matching element, if found. If no element was
- * found and $allowZeroHits is set to FALSE, an exception will
- * be thrown; otherwise NULL will be returned.
- * @throws SimpleSAML_Error_AuthSource if:
- * - LDAP search encounter some problems when searching cataloge
- * - Not able to connect to LDAP server
- * @throws SimpleSAML_Error_UserNotFound if:
- * - $allowZeroHits er TRUE and no result is found
- *
- */
- public function searchfordn($attribute, $value, $allowZeroHits) {
- $ldap = new SimpleSAML_Auth_LDAP($this->hostname,
- $this->enableTLS,
- $this->debug,
- $this->timeout,
- $this->port,
- $this->referrals);
-
- if ($attribute == NULL)
- $attribute = $this->searchAttributes;
-
- if ($this->searchUsername !== NULL) {
- if(!$ldap->bind($this->searchUsername, $this->searchPassword)) {
- throw new Exception('Error authenticating using search username & password.');
- }
- }
-
- return $ldap->searchfordn($this->searchBase, $attribute,
- $value, $allowZeroHits);
- }
-
- public function getAttributes($dn, $attributes = NULL) {
- if ($attributes == NULL)
- $attributes = $this->attributes;
-
- $ldap = new SimpleSAML_Auth_LDAP($this->hostname,
- $this->enableTLS,
- $this->debug,
- $this->timeout,
- $this->port,
- $this->referrals);
-
- /* Are privs needed to get the attributes? */
- if ($this->privRead) {
- /* Yes, rebind with privs */
- if(!$ldap->bind($this->privUsername, $this->privPassword)) {
- throw new Exception('Error authenticating using privileged DN & password.');
- }
- }
-
- return $ldap->getAttributes($dn, $attributes);
- }
+ /**
+ * Array with the base DN(s) for the search.
+ */
+ private $searchBase;
+
+ /**
+ * Additional LDAP filter fields for the search
+ */
+ private $searchFilter;
+
+ /**
+ * The attributes which should match the username.
+ */
+ private $searchAttributes;
+
+
+ /**
+ * The DN pattern we should use to create the DN from the username.
+ */
+ private $dnPattern;
+
+
+ /**
+ * The attributes we should fetch. Can be NULL in which case we will fetch all attributes.
+ */
+ private $attributes;
+
+
+ /**
+ * The user cannot get all attributes, privileged reader required
+ */
+ private $privRead;
+
+
+ /**
+ * The DN we should bind with before we can get the attributes.
+ */
+ private $privUsername;
+
+
+ /**
+ * The password we should bind with before we can get the attributes.
+ */
+ private $privPassword;
+
+
+ /**
+ * Constructor for this configuration parser.
+ *
+ * @param array $config Configuration.
+ * @param string $location The location of this configuration. Used for error reporting.
+ */
+ public function __construct($config, $location)
+ {
+ assert('is_array($config)');
+ assert('is_string($location)');
+
+ $this->location = $location;
+
+ // Parse configuration
+ $config = SimpleSAML_Configuration::loadFromArray($config, $location);
+
+ $this->hostname = $config->getString('hostname');
+ $this->enableTLS = $config->getBoolean('enable_tls', false);
+ $this->debug = $config->getBoolean('debug', false);
+ $this->timeout = $config->getInteger('timeout', 0);
+ $this->port = $config->getInteger('port', 389);
+ $this->referrals = $config->getBoolean('referrals', true);
+ $this->searchEnable = $config->getBoolean('search.enable', false);
+ $this->privRead = $config->getBoolean('priv.read', false);
+
+ if ($this->searchEnable) {
+ $this->searchUsername = $config->getString('search.username', null);
+ if ($this->searchUsername !== null) {
+ $this->searchPassword = $config->getString('search.password');
+ }
+
+ $this->searchBase = $config->getArrayizeString('search.base');
+ $this->searchFilter = $config->getString('search.filter', null);
+ $this->searchAttributes = $config->getArray('search.attributes');
+
+ } else {
+ $this->dnPattern = $config->getString('dnpattern');
+ }
+
+ // Are privs needed to get to the attributes?
+ if ($this->privRead) {
+ $this->privUsername = $config->getString('priv.username');
+ $this->privPassword = $config->getString('priv.password');
+ }
+
+ $this->attributes = $config->getArray('attributes', null);
+ }
+
+
+ /**
+ * Attempt to log in using the given username and password.
+ *
+ * Will throw a SimpleSAML_Error_Error('WRONGUSERPASS') if the username or password is wrong.
+ * If there is a configuration problem, an Exception will be thrown.
+ *
+ * @param string $username The username the user wrote.
+ * @param string $password The password the user wrote.
+ * @param arrray $sasl_args Array of SASL options for LDAP bind.
+ * @return array Associative array with the users attributes.
+ */
+ public function login($username, $password, array $sasl_args = null)
+ {
+ assert('is_string($username)');
+ assert('is_string($password)');
+
+ if (empty($password)) {
+ SimpleSAML\Logger::info($this->location . ': Login with empty password disallowed.');
+ throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+ }
+
+ $ldap = new SimpleSAML_Auth_LDAP($this->hostname, $this->enableTLS, $this->debug, $this->timeout, $this->port, $this->referrals);
+
+ if (!$this->searchEnable) {
+ $ldapusername = addcslashes($username, ',+"\\<>;*');
+ $dn = str_replace('%username%', $ldapusername, $this->dnPattern);
+ } else {
+ if ($this->searchUsername !== null) {
+ if (!$ldap->bind($this->searchUsername, $this->searchPassword)) {
+ throw new Exception('Error authenticating using search username & password.');
+ }
+ }
+
+ $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, true, $this->searchFilter);
+ if ($dn === null) {
+ /* User not found with search. */
+ SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\'');
+ throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+ }
+ }
+
+ if (!$ldap->bind($dn, $password, $sasl_args)) {
+ SimpleSAML\Logger::info($this->location . ': '. $username . ' failed to authenticate. DN=' . $dn);
+ throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+ }
+
+ /* In case of SASL bind, authenticated and authorized DN may differ */
+ if (isset($sasl_args)) {
+ $dn = $ldap->whoami($this->searchBase, $this->searchAttributes);
+ }
+
+ /* Are privs needed to get the attributes? */
+ if ($this->privRead) {
+ /* Yes, rebind with privs */
+ if (!$ldap->bind($this->privUsername, $this->privPassword)) {
+ throw new Exception('Error authenticating using privileged DN & password.');
+ }
+ }
+
+ return $ldap->getAttributes($dn, $this->attributes);
+ }
+
+
+ /**
+ * Search for a DN.
+ *
+ * @param string|array $attribute
+ * The attribute name(s) searched for. If set to NULL, values from
+ * configuration is used.
+ * @param string $value
+ * The attribute value searched for.
+ * @param bool $allowZeroHits
+ * Determines if the method will throw an exception if no
+ * hits are found. Defaults to FALSE.
+ * @return string
+ * The DN of the matching element, if found. If no element was
+ * found and $allowZeroHits is set to FALSE, an exception will
+ * be thrown; otherwise NULL will be returned.
+ * @throws SimpleSAML_Error_AuthSource if:
+ * - LDAP search encounter some problems when searching cataloge
+ * - Not able to connect to LDAP server
+ * @throws SimpleSAML_Error_UserNotFound if:
+ * - $allowZeroHits is FALSE and no result is found
+ *
+ */
+ public function searchfordn($attribute, $value, $allowZeroHits)
+ {
+ $ldap = new SimpleSAML_Auth_LDAP($this->hostname,
+ $this->enableTLS,
+ $this->debug,
+ $this->timeout,
+ $this->port,
+ $this->referrals);
+
+ if ($attribute == null) {
+ $attribute = $this->searchAttributes;
+ }
+
+ if ($this->searchUsername !== null) {
+ if (!$ldap->bind($this->searchUsername, $this->searchPassword)) {
+ throw new Exception('Error authenticating using search username & password.');
+ }
+ }
+
+ return $ldap->searchfordn($this->searchBase, $attribute,
+ $value, $allowZeroHits);
+ }
+
+ public function getAttributes($dn, $attributes = null)
+ {
+ if ($attributes == null) {
+ $attributes = $this->attributes;
+ }
+
+ $ldap = new SimpleSAML_Auth_LDAP($this->hostname,
+ $this->enableTLS,
+ $this->debug,
+ $this->timeout,
+ $this->port,
+ $this->referrals);
+
+ /* Are privs needed to get the attributes? */
+ if ($this->privRead) {
+ /* Yes, rebind with privs */
+ if (!$ldap->bind($this->privUsername, $this->privPassword)) {
+ throw new Exception('Error authenticating using privileged DN & password.');
+ }
+ }
+ return $ldap->getAttributes($dn, $attributes);
+ }
}
diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php
index 8fbf235..92e5363 100644
--- a/modules/saml/lib/IdP/SAML2.php
+++ b/modules/saml/lib/IdP/SAML2.php
@@ -1,1054 +1,1149 @@
<?php
+
+use RobRichards\XMLSecLibs\XMLSecurityKey;
+
/**
* IdP implementation for SAML 2.0 protocol.
*
* @package SimpleSAMLphp
*/
-class sspmod_saml_IdP_SAML2 {
-
- /**
- * Send a response to the SP.
- *
- * @param array $state The authentication state.
- */
- public static function sendResponse(array $state) {
- assert('isset($state["Attributes"])');
- assert('isset($state["SPMetadata"])');
- assert('isset($state["saml:ConsumerURL"])');
- assert('array_key_exists("saml:RequestId", $state)'); // Can be NULL
- assert('array_key_exists("saml:RelayState", $state)'); // Can be NULL.
-
- $spMetadata = $state["SPMetadata"];
- $spEntityId = $spMetadata['entityid'];
- $spMetadata = SimpleSAML_Configuration::loadFromArray($spMetadata,
- '$metadata[' . var_export($spEntityId, TRUE) . ']');
-
- SimpleSAML\Logger::info('Sending SAML 2.0 Response to ' . var_export($spEntityId, TRUE));
-
- $requestId = $state['saml:RequestId'];
- $relayState = $state['saml:RelayState'];
- $consumerURL = $state['saml:ConsumerURL'];
- $protocolBinding = $state['saml:Binding'];
-
- $idp = SimpleSAML_IdP::getByState($state);
-
- $idpMetadata = $idp->getConfig();
-
- $assertion = self::buildAssertion($idpMetadata, $spMetadata, $state);
-
- if (isset($state['saml:AuthenticatingAuthority'])) {
- $assertion->setAuthenticatingAuthority($state['saml:AuthenticatingAuthority']);
- }
-
- // Create the session association (for logout).
- $association = array(
- 'id' => 'saml:' . $spEntityId,
- 'Handler' => 'sspmod_saml_IdP_SAML2',
- 'Expires' => $assertion->getSessionNotOnOrAfter(),
- 'saml:entityID' => $spEntityId,
- 'saml:NameID' => $state['saml:idp:NameID'],
- 'saml:SessionIndex' => $assertion->getSessionIndex(),
- );
-
- // Maybe encrypt the assertion.
- $assertion = self::encryptAssertion($idpMetadata, $spMetadata, $assertion);
-
- /* Create the response. */
- $ar = self::buildResponse($idpMetadata, $spMetadata, $consumerURL);
- $ar->setInResponseTo($requestId);
- $ar->setRelayState($relayState);
- $ar->setAssertions(array($assertion));
-
- /* Register the session association with the IdP. */
- $idp->addAssociation($association);
-
- $statsData = array(
- 'spEntityID' => $spEntityId,
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- 'protocol' => 'saml2',
- );
- if (isset($state['saml:AuthnRequestReceivedAt'])) {
- $statsData['logintime'] = microtime(TRUE) - $state['saml:AuthnRequestReceivedAt'];
- }
- SimpleSAML_Stats::log('saml:idp:Response', $statsData);
-
- /* Send the response. */
- $binding = \SAML2\Binding::getBinding($protocolBinding);
- $binding->send($ar);
- }
-
-
- /**
- * Handle authentication error.
- *
- * SimpleSAML_Error_Exception $exception The exception.
- * @param array $state The error state.
- */
- public static function handleAuthError(SimpleSAML_Error_Exception $exception, array $state) {
- assert('isset($state["SPMetadata"])');
- assert('isset($state["saml:ConsumerURL"])');
- assert('array_key_exists("saml:RequestId", $state)'); // Can be NULL.
- assert('array_key_exists("saml:RelayState", $state)'); // Can be NULL.
-
- $spMetadata = $state["SPMetadata"];
- $spEntityId = $spMetadata['entityid'];
- $spMetadata = SimpleSAML_Configuration::loadFromArray($spMetadata,
- '$metadata[' . var_export($spEntityId, TRUE) . ']');
-
- $requestId = $state['saml:RequestId'];
- $relayState = $state['saml:RelayState'];
- $consumerURL = $state['saml:ConsumerURL'];
- $protocolBinding = $state['saml:Binding'];
-
- $idp = SimpleSAML_IdP::getByState($state);
-
- $idpMetadata = $idp->getConfig();
-
- $error = sspmod_saml_Error::fromException($exception);
-
- SimpleSAML\Logger::warning("Returning error to SP with entity ID '".var_export($spEntityId, TRUE)."'.");
- $exception->log(SimpleSAML\Logger::WARNING);
-
- $ar = self::buildResponse($idpMetadata, $spMetadata, $consumerURL);
- $ar->setInResponseTo($requestId);
- $ar->setRelayState($relayState);
-
- $status = array(
- 'Code' => $error->getStatus(),
- 'SubCode' => $error->getSubStatus(),
- 'Message' => $error->getStatusMessage(),
- );
- $ar->setStatus($status);
-
- $statsData = array(
- 'spEntityID' => $spEntityId,
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- 'protocol' => 'saml2',
- 'error' => $status,
- );
- if (isset($state['saml:AuthnRequestReceivedAt'])) {
- $statsData['logintime'] = microtime(TRUE) - $state['saml:AuthnRequestReceivedAt'];
- }
- SimpleSAML_Stats::log('saml:idp:Response:error', $statsData);
-
- $binding = \SAML2\Binding::getBinding($protocolBinding);
- $binding->send($ar);
- }
-
-
- /**
- * Find SP AssertionConsumerService based on parameter in AuthnRequest.
- *
- * @param array $supportedBindings The bindings we allow for the response.
- * @param SimpleSAML_Configuration $spMetadata The metadata for the SP.
- * @param string|NULL $AssertionConsumerServiceURL AssertionConsumerServiceURL from request.
- * @param string|NULL $ProtocolBinding ProtocolBinding from request.
- * @param int|NULL $AssertionConsumerServiceIndex AssertionConsumerServiceIndex from request.
- * @return array Array with the Location and Binding we should use for the response.
- */
- private static function getAssertionConsumerService(array $supportedBindings, SimpleSAML_Configuration $spMetadata,
- $AssertionConsumerServiceURL, $ProtocolBinding, $AssertionConsumerServiceIndex) {
- assert('is_string($AssertionConsumerServiceURL) || is_null($AssertionConsumerServiceURL)');
- assert('is_string($ProtocolBinding) || is_null($ProtocolBinding)');
- assert('is_int($AssertionConsumerServiceIndex) || is_null($AssertionConsumerServiceIndex)');
-
- /* We want to pick the best matching endpoint in the case where for example
- * only the ProtocolBinding is given. We therefore pick endpoints with the
- * following priority:
- * 1. isDefault="true"
- * 2. isDefault unset
- * 3. isDefault="false"
- */
- $firstNotFalse = NULL;
- $firstFalse = NULL;
- foreach ($spMetadata->getEndpoints('AssertionConsumerService') as $ep) {
-
- if ($AssertionConsumerServiceURL !== NULL && $ep['Location'] !== $AssertionConsumerServiceURL) {
- continue;
- }
- if ($ProtocolBinding !== NULL && $ep['Binding'] !== $ProtocolBinding) {
- continue;
- }
- if ($AssertionConsumerServiceIndex !== NULL && $ep['index'] !== $AssertionConsumerServiceIndex) {
- continue;
- }
-
- if (!in_array($ep['Binding'], $supportedBindings, TRUE)) {
- /* The endpoint has an unsupported binding. */
- continue;
- }
-
- /* We have an endpoint that matches all our requirements. Check if it is the best one. */
-
- if (array_key_exists('isDefault', $ep)) {
- if ($ep['isDefault'] === TRUE) {
- /* This is the first matching endpoint with isDefault set to TRUE. */
- return $ep;
- }
- /* isDefault is set to FALSE, but the endpoint is still useable. */
- if ($firstFalse === NULL) {
- /* This is the first endpoint that we can use. */
- $firstFalse = $ep;
- }
- } else if ($firstNotFalse === NULL) {
- /* This is the first endpoint without isDefault set. */
- $firstNotFalse = $ep;
- }
- }
-
- if ($firstNotFalse !== NULL) {
- return $firstNotFalse;
- } elseif ($firstFalse !== NULL) {
- return $firstFalse;
- }
-
- SimpleSAML\Logger::warning('Authentication request specifies invalid AssertionConsumerService:');
- if ($AssertionConsumerServiceURL !== NULL) {
- SimpleSAML\Logger::warning('AssertionConsumerServiceURL: ' . var_export($AssertionConsumerServiceURL, TRUE));
- }
- if ($ProtocolBinding !== NULL) {
- SimpleSAML\Logger::warning('ProtocolBinding: ' . var_export($ProtocolBinding, TRUE));
- }
- if ($AssertionConsumerServiceIndex !== NULL) {
- SimpleSAML\Logger::warning('AssertionConsumerServiceIndex: ' . var_export($AssertionConsumerServiceIndex, TRUE));
- }
-
- /* We have no good endpoints. Our last resort is to just use the default endpoint. */
- return $spMetadata->getDefaultEndpoint('AssertionConsumerService', $supportedBindings);
- }
-
-
- /**
- * Receive an authentication request.
- *
- * @param SimpleSAML_IdP $idp The IdP we are receiving it for.
- */
- public static function receiveAuthnRequest(SimpleSAML_IdP $idp) {
-
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $idpMetadata = $idp->getConfig();
-
- $supportedBindings = array(\SAML2\Constants::BINDING_HTTP_POST);
- if ($idpMetadata->getBoolean('saml20.sendartifact', FALSE)) {
- $supportedBindings[] = \SAML2\Constants::BINDING_HTTP_ARTIFACT;
- }
- if ($idpMetadata->getBoolean('saml20.hok.assertion', FALSE)) {
- $supportedBindings[] = \SAML2\Constants::BINDING_HOK_SSO;
- }
-
- if (isset($_REQUEST['spentityid'])) {
- /* IdP initiated authentication. */
-
- if (isset($_REQUEST['cookieTime'])) {
- $cookieTime = (int)$_REQUEST['cookieTime'];
- if ($cookieTime + 5 > time()) {
- /*
- * Less than five seconds has passed since we were
- * here the last time. Cookies are probably disabled.
- */
- \SimpleSAML\Utils\HTTP::checkSessionCookie(\SimpleSAML\Utils\HTTP::getSelfURL());
- }
- }
-
- $spEntityId = (string)$_REQUEST['spentityid'];
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
-
- if (isset($_REQUEST['RelayState'])) {
- $relayState = (string)$_REQUEST['RelayState'];
- } else {
- $relayState = NULL;
- }
-
- if (isset($_REQUEST['binding'])){
- $protocolBinding = (string)$_REQUEST['binding'];
- } else {
- $protocolBinding = NULL;
- }
-
- if (isset($_REQUEST['NameIDFormat'])) {
- $nameIDFormat = (string)$_REQUEST['NameIDFormat'];
- } else {
- $nameIDFormat = NULL;
- }
-
- $requestId = NULL;
- $IDPList = array();
- $ProxyCount = NULL;
- $RequesterID = NULL;
- $forceAuthn = FALSE;
- $isPassive = FALSE;
- $consumerURL = NULL;
- $consumerIndex = NULL;
- $extensions = NULL;
- $allowCreate = TRUE;
- $authnContext = null;
-
- $idpInit = TRUE;
-
- SimpleSAML\Logger::info('SAML2.0 - IdP.SSOService: IdP initiated authentication: '. var_export($spEntityId, TRUE));
-
- } else {
-
- $binding = \SAML2\Binding::getCurrentBinding();
- $request = $binding->receive();
-
- if (!($request instanceof \SAML2\AuthnRequest)) {
- throw new SimpleSAML_Error_BadRequest('Message received on authentication request endpoint wasn\'t an authentication request.');
- }
-
- $spEntityId = $request->getIssuer();
- if ($spEntityId === NULL) {
- throw new SimpleSAML_Error_BadRequest('Received message on authentication request endpoint without issuer.');
- }
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
-
- sspmod_saml_Message::validateMessage($spMetadata, $idpMetadata, $request);
-
- $relayState = $request->getRelayState();
-
- $requestId = $request->getId();
- $IDPList = $request->getIDPList();
- $ProxyCount = $request->getProxyCount();
- if ($ProxyCount !== null) $ProxyCount--;
- $RequesterID = $request->getRequesterID();
- $forceAuthn = $request->getForceAuthn();
- $isPassive = $request->getIsPassive();
- $consumerURL = $request->getAssertionConsumerServiceURL();
- $protocolBinding = $request->getProtocolBinding();
- $consumerIndex = $request->getAssertionConsumerServiceIndex();
- $extensions = $request->getExtensions();
- $authnContext = $request->getRequestedAuthnContext();
-
- $nameIdPolicy = $request->getNameIdPolicy();
- if (isset($nameIdPolicy['Format'])) {
- $nameIDFormat = $nameIdPolicy['Format'];
- } else {
- $nameIDFormat = NULL;
- }
- if (isset($nameIdPolicy['AllowCreate'])) {
- $allowCreate = $nameIdPolicy['AllowCreate'];
- } else {
- $allowCreate = FALSE;
- }
-
- $idpInit = FALSE;
-
- SimpleSAML\Logger::info('SAML2.0 - IdP.SSOService: incoming authentication request: '. var_export($spEntityId, TRUE));
- }
-
- SimpleSAML_Stats::log('saml:idp:AuthnRequest', array(
- 'spEntityID' => $spEntityId,
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- 'forceAuthn' => $forceAuthn,
- 'isPassive' => $isPassive,
- 'protocol' => 'saml2',
- 'idpInit' => $idpInit,
- ));
-
- $acsEndpoint = self::getAssertionConsumerService($supportedBindings, $spMetadata, $consumerURL, $protocolBinding, $consumerIndex);
-
- $IDPList = array_unique(array_merge($IDPList, $spMetadata->getArrayizeString('IDPList', array())));
- if ($ProxyCount === null) $ProxyCount = $spMetadata->getInteger('ProxyCount', null);
-
- if (!$forceAuthn) {
- $forceAuthn = $spMetadata->getBoolean('ForceAuthn', FALSE);
- }
-
- $sessionLostParams = array(
- 'spentityid' => $spEntityId,
- 'cookieTime' => time(),
- );
- if ($relayState !== NULL) {
- $sessionLostParams['RelayState'] = $relayState;
- }
-
- $sessionLostURL = \SimpleSAML\Utils\HTTP::addURLParameters(
+class sspmod_saml_IdP_SAML2
+{
+
+ /**
+ * Send a response to the SP.
+ *
+ * @param array $state The authentication state.
+ */
+ public static function sendResponse(array $state)
+ {
+ assert('isset($state["Attributes"])');
+ assert('isset($state["SPMetadata"])');
+ assert('isset($state["saml:ConsumerURL"])');
+ assert('array_key_exists("saml:RequestId", $state)'); // Can be NULL
+ assert('array_key_exists("saml:RelayState", $state)'); // Can be NULL.
+
+ $spMetadata = $state["SPMetadata"];
+ $spEntityId = $spMetadata['entityid'];
+ $spMetadata = SimpleSAML_Configuration::loadFromArray(
+ $spMetadata,
+ '$metadata['.var_export($spEntityId, true).']'
+ );
+
+ SimpleSAML\Logger::info('Sending SAML 2.0 Response to '.var_export($spEntityId, true));
+
+ $requestId = $state['saml:RequestId'];
+ $relayState = $state['saml:RelayState'];
+ $consumerURL = $state['saml:ConsumerURL'];
+ $protocolBinding = $state['saml:Binding'];
+
+ $idp = SimpleSAML_IdP::getByState($state);
+
+ $idpMetadata = $idp->getConfig();
+
+ $assertion = self::buildAssertion($idpMetadata, $spMetadata, $state);
+
+ if (isset($state['saml:AuthenticatingAuthority'])) {
+ $assertion->setAuthenticatingAuthority($state['saml:AuthenticatingAuthority']);
+ }
+
+ // create the session association (for logout)
+ $association = array(
+ 'id' => 'saml:'.$spEntityId,
+ 'Handler' => 'sspmod_saml_IdP_SAML2',
+ 'Expires' => $assertion->getSessionNotOnOrAfter(),
+ 'saml:entityID' => $spEntityId,
+ 'saml:NameID' => $state['saml:idp:NameID'],
+ 'saml:SessionIndex' => $assertion->getSessionIndex(),
+ );
+
+ // maybe encrypt the assertion
+ $assertion = self::encryptAssertion($idpMetadata, $spMetadata, $assertion);
+
+ // create the response
+ $ar = self::buildResponse($idpMetadata, $spMetadata, $consumerURL);
+ $ar->setInResponseTo($requestId);
+ $ar->setRelayState($relayState);
+ $ar->setAssertions(array($assertion));
+
+ // register the session association with the IdP
+ $idp->addAssociation($association);
+
+ $statsData = array(
+ 'spEntityID' => $spEntityId,
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ 'protocol' => 'saml2',
+ );
+ if (isset($state['saml:AuthnRequestReceivedAt'])) {
+ $statsData['logintime'] = microtime(true) - $state['saml:AuthnRequestReceivedAt'];
+ }
+ SimpleSAML_Stats::log('saml:idp:Response', $statsData);
+
+ // send the response
+ $binding = \SAML2\Binding::getBinding($protocolBinding);
+ $binding->send($ar);
+ }
+
+
+ /**
+ * Handle authentication error.
+ *
+ * SimpleSAML_Error_Exception $exception The exception.
+ *
+ * @param array $state The error state.
+ */
+ public static function handleAuthError(SimpleSAML_Error_Exception $exception, array $state)
+ {
+ assert('isset($state["SPMetadata"])');
+ assert('isset($state["saml:ConsumerURL"])');
+ assert('array_key_exists("saml:RequestId", $state)'); // Can be NULL.
+ assert('array_key_exists("saml:RelayState", $state)'); // Can be NULL.
+
+ $spMetadata = $state["SPMetadata"];
+ $spEntityId = $spMetadata['entityid'];
+ $spMetadata = SimpleSAML_Configuration::loadFromArray(
+ $spMetadata,
+ '$metadata['.var_export($spEntityId, true).']'
+ );
+
+ $requestId = $state['saml:RequestId'];
+ $relayState = $state['saml:RelayState'];
+ $consumerURL = $state['saml:ConsumerURL'];
+ $protocolBinding = $state['saml:Binding'];
+
+ $idp = SimpleSAML_IdP::getByState($state);
+
+ $idpMetadata = $idp->getConfig();
+
+ $error = sspmod_saml_Error::fromException($exception);
+
+ SimpleSAML\Logger::warning("Returning error to SP with entity ID '".var_export($spEntityId, true)."'.");
+ $exception->log(SimpleSAML\Logger::WARNING);
+
+ $ar = self::buildResponse($idpMetadata, $spMetadata, $consumerURL);
+ $ar->setInResponseTo($requestId);
+ $ar->setRelayState($relayState);
+
+ $status = array(
+ 'Code' => $error->getStatus(),
+ 'SubCode' => $error->getSubStatus(),
+ 'Message' => $error->getStatusMessage(),
+ );
+ $ar->setStatus($status);
+
+ $statsData = array(
+ 'spEntityID' => $spEntityId,
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ 'protocol' => 'saml2',
+ 'error' => $status,
+ );
+ if (isset($state['saml:AuthnRequestReceivedAt'])) {
+ $statsData['logintime'] = microtime(true) - $state['saml:AuthnRequestReceivedAt'];
+ }
+ SimpleSAML_Stats::log('saml:idp:Response:error', $statsData);
+
+ $binding = \SAML2\Binding::getBinding($protocolBinding);
+ $binding->send($ar);
+ }
+
+
+ /**
+ * Find SP AssertionConsumerService based on parameter in AuthnRequest.
+ *
+ * @param array $supportedBindings The bindings we allow for the response.
+ * @param SimpleSAML_Configuration $spMetadata The metadata for the SP.
+ * @param string|NULL $AssertionConsumerServiceURL AssertionConsumerServiceURL from request.
+ * @param string|NULL $ProtocolBinding ProtocolBinding from request.
+ * @param int|NULL $AssertionConsumerServiceIndex AssertionConsumerServiceIndex from request.
+ *
+ * @return array Array with the Location and Binding we should use for the response.
+ */
+ private static function getAssertionConsumerService(
+ array $supportedBindings,
+ SimpleSAML_Configuration $spMetadata,
+ $AssertionConsumerServiceURL,
+ $ProtocolBinding,
+ $AssertionConsumerServiceIndex
+ ) {
+ assert('is_string($AssertionConsumerServiceURL) || is_null($AssertionConsumerServiceURL)');
+ assert('is_string($ProtocolBinding) || is_null($ProtocolBinding)');
+ assert('is_int($AssertionConsumerServiceIndex) || is_null($AssertionConsumerServiceIndex)');
+
+ /* We want to pick the best matching endpoint in the case where for example
+ * only the ProtocolBinding is given. We therefore pick endpoints with the
+ * following priority:
+ * 1. isDefault="true"
+ * 2. isDefault unset
+ * 3. isDefault="false"
+ */
+ $firstNotFalse = null;
+ $firstFalse = null;
+ foreach ($spMetadata->getEndpoints('AssertionConsumerService') as $ep) {
+ if ($AssertionConsumerServiceURL !== null && $ep['Location'] !== $AssertionConsumerServiceURL) {
+ continue;
+ }
+ if ($ProtocolBinding !== null && $ep['Binding'] !== $ProtocolBinding) {
+ continue;
+ }
+ if ($AssertionConsumerServiceIndex !== null && $ep['index'] !== $AssertionConsumerServiceIndex) {
+ continue;
+ }
+
+ if (!in_array($ep['Binding'], $supportedBindings, true)) {
+ /* The endpoint has an unsupported binding. */
+ continue;
+ }
+
+ // we have an endpoint that matches all our requirements. Check if it is the best one
+
+ if (array_key_exists('isDefault', $ep)) {
+ if ($ep['isDefault'] === true) {
+ // this is the first matching endpoint with isDefault set to true
+ return $ep;
+ }
+ // isDefault is set to FALSE, but the endpoint is still usable
+ if ($firstFalse === null) {
+ // this is the first endpoint that we can use
+ $firstFalse = $ep;
+ }
+ } else {
+ if ($firstNotFalse === null) {
+ // this is the first endpoint without isDefault set
+ $firstNotFalse = $ep;
+ }
+ }
+ }
+
+ if ($firstNotFalse !== null) {
+ return $firstNotFalse;
+ } elseif ($firstFalse !== null) {
+ return $firstFalse;
+ }
+
+ SimpleSAML\Logger::warning('Authentication request specifies invalid AssertionConsumerService:');
+ if ($AssertionConsumerServiceURL !== null) {
+ SimpleSAML\Logger::warning('AssertionConsumerServiceURL: '.var_export($AssertionConsumerServiceURL, true));
+ }
+ if ($ProtocolBinding !== null) {
+ SimpleSAML\Logger::warning('ProtocolBinding: '.var_export($ProtocolBinding, true));
+ }
+ if ($AssertionConsumerServiceIndex !== null) {
+ SimpleSAML\Logger::warning(
+ 'AssertionConsumerServiceIndex: '.var_export($AssertionConsumerServiceIndex, true)
+ );
+ }
+
+ // we have no good endpoints. Our last resort is to just use the default endpoint
+ return $spMetadata->getDefaultEndpoint('AssertionConsumerService', $supportedBindings);
+ }
+
+
+ /**
+ * Receive an authentication request.
+ *
+ * @param SimpleSAML_IdP $idp The IdP we are receiving it for.
+ * @throws SimpleSAML_Error_BadRequest In case an error occurs when trying to receive the request.
+ */
+ public static function receiveAuthnRequest(SimpleSAML_IdP $idp)
+ {
+
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $idpMetadata = $idp->getConfig();
+
+ $supportedBindings = array(\SAML2\Constants::BINDING_HTTP_POST);
+ if ($idpMetadata->getBoolean('saml20.sendartifact', false)) {
+ $supportedBindings[] = \SAML2\Constants::BINDING_HTTP_ARTIFACT;
+ }
+ if ($idpMetadata->getBoolean('saml20.hok.assertion', false)) {
+ $supportedBindings[] = \SAML2\Constants::BINDING_HOK_SSO;
+ }
+
+ if (isset($_REQUEST['spentityid'])) {
+ /* IdP initiated authentication. */
+
+ if (isset($_REQUEST['cookieTime'])) {
+ $cookieTime = (int) $_REQUEST['cookieTime'];
+ if ($cookieTime + 5 > time()) {
+ /*
+ * Less than five seconds has passed since we were
+ * here the last time. Cookies are probably disabled.
+ */
+ \SimpleSAML\Utils\HTTP::checkSessionCookie(\SimpleSAML\Utils\HTTP::getSelfURL());
+ }
+ }
+
+ $spEntityId = (string) $_REQUEST['spentityid'];
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
+
+ if (isset($_REQUEST['RelayState'])) {
+ $relayState = (string) $_REQUEST['RelayState'];
+ } else {
+ $relayState = null;
+ }
+
+ if (isset($_REQUEST['binding'])) {
+ $protocolBinding = (string) $_REQUEST['binding'];
+ } else {
+ $protocolBinding = null;
+ }
+
+ if (isset($_REQUEST['NameIDFormat'])) {
+ $nameIDFormat = (string) $_REQUEST['NameIDFormat'];
+ } else {
+ $nameIDFormat = null;
+ }
+
+ $requestId = null;
+ $IDPList = array();
+ $ProxyCount = null;
+ $RequesterID = null;
+ $forceAuthn = false;
+ $isPassive = false;
+ $consumerURL = null;
+ $consumerIndex = null;
+ $extensions = null;
+ $allowCreate = true;
+ $authnContext = null;
+
+ $idpInit = true;
+
+ SimpleSAML\Logger::info(
+ 'SAML2.0 - IdP.SSOService: IdP initiated authentication: '.var_export($spEntityId, true)
+ );
+ } else {
+ $binding = \SAML2\Binding::getCurrentBinding();
+ $request = $binding->receive();
+
+ if (!($request instanceof \SAML2\AuthnRequest)) {
+ throw new SimpleSAML_Error_BadRequest(
+ 'Message received on authentication request endpoint wasn\'t an authentication request.'
+ );
+ }
+
+ $spEntityId = $request->getIssuer();
+ if ($spEntityId === null) {
+ throw new SimpleSAML_Error_BadRequest(
+ 'Received message on authentication request endpoint without issuer.'
+ );
+ }
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
+
+ sspmod_saml_Message::validateMessage($spMetadata, $idpMetadata, $request);
+
+ $relayState = $request->getRelayState();
+
+ $requestId = $request->getId();
+ $IDPList = $request->getIDPList();
+ $ProxyCount = $request->getProxyCount();
+ if ($ProxyCount !== null) {
+ $ProxyCount--;
+ }
+ $RequesterID = $request->getRequesterID();
+ $forceAuthn = $request->getForceAuthn();
+ $isPassive = $request->getIsPassive();
+ $consumerURL = $request->getAssertionConsumerServiceURL();
+ $protocolBinding = $request->getProtocolBinding();
+ $consumerIndex = $request->getAssertionConsumerServiceIndex();
+ $extensions = $request->getExtensions();
+ $authnContext = $request->getRequestedAuthnContext();
+
+ $nameIdPolicy = $request->getNameIdPolicy();
+ if (isset($nameIdPolicy['Format'])) {
+ $nameIDFormat = $nameIdPolicy['Format'];
+ } else {
+ $nameIDFormat = null;
+ }
+ if (isset($nameIdPolicy['AllowCreate'])) {
+ $allowCreate = $nameIdPolicy['AllowCreate'];
+ } else {
+ $allowCreate = false;
+ }
+
+ $idpInit = false;
+
+ SimpleSAML\Logger::info(
+ 'SAML2.0 - IdP.SSOService: incoming authentication request: '.var_export($spEntityId, true)
+ );
+ }
+
+ SimpleSAML_Stats::log('saml:idp:AuthnRequest', array(
+ 'spEntityID' => $spEntityId,
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ 'forceAuthn' => $forceAuthn,
+ 'isPassive' => $isPassive,
+ 'protocol' => 'saml2',
+ 'idpInit' => $idpInit,
+ ));
+
+ $acsEndpoint = self::getAssertionConsumerService(
+ $supportedBindings,
+ $spMetadata,
+ $consumerURL,
+ $protocolBinding,
+ $consumerIndex
+ );
+
+ $IDPList = array_unique(array_merge($IDPList, $spMetadata->getArrayizeString('IDPList', array())));
+ if ($ProxyCount === null) {
+ $ProxyCount = $spMetadata->getInteger('ProxyCount', null);
+ }
+
+ if (!$forceAuthn) {
+ $forceAuthn = $spMetadata->getBoolean('ForceAuthn', false);
+ }
+
+ $sessionLostParams = array(
+ 'spentityid' => $spEntityId,
+ 'cookieTime' => time(),
+ );
+ if ($relayState !== null) {
+ $sessionLostParams['RelayState'] = $relayState;
+ }
+
+ $sessionLostURL = \SimpleSAML\Utils\HTTP::addURLParameters(
\SimpleSAML\Utils\HTTP::getSelfURLNoQuery(),
- $sessionLostParams);
-
- $state = array(
- 'Responder' => array('sspmod_saml_IdP_SAML2', 'sendResponse'),
- SimpleSAML_Auth_State::EXCEPTION_HANDLER_FUNC => array('sspmod_saml_IdP_SAML2', 'handleAuthError'),
- SimpleSAML_Auth_State::RESTART => $sessionLostURL,
-
- 'SPMetadata' => $spMetadata->toArray(),
- 'saml:RelayState' => $relayState,
- 'saml:RequestId' => $requestId,
- 'saml:IDPList' => $IDPList,
- 'saml:ProxyCount' => $ProxyCount,
- 'saml:RequesterID' => $RequesterID,
- 'ForceAuthn' => $forceAuthn,
- 'isPassive' => $isPassive,
- 'saml:ConsumerURL' => $acsEndpoint['Location'],
- 'saml:Binding' => $acsEndpoint['Binding'],
- 'saml:NameIDFormat' => $nameIDFormat,
- 'saml:AllowCreate' => $allowCreate,
- 'saml:Extensions' => $extensions,
- 'saml:AuthnRequestReceivedAt' => microtime(TRUE),
- 'saml:RequestedAuthnContext' => $authnContext,
- );
-
- $idp->handleAuthenticationRequest($state);
- }
-
-
- /**
- * Send a logout request to a given association.
- *
- * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
- * @param array $association The association that should be terminated.
- * @param string|NULL $relayState An id that should be carried across the logout.
- */
- public static function sendLogoutRequest(SimpleSAML_IdP $idp, array $association, $relayState) {
- assert('is_string($relayState) || is_null($relayState)');
-
- SimpleSAML\Logger::info('Sending SAML 2.0 LogoutRequest to: '. var_export($association['saml:entityID'], TRUE));
-
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $idpMetadata = $idp->getConfig();
- $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
-
- SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array(
- 'spEntityID' => $association['saml:entityID'],
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- ));
-
- $dst = $spMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array(
- \SAML2\Constants::BINDING_HTTP_REDIRECT,
- \SAML2\Constants::BINDING_HTTP_POST)
- );
- $binding = \SAML2\Binding::getBinding($dst['Binding']);
- $lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState);
- $lr->setDestination($dst['Location']);
-
- $binding->send($lr);
- }
-
-
- /**
- * Send a logout response.
- *
- * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
- * @param array &$state The logout state array.
- */
- public static function sendLogoutResponse(SimpleSAML_IdP $idp, array $state) {
- assert('isset($state["saml:SPEntityId"])');
- assert('isset($state["saml:RequestId"])');
- assert('array_key_exists("saml:RelayState", $state)'); // Can be NULL.
-
- $spEntityId = $state['saml:SPEntityId'];
-
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $idpMetadata = $idp->getConfig();
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
-
- $lr = sspmod_saml_Message::buildLogoutResponse($idpMetadata, $spMetadata);
- $lr->setInResponseTo($state['saml:RequestId']);
- $lr->setRelayState($state['saml:RelayState']);
-
- if (isset($state['core:Failed']) && $state['core:Failed']) {
- $partial = TRUE;
- $lr->setStatus(array(
- 'Code' => \SAML2\Constants::STATUS_SUCCESS,
- 'SubCode' => \SAML2\Constants::STATUS_PARTIAL_LOGOUT,
- ));
- SimpleSAML\Logger::info('Sending logout response for partial logout to SP ' . var_export($spEntityId, TRUE));
- } else {
- $partial = FALSE;
- SimpleSAML\Logger::debug('Sending logout response to SP ' . var_export($spEntityId, TRUE));
- }
-
- SimpleSAML_Stats::log('saml:idp:LogoutResponse:sent', array(
- 'spEntityID' => $spEntityId,
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- 'partial' => $partial
- ));
- $dst = $spMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array(
- \SAML2\Constants::BINDING_HTTP_REDIRECT,
- \SAML2\Constants::BINDING_HTTP_POST)
- );
- $binding = \SAML2\Binding::getBinding($dst['Binding']);
- if (isset($dst['ResponseLocation'])) {
- $dst = $dst['ResponseLocation'];
- } else {
- $dst = $dst['Location'];
- }
- $lr->setDestination($dst);
-
- $binding->send($lr);
- }
-
-
- /**
- * Receive a logout message.
- *
- * @param SimpleSAML_IdP $idp The IdP we are receiving it for.
- */
- public static function receiveLogoutMessage(SimpleSAML_IdP $idp) {
-
- $binding = \SAML2\Binding::getCurrentBinding();
- $message = $binding->receive();
-
- $spEntityId = $message->getIssuer();
- if ($spEntityId === NULL) {
- /* Without an issuer we have no way to respond to the message. */
- throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.');
- }
-
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $idpMetadata = $idp->getConfig();
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
-
- sspmod_saml_Message::validateMessage($spMetadata, $idpMetadata, $message);
-
- if ($message instanceof \SAML2\LogoutResponse) {
-
- SimpleSAML\Logger::info('Received SAML 2.0 LogoutResponse from: '. var_export($spEntityId, TRUE));
- $statsData = array(
- 'spEntityID' => $spEntityId,
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- );
- if (!$message->isSuccess()) {
- $statsData['error'] = $message->getStatus();
- }
- SimpleSAML_Stats::log('saml:idp:LogoutResponse:recv', $statsData);
-
- $relayState = $message->getRelayState();
-
- if (!$message->isSuccess()) {
- $logoutError = sspmod_saml_Message::getResponseError($message);
- SimpleSAML\Logger::warning('Unsuccessful logout. Status was: ' . $logoutError);
- } else {
- $logoutError = NULL;
- }
-
- $assocId = 'saml:' . $spEntityId;
-
- $idp->handleLogoutResponse($assocId, $relayState, $logoutError);
-
-
- } elseif ($message instanceof \SAML2\LogoutRequest) {
-
- SimpleSAML\Logger::info('Received SAML 2.0 LogoutRequest from: '. var_export($spEntityId, TRUE));
- SimpleSAML_Stats::log('saml:idp:LogoutRequest:recv', array(
- 'spEntityID' => $spEntityId,
- 'idpEntityID' => $idpMetadata->getString('entityid'),
- ));
-
- $spStatsId = $spMetadata->getString('core:statistics-id', $spEntityId);
- SimpleSAML\Logger::stats('saml20-idp-SLO spinit ' . $spStatsId . ' ' . $idpMetadata->getString('entityid'));
-
- $state = array(
- 'Responder' => array('sspmod_saml_IdP_SAML2', 'sendLogoutResponse'),
- 'saml:SPEntityId' => $spEntityId,
- 'saml:RelayState' => $message->getRelayState(),
- 'saml:RequestId' => $message->getId(),
- );
-
- $assocId = 'saml:' . $spEntityId;
- $idp->handleLogoutRequest($state, $assocId);
-
- } else {
- throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message));
- }
-
- }
-
-
- /**
+ $sessionLostParams
+ );
+
+ $state = array(
+ 'Responder' => array('sspmod_saml_IdP_SAML2', 'sendResponse'),
+ SimpleSAML_Auth_State::EXCEPTION_HANDLER_FUNC => array('sspmod_saml_IdP_SAML2', 'handleAuthError'),
+ SimpleSAML_Auth_State::RESTART => $sessionLostURL,
+
+ 'SPMetadata' => $spMetadata->toArray(),
+ 'saml:RelayState' => $relayState,
+ 'saml:RequestId' => $requestId,
+ 'saml:IDPList' => $IDPList,
+ 'saml:ProxyCount' => $ProxyCount,
+ 'saml:RequesterID' => $RequesterID,
+ 'ForceAuthn' => $forceAuthn,
+ 'isPassive' => $isPassive,
+ 'saml:ConsumerURL' => $acsEndpoint['Location'],
+ 'saml:Binding' => $acsEndpoint['Binding'],
+ 'saml:NameIDFormat' => $nameIDFormat,
+ 'saml:AllowCreate' => $allowCreate,
+ 'saml:Extensions' => $extensions,
+ 'saml:AuthnRequestReceivedAt' => microtime(true),
+ 'saml:RequestedAuthnContext' => $authnContext,
+ );
+
+ $idp->handleAuthenticationRequest($state);
+ }
+
+
+ /**
+ * Send a logout request to a given association.
+ *
+ * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
+ * @param array $association The association that should be terminated.
+ * @param string|NULL $relayState An id that should be carried across the logout.
+ */
+ public static function sendLogoutRequest(SimpleSAML_IdP $idp, array $association, $relayState)
+ {
+ assert('is_string($relayState) || is_null($relayState)');
+
+ SimpleSAML\Logger::info('Sending SAML 2.0 LogoutRequest to: '.var_export($association['saml:entityID'], true));
+
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $idpMetadata = $idp->getConfig();
+ $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
+
+ SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array(
+ 'spEntityID' => $association['saml:entityID'],
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ ));
+
+ $dst = $spMetadata->getEndpointPrioritizedByBinding(
+ 'SingleLogoutService',
+ array(
+ \SAML2\Constants::BINDING_HTTP_REDIRECT,
+ \SAML2\Constants::BINDING_HTTP_POST
+ )
+ );
+ $binding = \SAML2\Binding::getBinding($dst['Binding']);
+ $lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState);
+ $lr->setDestination($dst['Location']);
+
+ $binding->send($lr);
+ }
+
+
+ /**
+ * Send a logout response.
+ *
+ * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
+ * @param array &$state The logout state array.
+ */
+ public static function sendLogoutResponse(SimpleSAML_IdP $idp, array $state)
+ {
+ assert('isset($state["saml:SPEntityId"])');
+ assert('isset($state["saml:RequestId"])');
+ assert('array_key_exists("saml:RelayState", $state)'); // Can be NULL.
+
+ $spEntityId = $state['saml:SPEntityId'];
+
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $idpMetadata = $idp->getConfig();
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
+
+ $lr = sspmod_saml_Message::buildLogoutResponse($idpMetadata, $spMetadata);
+ $lr->setInResponseTo($state['saml:RequestId']);
+ $lr->setRelayState($state['saml:RelayState']);
+
+ if (isset($state['core:Failed']) && $state['core:Failed']) {
+ $partial = true;
+ $lr->setStatus(array(
+ 'Code' => \SAML2\Constants::STATUS_SUCCESS,
+ 'SubCode' => \SAML2\Constants::STATUS_PARTIAL_LOGOUT,
+ ));
+ SimpleSAML\Logger::info('Sending logout response for partial logout to SP '.var_export($spEntityId, true));
+ } else {
+ $partial = false;
+ SimpleSAML\Logger::debug('Sending logout response to SP '.var_export($spEntityId, true));
+ }
+
+ SimpleSAML_Stats::log('saml:idp:LogoutResponse:sent', array(
+ 'spEntityID' => $spEntityId,
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ 'partial' => $partial
+ ));
+ $dst = $spMetadata->getEndpointPrioritizedByBinding(
+ 'SingleLogoutService',
+ array(
+ \SAML2\Constants::BINDING_HTTP_REDIRECT,
+ \SAML2\Constants::BINDING_HTTP_POST
+ )
+ );
+ $binding = \SAML2\Binding::getBinding($dst['Binding']);
+ if (isset($dst['ResponseLocation'])) {
+ $dst = $dst['ResponseLocation'];
+ } else {
+ $dst = $dst['Location'];
+ }
+ $lr->setDestination($dst);
+
+ $binding->send($lr);
+ }
+
+
+ /**
+ * Receive a logout message.
+ *
+ * @param SimpleSAML_IdP $idp The IdP we are receiving it for.
+ * @throws SimpleSAML_Error_BadRequest In case an error occurs while trying to receive the logout message.
+ */
+ public static function receiveLogoutMessage(SimpleSAML_IdP $idp)
+ {
+
+ $binding = \SAML2\Binding::getCurrentBinding();
+ $message = $binding->receive();
+
+ $spEntityId = $message->getIssuer();
+ if ($spEntityId === null) {
+ /* Without an issuer we have no way to respond to the message. */
+ throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.');
+ }
+
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $idpMetadata = $idp->getConfig();
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
+
+ sspmod_saml_Message::validateMessage($spMetadata, $idpMetadata, $message);
+
+ if ($message instanceof \SAML2\LogoutResponse) {
+ SimpleSAML\Logger::info('Received SAML 2.0 LogoutResponse from: '.var_export($spEntityId, true));
+ $statsData = array(
+ 'spEntityID' => $spEntityId,
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ );
+ if (!$message->isSuccess()) {
+ $statsData['error'] = $message->getStatus();
+ }
+ SimpleSAML_Stats::log('saml:idp:LogoutResponse:recv', $statsData);
+
+ $relayState = $message->getRelayState();
+
+ if (!$message->isSuccess()) {
+ $logoutError = sspmod_saml_Message::getResponseError($message);
+ SimpleSAML\Logger::warning('Unsuccessful logout. Status was: '.$logoutError);
+ } else {
+ $logoutError = null;
+ }
+
+ $assocId = 'saml:'.$spEntityId;
+
+ $idp->handleLogoutResponse($assocId, $relayState, $logoutError);
+ } elseif ($message instanceof \SAML2\LogoutRequest) {
+ SimpleSAML\Logger::info('Received SAML 2.0 LogoutRequest from: '.var_export($spEntityId, true));
+ SimpleSAML_Stats::log('saml:idp:LogoutRequest:recv', array(
+ 'spEntityID' => $spEntityId,
+ 'idpEntityID' => $idpMetadata->getString('entityid'),
+ ));
+
+ $spStatsId = $spMetadata->getString('core:statistics-id', $spEntityId);
+ SimpleSAML\Logger::stats('saml20-idp-SLO spinit '.$spStatsId.' '.$idpMetadata->getString('entityid'));
+
+ $state = array(
+ 'Responder' => array('sspmod_saml_IdP_SAML2', 'sendLogoutResponse'),
+ 'saml:SPEntityId' => $spEntityId,
+ 'saml:RelayState' => $message->getRelayState(),
+ 'saml:RequestId' => $message->getId(),
+ );
+
+ $assocId = 'saml:'.$spEntityId;
+ $idp->handleLogoutRequest($state, $assocId);
+ } else {
+ throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: '.get_class($message));
+ }
+ }
+
+
+ /**
* Retrieve a logout URL for a given logout association.
- *
- * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
- * @param array $association The association that should be terminated.
- * @param string|NULL $relayState An id that should be carried across the logout.
- */
- public static function getLogoutURL(SimpleSAML_IdP $idp, array $association, $relayState) {
- assert('is_string($relayState) || is_null($relayState)');
-
- SimpleSAML\Logger::info('Sending SAML 2.0 LogoutRequest to: '. var_export($association['saml:entityID'], TRUE));
-
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $idpMetadata = $idp->getConfig();
- $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
-
- $bindings = array(\SAML2\Constants::BINDING_HTTP_REDIRECT,
- \SAML2\Constants::BINDING_HTTP_POST);
- $dst = $spMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', $bindings);
-
- if ($dst['Binding'] === \SAML2\Constants::BINDING_HTTP_POST) {
- $params = array('association' => $association['id'], 'idp' => $idp->getId());
- if ($relayState !== NULL) {
- $params['RelayState'] = $relayState;
- }
- return SimpleSAML\Module::getModuleURL('core/idp/logout-iframe-post.php', $params);
- }
-
- $lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState);
- $lr->setDestination($dst['Location']);
-
- $binding = new \SAML2\HTTPRedirect();
- return $binding->getRedirectURL($lr);
- }
-
-
- /**
- * Retrieve the metadata for the given SP association.
- *
- * @param SimpleSAML_IdP $idp The IdP the association belongs to.
- * @param array $association The SP association.
- * @return SimpleSAML_Configuration Configuration object for the SP metadata.
- */
- public static function getAssociationConfig(SimpleSAML_IdP $idp, array $association) {
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- try {
- return $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
- } catch (Exception $e) {
- return SimpleSAML_Configuration::loadFromArray(array(), 'Unknown SAML 2 entity.');
- }
- }
-
-
- /**
- * Calculate the NameID value that should be used.
- *
- * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration $dstMetadata The metadata of the SP.
- * @param array &$state The authentication state of the user.
- * @return string The NameID value.
- */
- private static function generateNameIdValue(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata, array &$state) {
-
- $attribute = $spMetadata->getString('simplesaml.nameidattribute', NULL);
- if ($attribute === NULL) {
- $attribute = $idpMetadata->getString('simplesaml.nameidattribute', NULL);
- if ($attribute === NULL) {
- if (!isset($state['UserID'])) {
- SimpleSAML\Logger::error('Unable to generate NameID. Check the userid.attribute option.');
- }
- $attributeValue = $state['UserID'];
- $idpEntityId = $idpMetadata->getString('entityid');
- $spEntityId = $spMetadata->getString('entityid');
-
- $secretSalt = SimpleSAML\Utils\Config::getSecretSalt();
-
- $uidData = 'uidhashbase' . $secretSalt;
- $uidData .= strlen($idpEntityId) . ':' . $idpEntityId;
- $uidData .= strlen($spEntityId) . ':' . $spEntityId;
- $uidData .= strlen($attributeValue) . ':' . $attributeValue;
- $uidData .= $secretSalt;
-
- return hash('sha1', $uidData);
- }
- }
-
- $attributes = $state['Attributes'];
- if (!array_key_exists($attribute, $attributes)) {
- SimpleSAML\Logger::error('Unable to add NameID: Missing ' . var_export($attribute, TRUE) .
- ' in the attributes of the user.');
- return NULL;
- }
-
- return $attributes[$attribute][0];
- }
-
-
- /**
- * Helper function for encoding attributes.
- *
- * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
- * @param array $attributes The attributes of the user
- * @return array The encoded attributes.
- */
- private static function encodeAttributes(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata, array $attributes) {
-
- $base64Attributes = $spMetadata->getBoolean('base64attributes', NULL);
- if ($base64Attributes === NULL) {
- $base64Attributes = $idpMetadata->getBoolean('base64attributes', FALSE);
- }
-
- if ($base64Attributes) {
- $defaultEncoding = 'base64';
- } else {
- $defaultEncoding = 'string';
- }
-
- $srcEncodings = $idpMetadata->getArray('attributeencodings', array());
- $dstEncodings = $spMetadata->getArray('attributeencodings', array());
-
- /*
- * Merge the two encoding arrays. Encodings specified in the target metadata
- * takes precedence over the source metadata.
- */
- $encodings = array_merge($srcEncodings, $dstEncodings);
-
- $ret = array();
- foreach ($attributes as $name => $values) {
- $ret[$name] = array();
- if (array_key_exists($name, $encodings)) {
- $encoding = $encodings[$name];
- } else {
- $encoding = $defaultEncoding;
- }
-
- foreach ($values as $value) {
+ *
+ * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
+ * @param array $association The association that should be terminated.
+ * @param string|NULL $relayState An id that should be carried across the logout.
+ *
+ * @return string The logout URL.
+ */
+ public static function getLogoutURL(SimpleSAML_IdP $idp, array $association, $relayState)
+ {
+ assert('is_string($relayState) || is_null($relayState)');
+
+ SimpleSAML\Logger::info('Sending SAML 2.0 LogoutRequest to: '.var_export($association['saml:entityID'], true));
+
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $idpMetadata = $idp->getConfig();
+ $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
+
+ $bindings = array(
+ \SAML2\Constants::BINDING_HTTP_REDIRECT,
+ \SAML2\Constants::BINDING_HTTP_POST
+ );
+ $dst = $spMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', $bindings);
+
+ if ($dst['Binding'] === \SAML2\Constants::BINDING_HTTP_POST) {
+ $params = array('association' => $association['id'], 'idp' => $idp->getId());
+ if ($relayState !== null) {
+ $params['RelayState'] = $relayState;
+ }
+ return SimpleSAML\Module::getModuleURL('core/idp/logout-iframe-post.php', $params);
+ }
+
+ $lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState);
+ $lr->setDestination($dst['Location']);
+
+ $binding = new \SAML2\HTTPRedirect();
+ return $binding->getRedirectURL($lr);
+ }
+
+
+ /**
+ * Retrieve the metadata for the given SP association.
+ *
+ * @param SimpleSAML_IdP $idp The IdP the association belongs to.
+ * @param array $association The SP association.
+ *
+ * @return SimpleSAML_Configuration Configuration object for the SP metadata.
+ */
+ public static function getAssociationConfig(SimpleSAML_IdP $idp, array $association)
+ {
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ try {
+ return $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
+ } catch (Exception $e) {
+ return SimpleSAML_Configuration::loadFromArray(array(), 'Unknown SAML 2 entity.');
+ }
+ }
+
+
+ /**
+ * Calculate the NameID value that should be used.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $dstMetadata The metadata of the SP.
+ * @param array &$state The authentication state of the user.
+ *
+ * @return string The NameID value.
+ */
+ private static function generateNameIdValue(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata,
+ array &$state
+ ) {
+
+ $attribute = $spMetadata->getString('simplesaml.nameidattribute', null);
+ if ($attribute === null) {
+ $attribute = $idpMetadata->getString('simplesaml.nameidattribute', null);
+ if ($attribute === null) {
+ if (!isset($state['UserID'])) {
+ SimpleSAML\Logger::error('Unable to generate NameID. Check the userid.attribute option.');
+ }
+ $attributeValue = $state['UserID'];
+ $idpEntityId = $idpMetadata->getString('entityid');
+ $spEntityId = $spMetadata->getString('entityid');
+
+ $secretSalt = SimpleSAML\Utils\Config::getSecretSalt();
+
+ $uidData = 'uidhashbase'.$secretSalt;
+ $uidData .= strlen($idpEntityId).':'.$idpEntityId;
+ $uidData .= strlen($spEntityId).':'.$spEntityId;
+ $uidData .= strlen($attributeValue).':'.$attributeValue;
+ $uidData .= $secretSalt;
+
+ return hash('sha1', $uidData);
+ }
+ }
+
+ $attributes = $state['Attributes'];
+ if (!array_key_exists($attribute, $attributes)) {
+ SimpleSAML\Logger::error('Unable to add NameID: Missing '.var_export($attribute, true).
+ ' in the attributes of the user.');
+ return null;
+ }
+
+ return $attributes[$attribute][0];
+ }
+
+
+ /**
+ * Helper function for encoding attributes.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
+ * @param array $attributes The attributes of the user.
+ *
+ * @return array The encoded attributes.
+ *
+ * @throws SimpleSAML_Error_Exception In case an unsupported encoding is specified by configuration.
+ */
+ private static function encodeAttributes(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata,
+ array $attributes
+ ) {
+
+ $base64Attributes = $spMetadata->getBoolean('base64attributes', null);
+ if ($base64Attributes === null) {
+ $base64Attributes = $idpMetadata->getBoolean('base64attributes', false);
+ }
+
+ if ($base64Attributes) {
+ $defaultEncoding = 'base64';
+ } else {
+ $defaultEncoding = 'string';
+ }
+
+ $srcEncodings = $idpMetadata->getArray('attributeencodings', array());
+ $dstEncodings = $spMetadata->getArray('attributeencodings', array());
+
+ /*
+ * Merge the two encoding arrays. Encodings specified in the target metadata
+ * takes precedence over the source metadata.
+ */
+ $encodings = array_merge($srcEncodings, $dstEncodings);
+
+ $ret = array();
+ foreach ($attributes as $name => $values) {
+ $ret[$name] = array();
+ if (array_key_exists($name, $encodings)) {
+ $encoding = $encodings[$name];
+ } else {
+ $encoding = $defaultEncoding;
+ }
+
+ foreach ($values as $value) {
// allow null values
if ($value === null) {
$ret[$name][] = $value;
continue;
}
- $attrval = $value;
- if ($value instanceof DOMNodeList) {
- $attrval = new \SAML2\XML\saml\AttributeValue($value->item(0)->parentNode);
- }
-
- switch ($encoding) {
- case 'string':
- $value = (string)$attrval;
- break;
- case 'base64':
- $value = base64_encode((string)$attrval);
- break;
- case 'raw':
- if (is_string($value)) {
- $doc = \SAML2\DOMDocumentFactory::fromString('<root>' . $value . '</root>');
- $value = $doc->firstChild->childNodes;
- }
- assert('$value instanceof DOMNodeList || $value instanceof \SAML2\XML\saml\NameID');
- break;
- default:
- throw new SimpleSAML_Error_Exception('Invalid encoding for attribute ' .
- var_export($name, TRUE) . ': ' . var_export($encoding, TRUE));
- }
- $ret[$name][] = $value;
- }
- }
-
- return $ret;
- }
-
-
- /**
- * Determine which NameFormat we should use for attributes.
- *
- * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
- * @return string The NameFormat.
- */
- private static function getAttributeNameFormat(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata) {
-
- /* Try SP metadata first. */
- $attributeNameFormat = $spMetadata->getString('attributes.NameFormat', NULL);
- if ($attributeNameFormat !== NULL) {
- return $attributeNameFormat;
- }
- $attributeNameFormat = $spMetadata->getString('AttributeNameFormat', NULL);
- if ($attributeNameFormat !== NULL) {
- return $attributeNameFormat;
- }
-
- /* Look in IdP metadata. */
- $attributeNameFormat = $idpMetadata->getString('attributes.NameFormat', NULL);
- if ($attributeNameFormat !== NULL) {
- return $attributeNameFormat;
- }
- $attributeNameFormat = $idpMetadata->getString('AttributeNameFormat', NULL);
- if ($attributeNameFormat !== NULL) {
- return $attributeNameFormat;
- }
-
- /* Default. */
- return 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic';
- }
-
-
- /**
- * Build an assertion based on information in the metadata.
- *
- * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
- * @param array &$state The state array with information about the request.
- * @return \SAML2\Assertion The assertion.
- */
- private static function buildAssertion(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata, array &$state) {
- assert('isset($state["Attributes"])');
- assert('isset($state["saml:ConsumerURL"])');
-
- $now = time();
-
- $signAssertion = $spMetadata->getBoolean('saml20.sign.assertion', NULL);
- if ($signAssertion === NULL) {
- $signAssertion = $idpMetadata->getBoolean('saml20.sign.assertion', TRUE);
- }
-
- $config = SimpleSAML_Configuration::getInstance();
-
- $a = new \SAML2\Assertion();
- if ($signAssertion) {
- sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $a);
- }
-
- $a->setIssuer($idpMetadata->getString('entityid'));
- $a->setValidAudiences(array($spMetadata->getString('entityid')));
-
- $a->setNotBefore($now - 30);
-
- $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL);
- if ($assertionLifetime === NULL) {
- $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
- }
- $a->setNotOnOrAfter($now + $assertionLifetime);
-
- if (isset($state['saml:AuthnContextClassRef'])) {
- $a->setAuthnContext($state['saml:AuthnContextClassRef']);
- } else {
- $a->setAuthnContext(\SAML2\Constants::AC_PASSWORD);
- }
-
- $sessionStart = $now;
- if (isset($state['AuthnInstant'])) {
- $a->setAuthnInstant($state['AuthnInstant']);
- $sessionStart = $state['AuthnInstant'];
- }
-
- $sessionLifetime = $config->getInteger('session.duration', 8*60*60);
- $a->setSessionNotOnOrAfter($sessionStart + $sessionLifetime);
-
- $a->setSessionIndex(SimpleSAML\Utils\Random::generateID());
-
- $sc = new \SAML2\XML\saml\SubjectConfirmation();
- $sc->SubjectConfirmationData = new \SAML2\XML\saml\SubjectConfirmationData();
- $sc->SubjectConfirmationData->NotOnOrAfter = $now + $assertionLifetime;
- $sc->SubjectConfirmationData->Recipient = $state['saml:ConsumerURL'];
- $sc->SubjectConfirmationData->InResponseTo = $state['saml:RequestId'];
-
- /* ProtcolBinding of SP's <AuthnRequest> overwrites IdP hosted metadata configuration. */
- $hokAssertion = NULL;
- if ($state['saml:Binding'] === \SAML2\Constants::BINDING_HOK_SSO) {
- $hokAssertion = TRUE;
- }
- if ($hokAssertion === NULL) {
- $hokAssertion = $idpMetadata->getBoolean('saml20.hok.assertion', FALSE);
- }
-
- if ($hokAssertion) {
- /* Holder-of-Key */
- $sc->Method = \SAML2\Constants::CM_HOK;
- if (\SimpleSAML\Utils\HTTP::isHTTPS()) {
- if (isset($_SERVER['SSL_CLIENT_CERT']) && !empty($_SERVER['SSL_CLIENT_CERT'])) {
- /* Extract certificate data (if this is a certificate). */
- $clientCert = $_SERVER['SSL_CLIENT_CERT'];
- $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
- if (preg_match($pattern, $clientCert, $matches)) {
- /* We have a client certificate from the browser which we add to the HoK assertion. */
- $x509Certificate = new \SAML2\XML\ds\X509Certificate();
- $x509Certificate->certificate = str_replace(array("\r", "\n", " "), '', $matches[1]);
-
- $x509Data = new \SAML2\XML\ds\X509Data();
- $x509Data->data[] = $x509Certificate;
-
- $keyInfo = new \SAML2\XML\ds\KeyInfo();
- $keyInfo->info[] = $x509Data;
-
- $sc->SubjectConfirmationData->info[] = $keyInfo;
- } else throw new SimpleSAML_Error_Exception('Error creating HoK assertion: No valid client certificate provided during TLS handshake with IdP');
- } else throw new SimpleSAML_Error_Exception('Error creating HoK assertion: No client certificate provided during TLS handshake with IdP');
- } else throw new SimpleSAML_Error_Exception('Error creating HoK assertion: No HTTPS connection to IdP, but required for Holder-of-Key SSO');
- } else {
- /* Bearer */
- $sc->Method = \SAML2\Constants::CM_BEARER;
- }
- $a->setSubjectConfirmation(array($sc));
-
- /* Add attributes. */
-
- if ($spMetadata->getBoolean('simplesaml.attributes', TRUE)) {
- $attributeNameFormat = self::getAttributeNameFormat($idpMetadata, $spMetadata);
- $a->setAttributeNameFormat($attributeNameFormat);
- $attributes = self::encodeAttributes($idpMetadata, $spMetadata, $state['Attributes']);
- $a->setAttributes($attributes);
- }
-
-
- /* Generate the NameID for the assertion. */
-
- if (isset($state['saml:NameIDFormat'])) {
- $nameIdFormat = $state['saml:NameIDFormat'];
- } else {
- $nameIdFormat = NULL;
- }
-
- if ($nameIdFormat === NULL || !isset($state['saml:NameID'][$nameIdFormat])) {
- /* Either not set in request, or not set to a format we supply. Fall back to old generation method. */
- $nameIdFormat = $spMetadata->getString('NameIDFormat', NULL);
- if ($nameIdFormat === NULL) {
- $nameIdFormat = $idpMetadata->getString('NameIDFormat', \SAML2\Constants::NAMEID_TRANSIENT);
- }
- }
-
- if (isset($state['saml:NameID'][$nameIdFormat])) {
- $nameId = $state['saml:NameID'][$nameIdFormat];
- $nameId['Format'] = $nameIdFormat;
- } else {
- $spNameQualifier = $spMetadata->getString('SPNameQualifier', NULL);
- if ($spNameQualifier === NULL) {
- $spNameQualifier = $spMetadata->getString('entityid');
- }
-
- if ($nameIdFormat === \SAML2\Constants::NAMEID_TRANSIENT) {
- /* generate a random id */
- $nameIdValue = SimpleSAML\Utils\Random::generateID();
- } else {
- /* this code will end up generating either a fixed assigned id (via nameid.attribute)
- or random id if not assigned/configured */
- $nameIdValue = self::generateNameIdValue($idpMetadata, $spMetadata, $state);
- if ($nameIdValue === NULL) {
- SimpleSAML\Logger::warning('Falling back to transient NameID.');
- $nameIdFormat = \SAML2\Constants::NAMEID_TRANSIENT;
- $nameIdValue = SimpleSAML\Utils\Random::generateID();
- }
- }
-
- $nameId = array(
- 'Format' => $nameIdFormat,
- 'Value' => $nameIdValue,
- 'SPNameQualifier' => $spNameQualifier,
- );
- }
-
- $state['saml:idp:NameID'] = $nameId;
-
- $a->setNameId($nameId);
-
- $encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL);
- if ($encryptNameId === NULL) {
- $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE);
- }
- if ($encryptNameId) {
- $a->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
- }
-
- return $a;
- }
-
-
- /**
- * Encrypt an assertion.
- *
- * This function takes in a \SAML2\Assertion and encrypts it if encryption of
- * assertions are enabled in the metadata.
- *
- * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
- * @param \SAML2\Assertion $assertion The assertion we are encrypting.
- * @return \SAML2\Assertion|\SAML2\EncryptedAssertion The assertion.
- */
- private static function encryptAssertion(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata, \SAML2\Assertion $assertion) {
-
- $encryptAssertion = $spMetadata->getBoolean('assertion.encryption', NULL);
- if ($encryptAssertion === NULL) {
- $encryptAssertion = $idpMetadata->getBoolean('assertion.encryption', FALSE);
- }
- if (!$encryptAssertion) {
- /* We are _not_ encrypting this assertion, and are therefore done. */
- return $assertion;
- }
-
-
- $sharedKey = $spMetadata->getString('sharedkey', NULL);
- if ($sharedKey !== NULL) {
- $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
- $key->loadKey($sharedKey);
- } else {
- $keys = $spMetadata->getPublicKeys('encryption', TRUE);
- $key = $keys[0];
- switch ($key['type']) {
- case 'X509Certificate':
- $pemKey = "-----BEGIN CERTIFICATE-----\n" .
- chunk_split($key['X509Certificate'], 64) .
- "-----END CERTIFICATE-----\n";
- break;
- default:
- throw new SimpleSAML_Error_Exception('Unsupported encryption key type: ' . $key['type']);
- }
-
- /* Extract the public key from the certificate for encryption. */
- $key = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array('type'=>'public'));
- $key->loadKey($pemKey);
- }
-
- $ea = new \SAML2\EncryptedAssertion();
- $ea->setAssertion($assertion, $key);
- return $ea;
- }
-
-
- /**
- * Build a logout request based on information in the metadata.
- *
- * @param SimpleSAML_Configuration idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration spMetadata The metadata of the SP.
- * @param array $association The SP association.
- * @param string|NULL $relayState An id that should be carried across the logout.
- */
- private static function buildLogoutRequest(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata, array $association, $relayState) {
-
- $lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata);
- $lr->setRelayState($relayState);
- $lr->setSessionIndex($association['saml:SessionIndex']);
- $lr->setNameId($association['saml:NameID']);
-
- $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL);
- if ($assertionLifetime === NULL) {
- $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
- }
- $lr->setNotOnOrAfter(time() + $assertionLifetime);
-
- $encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL);
- if ($encryptNameId === NULL) {
- $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE);
- }
- if ($encryptNameId) {
- $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
- }
-
- return $lr;
- }
-
-
- /**
- * Build a authentication response based on information in the metadata.
- *
- * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
- * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
- * @param string $consumerURL The Destination URL of the response.
- */
- private static function buildResponse(SimpleSAML_Configuration $idpMetadata,
- SimpleSAML_Configuration $spMetadata, $consumerURL) {
-
- $signResponse = $spMetadata->getBoolean('saml20.sign.response', NULL);
- if ($signResponse === NULL) {
- $signResponse = $idpMetadata->getBoolean('saml20.sign.response', TRUE);
- }
-
- $r = new \SAML2\Response();
-
- $r->setIssuer($idpMetadata->getString('entityid'));
- $r->setDestination($consumerURL);
-
- if ($signResponse) {
- sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $r);
- }
-
- return $r;
- }
-
-} \ No newline at end of file
+ $attrval = $value;
+ if ($value instanceof DOMNodeList) {
+ $attrval = new \SAML2\XML\saml\AttributeValue($value->item(0)->parentNode);
+ }
+
+ switch ($encoding) {
+ case 'string':
+ $value = (string) $attrval;
+ break;
+ case 'base64':
+ $value = base64_encode((string) $attrval);
+ break;
+ case 'raw':
+ if (is_string($value)) {
+ $doc = \SAML2\DOMDocumentFactory::fromString('<root>'.$value.'</root>');
+ $value = $doc->firstChild->childNodes;
+ }
+ assert('$value instanceof DOMNodeList || $value instanceof \SAML2\XML\saml\NameID');
+ break;
+ default:
+ throw new SimpleSAML_Error_Exception('Invalid encoding for attribute '.
+ var_export($name, true).': '.var_export($encoding, true));
+ }
+ $ret[$name][] = $value;
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Determine which NameFormat we should use for attributes.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
+ *
+ * @return string The NameFormat.
+ */
+ private static function getAttributeNameFormat(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata
+ ) {
+
+ // try SP metadata first
+ $attributeNameFormat = $spMetadata->getString('attributes.NameFormat', null);
+ if ($attributeNameFormat !== null) {
+ return $attributeNameFormat;
+ }
+ $attributeNameFormat = $spMetadata->getString('AttributeNameFormat', null);
+ if ($attributeNameFormat !== null) {
+ return $attributeNameFormat;
+ }
+
+ // look in IdP metadata
+ $attributeNameFormat = $idpMetadata->getString('attributes.NameFormat', null);
+ if ($attributeNameFormat !== null) {
+ return $attributeNameFormat;
+ }
+ $attributeNameFormat = $idpMetadata->getString('AttributeNameFormat', null);
+ if ($attributeNameFormat !== null) {
+ return $attributeNameFormat;
+ }
+
+ // default
+ return 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic';
+ }
+
+
+ /**
+ * Build an assertion based on information in the metadata.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
+ * @param array &$state The state array with information about the request.
+ *
+ * @return \SAML2\Assertion The assertion.
+ *
+ * @throws SimpleSAML_Error_Exception In case an error occurs when creating a holder-of-key assertion.
+ */
+ private static function buildAssertion(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata,
+ array &$state
+ ) {
+ assert('isset($state["Attributes"])');
+ assert('isset($state["saml:ConsumerURL"])');
+
+ $now = time();
+
+ $signAssertion = $spMetadata->getBoolean('saml20.sign.assertion', null);
+ if ($signAssertion === null) {
+ $signAssertion = $idpMetadata->getBoolean('saml20.sign.assertion', true);
+ }
+
+ $config = SimpleSAML_Configuration::getInstance();
+
+ $a = new \SAML2\Assertion();
+ if ($signAssertion) {
+ sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $a);
+ }
+
+ $a->setIssuer($idpMetadata->getString('entityid'));
+ $a->setValidAudiences(array($spMetadata->getString('entityid')));
+
+ $a->setNotBefore($now - 30);
+
+ $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', null);
+ if ($assertionLifetime === null) {
+ $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
+ }
+ $a->setNotOnOrAfter($now + $assertionLifetime);
+
+ if (isset($state['saml:AuthnContextClassRef'])) {
+ $a->setAuthnContext($state['saml:AuthnContextClassRef']);
+ } else {
+ $a->setAuthnContext(\SAML2\Constants::AC_PASSWORD);
+ }
+
+ $sessionStart = $now;
+ if (isset($state['AuthnInstant'])) {
+ $a->setAuthnInstant($state['AuthnInstant']);
+ $sessionStart = $state['AuthnInstant'];
+ }
+
+ $sessionLifetime = $config->getInteger('session.duration', 8 * 60 * 60);
+ $a->setSessionNotOnOrAfter($sessionStart + $sessionLifetime);
+
+ $a->setSessionIndex(SimpleSAML\Utils\Random::generateID());
+
+ $sc = new \SAML2\XML\saml\SubjectConfirmation();
+ $sc->SubjectConfirmationData = new \SAML2\XML\saml\SubjectConfirmationData();
+ $sc->SubjectConfirmationData->NotOnOrAfter = $now + $assertionLifetime;
+ $sc->SubjectConfirmationData->Recipient = $state['saml:ConsumerURL'];
+ $sc->SubjectConfirmationData->InResponseTo = $state['saml:RequestId'];
+
+ // ProtcolBinding of SP's <AuthnRequest> overwrites IdP hosted metadata configuration
+ $hokAssertion = null;
+ if ($state['saml:Binding'] === \SAML2\Constants::BINDING_HOK_SSO) {
+ $hokAssertion = true;
+ }
+ if ($hokAssertion === null) {
+ $hokAssertion = $idpMetadata->getBoolean('saml20.hok.assertion', false);
+ }
+
+ if ($hokAssertion) {
+ // Holder-of-Key
+ $sc->Method = \SAML2\Constants::CM_HOK;
+ if (\SimpleSAML\Utils\HTTP::isHTTPS()) {
+ if (isset($_SERVER['SSL_CLIENT_CERT']) && !empty($_SERVER['SSL_CLIENT_CERT'])) {
+ // extract certificate data (if this is a certificate)
+ $clientCert = $_SERVER['SSL_CLIENT_CERT'];
+ $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
+ if (preg_match($pattern, $clientCert, $matches)) {
+ // we have a client certificate from the browser which we add to the HoK assertion
+ $x509Certificate = new \SAML2\XML\ds\X509Certificate();
+ $x509Certificate->certificate = str_replace(array("\r", "\n", " "), '', $matches[1]);
+
+ $x509Data = new \SAML2\XML\ds\X509Data();
+ $x509Data->data[] = $x509Certificate;
+
+ $keyInfo = new \SAML2\XML\ds\KeyInfo();
+ $keyInfo->info[] = $x509Data;
+
+ $sc->SubjectConfirmationData->info[] = $keyInfo;
+ } else {
+ throw new SimpleSAML_Error_Exception(
+ 'Error creating HoK assertion: No valid client certificate provided during TLS handshake '.
+ 'with IdP'
+ );
+ }
+ } else {
+ throw new SimpleSAML_Error_Exception(
+ 'Error creating HoK assertion: No client certificate provided during TLS handshake with IdP'
+ );
+ }
+ } else {
+ throw new SimpleSAML_Error_Exception(
+ 'Error creating HoK assertion: No HTTPS connection to IdP, but required for Holder-of-Key SSO'
+ );
+ }
+ } else {
+ // Bearer
+ $sc->Method = \SAML2\Constants::CM_BEARER;
+ }
+ $a->setSubjectConfirmation(array($sc));
+
+ // add attributes
+ if ($spMetadata->getBoolean('simplesaml.attributes', true)) {
+ $attributeNameFormat = self::getAttributeNameFormat($idpMetadata, $spMetadata);
+ $a->setAttributeNameFormat($attributeNameFormat);
+ $attributes = self::encodeAttributes($idpMetadata, $spMetadata, $state['Attributes']);
+ $a->setAttributes($attributes);
+ }
+
+ // generate the NameID for the assertion
+ if (isset($state['saml:NameIDFormat'])) {
+ $nameIdFormat = $state['saml:NameIDFormat'];
+ } else {
+ $nameIdFormat = null;
+ }
+
+ if ($nameIdFormat === null || !isset($state['saml:NameID'][$nameIdFormat])) {
+ // either not set in request, or not set to a format we supply. Fall back to old generation method
+ $nameIdFormat = $spMetadata->getString('NameIDFormat', null);
+ if ($nameIdFormat === null) {
+ $nameIdFormat = $idpMetadata->getString('NameIDFormat', \SAML2\Constants::NAMEID_TRANSIENT);
+ }
+ }
+
+ if (isset($state['saml:NameID'][$nameIdFormat])) {
+ $nameId = $state['saml:NameID'][$nameIdFormat];
+ $nameId['Format'] = $nameIdFormat;
+ } else {
+ $spNameQualifier = $spMetadata->getString('SPNameQualifier', null);
+ if ($spNameQualifier === null) {
+ $spNameQualifier = $spMetadata->getString('entityid');
+ }
+
+ if ($nameIdFormat === \SAML2\Constants::NAMEID_TRANSIENT) {
+ // generate a random id
+ $nameIdValue = SimpleSAML\Utils\Random::generateID();
+ } else {
+ /* this code will end up generating either a fixed assigned id (via nameid.attribute)
+ or random id if not assigned/configured */
+ $nameIdValue = self::generateNameIdValue($idpMetadata, $spMetadata, $state);
+ if ($nameIdValue === null) {
+ SimpleSAML\Logger::warning('Falling back to transient NameID.');
+ $nameIdFormat = \SAML2\Constants::NAMEID_TRANSIENT;
+ $nameIdValue = SimpleSAML\Utils\Random::generateID();
+ }
+ }
+
+ $nameId = array(
+ 'Format' => $nameIdFormat,
+ 'Value' => $nameIdValue,
+ 'SPNameQualifier' => $spNameQualifier,
+ );
+ }
+
+ $state['saml:idp:NameID'] = $nameId;
+
+ $a->setNameId($nameId);
+
+ $encryptNameId = $spMetadata->getBoolean('nameid.encryption', null);
+ if ($encryptNameId === null) {
+ $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', false);
+ }
+ if ($encryptNameId) {
+ $a->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
+ }
+
+ return $a;
+ }
+
+
+ /**
+ * Encrypt an assertion.
+ *
+ * This function takes in a \SAML2\Assertion and encrypts it if encryption of
+ * assertions are enabled in the metadata.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
+ * @param \SAML2\Assertion $assertion The assertion we are encrypting.
+ *
+ * @return \SAML2\Assertion|\SAML2\EncryptedAssertion The assertion.
+ *
+ * @throws SimpleSAML_Error_Exception In case the encryption key type is not supported.
+ */
+ private static function encryptAssertion(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata,
+ \SAML2\Assertion $assertion
+ ) {
+
+ $encryptAssertion = $spMetadata->getBoolean('assertion.encryption', null);
+ if ($encryptAssertion === null) {
+ $encryptAssertion = $idpMetadata->getBoolean('assertion.encryption', false);
+ }
+ if (!$encryptAssertion) {
+ // we are _not_ encrypting this assertion, and are therefore done
+ return $assertion;
+ }
+
+
+ $sharedKey = $spMetadata->getString('sharedkey', null);
+ if ($sharedKey !== null) {
+ $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
+ $key->loadKey($sharedKey);
+ } else {
+ $keys = $spMetadata->getPublicKeys('encryption', true);
+ $key = $keys[0];
+ switch ($key['type']) {
+ case 'X509Certificate':
+ $pemKey = "-----BEGIN CERTIFICATE-----\n".
+ chunk_split($key['X509Certificate'], 64).
+ "-----END CERTIFICATE-----\n";
+ break;
+ default:
+ throw new SimpleSAML_Error_Exception('Unsupported encryption key type: '.$key['type']);
+ }
+
+ // extract the public key from the certificate for encryption
+ $key = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array('type' => 'public'));
+ $key->loadKey($pemKey);
+ }
+
+ $ea = new \SAML2\EncryptedAssertion();
+ $ea->setAssertion($assertion, $key);
+ return $ea;
+ }
+
+
+ /**
+ * Build a logout request based on information in the metadata.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
+ * @param array $association The SP association.
+ * @param string|null $relayState An id that should be carried across the logout.
+ *
+ * @return \SAML2\LogoutResponse The corresponding SAML2 logout response.
+ */
+ private static function buildLogoutRequest(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata,
+ array $association,
+ $relayState
+ ) {
+
+ $lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata);
+ $lr->setRelayState($relayState);
+ $lr->setSessionIndex($association['saml:SessionIndex']);
+ $lr->setNameId($association['saml:NameID']);
+
+ $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', null);
+ if ($assertionLifetime === null) {
+ $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
+ }
+ $lr->setNotOnOrAfter(time() + $assertionLifetime);
+
+ $encryptNameId = $spMetadata->getBoolean('nameid.encryption', null);
+ if ($encryptNameId === null) {
+ $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', false);
+ }
+ if ($encryptNameId) {
+ $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
+ }
+
+ return $lr;
+ }
+
+
+ /**
+ * Build a authentication response based on information in the metadata.
+ *
+ * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP.
+ * @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
+ * @param string $consumerURL The Destination URL of the response.
+ *
+ * @return \SAML2\Response The SAML2 response corresponding to the given data.
+ */
+ private static function buildResponse(
+ SimpleSAML_Configuration $idpMetadata,
+ SimpleSAML_Configuration $spMetadata,
+ $consumerURL
+ ) {
+
+ $signResponse = $spMetadata->getBoolean('saml20.sign.response', null);
+ if ($signResponse === null) {
+ $signResponse = $idpMetadata->getBoolean('saml20.sign.response', true);
+ }
+
+ $r = new \SAML2\Response();
+
+ $r->setIssuer($idpMetadata->getString('entityid'));
+ $r->setDestination($consumerURL);
+
+ if ($signResponse) {
+ sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $r);
+ }
+
+ return $r;
+ }
+}
diff --git a/modules/sanitycheck/config-templates/config-sanitycheck.php b/modules/sanitycheck/config-templates/config-sanitycheck.php
index 748a21b..ac40a0f 100644
--- a/modules/sanitycheck/config-templates/config-sanitycheck.php
+++ b/modules/sanitycheck/config-templates/config-sanitycheck.php
@@ -5,12 +5,12 @@
$config = array (
- /*
- * Do you want to generate statistics using the cron module? If so, specify which cron tag to use.
- * Examples: daily, weekly
- * To not run statistics in cron, set value to
- * 'cron_tag' => NULL,
- */
- 'cron_tag' => 'hourly',
+ /*
+ * Do you want to generate statistics using the cron module? If so, specify which cron tag to use.
+ * Examples: daily, weekly
+ * To not run statistics in cron, set value to
+ * 'cron_tag' => null,
+ */
+ 'cron_tag' => 'hourly',
);
diff --git a/modules/sanitycheck/hooks/hook_cron.php b/modules/sanitycheck/hooks/hook_cron.php
index 37c020e..44abd49 100644
--- a/modules/sanitycheck/hooks/hook_cron.php
+++ b/modules/sanitycheck/hooks/hook_cron.php
@@ -4,39 +4,38 @@
*
* @param array &$croninfo Output
*/
-function sanitycheck_hook_cron(&$croninfo) {
- assert('is_array($croninfo)');
- assert('array_key_exists("summary", $croninfo)');
- assert('array_key_exists("tag", $croninfo)');
+function sanitycheck_hook_cron(&$croninfo)
+{
+ assert('is_array($croninfo)');
+ assert('array_key_exists("summary", $croninfo)');
+ assert('array_key_exists("tag", $croninfo)');
- SimpleSAML\Logger::info('cron [sanitycheck]: Running cron in cron tag [' . $croninfo['tag'] . '] ');
+ SimpleSAML\Logger::info('cron [sanitycheck]: Running cron in cron tag [' . $croninfo['tag'] . '] ');
- try {
-
- $sconfig = SimpleSAML_Configuration::getOptionalConfig('config-sanitycheck.php');
+ try {
+ $sconfig = SimpleSAML_Configuration::getOptionalConfig('config-sanitycheck.php');
- $cronTag = $sconfig->getString('cron_tag', NULL);
- if ($cronTag === NULL || $cronTag !== $croninfo['tag']) {
- return;
- }
+ $cronTag = $sconfig->getString('cron_tag', null);
+ if ($cronTag === null || $cronTag !== $croninfo['tag']) {
+ return;
+ }
- $info = array();
- $errors = array();
- $hookinfo = array(
- 'info' => &$info,
- 'errors' => &$errors,
- );
-
- SimpleSAML\Module::callHooks('sanitycheck', $hookinfo);
-
- if (count($errors) > 0) {
- foreach ($errors AS $err) {
- $croninfo['summary'][] = 'Sanitycheck error: ' . $err;
- }
- }
-
- } catch (Exception $e) {
- $croninfo['summary'][] = 'Error executing sanity check: ' . $e->getMessage();
- }
+ $info = array();
+ $errors = array();
+ $hookinfo = array(
+ 'info' => &$info,
+ 'errors' => &$errors,
+ );
+ SimpleSAML\Module::callHooks('sanitycheck', $hookinfo);
+
+ if (count($errors) > 0) {
+ foreach ($errors AS $err) {
+ $croninfo['summary'][] = 'Sanitycheck error: ' . $err;
+ }
+ }
+
+ } catch (Exception $e) {
+ $croninfo['summary'][] = 'Error executing sanity check: ' . $e->getMessage();
+ }
}
diff --git a/modules/sanitycheck/hooks/hook_frontpage.php b/modules/sanitycheck/hooks/hook_frontpage.php
index 27d55ab..b40e657 100644
--- a/modules/sanitycheck/hooks/hook_frontpage.php
+++ b/modules/sanitycheck/hooks/hook_frontpage.php
@@ -4,14 +4,14 @@
*
* @param array &$links The links on the frontpage, split into sections.
*/
-function sanitycheck_hook_frontpage(&$links) {
- assert('is_array($links)');
- assert('array_key_exists("links", $links)');
-
- $links['config']['santitycheck'] = array(
- 'href' => SimpleSAML\Module::getModuleURL('sanitycheck/index.php'),
- 'text' => array('en' => 'Sanity check of your SimpleSAMLphp setup'),
- 'shorttext' => array('en' => 'SanityCheck'),
- );
+function sanitycheck_hook_frontpage(&$links)
+{
+ assert('is_array($links)');
+ assert('array_key_exists("links", $links)');
+ $links['config']['santitycheck'] = array(
+ 'href' => SimpleSAML\Module::getModuleURL('sanitycheck/index.php'),
+ 'text' => array('en' => 'Sanity check of your SimpleSAMLphp setup'),
+ 'shorttext' => array('en' => 'SanityCheck'),
+ );
}
diff --git a/modules/sanitycheck/hooks/hook_moduleinfo.php b/modules/sanitycheck/hooks/hook_moduleinfo.php
index c186727..679ac17 100644
--- a/modules/sanitycheck/hooks/hook_moduleinfo.php
+++ b/modules/sanitycheck/hooks/hook_moduleinfo.php
@@ -4,16 +4,16 @@
*
* @param array &$moduleinfo The links on the frontpage, split into sections.
*/
-function sanitycheck_hook_moduleinfo(&$moduleinfo) {
- assert('is_array($moduleinfo)');
- assert('array_key_exists("info", $moduleinfo)');
+function sanitycheck_hook_moduleinfo(&$moduleinfo)
+{
+ assert('is_array($moduleinfo)');
+ assert('array_key_exists("info", $moduleinfo)');
- $moduleinfo['info']['sanitycheck'] = array(
- 'name' => array('en' => 'Sanity check'),
- 'description' => array('en' => 'This module adds functionality for other modules to provide santity checks.'),
-
- 'dependencies' => array('core'),
- 'uses' => array('cron'),
- );
+ $moduleinfo['info']['sanitycheck'] = array(
+ 'name' => array('en' => 'Sanity check'),
+ 'description' => array('en' => 'This module adds functionality for other modules to provide santity checks.'),
+ 'dependencies' => array('core'),
+ 'uses' => array('cron'),
+ );
}
diff --git a/modules/sanitycheck/hooks/hook_sanitycheck.php b/modules/sanitycheck/hooks/hook_sanitycheck.php
index c15f1c6..867eab5 100644
--- a/modules/sanitycheck/hooks/hook_sanitycheck.php
+++ b/modules/sanitycheck/hooks/hook_sanitycheck.php
@@ -4,11 +4,11 @@
*
* @param array &$hookinfo hookinfo
*/
-function sanitycheck_hook_sanitycheck(&$hookinfo) {
- assert('is_array($hookinfo)');
- assert('array_key_exists("errors", $hookinfo)');
- assert('array_key_exists("info", $hookinfo)');
+function sanitycheck_hook_sanitycheck(&$hookinfo)
+{
+ assert('is_array($hookinfo)');
+ assert('array_key_exists("errors", $hookinfo)');
+ assert('array_key_exists("info", $hookinfo)');
- $hookinfo['info'][] = '[sanitycheck] At least the sanity check itself is working :)';
-
+ $hookinfo['info'][] = '[sanitycheck] At least the sanity check itself is working :)';
}
diff --git a/modules/sanitycheck/www/index.php b/modules/sanitycheck/www/index.php
index 708348b..e365db2 100644
--- a/modules/sanitycheck/www/index.php
+++ b/modules/sanitycheck/www/index.php
@@ -1,25 +1,23 @@
<?php
-
$config = SimpleSAML_Configuration::getInstance();
$info = array();
$errors = array();
$hookinfo = array(
- 'info' => &$info,
- 'errors' => &$errors,
+ 'info' => &$info,
+ 'errors' => &$errors,
);
SimpleSAML\Module::callHooks('sanitycheck', $hookinfo);
-
if (isset($_REQUEST['output']) && $_REQUEST['output'] == 'text') {
-
- if (count($errors) === 0) {
- echo 'OK';
- } else {
- echo 'FAIL';
- }
- exit;
+
+ if (count($errors) === 0) {
+ echo 'OK';
+ } else {
+ echo 'FAIL';
+ }
+ exit;
}
$t = new SimpleSAML_XHTML_Template($config, 'sanitycheck:check.tpl.php');
diff --git a/tests/lib/SimpleSAML/Locale/LanguageTest.php b/tests/lib/SimpleSAML/Locale/LanguageTest.php
index d6e6c81..7072a49 100644
--- a/tests/lib/SimpleSAML/Locale/LanguageTest.php
+++ b/tests/lib/SimpleSAML/Locale/LanguageTest.php
@@ -20,11 +20,11 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
// test defaults coming from configuration
$c = \SimpleSAML_Configuration::loadFromArray(array(
- 'language.available' => array('xx', 'yy', 'zz'),
- 'language.default' => 'yy',
+ 'language.available' => array('en', 'es', 'nn'),
+ 'language.default' => 'es',
));
$l = new Language($c);
- $this->assertEquals('yy', $l->getDefaultLanguage());
+ $this->assertEquals('es', $l->getDefaultLanguage());
}
@@ -44,40 +44,62 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
// test that it works with non-defaults
\SimpleSAML_Configuration::loadFromArray(array(
- 'language.available' => array('xx', 'yy', 'zz'),
+ 'language.available' => array('en', 'es', 'nn'),
'language.cookie.name' => 'xyz'
), '', 'simplesaml');
- $_COOKIE['xyz'] = 'Yy'; // test values are converted to lowercase too
- $this->assertEquals('yy', Language::getLanguageCookie());
+ $_COOKIE['xyz'] = 'Es'; // test values are converted to lowercase too
+ $this->assertEquals('es', Language::getLanguageCookie());
}
/**
* Test SimpleSAML\Locale\Language::getLanguageList().
*/
- public function testGetLanguageList()
+ public function testGetLanguageListNoConfig()
{
// test defaults
$c = \SimpleSAML_Configuration::loadFromArray(array(), '', 'simplesaml');
$l = new Language($c);
$l->setLanguage('en');
$this->assertEquals(array('en' => true), $l->getLanguageList());
+ }
+
- // test non-defaults
+ /**
+ * Test SimpleSAML\Locale\Language::getLanguageList().
+ */
+ public function testGetLanguageListCorrectConfig()
+ {
+ // test langs from from language_names
$c = \SimpleSAML_Configuration::loadFromArray(array(
- 'language.available' => array('xx', 'yy', 'zz'),
+ 'language.available' => array('en', 'nn', 'es'),
), '', 'simplesaml');
$l = new Language($c);
- $l->setLanguage('yy');
+ $l->setLanguage('es');
$this->assertEquals(array(
- 'xx' => false,
- 'yy' => true,
- 'zz' => false,
+ 'en' => false,
+ 'es' => true,
+ 'nn' => false,
), $l->getLanguageList());
}
/**
+ * Test SimpleSAML\Locale\Language::getLanguageList().
+ */
+ public function testGetLanguageListIncorrectConfig()
+ {
+ // test non-existent langs
+ $c = \SimpleSAML_Configuration::loadFromArray(array(
+ 'language.available' => array('foo', 'bar'),
+ ), '', 'simplesaml');
+ $l = new Language($c);
+ $l->setLanguage('foo');
+ $this->assertEquals(array('en' => true), $l->getLanguageList());
+ }
+
+
+ /**
* Test SimpleSAML\Locale\Language::getLanguageParameterName().
*/
public function testGetLanguageParameterName()
@@ -109,7 +131,7 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
// test non-defaults, non-RTL
$c = \SimpleSAML_Configuration::loadFromArray(array(
- 'language.rtl' => array('xx', 'yy', 'zz'),
+ 'language.rtl' => array('foo', 'bar'),
), '', 'simplesaml');
$l = new Language($c);
$l->setLanguage('en');
@@ -117,11 +139,11 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
// test non-defaults, RTL
$c = \SimpleSAML_Configuration::loadFromArray(array(
- 'language.available' => array('xx', 'yy', 'zz', 'en'),
- 'language.rtl' => array('xx', 'yy'),
+ 'language.available' => array('en', 'nn', 'es'),
+ 'language.rtl' => array('nn', 'es'),
), '', 'simplesaml');
$l = new Language($c);
- $l->setLanguage('yy');
+ $l->setLanguage('es');
$this->assertTrue($l->isLanguageRTL());
}
@@ -133,13 +155,13 @@ class LanguageTest extends \PHPUnit_Framework_TestCase
{
// test with valid configuration, no cookies set
$c = \SimpleSAML_Configuration::loadFromArray(array(
- 'language.available' => array('xx', 'yy', 'zz'),
+ 'language.available' => array('en', 'nn', 'es'),
'language.parameter.name' => 'xyz',
'language.parameter.setcookie' => false,
), '', 'simplesaml');
- $_GET['xyz'] = 'Zz'; // test also that lang code is transformed to lower caps
+ $_GET['xyz'] = 'Es'; // test also that lang code is transformed to lower caps
$l = new Language($c);
- $this->assertEquals('zz', $l->getLanguage());
+ $this->assertEquals('es', $l->getLanguage());
// test with valid configuration, no cookies, language set unavailable
$_GET['xyz'] = 'unavailable';