summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-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
18 files changed, 2298 insertions, 2140 deletions
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');