From 41693e6dad1a48b1165ed83c08a2a194eccd8bf8 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Thu, 23 Sep 2010 06:00:39 +0000
Subject: [PATCH] New filter: AttributeAddFromLDAP

Thanks to Steve Moitozo II for implementing this filter.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2564 44740490-163a-0410-bde0-09ae8108e29a
---
 .../lib/Auth/Process/AttributeAddFromLDAP.php | 233 ++++++++++++++++++
 1 file changed, 233 insertions(+)
 create mode 100644 modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php

diff --git a/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php b/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php
new file mode 100644
index 000000000..ed0cd81ae
--- /dev/null
+++ b/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php
@@ -0,0 +1,233 @@
+<?php
+// +---------------------------------------------------+
+// | PHP Version: 5.2.x                                |
+// +---------------------------------------------------+
+// | simpleSAMLphp Auth Proc for adding additional     |
+// | identity attributes from a secondary LDAP query   |
+// +---------------------------------------------------+
+// |                                                   |
+// | This Auth Proc needs the following configuration  |
+// | directives set in order to function properly.     |
+// |                                                   |
+// | 'ldap_host' the hostname of the LDAP server       |
+// |                                                   |
+// | 'ldap_port' the port for the LDAP process         |
+// |                                                   |
+// | 'ldap_bind_user' the user to bind as (optional)   |
+// |                                                   |
+// | 'ldap_bind_pwd' the password for the bind user    |
+// |                 required if ldap_bind_user is     |
+// |                 specified                         |
+// |                                                   |
+// | 'userid_attribute' the attribute you will use to  |
+// |    filter results                                 |
+// |                                                   |
+// | 'ldap_search_base_dn' the search base             |
+// |                                                   |
+// | 'ldap_search_filter' the search filter.           |
+// | NOTE: Variable substitution will be performed on  |
+// |      ldap_search_filter. Any attribute in the     |
+// |      identity can be substituted by surrounding   |
+// |      it with percent symbols (%). For instance    |
+// |      %cn% would be replaced with the cn of the    |
+// |      user.                                        |
+// |                                                   |
+// | 'ldap_search_attribute' the name of the attribute |
+// |      in the search results that you want to add   |
+// |      to the identity attributes                   |
+// |                                                   |
+// | 'new_attribute_name' the name you want the newly  |
+// |      added attribute to be called when it's added |
+// |                                                   |
+// | EXAMPLE                                           |
+// |                                                   |
+// | 'authproc' => array(
+// |    50 => array(
+// |    'class' => 'ldap:AttributeAddFromLDAP',
+// |    'ldap_host' => 'ldap.example.org',
+// |    'ldap_port' => '389',
+// |    'ldap_bind_user' => 'ldap_bind_user',
+// |    'ldap_bind_pwd' => 'ldap_bind_pwd',
+// |    'userid_attribute' => 'cn',
+// |    'ldap_search_base_dn' => 'cn=security_tags,dc=example,dc=org',
+// |    'ldap_search_filter' => '(uniquemember=cn=%cn%,cn=users,cn=example,dc=org)',
+// |    'ldap_search_attribute' => 'displayname',
+// |    'new_attribute_name' => 'security_tags',
+// |    ),
+// | ),
+// |                                                   |
+// | This will cause the Auth Proc to query the LDAP   |
+// | looking for all the security tags that the        |
+// | current user is assigned. It will take the value  |
+// | contained in displayname and put it into a mutli- |
+// | value attribute called security_tags              |
+// |                                                   |
+// +---------------------------------------------------+
+// | Author: Steve Moitozo II <steve_moitozo@jaars.org>|
+// | Created: 20100513                                 |
+// +---------------------------------------------------+
+// | 20100920 Steve Moitozo II                         |
+// |    - incorporated feedback from Olav Morken to    |
+// |    prep code for inclusion in SimpleSAMLphp distro|
+// |    - moved call to ldap_set_options() inside test |
+// |    for $ds                                        |
+// |    - added the output of ldap_error() to the      |
+// |    exceptions                                     |
+// |    - reduced some of the nested ifs               |
+// |    - added support for multiple values            |
+// |    - added support for anonymous binds            |
+// |    - added escaping of search filter and attribute|
+// +---------------------------------------------------+
+
+
+/**
+ * Filter to add attributes to the identity by executing a query against an LDAP directory
+ *
+ *
+ * @author Steve Moitozo, JAARS, Inc.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends SimpleSAML_Auth_ProcessingFilter {
+
+	/**
+	 * The configuration.
+	 *
+	 * Associative array of strings.
+	 */
+	private $config = array();
+
+
+	/**
+	 * 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)');
+
+		$reqConfigVars = array(
+			'ldap_host',
+			'ldap_port',
+			'ldap_bind_user',
+			'ldap_bind_pwd',
+			'userid_attribute',
+			'ldap_search_base_dn',
+			'ldap_search_filter',
+			'ldap_search_attribute',
+			'new_attribute_name'
+		);
+
+		foreach($config as $name => $values){
+			if(!is_string($name)){
+				throw new Exception('Invalid attribute name: ' . var_export($name, TRUE));
+			}
+
+			// make sure the name is in the list of required config variables
+			if(in_array($name,$reqConfigVars)){
+
+
+				if(is_array($values)){
+					throw new Exception('Configuration parameters must not contain arrays. The value for parameter "'.$name.'" is an array.');
+				}
+
+				$this->config[$name] = $values;
+
+			}else{
+				// unknown config variable, skipping
+				throw new Exception('Unknown configuration variable "'.$name.'"');
+			}
+
+
+		}
+
+		$configVarsSet = array_keys($this->config);
+		foreach($reqConfigVars as $configVar){
+			if(!in_array($configVar, $configVarsSet)){
+				throw new Exception('Please provide a value for configuration parameter "'.$configVar.'".');
+			}
+		}
+	}
+
+
+	/**
+	 * Add attributes from an LDAP server.
+	 *
+	 * @param array &$request  The current request
+	 */
+	public function process(&$request) {
+		assert('is_array($request)');
+		assert('array_key_exists("Attributes", $request)');
+
+		$attributes =& $request['Attributes'];
+
+		if(!isset($attributes[$this->config['userid_attribute']])){
+			throw new Exception('The user\'s identity does not have an attribute called "'.$this->config['userid_attribute'].'"');
+		}
+
+
+		// perform a merge on the ldap_search_filter
+
+		// loop over the attributes and build the search and replace arrays
+		foreach($attributes as $attr => $val){
+			$arrSearch[] = '%'.$attr.'%';
+
+			if(strlen($val[0]) > 0){
+				$arrReplace[] = SimpleSAML_Auth_LDAP::escape_filter_value($val[0]);
+			}else{
+				$arrReplace[] = '';
+			}
+		}
+
+		// merge the attributes into the ldap_search_filter
+		$merged_ldap_search_filter = str_replace($arrSearch, $arrReplace, $this->config['ldap_search_filter']);
+
+
+		// connect to the LDAP directory
+		$ds = ldap_connect($this->config['ldap_host'], $this->config['ldap_port']);
+
+		if(!$ds){
+			throw new Exception('Failed to initialize LDAP connection parameters ('.ldap_error(NULL).')');
+		}
+
+		ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
+
+		// if we're supposed to bind as a specified user
+		if((isset($this->config['ldap_bind_user']) && $this->config['ldap_bind_user']) && 
+		   (isset($this->config['ldap_bind_pwd'])  && $this->config['ldap_bind_pwd'])){
+
+			// bind to the directory as the specified user
+			if(!ldap_bind($ds, $this->config['ldap_bind_user'], $this->config['ldap_bind_pwd'])){
+				throw new Exception($this->config['ldap_bind_user'].' failed to bind against '.$this->config['ldap_host'].' ('.ldap_error($ds).')');
+			}
+
+		}else{	// bind to the directory anonymously
+
+			if(!ldap_bind($ds)){
+				throw new Exception('Failed to anonymously bind against '.$this->config['ldap_host'].' ('.ldap_error($ds).')');
+			}
+
+		}
+
+		// search for matching entries
+		$sr = ldap_search($ds, $this->config['ldap_search_base_dn'], $merged_ldap_search_filter, array($this->config['ldap_search_attribute']));
+		$entries = ldap_get_entries($ds, $sr);
+
+		// handle [multiple] values
+		if(is_array($entries) && is_array($entries[0])){
+			$entry = $entries[0][strtolower($this->config['ldap_search_attribute'])];
+			$results = array();
+			for($i = 0; $i < $entry['count']; $i++){
+				$results[] = $entry[$i];
+			}
+			$attributes[$this->config['new_attribute_name']] = array_values($results);
+		}
+
+		ldap_unbind($ds);
+
+	}
+
+}
-- 
GitLab