mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 22:10:55 +01:00
Support multiple LDAP filters in the Phabricator UI
Summary: Ref T3208. Not ready for prime time yet. Test Plan: ldap T.T Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, frgtn, aran Maniphest Tasks: T3208 Differential Revision: https://secure.phabricator.com/D8160
This commit is contained in:
parent
7167a729bf
commit
ba8925a531
1 changed files with 94 additions and 21 deletions
|
@ -30,6 +30,10 @@ final class PhabricatorAuthProviderLDAP
|
||||||
$realname_attributes = array();
|
$realname_attributes = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$search_attributes = $conf->getProperty(self::KEY_SEARCH_ATTRIBUTES);
|
||||||
|
$search_attributes = phutil_split_lines($search_attributes, false);
|
||||||
|
$search_attributes = array_filter($search_attributes);
|
||||||
|
|
||||||
$adapter = id(new PhutilAuthAdapterLDAP())
|
$adapter = id(new PhutilAuthAdapterLDAP())
|
||||||
->setHostname(
|
->setHostname(
|
||||||
$conf->getProperty(self::KEY_HOSTNAME))
|
$conf->getProperty(self::KEY_HOSTNAME))
|
||||||
|
@ -37,8 +41,7 @@ final class PhabricatorAuthProviderLDAP
|
||||||
$conf->getProperty(self::KEY_PORT))
|
$conf->getProperty(self::KEY_PORT))
|
||||||
->setBaseDistinguishedName(
|
->setBaseDistinguishedName(
|
||||||
$conf->getProperty(self::KEY_DISTINGUISHED_NAME))
|
$conf->getProperty(self::KEY_DISTINGUISHED_NAME))
|
||||||
->setSearchAttribute(
|
->setSearchAttributes($search_attributes)
|
||||||
$conf->getProperty(self::KEY_SEARCH_ATTRIBUTE))
|
|
||||||
->setUsernameAttribute(
|
->setUsernameAttribute(
|
||||||
$conf->getProperty(self::KEY_USERNAME_ATTRIBUTE))
|
$conf->getProperty(self::KEY_USERNAME_ATTRIBUTE))
|
||||||
->setRealNameAttributes($realname_attributes)
|
->setRealNameAttributes($realname_attributes)
|
||||||
|
@ -53,8 +56,6 @@ final class PhabricatorAuthProviderLDAP
|
||||||
->setAnonymousPassword(
|
->setAnonymousPassword(
|
||||||
new PhutilOpaqueEnvelope(
|
new PhutilOpaqueEnvelope(
|
||||||
$conf->getProperty(self::KEY_ANONYMOUS_PASSWORD)))
|
$conf->getProperty(self::KEY_ANONYMOUS_PASSWORD)))
|
||||||
->setSearchFirst(
|
|
||||||
$conf->getProperty(self::KEY_SEARCH_FIRST))
|
|
||||||
->setActiveDirectoryDomain(
|
->setActiveDirectoryDomain(
|
||||||
$conf->getProperty(self::KEY_ACTIVEDIRECTORY_DOMAIN));
|
$conf->getProperty(self::KEY_ACTIVEDIRECTORY_DOMAIN));
|
||||||
$this->adapter = $adapter;
|
$this->adapter = $adapter;
|
||||||
|
@ -167,6 +168,11 @@ final class PhabricatorAuthProviderLDAP
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("Username and password are required!");
|
throw new Exception("Username and password are required!");
|
||||||
}
|
}
|
||||||
|
} catch (PhutilAuthCredentialException $ex) {
|
||||||
|
$response = $controller->buildProviderPageResponse(
|
||||||
|
$this,
|
||||||
|
$this->renderLoginForm($request, 'login'));
|
||||||
|
return array($account, $response);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
// TODO: Make this cleaner.
|
// TODO: Make this cleaner.
|
||||||
throw $ex;
|
throw $ex;
|
||||||
|
@ -180,7 +186,7 @@ final class PhabricatorAuthProviderLDAP
|
||||||
const KEY_HOSTNAME = 'ldap:host';
|
const KEY_HOSTNAME = 'ldap:host';
|
||||||
const KEY_PORT = 'ldap:port';
|
const KEY_PORT = 'ldap:port';
|
||||||
const KEY_DISTINGUISHED_NAME = 'ldap:dn';
|
const KEY_DISTINGUISHED_NAME = 'ldap:dn';
|
||||||
const KEY_SEARCH_ATTRIBUTE = 'ldap:search-attribute';
|
const KEY_SEARCH_ATTRIBUTES = 'ldap:search-attribute';
|
||||||
const KEY_USERNAME_ATTRIBUTE = 'ldap:username-attribute';
|
const KEY_USERNAME_ATTRIBUTE = 'ldap:username-attribute';
|
||||||
const KEY_REALNAME_ATTRIBUTES = 'ldap:realname-attributes';
|
const KEY_REALNAME_ATTRIBUTES = 'ldap:realname-attributes';
|
||||||
const KEY_VERSION = 'ldap:version';
|
const KEY_VERSION = 'ldap:version';
|
||||||
|
@ -188,7 +194,6 @@ final class PhabricatorAuthProviderLDAP
|
||||||
const KEY_START_TLS = 'ldap:start-tls';
|
const KEY_START_TLS = 'ldap:start-tls';
|
||||||
const KEY_ANONYMOUS_USERNAME = 'ldap:anoynmous-username';
|
const KEY_ANONYMOUS_USERNAME = 'ldap:anoynmous-username';
|
||||||
const KEY_ANONYMOUS_PASSWORD = 'ldap:anonymous-password';
|
const KEY_ANONYMOUS_PASSWORD = 'ldap:anonymous-password';
|
||||||
const KEY_SEARCH_FIRST = 'ldap:search-first';
|
|
||||||
const KEY_ACTIVEDIRECTORY_DOMAIN = 'ldap:activedirectory-domain';
|
const KEY_ACTIVEDIRECTORY_DOMAIN = 'ldap:activedirectory-domain';
|
||||||
|
|
||||||
private function getPropertyKeys() {
|
private function getPropertyKeys() {
|
||||||
|
@ -200,15 +205,14 @@ final class PhabricatorAuthProviderLDAP
|
||||||
self::KEY_HOSTNAME => pht('LDAP Hostname'),
|
self::KEY_HOSTNAME => pht('LDAP Hostname'),
|
||||||
self::KEY_PORT => pht('LDAP Port'),
|
self::KEY_PORT => pht('LDAP Port'),
|
||||||
self::KEY_DISTINGUISHED_NAME => pht('Base Distinguished Name'),
|
self::KEY_DISTINGUISHED_NAME => pht('Base Distinguished Name'),
|
||||||
self::KEY_SEARCH_ATTRIBUTE => pht('Search Attribute'),
|
self::KEY_SEARCH_ATTRIBUTES => pht('Search Attributes'),
|
||||||
|
self::KEY_ANONYMOUS_USERNAME => pht('Anonymous Username'),
|
||||||
|
self::KEY_ANONYMOUS_PASSWORD => pht('Anonymous Password'),
|
||||||
self::KEY_USERNAME_ATTRIBUTE => pht('Username Attribute'),
|
self::KEY_USERNAME_ATTRIBUTE => pht('Username Attribute'),
|
||||||
self::KEY_REALNAME_ATTRIBUTES => pht('Realname Attributes'),
|
self::KEY_REALNAME_ATTRIBUTES => pht('Realname Attributes'),
|
||||||
self::KEY_VERSION => pht('LDAP Version'),
|
self::KEY_VERSION => pht('LDAP Version'),
|
||||||
self::KEY_REFERRALS => pht('Enable Referrals'),
|
self::KEY_REFERRALS => pht('Enable Referrals'),
|
||||||
self::KEY_START_TLS => pht('Use TLS'),
|
self::KEY_START_TLS => pht('Use TLS'),
|
||||||
self::KEY_SEARCH_FIRST => pht('Search First'),
|
|
||||||
self::KEY_ANONYMOUS_USERNAME => pht('Anonymous Username'),
|
|
||||||
self::KEY_ANONYMOUS_PASSWORD => pht('Anonymous Password'),
|
|
||||||
self::KEY_ACTIVEDIRECTORY_DOMAIN => pht('ActiveDirectory Domain'),
|
self::KEY_ACTIVEDIRECTORY_DOMAIN => pht('ActiveDirectory Domain'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -262,24 +266,16 @@ final class PhabricatorAuthProviderLDAP
|
||||||
self::KEY_DISTINGUISHED_NAME =>
|
self::KEY_DISTINGUISHED_NAME =>
|
||||||
pht('Example: %s',
|
pht('Example: %s',
|
||||||
phutil_tag('tt', array(), pht('ou=People, dc=example, dc=com'))),
|
phutil_tag('tt', array(), pht('ou=People, dc=example, dc=com'))),
|
||||||
self::KEY_SEARCH_ATTRIBUTE =>
|
self::KEY_USERNAME_ATTRIBUTE =>
|
||||||
pht('Example: %s',
|
pht('Example: %s',
|
||||||
phutil_tag('tt', array(), pht('sn'))),
|
phutil_tag('tt', array(), pht('sn'))),
|
||||||
self::KEY_USERNAME_ATTRIBUTE =>
|
|
||||||
pht('Optional, if different from search attribute.'),
|
|
||||||
self::KEY_REALNAME_ATTRIBUTES =>
|
self::KEY_REALNAME_ATTRIBUTES =>
|
||||||
pht('Optional. Example: %s',
|
pht('Example: %s',
|
||||||
phutil_tag('tt', array(), pht('firstname, lastname'))),
|
phutil_tag('tt', array(), pht('firstname, lastname'))),
|
||||||
self::KEY_REFERRALS =>
|
self::KEY_REFERRALS =>
|
||||||
pht('Follow referrals. Disable this for Windows AD 2003.'),
|
pht('Follow referrals. Disable this for Windows AD 2003.'),
|
||||||
self::KEY_START_TLS =>
|
self::KEY_START_TLS =>
|
||||||
pht('Start TLS after binding to the LDAP server.'),
|
pht('Start TLS after binding to the LDAP server.'),
|
||||||
self::KEY_SEARCH_FIRST =>
|
|
||||||
pht(
|
|
||||||
'When the user enters their username, search for a matching '.
|
|
||||||
'record using the "Search Attribute", then try to bind using '.
|
|
||||||
'the DN for the record. This is useful if usernames are not '.
|
|
||||||
'part of the record DN.'),
|
|
||||||
self::KEY_ANONYMOUS_USERNAME =>
|
self::KEY_ANONYMOUS_USERNAME =>
|
||||||
pht('Username to bind with before searching.'),
|
pht('Username to bind with before searching.'),
|
||||||
self::KEY_ANONYMOUS_PASSWORD =>
|
self::KEY_ANONYMOUS_PASSWORD =>
|
||||||
|
@ -289,11 +285,76 @@ final class PhabricatorAuthProviderLDAP
|
||||||
$types = array(
|
$types = array(
|
||||||
self::KEY_REFERRALS => 'checkbox',
|
self::KEY_REFERRALS => 'checkbox',
|
||||||
self::KEY_START_TLS => 'checkbox',
|
self::KEY_START_TLS => 'checkbox',
|
||||||
self::KEY_SEARCH_FIRST => 'checkbox',
|
self::KEY_SEARCH_ATTRIBUTES => 'textarea',
|
||||||
self::KEY_REALNAME_ATTRIBUTES => 'list',
|
self::KEY_REALNAME_ATTRIBUTES => 'list',
|
||||||
self::KEY_ANONYMOUS_PASSWORD => 'password',
|
self::KEY_ANONYMOUS_PASSWORD => 'password',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$instructions = array(
|
||||||
|
self::KEY_SEARCH_ATTRIBUTES => pht(
|
||||||
|
"When a user types their LDAP username and password into Phabricator, ".
|
||||||
|
"Phabricator can either bind to LDAP with those credentials directly ".
|
||||||
|
"(which is simpler, but not as powerful) or bind to LDAP with ".
|
||||||
|
"anonymous credentials, then search for record matching the supplied ".
|
||||||
|
"credentials (which is more complicated, but more powerful).\n\n".
|
||||||
|
"For many installs, direct binding is sufficient. However, you may ".
|
||||||
|
"want to search first if:\n\n".
|
||||||
|
" - You want users to be able to login with either their username ".
|
||||||
|
" or their email address.\n".
|
||||||
|
" - The login/username is not part of the distinguished name in ".
|
||||||
|
" your LDAP records.\n".
|
||||||
|
" - You want to restrict logins to a subset of users (like only ".
|
||||||
|
" those in certain departments).\n".
|
||||||
|
" - Your LDAP server is configured in some other way that prevents ".
|
||||||
|
" direct binding from working correctly.\n\n".
|
||||||
|
"**To bind directly**, enter the LDAP attribute corresponding to the ".
|
||||||
|
"login name into this box. Often, this is something like `sn` or ".
|
||||||
|
"`uid`. This is the simplest configuration, but will only work if the ".
|
||||||
|
"username is part of the distinguished name, and won't let you apply ".
|
||||||
|
"complex restrictions to logins.\n\n".
|
||||||
|
" lang=text,name=Simple Direct Binding\n".
|
||||||
|
" sn\n\n".
|
||||||
|
"**To search first**, provide an anonymous username and password ".
|
||||||
|
"below, then enter one or more search queries into this field, one ".
|
||||||
|
"per line. After binding, these queries will be used to identify the ".
|
||||||
|
"record associated with the login name the user typed.\n\n".
|
||||||
|
"Searches will be tried in order until a matching record is found. ".
|
||||||
|
"Each query can be a simple attribute name (like `sn` or `mail`), ".
|
||||||
|
"which will search for a matching record, or it can be a complex ".
|
||||||
|
"query that uses the string `\${login}` to represent the login ".
|
||||||
|
"name.\n\n".
|
||||||
|
"A common simple configuration is just an attribute name, like ".
|
||||||
|
"`sn`, which will work the same way direct binding works:\n\n".
|
||||||
|
" lang=text,name=Simple Example\n".
|
||||||
|
" sn\n\n".
|
||||||
|
"A slightly more complex configuration might let the user login with ".
|
||||||
|
"either their login name or email address:\n\n".
|
||||||
|
" lang=text,name=Match Several Attributes\n".
|
||||||
|
" mail\n".
|
||||||
|
" sn\n\n".
|
||||||
|
"If your LDAP directory is more complex, or you want to perform ".
|
||||||
|
"sophisticated filtering, you can use one or more queries. Depending ".
|
||||||
|
"on your directory structure, this example might allow users to login ".
|
||||||
|
"with either their email address or username, but only if they're in ".
|
||||||
|
"specific departments:\n\n".
|
||||||
|
" lang=text,name=Complex Example\n".
|
||||||
|
" (&(mail=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n".
|
||||||
|
" (&(sn=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n\n".
|
||||||
|
"All of the attribute names used here are just examples: your LDAP ".
|
||||||
|
"server may use different attribute names."),
|
||||||
|
self::KEY_USERNAME_ATTRIBUTE => pht(
|
||||||
|
"Optionally, specify a username attribute to use to prefill usernames ".
|
||||||
|
"when registering a new account. This is purely cosmetic and does not ".
|
||||||
|
"affect the login process, but you can configure it to make sure ".
|
||||||
|
"users get the same default username as their LDAP username, so ".
|
||||||
|
"usernames remain consistent across systems."),
|
||||||
|
self::KEY_REALNAME_ATTRIBUTES => pht(
|
||||||
|
"Optionally, specify one or more comma-separated attributes to use to ".
|
||||||
|
"prefill the \"Real Name\" field when registering a new account. This ".
|
||||||
|
"is purely cosmetic and does not affect the login process, but can ".
|
||||||
|
"make registration a little easier."),
|
||||||
|
);
|
||||||
|
|
||||||
foreach ($labels as $key => $label) {
|
foreach ($labels as $key => $label) {
|
||||||
$caption = idx($captions, $key);
|
$caption = idx($captions, $key);
|
||||||
$type = idx($types, $key);
|
$type = idx($types, $key);
|
||||||
|
@ -323,6 +384,13 @@ final class PhabricatorAuthProviderLDAP
|
||||||
->setCaption($caption)
|
->setCaption($caption)
|
||||||
->setValue($value);
|
->setValue($value);
|
||||||
break;
|
break;
|
||||||
|
case 'textarea':
|
||||||
|
$control = id(new AphrontFormTextAreaControl())
|
||||||
|
->setName($key)
|
||||||
|
->setLabel($label)
|
||||||
|
->setCaption($caption)
|
||||||
|
->setValue($value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$control = id(new AphrontFormTextControl())
|
$control = id(new AphrontFormTextControl())
|
||||||
->setName($key)
|
->setName($key)
|
||||||
|
@ -332,6 +400,11 @@ final class PhabricatorAuthProviderLDAP
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$instruction_text = idx($instructions, $key);
|
||||||
|
if (strlen($instruction_text)) {
|
||||||
|
$form->appendRemarkupInstructions($instruction_text);
|
||||||
|
}
|
||||||
|
|
||||||
$form->appendChild($control);
|
$form->appendChild($control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue