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.