<commit_before><?php

/**
 * Constants defining possible errors
 */
define('ERR_INTERNAL', 1);
define('ERR_NO_USER', 2);
define('ERR_WRONG_PW', 3);
define('ERR_AS_DATA_INCONSIST', 4);
define('ERR_AS_INTERNAL', 5);
define('ERR_AS_ATTRIBUTE', 6);


/**
 * The LDAP class holds helper functions to access an LDAP database.
 *
 * @author <NAME>, UNINETT AS. <<EMAIL>>
 * @author <NAME>, UNINETT AS. <<EMAIL>>
 * @package simpleSAMLphp
 * @version $Id: Session.php 244 2008-02-04 08:36:24Z andreassolberg $
 */
class SimpleSAML_Auth_LDAP {


	/**
	 * LDAP link identifier.
	 *
	 * @var resource
	 */
	protected $ldap = null;

	/**
	 * LDAP user: authz_id if SASL is in use, binding dn otherwise
	 */
	protected $authz_id = null;

	/**
	 * Timeout value, in seconds.
	 *
	 * @var int
	 */
	protected $timeout = 0;

	/**
	 * Private constructor restricts instantiation to getInstance().
	 *
	 * @param string $hostname
	 * @param bool $enable_tls
	 * @param bool $debug
	 * @param int $timeout
	 */
	// TODO: Flesh out documentation.
	public function __construct($hostname, $enable_tls = TRUE, $debug = FALSE, $timeout = 0) {

		// Debug.
		SimpleSAML_Logger::debug('Library - LDAP __construct(): Setup LDAP with ' .
			'host=\'' . $hostname .
			'\', tls=' . var_export($enable_tls, true) .
			', debug=' . var_export($debug, true) .
			', timeout=' . var_export($timeout, true));

		/*
		 * Set debug level before calling connect. Note that this passes
		 * NULL to ldap_set_option, which is an undocumented feature.
		 *
		 * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
		 */
		if ($debug && !ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7))
			SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7');

		/*
		 * Prepare a connection for to this LDAP server. Note that this function
		 * doesn't actually connect to the server.
		 */
		$this->ldap = @ldap_connect($hostname);
		if ($this->ldap == FALSE)
			throw new $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL);

		/* Enable LDAP protocol version 3. */
		if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3))
			throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', ERR_INTERNAL);

		// Set timeouts, if supported.
		// (OpenLDAP 2.x.x or Netscape Directory SDK x.x needed).
		$this->timeout = $timeout;
		if ($timeout > 0) {
			if (defined('LDAP_OPT_NETWORK_TIMEOUT')) {
				/* This option isn't present before PHP 5.3. */
				if (!@ldap_set_option($this->ldap, constant('LDAP_OPT_NETWORK_TIMEOUT'), $timeout))
					SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to ' . $timeout);
			}
			if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout))
				SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to ' . $timeout);
		}

		// Enable TLS, if needed.
		if (!preg_match("/ldaps:/i", $hostname) and $enable_tls)
			if (!@ldap_start_tls($this->ldap))
				throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL);

	}
	
	
	/**
	 * Convenience method to create an LDAPException as well as log the
	 * description.
	 *
	 * @param string $description
	 * The exception's description
	 * @return Exception
	 */
	private function makeException($description, $type = NULL) {
		$errNo = 0x00;
		
		// Log LDAP code and description, if possible.
		if (empty($this->ldap)){
			SimpleSAML_Logger::error($description);
		}else{
			$errNo = @ldap_errno($this->ldap);
		}
		
		// Decide exception type and return
		if($type){
			if($errNo !== 0){
				// Only log real LDAP errors; not success.
				SimpleSAML_Logger::error($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
			}else{
				SimpleSAML_Logger::error($description);
			}
			
			switch ($type){
				case ERR_INTERNAL:// 1 - ExInternal
					return new SimpleSAML_Error_Exception($description, $errNo);
				case ERR_NO_USER:// 2 - ExUserNotFound
					return new SimpleSAML_Error_UserNotFound($description, $errNo);
				case ERR_WRONG_PW:// 3 - ExInvalidCredential
					return new SimpleSAML_Error_InvalidCredential($description, $errNo);
				case ERR_AS_DATA_INCONSIST:// 4 - ExAsDataInconsist
					return new SimpleSAML_Error_AuthSource('ldap', $description);
				case ERR_AS_INTERNAL:// 5 - ExAsInternal
					return new SimpleSAML_Error_AuthSource('ldap', $description);
			}
		}else{
			switch ($errNo){
				case 0x20://LDAP_NO_SUCH_OBJECT
					SimpleSAML_Logger::warning($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
					return new SimpleSAML_Error_UserNotFound($description, $errNo);
				case 0x31://LDAP_INVALID_CREDENTIALS
					SimpleSAML_Logger::info($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
					return new SimpleSAML_Error_InvalidCredential($description, $errNo);
				case -1://NO_SERVER_CONNECTION
					SimpleSAML_Logger::error($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
					return new SimpleSAML_Error_AuthSource('ldap', $description);
				default:
					SimpleSAML_Logger::error($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
					return new SimpleSAML_Error_AuthSource('ldap', $description);
			}
		}
	}


	/**
	 * Search for DN from a single base.
	 *
	 * @param string $base
	 * Indication of root of subtree to search
	 * @param string|array $attribute
	 * The attribute name(s) to search for.
	 * @param string $value
	 * The attribute value to search for.
	 * @return string
	 * The DN of the resulting found element.
	 * @throws SimpleSAML_Error_Exception if:
	 * - Attribute parameter is wrong type
	 * @throws SimpleSAML_Error_AuthSource if:
	 * - Not able to connect to LDAP server<commit_msg>SimpleSAML_Auth_LDAP: Also include the cause in exceptions.<commit_after><?php

/**
 * Constants defining possible errors
 */
define('ERR_INTERNAL', 1);
define('ERR_NO_USER', 2);
define('ERR_WRONG_PW', 3);
define('ERR_AS_DATA_INCONSIST', 4);
define('ERR_AS_INTERNAL', 5);
define('ERR_AS_ATTRIBUTE', 6);


/**
 * The LDAP class holds helper functions to access an LDAP database.
 *
 * @author <NAME>, UNINETT AS. <<EMAIL>>
 * @author <NAME>, UNINETT AS. <<EMAIL>>
 * @package simpleSAMLphp
 * @version $Id: Session.php 244 2008-02-04 08:36:24Z andreassolberg $
 */
class SimpleSAML_Auth_LDAP {


	/**
	 * LDAP link identifier.
	 *
	 * @var resource
	 */
	protected $ldap = null;

	/**
	 * LDAP user: authz_id if SASL is in use, binding dn otherwise
	 */
	protected $authz_id = null;

	/**
	 * Timeout value, in seconds.
	 *
	 * @var int
	 */
	protected $timeout = 0;

	/**
	 * Private constructor restricts instantiation to getInstance().
	 *
	 * @param string $hostname
	 * @param bool $enable_tls
	 * @param bool $debug
	 * @param int $timeout
	 */
	// TODO: Flesh out documentation.
	public function __construct($hostname, $enable_tls = TRUE, $debug = FALSE, $timeout = 0) {

		// Debug.
		SimpleSAML_Logger::debug('Library - LDAP __construct(): Setup LDAP with ' .
			'host=\'' . $hostname .
			'\', tls=' . var_export($enable_tls, true) .
			', debug=' . var_export($debug, true) .
			', timeout=' . var_export($timeout, true));

		/*
		 * Set debug level before calling connect. Note that this passes
		 * NULL to ldap_set_option, which is an undocumented feature.
		 *
		 * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
		 */
		if ($debug && !ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7))
			SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7');

		/*
		 * Prepare a connection for to this LDAP server. Note that this function
		 * doesn't actually connect to the server.
		 */
		$this->ldap = @ldap_connect($hostname);
		if ($this->ldap == FALSE)
			throw new $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL);

		/* Enable LDAP protocol version 3. */
		if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3))
			throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', ERR_INTERNAL);

		// Set timeouts, if supported.
		// (OpenLDAP 2.x.x or Netscape Directory SDK x.x needed).
		$this->timeout = $timeout;
		if ($timeout > 0) {
			if (defined('LDAP_OPT_NETWORK_TIMEOUT')) {
				/* This option isn't present before PHP 5.3. */
				if (!@ldap_set_option($this->ldap, constant('LDAP_OPT_NETWORK_TIMEOUT'), $timeout))
					SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to ' . $timeout);
			}
			if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout))
				SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to ' . $timeout);
		}

		// Enable TLS, if needed.
		if (!preg_match("/ldaps:/i", $hostname) and $enable_tls)
			if (!@ldap_start_tls($this->ldap))
				throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL);

	}
	
	
	/**
	 * Convenience method to create an LDAPException as well as log the
	 * description.
	 *
	 * @param string $description
	 * The exception's description
	 * @return Exception
	 */
	private function makeException($description, $type = NULL) {
		$errNo = 0x00;
		
		// Log LDAP code and description, if possible.
		if (empty($this->ldap)){
			SimpleSAML_Logger::error($description);
		}else{
			$errNo = @ldap_errno($this->ldap);
		}
		
		// Decide exception type and return
		if($type){
			if($errNo !== 0){
				// Only log real LDAP errors; not success.
				SimpleSAML_Logger::error($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
			}else{
				SimpleSAML_Logger::error($description);
			}
			
			switch ($type){
				case ERR_INTERNAL:// 1 - ExInternal
					return new SimpleSAML_Error_Exception($description, $errNo);
				case ERR_NO_USER:// 2 - ExUserNotFound
					return new SimpleSAML_Error_UserNotFound($description, $errNo);
				case ERR_WRONG_PW:// 3 - ExInvalidCredential
					return new SimpleSAML_Error_InvalidCredential($description, $errNo);
				case ERR_AS_DATA_INCONSIST:// 4 - ExAsDataInconsist
					return new SimpleSAML_Error_AuthSource('ldap', $description);
				case ERR_AS_INTERNAL:// 5 - ExAsInternal
					return new SimpleSAML_Error_AuthSource('ldap', $description);
			}
		}else{
			if ($errNo !== 0) {
				$description .= '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')';
			}
			switch ($errNo){
				case 0x20://LDAP_NO_SUCH_OBJECT
					SimpleSAML_Logger::warning($description);
					return new SimpleSAML_Error_UserNotFound($description, $errNo);
				case 0x31://LDAP_INVALID_CREDENTIALS
					SimpleSAML_Logger::info($description);
					return new SimpleSAML_Error_InvalidCredential($description, $errNo);
				case -1://NO_SERVER_CONNECTION
					SimpleSAML_Logger::error($description);
					return new SimpleSAML_Error_AuthSource('ldap', $description);
				default:
					SimpleSAML_Logger::error($description);
					return new SimpleSAML_Error_AuthSource('ldap', $description);
			}
		}
	}


	/**
	 * Search for DN from a single base.
	 *
	 * @param string $base
	 * Indication of root of subtree to search
	 * @param string|array $attribute
	 * The attribute name(s) to search for.
	 * @param string $value
	 * The attribute value to search for.
	 * @return string
	 * The DN of the resulting found element.
	 * @throws SimpleSAML_Error_Exception if:
	 * - Attribute parameter is wrong type
	 * @throws SimpleSAML_Error_AuthSource if:
	 * - Not able to connect to LDAP server
{'fraction_non_alphanumeric': 0.1384112071134972, 'fraction_numerical': 0.00922741380756648, 'mean_word_length': 3.746019108280255, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4412591', 'n_tokens_mistral': 4517, 'n_tokens_neox': 3852, 'n_words': 1031}