Bind user before fetching (#9312)

* Bind user before fetching

* fix style
This commit is contained in:
Tony Murray
2018-10-11 14:29:57 -05:00
committed by Neil Lathwood
parent 7c45cd5f69
commit 44747fda25
3 changed files with 101 additions and 95 deletions

View File

@@ -59,17 +59,19 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
protected function userInGroup($username, $groupname)
{
$connection = $this->getConnection();
// check if user is member of the given group or nested groups
$search_filter = "(&(objectClass=group)(cn=$groupname))";
// get DN for auth_ad_group
$search = ldap_search(
$this->ldap_connection,
$connection,
Config::get('auth_ad_base_dn'),
$search_filter,
array("cn")
);
$result = ldap_get_entries($this->ldap_connection, $search);
$result = ldap_get_entries($connection, $search);
if ($result == false || $result['count'] !== 1) {
if (Config::get('auth_ad_debug', false)) {
@@ -89,29 +91,29 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
$group_dn = $result[0]["dn"];
$search = ldap_search(
$this->ldap_connection,
$connection,
Config::get('auth_ad_base_dn'),
// add 'LDAP_MATCHING_RULE_IN_CHAIN to the user filter to search for $username in nested $group_dn
// limiting to "DN" for shorter array
"(&" . $this->userFilter($username) . "(memberOf:1.2.840.113556.1.4.1941:=$group_dn))",
array("DN")
);
$entries = ldap_get_entries($this->ldap_connection, $search);
$entries = ldap_get_entries($connection, $search);
return ($entries["count"] > 0);
}
public function userExists($username, $throw_exception = false)
{
$this->bind(); // make sure we called bind
$connection = $this->getConnection();
$search = ldap_search(
$this->ldap_connection,
$connection,
Config::get('auth_ad_base_dn'),
$this->userFilter($username),
array('samaccountname')
);
$entries = ldap_get_entries($this->ldap_connection, $search);
$entries = ldap_get_entries($connection, $search);
if ($entries['count']) {
@@ -124,8 +126,6 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
public function getUserlevel($username)
{
$this->bind(); // make sure we called bind
$userlevel = 0;
if (!Config::get('auth_ad_require_groupmembership', true)) {
if (Config::get('auth_ad_global_read', false)) {
@@ -149,16 +149,16 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
public function getUserid($username)
{
$this->bind(); // make sure we called bind
$connection = $this->getConnection();
$attributes = array('objectsid');
$search = ldap_search(
$this->ldap_connection,
$connection,
Config::get('auth_ad_base_dn'),
$this->userFilter($username),
$attributes
);
$entries = ldap_get_entries($this->ldap_connection, $search);
$entries = ldap_get_entries($connection, $search);
if ($entries['count']) {
return $this->getUseridFromSid($this->sidFromLdap($entries[0]['objectsid'][0]));
@@ -170,49 +170,15 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
/**
* Bind to AD with the bind user if available, otherwise anonymous bind
* @internal
*
* @param bool $allow_anonymous attempt anonymous bind if bind user isn't available
* @param bool $force force rebind
* @return bool success or failure
*/
protected function bind($allow_anonymous = true, $force = false)
protected function init()
{
if ($this->is_bound && !$force) {
return true; // bind already attempted
if ($this->ldap_connection) {
return;
}
$this->connect(); // make sure we are connected
// set timeout
ldap_set_option(
$this->ldap_connection,
LDAP_OPT_NETWORK_TIMEOUT,
Config::get('auth_ad_timeout', 5)
);
// With specified bind user
if (Config::has('auth_ad_binduser') && Config::has('auth_ad_bindpassword')) {
$this->is_bound = true;
$bind = ldap_bind(
$this->ldap_connection,
Config::get('auth_ad_binduser') . '@' . Config::get('auth_ad_domain'),
Config::get('auth_ad_bindpassword')
);
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
return $bind;
}
$bind = false;
// Anonymous
if ($allow_anonymous) {
$this->is_bound = true;
$bind = ldap_bind($this->ldap_connection);
}
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
return $bind;
$this->connect();
$this->bind();
}
protected function connect()
@@ -242,9 +208,34 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
ldap_set_option($this->ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3);
}
public function bind($username = null, $password = null)
{
if (!$this->ldap_connection) {
$this->connect();
}
if (Config::has('auth_ad_binduser') && Config::has('auth_ad_bindpassword')) {
$username = Config::get('auth_ad_binduser');
$password = Config::get('auth_ad_bindpassword');
}
$username .= '@' . Config::get('auth_ad_domain');
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ad_timeout', 5));
$bind_result = ldap_bind($this->ldap_connection, $username, $password);
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
if ($bind_result) {
return;
}
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ad_timeout', 5));
ldap_bind($this->ldap_connection);
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
}
protected function getConnection()
{
$this->bind(); // make sure connected and bound
$this->init(); // make sure connected and bound
return $this->ldap_connection;
}
}

View File

@@ -267,6 +267,37 @@ class LdapAuthorizer extends AuthorizerBase
return $this->ldap_connection; // bind already attempted
}
if ($skip_bind) {
$this->connect();
} else {
$this->bind();
}
return $this->ldap_connection;
}
/**
* @param array $entry ldap entry array
* @return array
*/
private function ldapToUser($entry)
{
$uid_attr = strtolower(Config::get('auth_ldap_uid_attribute', 'uidnumber'));
return [
'username' => $entry['uid'][0],
'realname' => $entry['cn'][0],
'user_id' => (int)$entry[$uid_attr][0],
'email' => $entry[Config::get('auth_ldap_emailattr', 'mail')][0],
'level' => $this->getUserlevel($entry['uid'][0]),
];
}
private function connect()
{
if ($this->ldap_connection) {
return;
}
if (!function_exists('ldap_connect')) {
throw new AuthenticationException("PHP does not support LDAP, please install or enable the PHP LDAP extension.");
}
@@ -287,65 +318,43 @@ class LdapAuthorizer extends AuthorizerBase
throw new AuthenticationException("Fatal error: LDAP TLS required but not successfully negotiated: $error");
}
}
}
if ($skip_bind) {
return $this->ldap_connection;
}
public function bind($username = null, $password = null)
{
if (Config::get('auth_ldap_debug')) {
ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7);
}
// set timeout
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ldap_timeout', 5));
$this->connect();
if ((Config::has('auth_ldap_binduser') || Config::has('auth_ldap_binddn')) && Config::has('auth_ldap_bindpassword')) {
$username = Config::get('auth_ldap_binddn', $this->getFullDn(Config::get('auth_ldap_binduser')));
$password = Config::get('auth_ldap_bindpassword');
} elseif ($username) {
$username = $this->getFullDn($username);
}
// With specified bind user
if ((Config::has('auth_ldap_binduser') || Config::has('auth_ldap_binddn'))
&& Config::has('auth_ldap_bindpassword')
) {
if (Config::has('auth_ldap_binddn')) {
$bind_dn = Config::get('auth_ldap_binddn');
} else {
$bind_dn = $this->getFullDn(Config::get('auth_ldap_binduser'));
}
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ldap_timeout', 5));
$bind_result = ldap_bind($this->ldap_connection, $username, $password);
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
if (Config::get('auth_ldap_debug')) {
echo "Bind result: " . ldap_error($this->ldap_connection) . PHP_EOL;
}
$bind_result = ldap_bind($this->ldap_connection, $bind_dn, Config::get('auth_ldap_bindpassword'));
if (Config::get('auth_ldap_debug')) {
echo "Bind result: " . ldap_error($this->ldap_connection) . PHP_EOL;
}
if ($bind_result) {
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
return $this->ldap_connection;
}
if ($bind_result) {
return;
}
// Anonymous
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ldap_timeout', 5));
ldap_bind($this->ldap_connection);
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
if (Config::get('auth_ldap_debug')) {
echo "Anonymous bind result: " . ldap_error($this->ldap_connection) . PHP_EOL;
}
ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout
return $this->ldap_connection;
}
/**
* @param array $entry ldap entry array
* @return array
*/
private function ldapToUser($entry)
{
$uid_attr = strtolower(Config::get('auth_ldap_uid_attribute', 'uidnumber'));
return [
'username' => $entry['uid'][0],
'realname' => $entry['cn'][0],
'user_id' => (int)$entry[$uid_attr][0],
'email' => $entry[Config::get('auth_ldap_emailattr', 'mail')][0],
'level' => $this->getUserlevel($entry['uid'][0]),
];
}
}

View File

@@ -171,6 +171,7 @@ class LegacyUserProvider implements UserProvider
* Fetch user by username from legacy auth, update it or add it to the db then return it.
*
* @param string $username
* @param string $password
* @return User|null
*/
protected function fetchUserByName($username, $password = null)
@@ -180,6 +181,11 @@ class LegacyUserProvider implements UserProvider
$auth = LegacyAuth::get();
$type = LegacyAuth::getType();
// ldap based auth we should bind before using, otherwise searches may fail due to anonymous bind
if (method_exists($auth, 'bind')) {
$auth->bind($username, $password);
}
$auth_id = $auth->getUserid($username);
$new_user = $auth->getUser($auth_id);