Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14003466
D8159.id20306.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D8159.id20306.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -107,6 +107,7 @@
'PhutilAuthAdapterOAuthTwitch' => 'auth/PhutilAuthAdapterOAuthTwitch.php',
'PhutilAuthAdapterOAuthTwitter' => 'auth/PhutilAuthAdapterOAuthTwitter.php',
'PhutilAuthAdapterPersona' => 'auth/PhutilAuthAdapterPersona.php',
+ 'PhutilAuthCredentialException' => 'auth/exception/PhutilAuthCredentialException.php',
'PhutilAuthException' => 'auth/exception/PhutilAuthException.php',
'PhutilAuthUserAbortedException' => 'auth/exception/PhutilAuthUserAbortedException.php',
'PhutilBallOfPHP' => 'phage/util/PhutilBallOfPHP.php',
@@ -519,6 +520,7 @@
'PhutilAuthAdapterOAuthTwitch' => 'PhutilAuthAdapterOAuth',
'PhutilAuthAdapterOAuthTwitter' => 'PhutilAuthAdapterOAuth1',
'PhutilAuthAdapterPersona' => 'PhutilAuthAdapter',
+ 'PhutilAuthCredentialException' => 'PhutilAuthException',
'PhutilAuthException' => 'Exception',
'PhutilAuthUserAbortedException' => 'PhutilAuthException',
'PhutilBufferedIterator' => 'Iterator',
diff --git a/src/auth/PhutilAuthAdapterLDAP.php b/src/auth/PhutilAuthAdapterLDAP.php
--- a/src/auth/PhutilAuthAdapterLDAP.php
+++ b/src/auth/PhutilAuthAdapterLDAP.php
@@ -9,7 +9,7 @@
private $port = 389;
private $baseDistinguishedName;
- private $searchAttribute;
+ private $searchAttributes = array();
private $usernameAttribute;
private $realNameAttributes = array();
private $ldapVersion = 3;
@@ -18,7 +18,6 @@
private $anonymousUsername;
private $anonymousPassword;
private $activeDirectoryDomain;
- private $searchFirst;
private $loginUsername;
private $loginPassword;
@@ -49,8 +48,8 @@
return $this;
}
- public function setSearchAttribute($search_attribute) {
- $this->searchAttribute = $search_attribute;
+ public function setSearchAttributes(array $search_attributes) {
+ $this->searchAttributes = $search_attributes;
return $this;
}
@@ -100,11 +99,6 @@
return $this;
}
- public function setSearchFirst($search) {
- $this->searchFirst = $search;
- return $this;
- }
-
public function setActiveDirectoryDomain($domain) {
$this->activeDirectoryDomain = $domain;
return $this;
@@ -129,7 +123,7 @@
public function readLDAPRecordAccountID(array $record) {
$key = $this->usernameAttribute;
if (!strlen($key)) {
- $key = $this->searchAttribute;
+ $key = head($this->searchAttributes);
}
return $this->readLDAPData($record, $key);
}
@@ -180,8 +174,7 @@
// 0 => 'actual-value-we-want',
// )
//
- // However, in at least the case of 'dn' after we "searchFirst", the
- // property is a bare string.
+ // However, in at least the case of 'dn', the property is a bare string.
if (is_scalar($list) && strlen($list)) {
return $list;
@@ -192,36 +185,66 @@
}
}
+ private function formatLDAPAttributeSearch($attribute, $login_user) {
+ // If the attribute contains the literal token "${login}", treat it as a
+ // query and substitute the user's login name for the token.
+
+ if (strpos($attribute, '${login}') !== false) {
+ $escaped_user = ldap_sprintf('%S', $login_user);
+ $attribute = str_replace('${login}', $escaped_user, $attribute);
+ return $attribute;
+ }
+
+ // Otherwise, treat it as a simple attribute search.
+
+ return ldap_sprintf(
+ '%Q=%S',
+ $attribute,
+ $login_user);
+ }
+
private function loadLDAPUserData() {
$conn = $this->establishConnection();
$login_user = $this->loginUsername;
$login_pass = $this->loginPassword;
- $distinguished_name = ldap_sprintf(
- '%Q=%s,%Q',
- $this->searchAttribute,
- $login_user,
- $this->baseDistinguishedName);
-
- if ($this->searchFirst) {
- $user = $this->searchLDAPForUser($this->usernameAttribute, $login_user);
- if (!$user) {
- throw new Exception("Invalid credentials.");
- }
- $login_user = $this->readLDAPData($user, $this->searchAttribute);
- $distinguished_name = $this->readLDAPData($user, 'dn');
- }
- if ($this->activeDirectoryDomain) {
- $distinguished_name = ldap_sprintf(
- '%s@%Q',
- $login_user,
- $this->activeDirectoryDomain);
+ if ($this->anonymousUsername) {
+ $distinguished_name = null;
+ $search_query = null;
+ foreach ($this->searchAttributes as $attribute) {
+ $search_query = $this->formatLDAPAttributeSearch(
+ $attribute,
+ $login_user);
+ $record = $this->searchLDAPForRecord($search_query);
+ if ($record) {
+ $distinguished_name = $this->readLDAPData($record, 'dn');
+ break;
+ }
+ }
+ if ($distinguished_name === null) {
+ throw new PhutilAuthCredentialException();
+ }
+ } else {
+ $search_query = $this->formatLDAPAttributeSearch(
+ head($this->searchAttributes),
+ $login_user);
+ if ($this->activeDirectoryDomain) {
+ $distinguished_name = ldap_sprintf(
+ '%s@%Q',
+ $login_user,
+ $this->activeDirectoryDomain);
+ } else {
+ $distinguished_name = ldap_sprintf(
+ '%Q,%Q',
+ $search_query,
+ $this->baseDistinguishedName);
+ }
}
$this->bindLDAP($conn, $distinguished_name, $login_pass);
- $result = $this->searchLDAPForUser($this->searchAttribute, $login_user);
+ $result = $this->searchLDAPForRecord($search_query);
if (!$result) {
// This is unusual (since the bind succeeded) but we've seen it at least
// once in the wild, where the anonymous user is allowed to search but
@@ -248,7 +271,7 @@
$pass = $this->anonymousPassword;
$this->bindLDAP($conn, $user, $pass);
- $result = $this->searchLDAPForUser($this->searchAttribute, $login_user);
+ $result = $this->searchLDAPForRecord($search_query);
if (!$result) {
throw new Exception(
pht(
@@ -342,10 +365,10 @@
}
- private function searchLDAPForUser($attribute, $username) {
+ private function searchLDAPForRecord($dn) {
$conn = $this->establishConnection();
- $results = $this->searchLDAP('%Q=%S', $attribute, $username);
+ $results = $this->searchLDAP('%Q', $dn);
if (!$results) {
return null;
@@ -353,8 +376,9 @@
if (count($results) > 1) {
throw new Exception(
- "LDAP user query returned more than one result. It must uniquely ".
- "identify a user.");
+ pht(
+ 'LDAP record query returned more than one result. The query must '.
+ 'uniquely identify a record.'));
}
return head($results);
@@ -405,6 +429,11 @@
$errno = @ldap_errno($conn);
$error = @ldap_error($conn);
+ // This is `LDAP_INVALID_CREDENTIALS`.
+ if ($errno == 49) {
+ throw new PhutilAuthCredentialException();
+ }
+
if ($errno || $error) {
$full_message = pht(
"LDAP Exception: %s\nLDAP Error #%d: %s",
diff --git a/src/auth/exception/PhutilAuthCredentialException.php b/src/auth/exception/PhutilAuthCredentialException.php
new file mode 100644
--- /dev/null
+++ b/src/auth/exception/PhutilAuthCredentialException.php
@@ -0,0 +1,8 @@
+<?php
+
+/**
+ * The user provided invalid credentials.
+ */
+final class PhutilAuthCredentialException extends PhutilAuthException {
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 27, 6:27 AM (3 w, 13 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6731771
Default Alt Text
D8159.id20306.diff (7 KB)
Attached To
Mode
D8159: Allow multiple LDAP search filters, and complex search queries
Attached
Detach File
Event Timeline
Log In to Comment