protected function collectGarbage()
 {
     $table = new PhabricatorUserLog();
     $conn_w = $table->establishConnection('w');
     queryfx($conn_w, 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', $table->getTableName(), $this->getGarbageEpoch());
     return $conn_w->getAffectedRows() == 100;
 }
 protected function loadPage()
 {
     $table = new PhabricatorUserLog();
     $conn_r = $table->establishConnection('r');
     $data = queryfx_all($conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r));
     return $table->loadAllFromArray($data);
 }
 public function collectGarbage()
 {
     $ttl = phutil_units('180 days in seconds');
     $table = new PhabricatorUserLog();
     $conn_w = $table->establishConnection('w');
     queryfx($conn_w, 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', $table->getTableName(), time() - $ttl);
     return $conn_w->getAffectedRows() == 100;
 }
 public static function newLog(PhabricatorUser $actor = null, PhabricatorUser $user = null, $action)
 {
     $log = new PhabricatorUserLog();
     if ($actor) {
         $log->setActorPHID($actor->getPHID());
     }
     if ($user) {
         $log->setUserPHID($user->getPHID());
     }
     if ($action) {
         $log->setAction($action);
     }
     return $log;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     if ($request->isFormPost()) {
         $log = PhabricatorUserLog::initializeNewLog($user, $user->getPHID(), PhabricatorUserLog::ACTION_LOGOUT);
         $log->save();
         // Destroy the user's session in the database so logout works even if
         // their cookies have some issues. We'll detect cookie issues when they
         // try to login again and tell them to clear any junk.
         $phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
         if (strlen($phsid)) {
             $session = id(new PhabricatorAuthSessionQuery())->setViewer($user)->withSessionKeys(array($phsid))->executeOne();
             if ($session) {
                 $session->delete();
             }
         }
         $request->clearCookie(PhabricatorCookies::COOKIE_SESSION);
         return id(new AphrontRedirectResponse())->setURI('/auth/loggedout/');
     }
     if ($user->getPHID()) {
         $dialog = id(new AphrontDialogView())->setUser($user)->setTitle(pht('Log out of Phabricator?'))->appendChild(pht('Are you sure you want to log out?'))->addSubmitButton(pht('Logout'))->addCancelButton('/');
         return id(new AphrontDialogResponse())->setDialog($dialog);
     }
     return id(new AphrontRedirectResponse())->setURI('/');
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     if ($request->isFormPost()) {
         $log = PhabricatorUserLog::newLog($user, $user, PhabricatorUserLog::ACTION_LOGOUT);
         $log->save();
         $request->clearCookie('phsid');
         return id(new AphrontRedirectResponse())->setURI('/login/');
     }
     return id(new AphrontRedirectResponse())->setURI('/');
 }
 public function buildSearchForm(AphrontFormView $form, PhabricatorSavedQuery $saved)
 {
     $actor_phids = $saved->getParameter('actorPHIDs', array());
     $user_phids = $saved->getParameter('userPHIDs', array());
     $actions = $saved->getParameter('actions', array());
     $remote_prefix = $saved->getParameter('ip');
     $sessions = $saved->getParameter('sessions', array());
     $actions = array_fuse($actions);
     $action_control = id(new AphrontFormCheckboxControl())->setLabel(pht('Actions'));
     $action_types = PhabricatorUserLog::getActionTypeMap();
     foreach ($action_types as $type => $label) {
         $action_control->addCheckbox('actions[]', $type, $label, isset($actions[$label]));
     }
     $form->appendControl(id(new AphrontFormTokenizerControl())->setDatasource(new PhabricatorPeopleDatasource())->setName('actors')->setLabel(pht('Actors'))->setValue($actor_phids))->appendControl(id(new AphrontFormTokenizerControl())->setDatasource(new PhabricatorPeopleDatasource())->setName('users')->setLabel(pht('Users'))->setValue($user_phids))->appendChild($action_control)->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Filter IP'))->setName('ip')->setValue($remote_prefix))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Sessions'))->setName('sessions')->setValue(implode(', ', $sessions)));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     if ($request->isFormPost()) {
         $log = PhabricatorUserLog::newLog($user, $user, PhabricatorUserLog::ACTION_LOGOUT);
         $log->save();
         // Destroy the user's session in the database so logout works even if
         // their cookies have some issues. We'll detect cookie issues when they
         // try to login again and tell them to clear any junk.
         $phsid = $request->getCookie('phsid');
         if ($phsid) {
             $user->destroySession($phsid);
         }
         $request->clearCookie('phsid');
         return id(new AphrontRedirectResponse())->setURI('/login/');
     }
     return id(new AphrontRedirectResponse())->setURI('/');
 }
 protected function execute(ConduitAPIRequest $request)
 {
     $this->validateHost($request->getValue('host'));
     $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE, 60 * 5);
     if (count($failed_attempts) > 5) {
         $this->logFailure();
         throw new ConduitException('ERR-RATE-LIMIT');
     }
     $token = $request->getValue('token');
     $info = id(new PhabricatorConduitCertificateToken())->loadOneWhere('token = %s', trim($token));
     if (!$info || $info->getDateCreated() < time() - 60 * 15) {
         $this->logFailure();
         throw new ConduitException('ERR-BAD-TOKEN');
     } else {
         $log = id(new PhabricatorUserLog())->setActorPHID($info->getUserPHID())->setUserPHID($info->getUserPHID())->setAction(PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE)->save();
     }
     $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $info->getUserPHID());
     if (!$user) {
         throw new Exception("Certificate token points to an invalid user!");
     }
     return array('username' => $user->getUserName(), 'certificate' => $user->getConduitCertificate());
 }
Esempio n. 10
0
 public static function initializeNewLog(PhabricatorUser $actor = null, $object_phid = null, $action = null)
 {
     $log = new PhabricatorUserLog();
     if ($actor) {
         $log->setActorPHID($actor->getPHID());
         if ($actor->hasSession()) {
             $session = $actor->getSession();
             // NOTE: This is a hash of the real session value, so it's safe to
             // store it directly in the logs.
             $log->setSession($session->getSessionKey());
         }
     }
     $log->setUserPHID((string) $object_phid);
     $log->setAction($action);
     $log->remoteAddr = (string) idx($_SERVER, 'REMOTE_ADDR', '');
     return $log;
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     if ($request->isFormPost()) {
         $log = PhabricatorUserLog::newLog($user, $user, PhabricatorUserLog::ACTION_LOGOUT);
         $log->save();
         // Destroy the user's session in the database so logout works even if
         // their cookies have some issues. We'll detect cookie issues when they
         // try to login again and tell them to clear any junk.
         $phsid = $request->getCookie('phsid');
         if ($phsid) {
             $user->destroySession($phsid);
         }
         $request->clearCookie('phsid');
         return id(new AphrontRedirectResponse())->setURI('/login/');
     }
     if ($user->getPHID()) {
         $dialog = id(new AphrontDialogView())->setUser($user)->setTitle('Log out of Phabricator?')->appendChild('<p>Are you sure you want to log out?</p>')->addSubmitButton('Log Out')->addCancelButton('/');
         return id(new AphrontDialogResponse())->setDialog($dialog);
     }
     return id(new AphrontRedirectResponse())->setURI('/');
 }
 public function render()
 {
     $logs = $this->logs;
     $handles = $this->handles;
     $viewer = $this->getUser();
     $action_map = PhabricatorUserLog::getActionTypeMap();
     $base_uri = $this->searchBaseURI;
     $rows = array();
     foreach ($logs as $log) {
         $ip = $log->getRemoteAddr();
         $session = substr($log->getSession(), 0, 6);
         if ($base_uri) {
             $ip = phutil_tag('a', array('href' => $base_uri . '?ip=' . $log->getRemoteAddr() . '#R'), $ip);
             $session = phutil_tag('a', array('href' => $base_uri . '?sessions=' . $log->getSession() . '#R'), $session);
         }
         $action = $log->getAction();
         $action_name = idx($action_map, $action, $action);
         $rows[] = array(phabricator_date($log->getDateCreated(), $viewer), phabricator_time($log->getDateCreated(), $viewer), $action_name, $log->getActorPHID() ? $handles[$log->getActorPHID()]->getName() : null, $handles[$log->getUserPHID()]->getName(), $ip, $session);
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array(pht('Date'), pht('Time'), pht('Action'), pht('Actor'), pht('User'), pht('IP'), pht('Session')));
     $table->setColumnClasses(array('', 'right', 'wide', '', '', '', 'n'));
     return $table;
 }
 private function processDelete(AphrontRequest $request)
 {
     $viewer = $request->getUser();
     $user = $this->getUser();
     $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($viewer, $request, $this->getPanelURI());
     $factor = id(new PhabricatorAuthFactorConfig())->loadOneWhere('id = %d AND userPHID = %s', $request->getInt('delete'), $user->getPHID());
     if (!$factor) {
         return new Aphront404Response();
     }
     if ($request->isFormPost()) {
         $factor->delete();
         $log = PhabricatorUserLog::initializeNewLog($viewer, $user->getPHID(), PhabricatorUserLog::ACTION_MULTI_REMOVE);
         $log->save();
         $user->updateMultiFactorEnrollment();
         return id(new AphrontRedirectResponse())->setURI($this->getPanelURI());
     }
     $dialog = id(new AphrontDialogView())->setUser($viewer)->addHiddenInput('delete', $factor->getID())->setTitle(pht('Delete Authentication Factor'))->appendParagraph(pht('Really remove the authentication factor %s from your account?', phutil_tag('strong', array(), $factor->getFactorName())))->addSubmitButton(pht('Remove Factor'))->addCancelButton($this->getPanelURI());
     return id(new AphrontDialogResponse())->setDialog($dialog);
 }
Esempio n. 14
0
 /**
  * @task email
  */
 public function changePrimaryEmail(PhabricatorUser $user, PhabricatorUserEmail $email)
 {
     $actor = $this->requireActor();
     if (!$user->getID()) {
         throw new Exception("User has not been created yet!");
     }
     if (!$email->getID()) {
         throw new Exception("Email has not been created yet!");
     }
     $user->openTransaction();
     $user->beginWriteLocking();
     $user->reload();
     $email->reload();
     if ($email->getUserPHID() != $user->getPHID()) {
         throw new Exception("User does not own email!");
     }
     if ($email->getIsPrimary()) {
         throw new Exception("Email is already primary!");
     }
     if (!$email->getIsVerified()) {
         throw new Exception("Email is not verified!");
     }
     $old_primary = $user->loadPrimaryEmail();
     if ($old_primary) {
         $old_primary->setIsPrimary(0);
         $old_primary->save();
     }
     $email->setIsPrimary(1);
     $email->save();
     $log = PhabricatorUserLog::newLog($actor, $user, PhabricatorUserLog::ACTION_EMAIL_PRIMARY);
     $log->setOldValue($old_primary ? $old_primary->getAddress() : null);
     $log->setNewValue($email->getAddress());
     $log->save();
     $user->endWriteLocking();
     $user->saveTransaction();
     if ($old_primary) {
         $old_primary->sendOldPrimaryEmail($user, $email);
     }
     $email->sendNewPrimaryEmail($user);
     return $this;
 }
 /**
  * Upgrade a session to have all legalpad documents signed.
  *
  * @param PhabricatorUser User whose session should upgrade.
  * @param array LegalpadDocument objects
  * @return void
  * @task partial
  */
 public function signLegalpadDocuments(PhabricatorUser $viewer, array $docs)
 {
     if (!$viewer->hasSession()) {
         throw new Exception(pht('Signing session legalpad documents of user with no session!'));
     }
     $session = $viewer->getSession();
     if ($session->getSignedLegalpadDocuments()) {
         throw new Exception(pht('Session has already signed required legalpad documents!'));
     }
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
     $session->setSignedLegalpadDocuments(1);
     queryfx($session->establishConnection('w'), 'UPDATE %T SET signedLegalpadDocuments = %d WHERE id = %d', $session->getTableName(), 1, $session->getID());
     if (!empty($docs)) {
         $log = PhabricatorUserLog::initializeNewLog($viewer, $viewer->getPHID(), PhabricatorUserLog::ACTION_LOGIN_LEGALPAD);
         $log->save();
     }
     unset($unguarded);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $filter_activity = $request->getStr('activity');
     $filter_ip = $request->getStr('ip');
     $filter_session = $request->getStr('session');
     $filter_user = $request->getArr('user', array());
     $filter_actor = $request->getArr('actor', array());
     $user_value = array();
     $actor_value = array();
     $phids = array_merge($filter_user, $filter_actor);
     if ($phids) {
         $handles = $this->loadViewerHandles($phids);
         if ($filter_user) {
             $filter_user = reset($filter_user);
             $user_value = array($filter_user => $handles[$filter_user]->getFullName());
         }
         if ($filter_actor) {
             $filter_actor = reset($filter_actor);
             $actor_value = array($filter_actor => $handles[$filter_actor]->getFullName());
         }
     }
     $form = new AphrontFormView();
     $form->setUser($user)->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Filter Actor')->setName('actor')->setLimit(1)->setValue($actor_value)->setDatasource('/typeahead/common/accounts/'))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Filter User')->setName('user')->setLimit(1)->setValue($user_value)->setDatasource('/typeahead/common/accounts/'))->appendChild(id(new AphrontFormSelectControl())->setLabel('Show Activity')->setName('activity')->setValue($filter_activity)->setOptions(array('' => 'All Activity', 'admin' => 'Admin Activity')))->appendChild(id(new AphrontFormTextControl())->setLabel('Filter IP')->setName('ip')->setValue($filter_ip)->setCaption('Enter an IP (or IP prefix) to show only activity by that remote ' . 'address.'))->appendChild(id(new AphrontFormTextControl())->setLabel('Filter Session')->setName('session')->setValue($filter_session))->appendChild(id(new AphrontFormSubmitControl())->setValue('Filter Logs'));
     $log_table = new PhabricatorUserLog();
     $conn_r = $log_table->establishConnection('r');
     $where_clause = array();
     $where_clause[] = '1 = 1';
     if ($filter_user) {
         $where_clause[] = qsprintf($conn_r, 'userPHID = %s', $filter_user);
     }
     if ($filter_actor) {
         $where_clause[] = qsprintf($conn_r, 'actorPHID = %s', $filter_actor);
     }
     if ($filter_activity == 'admin') {
         $where_clause[] = qsprintf($conn_r, 'action NOT IN (%Ls)', array(PhabricatorUserLog::ACTION_LOGIN, PhabricatorUserLog::ACTION_LOGOUT, PhabricatorUserLog::ACTION_LOGIN_FAILURE));
     }
     if ($filter_ip) {
         $where_clause[] = qsprintf($conn_r, 'remoteAddr LIKE %>', $filter_ip);
     }
     if ($filter_session) {
         $where_clause[] = qsprintf($conn_r, 'session = %s', $filter_session);
     }
     $where_clause = '(' . implode(') AND (', $where_clause) . ')';
     $pager = new AphrontPagerView();
     $pager->setURI($request->getRequestURI(), 'page');
     $pager->setOffset($request->getInt('page'));
     $pager->setPageSize(500);
     $logs = $log_table->loadAllWhere('(%Q) ORDER BY dateCreated DESC LIMIT %d, %d', $where_clause, $pager->getOffset(), $pager->getPageSize() + 1);
     $logs = $pager->sliceResults($logs);
     $phids = array();
     foreach ($logs as $log) {
         $phids[$log->getActorPHID()] = true;
         $phids[$log->getUserPHID()] = true;
     }
     $phids = array_keys($phids);
     $handles = $this->loadViewerHandles($phids);
     $rows = array();
     foreach ($logs as $log) {
         $rows[] = array(phabricator_date($log->getDateCreated(), $user), phabricator_time($log->getDateCreated(), $user), $log->getAction(), $log->getActorPHID() ? phutil_escape_html($handles[$log->getActorPHID()]->getName()) : null, phutil_escape_html($handles[$log->getUserPHID()]->getName()), json_encode($log->getOldValue(), true), json_encode($log->getNewValue(), true), phutil_render_tag('a', array('href' => $request->getRequestURI()->alter('ip', $log->getRemoteAddr())), phutil_escape_html($log->getRemoteAddr())), phutil_render_tag('a', array('href' => $request->getRequestURI()->alter('session', $log->getSession())), phutil_escape_html($log->getSession())));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Date', 'Time', 'Action', 'Actor', 'User', 'Old', 'New', 'IP', 'Session'));
     $table->setColumnClasses(array('', 'right', '', '', '', 'wrap', 'wrap', '', 'wide'));
     $panel = new AphrontPanelView();
     $panel->setHeader('Activity Logs');
     $panel->appendChild($table);
     $panel->appendChild($pager);
     $filter = new AphrontListFilterView();
     $filter->appendChild($form);
     $nav = $this->buildSideNavView();
     $nav->selectFilter('logs');
     $nav->appendChild(array($filter, $panel));
     return $this->buildApplicationPage($nav, array('title' => 'Activity Logs'));
 }
 /**
  * Upgrade a partial session to a full session.
  *
  * @param PhabricatorAuthSession Session to upgrade.
  * @return void
  * @task partial
  */
 public function upgradePartialSession(PhabricatorUser $viewer)
 {
     if (!$viewer->hasSession()) {
         throw new Exception(pht('Upgrading partial session of user with no session!'));
     }
     $session = $viewer->getSession();
     if (!$session->getIsPartial()) {
         throw new Exception(pht('Session is not partial!'));
     }
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
     $session->setIsPartial(0);
     queryfx($session->establishConnection('w'), 'UPDATE %T SET isPartial = %d WHERE id = %d', $session->getTableName(), 0, $session->getID());
     $log = PhabricatorUserLog::initializeNewLog($viewer, $viewer->getPHID(), PhabricatorUserLog::ACTION_LOGIN_FULL);
     $log->save();
     unset($unguarded);
 }
Esempio n. 18
0
 /**
  * Issue a new session key to this user. Phabricator supports different
  * types of sessions (like "web" and "conduit") and each session type may
  * have multiple concurrent sessions (this allows a user to be logged in on
  * multiple browsers at the same time, for instance).
  *
  * Note that this method is transport-agnostic and does not set cookies or
  * issue other types of tokens, it ONLY generates a new session key.
  *
  * You can configure the maximum number of concurrent sessions for various
  * session types in the Phabricator configuration.
  *
  * @param   string  Session type, like "web".
  * @return  string  Newly generated session key.
  */
 public function establishSession($session_type)
 {
     $conn_w = $this->establishConnection('w');
     if (strpos($session_type, '-') !== false) {
         throw new Exception("Session type must not contain hyphen ('-')!");
     }
     // We allow multiple sessions of the same type, so when a caller requests
     // a new session of type "web", we give them the first available session in
     // "web-1", "web-2", ..., "web-N", up to some configurable limit. If none
     // of these sessions is available, we overwrite the oldest session and
     // reissue a new one in its place.
     $session_limit = 1;
     switch ($session_type) {
         case 'web':
             $session_limit = PhabricatorEnv::getEnvConfig('auth.sessions.web');
             break;
         case 'conduit':
             $session_limit = PhabricatorEnv::getEnvConfig('auth.sessions.conduit');
             break;
         default:
             throw new Exception("Unknown session type '{$session_type}'!");
     }
     $session_limit = (int) $session_limit;
     if ($session_limit <= 0) {
         throw new Exception("Session limit for '{$session_type}' must be at least 1!");
     }
     // Load all the currently active sessions.
     $sessions = queryfx_all($conn_w, 'SELECT type, sessionStart FROM %T WHERE userPHID = %s AND type LIKE %>', PhabricatorUser::SESSION_TABLE, $this->getPHID(), $session_type . '-');
     // Choose which 'type' we'll actually establish, i.e. what number we're
     // going to append to the basic session type. To do this, just check all
     // the numbers sequentially until we find an available session.
     $establish_type = null;
     $sessions = ipull($sessions, null, 'type');
     for ($ii = 1; $ii <= $session_limit; $ii++) {
         if (empty($sessions[$session_type . '-' . $ii])) {
             $establish_type = $session_type . '-' . $ii;
             break;
         }
     }
     // If we didn't find an available session, choose the oldest session and
     // overwrite it.
     if (!$establish_type) {
         $sessions = isort($sessions, 'sessionStart');
         $oldest = reset($sessions);
         $establish_type = $oldest['type'];
     }
     // Consume entropy to generate a new session key, forestalling the eventual
     // heat death of the universe.
     $entropy = Filesystem::readRandomBytes(20);
     $session_key = sha1($entropy);
     // UNGUARDED WRITES: Logging-in users don't have CSRF stuff yet.
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
     queryfx($conn_w, 'INSERT INTO %T ' . '(userPHID, type, sessionKey, sessionStart)' . ' VALUES ' . '(%s, %s, %s, UNIX_TIMESTAMP()) ' . 'ON DUPLICATE KEY UPDATE ' . 'sessionKey = VALUES(sessionKey), ' . 'sessionStart = VALUES(sessionStart)', self::SESSION_TABLE, $this->getPHID(), $establish_type, $session_key);
     $log = PhabricatorUserLog::newLog($this, $this, PhabricatorUserLog::ACTION_LOGIN);
     $log->setDetails(array('session_type' => $session_type, 'session_issued' => $establish_type));
     $log->setSession($session_key);
     $log->save();
     return $session_key;
 }
 /**
  * Reassign an unverified email address.
  */
 public function reassignEmail(PhabricatorUser $user, PhabricatorUserEmail $email)
 {
     $actor = $this->requireActor();
     if (!$user->getID()) {
         throw new Exception(pht('User has not been created yet!'));
     }
     if (!$email->getID()) {
         throw new Exception(pht('Email has not been created yet!'));
     }
     $user->openTransaction();
     $user->beginWriteLocking();
     $user->reload();
     $email->reload();
     $old_user = $email->getUserPHID();
     if ($old_user != $user->getPHID()) {
         if ($email->getIsVerified()) {
             throw new Exception(pht('Verified email addresses can not be reassigned.'));
         }
         if ($email->getIsPrimary()) {
             throw new Exception(pht('Primary email addresses can not be reassigned.'));
         }
         $email->setUserPHID($user->getPHID());
         $email->save();
         $log = PhabricatorUserLog::initializeNewLog($actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_REASSIGN);
         $log->setNewValue($email->getAddress());
         $log->save();
     }
     $user->endWriteLocking();
     $user->saveTransaction();
 }
 private function processRoleRequest(PhabricatorUser $user)
 {
     $request = $this->getRequest();
     $admin = $request->getUser();
     $is_self = $user->getID() == $admin->getID();
     $errors = array();
     if ($request->isFormPost()) {
         $log_template = PhabricatorUserLog::newLog($admin, $user, null);
         $logs = array();
         if ($is_self) {
             $errors[] = "You can not edit your own role.";
         } else {
             $new_admin = (bool) $request->getBool('is_admin');
             $old_admin = (bool) $user->getIsAdmin();
             if ($new_admin != $old_admin) {
                 id(new PhabricatorUserEditor())->setActor($admin)->makeAdminUser($user, $new_admin);
             }
             $new_disabled = (bool) $request->getBool('is_disabled');
             $old_disabled = (bool) $user->getIsDisabled();
             if ($new_disabled != $old_disabled) {
                 id(new PhabricatorUserEditor())->setActor($admin)->disableUser($user, $new_disabled);
             }
         }
         if (!$errors) {
             return id(new AphrontRedirectResponse())->setURI($request->getRequestURI()->alter('saved', 'true'));
         }
     }
     $error_view = null;
     if ($errors) {
         $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors);
     }
     $form = id(new AphrontFormView())->setUser($admin)->setAction($request->getRequestURI()->alter('saved', null));
     if ($is_self) {
         $form->appendChild('<p class="aphront-form-instructions">NOTE: You can not edit your own ' . 'role.</p>');
     }
     $form->appendChild($this->getRoleInstructions())->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('is_admin', 1, 'Administrator', $user->getIsAdmin())->setDisabled($is_self))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('is_disabled', 1, 'Disabled', $user->getIsDisabled())->setDisabled($is_self))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('is_agent', 1, 'System Agent (Bot/Script User)', $user->getIsSystemAgent())->setDisabled(true));
     if (!$is_self) {
         $form->appendChild(id(new AphrontFormSubmitControl())->setValue('Edit Role'));
     }
     $panel = new AphrontPanelView();
     $panel->setHeader('Edit Role');
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     $panel->appendChild($form);
     return array($error_view, $panel);
 }
Esempio n. 21
0
 /**
  * Issue a new session key to this user. Phabricator supports different
  * types of sessions (like "web" and "conduit") and each session type may
  * have multiple concurrent sessions (this allows a user to be logged in on
  * multiple browsers at the same time, for instance).
  *
  * Note that this method is transport-agnostic and does not set cookies or
  * issue other types of tokens, it ONLY generates a new session key.
  *
  * You can configure the maximum number of concurrent sessions for various
  * session types in the Phabricator configuration.
  *
  * @param   string  Session type, like "web".
  * @return  string  Newly generated session key.
  */
 public function establishSession($session_type)
 {
     $conn_w = $this->establishConnection('w');
     if (strpos($session_type, '-') !== false) {
         throw new Exception("Session type must not contain hyphen ('-')!");
     }
     // We allow multiple sessions of the same type, so when a caller requests
     // a new session of type "web", we give them the first available session in
     // "web-1", "web-2", ..., "web-N", up to some configurable limit. If none
     // of these sessions is available, we overwrite the oldest session and
     // reissue a new one in its place.
     $session_limit = 1;
     switch ($session_type) {
         case 'web':
             $session_limit = PhabricatorEnv::getEnvConfig('auth.sessions.web');
             break;
         case 'conduit':
             $session_limit = PhabricatorEnv::getEnvConfig('auth.sessions.conduit');
             break;
         default:
             throw new Exception("Unknown session type '{$session_type}'!");
     }
     $session_limit = (int) $session_limit;
     if ($session_limit <= 0) {
         throw new Exception("Session limit for '{$session_type}' must be at least 1!");
     }
     // NOTE: Session establishment is sensitive to race conditions, as when
     // piping `arc` to `arc`:
     //
     //   arc export ... | arc paste ...
     //
     // To avoid this, we overwrite an old session only if it hasn't been
     // re-established since we read it.
     // Consume entropy to generate a new session key, forestalling the eventual
     // heat death of the universe.
     $session_key = Filesystem::readRandomCharacters(40);
     // Load all the currently active sessions.
     $sessions = queryfx_all($conn_w, 'SELECT type, sessionKey, sessionStart FROM %T
     WHERE userPHID = %s AND type LIKE %>', PhabricatorUser::SESSION_TABLE, $this->getPHID(), $session_type . '-');
     $sessions = ipull($sessions, null, 'type');
     $sessions = isort($sessions, 'sessionStart');
     $existing_sessions = array_keys($sessions);
     $retries = 0;
     while (true) {
         // Choose which 'type' we'll actually establish, i.e. what number we're
         // going to append to the basic session type. To do this, just check all
         // the numbers sequentially until we find an available session.
         $establish_type = null;
         for ($ii = 1; $ii <= $session_limit; $ii++) {
             $try_type = $session_type . '-' . $ii;
             if (!in_array($try_type, $existing_sessions)) {
                 $establish_type = $try_type;
                 $expect_key = $session_key;
                 $existing_sessions[] = $try_type;
                 // Ensure the row exists so we can issue an update below. We don't
                 // care if we race here or not.
                 queryfx($conn_w, 'INSERT IGNORE INTO %T (userPHID, type, sessionKey, sessionStart)
           VALUES (%s, %s, %s, 0)', self::SESSION_TABLE, $this->getPHID(), $establish_type, $session_key);
                 break;
             }
         }
         // If we didn't find an available session, choose the oldest session and
         // overwrite it.
         if (!$establish_type) {
             $oldest = reset($sessions);
             $establish_type = $oldest['type'];
             $expect_key = $oldest['sessionKey'];
         }
         // UNGUARDED WRITES: Logging-in users don't have CSRF stuff yet.
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         // This is so that we'll only overwrite the session if it hasn't been
         // refreshed since we read it. If it has, the session key will be
         // different and we know we're racing other processes. Whichever one
         // won gets the session, we go back and try again.
         queryfx($conn_w, 'UPDATE %T SET sessionKey = %s, sessionStart = UNIX_TIMESTAMP()
         WHERE userPHID = %s AND type = %s AND sessionKey = %s', self::SESSION_TABLE, $session_key, $this->getPHID(), $establish_type, $expect_key);
         unset($unguarded);
         if ($conn_w->getAffectedRows()) {
             // The update worked, so the session is valid.
             break;
         } else {
             // We know this just got grabbed, so don't try it again.
             unset($sessions[$establish_type]);
         }
         if (++$retries > $session_limit) {
             throw new Exception("Failed to establish a session!");
         }
     }
     $log = PhabricatorUserLog::newLog($this, $this, PhabricatorUserLog::ACTION_LOGIN);
     $log->setDetails(array('session_type' => $session_type, 'session_issued' => $establish_type));
     $log->setSession($session_key);
     $log->save();
     return $session_key;
 }
 public function processLoginRequest(PhabricatorAuthLoginController $controller)
 {
     $request = $controller->getRequest();
     $viewer = $request->getUser();
     $require_captcha = false;
     $captcha_valid = false;
     if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) {
         $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(PhabricatorUserLog::ACTION_LOGIN_FAILURE, 60 * 15);
         if (count($failed_attempts) > 5) {
             $require_captcha = true;
             $captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request);
         }
     }
     $response = null;
     $account = null;
     $log_user = null;
     if ($request->isFormPost()) {
         if (!$require_captcha || $captcha_valid) {
             $username_or_email = $request->getStr('username');
             if (strlen($username_or_email)) {
                 $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username_or_email);
                 if (!$user) {
                     $user = PhabricatorUser::loadOneWithEmailAddress($username_or_email);
                 }
                 if ($user) {
                     $envelope = new PhutilOpaqueEnvelope($request->getStr('password'));
                     if ($user->comparePassword($envelope)) {
                         $account = $this->loadOrCreateAccount($user->getPHID());
                         $log_user = $user;
                         // If the user's password is stored using a less-than-optimal
                         // hash, upgrade them to the strongest available hash.
                         $hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash());
                         if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
                             $user->setPassword($envelope);
                             $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
                             $user->save();
                             unset($unguarded);
                         }
                     }
                 }
             }
         }
     }
     if (!$account) {
         if ($request->isFormPost()) {
             $log = PhabricatorUserLog::initializeNewLog(null, $log_user ? $log_user->getPHID() : null, PhabricatorUserLog::ACTION_LOGIN_FAILURE);
             $log->save();
         }
         $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME);
         $response = $controller->buildProviderPageResponse($this, $this->renderPasswordLoginForm($request, $require_captcha, $captcha_valid));
     }
     return array($account, $response);
 }
 private function processRoleRequest(PhabricatorUser $user)
 {
     $request = $this->getRequest();
     $admin = $request->getUser();
     $is_self = $user->getID() == $admin->getID();
     $errors = array();
     if ($request->isFormPost()) {
         $log_template = PhabricatorUserLog::newLog($admin, $user, null);
         $logs = array();
         if ($is_self) {
             $errors[] = "You can not edit your own role.";
         } else {
             $new_admin = (bool) $request->getBool('is_admin');
             $old_admin = (bool) $user->getIsAdmin();
             if ($new_admin != $old_admin) {
                 $log = clone $log_template;
                 $log->setAction(PhabricatorUserLog::ACTION_ADMIN);
                 $log->setOldValue($old_admin);
                 $log->setNewValue($new_admin);
                 $user->setIsAdmin($new_admin);
                 $logs[] = $log;
             }
             $new_disabled = (bool) $request->getBool('is_disabled');
             $old_disabled = (bool) $user->getIsDisabled();
             if ($new_disabled != $old_disabled) {
                 $log = clone $log_template;
                 $log->setAction(PhabricatorUserLog::ACTION_DISABLE);
                 $log->setOldValue($old_disabled);
                 $log->setNewValue($new_disabled);
                 $user->setIsDisabled($new_disabled);
                 $logs[] = $log;
             }
         }
         if (!$errors) {
             $user->save();
             foreach ($logs as $log) {
                 $log->save();
             }
             return id(new AphrontRedirectResponse())->setURI($request->getRequestURI()->alter('saved', 'true'));
         }
     }
     $error_view = null;
     if ($errors) {
         $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors);
     }
     $form = id(new AphrontFormView())->setUser($admin)->setAction($request->getRequestURI()->alter('saved', null));
     if ($is_self) {
         $form->appendChild('<p class="aphront-form-instructions">NOTE: You can not edit your own ' . 'role.</p>');
     }
     $form->appendChild($this->getRoleInstructions())->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('is_admin', 1, 'Admin: wields absolute power.', $user->getIsAdmin())->setDisabled($is_self))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('is_disabled', 1, 'Disabled: can not login.', $user->getIsDisabled())->setDisabled($is_self));
     if (!$is_self) {
         $form->appendChild(id(new AphrontFormSubmitControl())->setValue('Edit Role'));
     }
     $panel = new AphrontPanelView();
     $panel->setHeader('Edit Role');
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     $panel->appendChild($form);
     return array($error_view, $panel);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     if ($request->getUser()->getPHID()) {
         // Kick the user out if they're already logged in.
         return id(new AphrontRedirectResponse())->setURI('/');
     }
     if ($request->isConduit()) {
         // A common source of errors in Conduit client configuration is getting
         // the request path wrong. The client will end up here, so make some
         // effort to give them a comprehensible error message.
         $request_path = $this->getRequest()->getPath();
         $conduit_path = '/api/<method>';
         $example_path = '/api/conduit.ping';
         $message = "ERROR: You are making a Conduit API request to '{$request_path}', " . "but the correct HTTP request path to use in order to access a " . "Conduit method is '{$conduit_path}' (for example, " . "'{$example_path}'). Check your configuration.";
         return id(new AphrontPlainTextResponse())->setContent($message);
     }
     $error_view = null;
     if ($request->getCookie('phusr') && $request->getCookie('phsid')) {
         // The session cookie is invalid, so clear it.
         $request->clearCookie('phusr');
         $request->clearCookie('phsid');
         $error_view = new AphrontErrorView();
         $error_view->setTitle('Invalid Session');
         $error_view->setErrors(array("Your login session is invalid. Try logging in again. If that " . "doesn't work, clear your browser cookies."));
     }
     $next_uri = $this->getRequest()->getPath();
     if ($next_uri == '/login/') {
         $next_uri = '/';
     }
     if (!$request->isFormPost()) {
         $request->setCookie('next_uri', $next_uri);
     }
     $password_auth = PhabricatorEnv::getEnvConfig('auth.password-auth-enabled');
     $forms = array();
     $errors = array();
     if ($password_auth) {
         $require_captcha = false;
         $e_captcha = true;
         $username_or_email = $request->getCookie('phusr');
         if ($request->isFormPost()) {
             if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) {
                 $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(PhabricatorUserLog::ACTION_LOGIN_FAILURE, 60 * 15);
                 if (count($failed_attempts) > 5) {
                     $require_captcha = true;
                     if (!AphrontFormRecaptchaControl::processCaptcha($request)) {
                         if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) {
                             $e_captcha = 'Invalid';
                             $errors[] = 'CAPTCHA was not entered correctly.';
                         } else {
                             $e_captcha = 'Required';
                             $errors[] = 'Too many login failures recently. You must ' . 'submit a CAPTCHA with your login request.';
                         }
                     }
                 }
             }
             $username_or_email = $request->getStr('username_or_email');
             $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username_or_email);
             if (!$user) {
                 $user = id(new PhabricatorUser())->loadOneWhere('email = %s', $username_or_email);
             }
             if (!$errors) {
                 // Perform username/password tests only if we didn't get rate limited
                 // by the CAPTCHA.
                 if (!$user || !$user->comparePassword($request->getStr('password'))) {
                     $errors[] = 'Bad username/password.';
                 }
             }
             if (!$errors) {
                 $session_key = $user->establishSession('web');
                 $request->setCookie('phusr', $user->getUsername());
                 $request->setCookie('phsid', $session_key);
                 $uri = new PhutilURI('/login/validate/');
                 $uri->setQueryParams(array('phusr' => $user->getUsername()));
                 return id(new AphrontRedirectResponse())->setURI((string) $uri);
             } else {
                 $log = PhabricatorUserLog::newLog(null, $user, PhabricatorUserLog::ACTION_LOGIN_FAILURE);
                 $log->save();
                 $request->clearCookie('phusr');
                 $request->clearCookie('phsid');
             }
         }
         if ($errors) {
             $error_view = new AphrontErrorView();
             $error_view->setTitle('Login Failed');
             $error_view->setErrors($errors);
         }
         $form = new AphrontFormView();
         $form->setUser($request->getUser())->setAction('/login/')->appendChild(id(new AphrontFormTextControl())->setLabel('Username/Email')->setName('username_or_email')->setValue($username_or_email))->appendChild(id(new AphrontFormPasswordControl())->setLabel('Password')->setName('password')->setCaption('<a href="/login/email/">' . 'Forgot your password? / Email Login</a>'));
         if ($require_captcha) {
             $form->appendChild(id(new AphrontFormRecaptchaControl())->setError($e_captcha));
         }
         $form->appendChild(id(new AphrontFormSubmitControl())->setValue('Login'));
         //    $panel->setCreateButton('Register New Account', '/login/register/');
         $forms['Phabricator Login'] = $form;
     }
     $providers = PhabricatorOAuthProvider::getAllProviders();
     foreach ($providers as $provider) {
         $enabled = $provider->isProviderEnabled();
         if (!$enabled) {
             continue;
         }
         $auth_uri = $provider->getAuthURI();
         $redirect_uri = $provider->getRedirectURI();
         $client_id = $provider->getClientID();
         $provider_name = $provider->getProviderName();
         $minimum_scope = $provider->getMinimumScope();
         $extra_auth = $provider->getExtraAuthParameters();
         // TODO: In theory we should use 'state' to prevent CSRF, but the total
         // effect of the CSRF attack is that an attacker can cause a user to login
         // to Phabricator if they're already logged into some OAuth provider. This
         // does not seem like the most severe threat in the world, and generating
         // CSRF for logged-out users is vaugely tricky.
         if ($provider->isProviderRegistrationEnabled()) {
             $title = "Login or Register with {$provider_name}";
             $body = 'Login or register for Phabricator using your ' . phutil_escape_html($provider_name) . ' account.';
             $button = "Login or Register with {$provider_name}";
         } else {
             $title = "Login with {$provider_name}";
             $body = 'Login to your existing Phabricator account using your ' . phutil_escape_html($provider_name) . ' account.<br /><br />' . '<strong>You can not use ' . phutil_escape_html($provider_name) . ' to register a new ' . 'account.</strong>';
             $button = "Login with {$provider_name}";
         }
         $auth_form = new AphrontFormView();
         $auth_form->setAction($auth_uri)->addHiddenInput('client_id', $client_id)->addHiddenInput('redirect_uri', $redirect_uri)->addHiddenInput('scope', $minimum_scope);
         foreach ($extra_auth as $key => $value) {
             $auth_form->addHiddenInput($key, $value);
         }
         $auth_form->setUser($request->getUser())->setMethod('GET')->appendChild('<p class="aphront-form-instructions">' . $body . '</p>')->appendChild(id(new AphrontFormSubmitControl())->setValue("{$button} »"));
         $forms[$title] = $auth_form;
     }
     $panel = new AphrontPanelView();
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     foreach ($forms as $name => $form) {
         $panel->appendChild('<h1>' . $name . '</h1>');
         $panel->appendChild($form);
         $panel->appendChild('<br />');
     }
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Login'));
 }
Esempio n. 25
0
 /**
  * Verify a user's email address.
  *
  * This verifies an individual email address. If the address is the user's
  * primary address and their account was not previously verified, their
  * account is marked as email verified.
  *
  * @task email
  */
 public function verifyEmail(PhabricatorUser $user, PhabricatorUserEmail $email)
 {
     $actor = $this->requireActor();
     if (!$user->getID()) {
         throw new Exception('User has not been created yet!');
     }
     if (!$email->getID()) {
         throw new Exception('Email has not been created yet!');
     }
     $user->openTransaction();
     $user->beginWriteLocking();
     $user->reload();
     $email->reload();
     if ($email->getUserPHID() != $user->getPHID()) {
         throw new Exception(pht('User does not own email!'));
     }
     if (!$email->getIsVerified()) {
         $email->setIsVerified(1);
         $email->save();
         $log = PhabricatorUserLog::initializeNewLog($actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_VERIFY);
         $log->setNewValue($email->getAddress());
         $log->save();
     }
     if (!$user->getIsEmailVerified()) {
         // If the user just verified their primary email address, mark their
         // account as email verified.
         $user_primary = $user->loadPrimaryEmail();
         if ($user_primary->getID() == $email->getID()) {
             $user->setIsEmailVerified(1);
             $user->save();
         }
     }
     $user->endWriteLocking();
     $user->saveTransaction();
 }
 private function logFailure(ConduitAPIRequest $request, PhabricatorConduitCertificateToken $info = null)
 {
     $log = PhabricatorUserLog::initializeNewLog($request->getUser(), $info ? $info->getUserPHID() : '-', PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE)->save();
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     if ($request->getUser()->getPHID()) {
         // Kick the user out if they're already logged in.
         return id(new AphrontRedirectResponse())->setURI('/');
     }
     $next_uri = $this->getRequest()->getPath();
     $request->setCookie('next_uri', $next_uri);
     if ($next_uri == '/login/' && !$request->isFormPost()) {
         // The user went straight to /login/, so presumably they want to go
         // to the dashboard upon logging in. Because, you know, that's logical.
         // And people are logical. Sometimes... Fine, no they're not.
         // We check for POST here because getPath() would get reset to /login/.
         $request->setCookie('next_uri', '/');
     }
     // Always use $request->getCookie('next_uri', '/') after the above.
     $password_auth = PhabricatorEnv::getEnvConfig('auth.password-auth-enabled');
     $forms = array();
     $error_view = null;
     if ($password_auth) {
         $error = false;
         $username_or_email = $request->getCookie('phusr');
         if ($request->isFormPost()) {
             $username_or_email = $request->getStr('username_or_email');
             $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username_or_email);
             if (!$user) {
                 $user = id(new PhabricatorUser())->loadOneWhere('email = %s', $username_or_email);
             }
             $okay = false;
             if ($user) {
                 if ($user->comparePassword($request->getStr('password'))) {
                     $session_key = $user->establishSession('web');
                     $request->setCookie('phusr', $user->getUsername());
                     $request->setCookie('phsid', $session_key);
                     return id(new AphrontRedirectResponse())->setURI($request->getCookie('next_uri', '/'));
                 } else {
                     $log = PhabricatorUserLog::newLog(null, $user, PhabricatorUserLog::ACTION_LOGIN_FAILURE);
                     $log->save();
                 }
             }
             if (!$okay) {
                 $request->clearCookie('phusr');
                 $request->clearCookie('phsid');
             }
             $error = true;
         }
         if ($error) {
             $error_view = new AphrontErrorView();
             $error_view->setTitle('Bad username/password.');
         }
         $form = new AphrontFormView();
         $form->setUser($request->getUser())->setAction('/login/')->appendChild(id(new AphrontFormTextControl())->setLabel('Username/Email')->setName('username_or_email')->setValue($username_or_email))->appendChild(id(new AphrontFormPasswordControl())->setLabel('Password')->setName('password')->setCaption('<a href="/login/email/">' . 'Forgot your password? / Email Login</a>'))->appendChild(id(new AphrontFormSubmitControl())->setValue('Login'));
         //    $panel->setCreateButton('Register New Account', '/login/register/');
         $forms['Phabricator Login'] = $form;
     }
     $providers = PhabricatorOAuthProvider::getAllProviders();
     foreach ($providers as $provider) {
         $enabled = $provider->isProviderEnabled();
         if (!$enabled) {
             continue;
         }
         $auth_uri = $provider->getAuthURI();
         $redirect_uri = $provider->getRedirectURI();
         $client_id = $provider->getClientID();
         $provider_name = $provider->getProviderName();
         $minimum_scope = $provider->getMinimumScope();
         $extra_auth = $provider->getExtraAuthParameters();
         // TODO: In theory we should use 'state' to prevent CSRF, but the total
         // effect of the CSRF attack is that an attacker can cause a user to login
         // to Phabricator if they're already logged into some OAuth provider. This
         // does not seem like the most severe threat in the world, and generating
         // CSRF for logged-out users is vaugely tricky.
         if ($provider->isProviderRegistrationEnabled()) {
             $title = "Login or Register with {$provider_name}";
             $body = "Login or register for Phabricator using your " . "{$provider_name} account.";
             $button = "Login or Register with {$provider_name}";
         } else {
             $title = "Login with {$provider_name}";
             $body = "Login to your existing Phabricator account using your " . "{$provider_name} account.<br /><br /><strong>You can not use " . "{$provider_name} to register a new account.</strong>";
             $button = "Login with {$provider_name}";
         }
         $auth_form = new AphrontFormView();
         $auth_form->setAction($auth_uri)->addHiddenInput('client_id', $client_id)->addHiddenInput('redirect_uri', $redirect_uri)->addHiddenInput('scope', $minimum_scope);
         foreach ($extra_auth as $key => $value) {
             $auth_form->addHiddenInput($key, $value);
         }
         $auth_form->setUser($request->getUser())->setMethod('GET')->appendChild('<p class="aphront-form-instructions">' . $body . '</p>')->appendChild(id(new AphrontFormSubmitControl())->setValue("{$button} »"));
         $forms[$title] = $auth_form;
     }
     $panel = new AphrontPanelView();
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     foreach ($forms as $name => $form) {
         $panel->appendChild('<h1>' . $name . '</h1>');
         $panel->appendChild($form);
         $panel->appendChild('<br />');
     }
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Login'));
 }