Changeset View
Standalone View
src/applications/people/controller/PhabricatorPeopleLdapController.php
- This file was deleted.
<?php | |||||
final class PhabricatorPeopleLdapController | |||||
extends PhabricatorPeopleController { | |||||
public function handleRequest(AphrontRequest $request) { | |||||
$this->requireApplicationCapability( | |||||
PeopleCreateUsersCapability::CAPABILITY); | |||||
$admin = $request->getUser(); | |||||
$content = array(); | |||||
$form = id(new AphrontFormView()) | |||||
->setAction($request->getRequestURI() | |||||
->alter('search', 'true')->alter('import', null)) | |||||
->setUser($admin) | |||||
->appendChild( | |||||
id(new AphrontFormTextControl()) | |||||
->setLabel(pht('LDAP username')) | |||||
->setName('username')) | |||||
->appendChild( | |||||
id(new AphrontFormPasswordControl()) | |||||
->setDisableAutocomplete(true) | |||||
->setLabel(pht('Password')) | |||||
->setName('password')) | |||||
->appendChild( | |||||
id(new AphrontFormTextControl()) | |||||
->setLabel(pht('LDAP query')) | |||||
->setCaption(pht('A filter such as %s.', '(objectClass=*)')) | |||||
->setName('query')) | |||||
->appendChild( | |||||
id(new AphrontFormSubmitControl()) | |||||
->setValue(pht('Search'))); | |||||
$panel = id(new PHUIObjectBoxView()) | |||||
->setHeaderText(pht('Import LDAP Users')) | |||||
->setForm($form); | |||||
$crumbs = $this->buildApplicationCrumbs(); | |||||
$crumbs->addTextCrumb( | |||||
pht('Import LDAP Users'), | |||||
$this->getApplicationURI('/ldap/')); | |||||
$nav = $this->buildSideNavView(); | |||||
$nav->selectFilter('ldap'); | |||||
$nav->appendChild($content); | |||||
if ($request->getStr('import')) { | |||||
$nav->appendChild($this->processImportRequest($request)); | |||||
} | |||||
$nav->appendChild($panel); | |||||
if ($request->getStr('search')) { | |||||
$nav->appendChild($this->processSearchRequest($request)); | |||||
} | |||||
return $this->newPage() | |||||
->setTitle(pht('Import LDAP Users')) | |||||
->setCrumbs($crumbs) | |||||
->setNavigation($nav); | |||||
} | |||||
private function processImportRequest($request) { | |||||
$admin = $request->getUser(); | |||||
$usernames = $request->getArr('usernames'); | |||||
$emails = $request->getArr('email'); | |||||
epriestley: Email addresses are read from the client, so anyone who can use this workflow can associate any… | |||||
$names = $request->getArr('name'); | |||||
$notice_view = new PHUIInfoView(); | |||||
$notice_view->setSeverity(PHUIInfoView::SEVERITY_NOTICE); | |||||
$notice_view->setTitle(pht('Import Successful')); | |||||
$notice_view->setErrors(array( | |||||
pht('Successfully imported users from LDAP'), | |||||
)); | |||||
$list = new PHUIObjectItemListView(); | |||||
$list->setNoDataString(pht('No users imported?')); | |||||
foreach ($usernames as $username) { | |||||
$user = new PhabricatorUser(); | |||||
$user->setUsername($username); | |||||
epriestleyAuthorUnsubmitted Done Inline ActionsThis does not validate usernames. Invalid LDAP usernames will fail during user creation with no ability to correct the error. epriestley: This does not validate usernames. Invalid LDAP usernames will fail during user creation with no… | |||||
$user->setRealname($names[$username]); | |||||
$email_obj = id(new PhabricatorUserEmail()) | |||||
->setAddress($emails[$username]) | |||||
->setIsVerified(1); | |||||
epriestleyAuthorUnsubmitted Done Inline ActionsEmail addresses are verified unconditionally, so any user with access to this workflow can launch a private LDAP server and verify control of any email address. Because we trust the client to provide the email address, they don't have to launch an LDAP server: they can just submit whatever they want. epriestley: Email addresses are verified unconditionally, so any user with access to this workflow can… | |||||
try { | |||||
id(new PhabricatorUserEditor()) | |||||
->setActor($admin) | |||||
->createNewUser($user, $email_obj); | |||||
id(new PhabricatorExternalAccount()) | |||||
->setUserPHID($user->getPHID()) | |||||
->setAccountType('ldap') | |||||
->setAccountDomain('self') | |||||
->setAccountID($username) | |||||
->save(); | |||||
epriestleyAuthorUnsubmitted Done Inline ActionsThis manual, unconditional construction of external accounts without a related ProviderConfig is the main thing I'm trying to get rid of. I could associate them with the provider instead, but I'm not convinced I can actually test LDAP. epriestley: This manual, unconditional construction of external accounts without a related `ProviderConfig`… | |||||
epriestleyAuthorUnsubmitted Done Inline ActionsThis may fail after the save() on line 91 succeeds. This will leave the user with a User record and no ExternalAccount record. This can't be fixed because the save() on line 91 will fail next time, since a user with the username already exists. epriestley: This may fail after the `save()` on line 91 succeeds. This will leave the user with a `User`… | |||||
$header = pht('Successfully added %s', $username); | |||||
$attribute = null; | |||||
$color = 'fa-check green'; | |||||
} catch (Exception $ex) { | |||||
$header = pht('Failed to add %s', $username); | |||||
$attribute = $ex->getMessage(); | |||||
$color = 'fa-times red'; | |||||
} | |||||
$item = id(new PHUIObjectItemView()) | |||||
->setHeader($header) | |||||
->addAttribute($attribute) | |||||
->setStatusIcon($color); | |||||
$list->addItem($item); | |||||
} | |||||
return array( | |||||
$notice_view, | |||||
$list, | |||||
); | |||||
} | |||||
private function processSearchRequest($request) { | |||||
$panel = new PHUIBoxView(); | |||||
$admin = $request->getUser(); | |||||
$search = $request->getStr('query'); | |||||
$ldap_provider = PhabricatorLDAPAuthProvider::getLDAPProvider(); | |||||
if (!$ldap_provider) { | |||||
throw new Exception(pht('No LDAP provider enabled!')); | |||||
} | |||||
$ldap_adapter = $ldap_provider->getAdapter(); | |||||
$ldap_adapter->setLoginUsername($request->getStr('username')); | |||||
$ldap_adapter->setLoginPassword( | |||||
new PhutilOpaqueEnvelope($request->getStr('password'))); | |||||
// This causes us to connect and bind. | |||||
// TODO: Clean up this discard mode stuff. | |||||
DarkConsoleErrorLogPluginAPI::enableDiscardMode(); | |||||
$ldap_adapter->getAccountID(); | |||||
DarkConsoleErrorLogPluginAPI::disableDiscardMode(); | |||||
$results = $ldap_adapter->searchLDAP('%Q', $search); | |||||
foreach ($results as $key => $record) { | |||||
$account_id = $ldap_adapter->readLDAPRecordAccountID($record); | |||||
if (!$account_id) { | |||||
unset($results[$key]); | |||||
continue; | |||||
} | |||||
$info = array( | |||||
$account_id, | |||||
$ldap_adapter->readLDAPRecordEmail($record), | |||||
$ldap_adapter->readLDAPRecordRealName($record), | |||||
); | |||||
$results[$key] = $info; | |||||
$results[$key][] = $this->renderUserInputs($info); | |||||
} | |||||
$form = id(new AphrontFormView()) | |||||
->setUser($admin); | |||||
$table = new AphrontTableView($results); | |||||
$table->setHeaders( | |||||
array( | |||||
pht('Username'), | |||||
pht('Email'), | |||||
pht('Real Name'), | |||||
pht('Import?'), | |||||
)); | |||||
$form->appendChild($table); | |||||
$form->setAction($request->getRequestURI() | |||||
->alter('import', 'true')->alter('search', null)) | |||||
->appendChild( | |||||
id(new AphrontFormSubmitControl()) | |||||
->setValue(pht('Import'))); | |||||
$panel->appendChild($form); | |||||
return $panel; | |||||
} | |||||
private function renderUserInputs($user) { | |||||
$username = $user[0]; | |||||
return hsprintf( | |||||
'%s%s%s', | |||||
phutil_tag( | |||||
'input', | |||||
array( | |||||
'type' => 'checkbox', | |||||
'name' => 'usernames[]', | |||||
'value' => $username, | |||||
)), | |||||
phutil_tag( | |||||
'input', | |||||
array( | |||||
'type' => 'hidden', | |||||
'name' => "email[$username]", | |||||
'value' => $user[1], | |||||
)), | |||||
phutil_tag( | |||||
'input', | |||||
array( | |||||
'type' => 'hidden', | |||||
'name' => "name[$username]", | |||||
'value' => $user[2], | |||||
))); | |||||
} | |||||
} |
Email addresses are read from the client, so anyone who can use this workflow can associate any username with any email address.