Page MenuHomePhabricator

D8159.id20304.diff
No OneTemporary

D8159.id20304.diff

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

Mime Type
text/plain
Expires
Sat, Nov 2, 2:43 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6731771
Default Alt Text
D8159.id20304.diff (7 KB)

Event Timeline