protected function renderLoginForm(AphrontRequest $request, $mode)
 {
     $viewer = $request->getUser();
     $dialog = id(new AphrontDialogView())->setSubmitURI($this->getLoginURI())->setUser($viewer);
     if ($mode == 'link') {
         $dialog->setTitle(pht('Link LDAP Account'));
         $dialog->addSubmitButton(pht('Link Accounts'));
         $dialog->addCancelButton($this->getSettingsURI());
     } else {
         if ($mode == 'refresh') {
             $dialog->setTitle(pht('Refresh LDAP Account'));
             $dialog->addSubmitButton(pht('Refresh Account'));
             $dialog->addCancelButton($this->getSettingsURI());
         } else {
             if ($this->shouldAllowRegistration()) {
                 $dialog->setTitle(pht('Login or Register with LDAP'));
                 $dialog->addSubmitButton(pht('Login or Register'));
             } else {
                 $dialog->setTitle(pht('Login with LDAP'));
                 $dialog->addSubmitButton(pht('Login'));
             }
             if ($mode == 'login') {
                 $dialog->addCancelButton($this->getStartURI());
             }
         }
     }
     $v_user = $request->getStr('ldap_username');
     $e_user = null;
     $e_pass = null;
     $errors = array();
     if ($request->isHTTPPost()) {
         // NOTE: This is intentionally vague so as not to disclose whether a
         // given username exists.
         $e_user = pht('Invalid');
         $e_pass = pht('Invalid');
         $errors[] = pht('Username or password are incorrect.');
     }
     $form = id(new PHUIFormLayoutView())->setUser($viewer)->setFullWidth(true)->appendChild(id(new AphrontFormTextControl())->setLabel(pht('LDAP Username'))->setName('ldap_username')->setValue($v_user)->setError($e_user))->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('LDAP Password'))->setName('ldap_password')->setError($e_pass));
     if ($errors) {
         $errors = id(new PHUIInfoView())->setErrors($errors);
     }
     $dialog->appendChild($errors);
     $dialog->appendChild($form);
     return $dialog;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $this->phid = $request->getURIData('phid');
     $this->key = $request->getURIData('key');
     $this->token = $request->getURIData('token');
     $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
     $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
     $alt_uri = new PhutilURI($alt);
     $alt_domain = $alt_uri->getDomain();
     $req_domain = $request->getHost();
     $main_domain = id(new PhutilURI($base_uri))->getDomain();
     $cache_response = true;
     if (empty($alt) || $main_domain == $alt_domain) {
         // Alternate files domain isn't configured or it's set
         // to the same as the default domain
         $response = $this->loadFile($viewer);
         if ($response) {
             return $response;
         }
         $file = $this->getFile();
         // when the file is not CDNable, don't allow cache
         $cache_response = $file->getCanCDN();
     } else {
         if ($req_domain != $alt_domain) {
             // Alternate domain is configured but this request isn't using it
             $response = $this->loadFile($viewer);
             if ($response) {
                 return $response;
             }
             $file = $this->getFile();
             // if the user can see the file, generate a token;
             // redirect to the alt domain with the token;
             $token_uri = $file->getCDNURIWithToken();
             $token_uri = new PhutilURI($token_uri);
             $token_uri = $this->addURIParameters($token_uri);
             return id(new AphrontRedirectResponse())->setIsExternal(true)->setURI($token_uri);
         } else {
             // We are using the alternate domain. We don't have authentication
             // on this domain, so we bypass policy checks when loading the file.
             $bypass_policies = PhabricatorUser::getOmnipotentUser();
             $response = $this->loadFile($bypass_policies);
             if ($response) {
                 return $response;
             }
             $file = $this->getFile();
             $acquire_token_uri = id(new PhutilURI($file->getViewURI()))->setDomain($main_domain);
             $acquire_token_uri = $this->addURIParameters($acquire_token_uri);
             if ($this->token) {
                 // validate the token, if it is valid, continue
                 $validated_token = $file->validateOneTimeToken($this->token);
                 if (!$validated_token) {
                     $dialog = $this->newDialog()->setShortTitle(pht('Expired File'))->setTitle(pht('File Link Has Expired'))->appendParagraph(pht('The link you followed to view this file is invalid or ' . 'expired.'))->appendParagraph(pht('Continue to generate a new link to the file. You may be ' . 'required to log in.'))->addCancelButton($acquire_token_uri, pht('Continue'));
                     // Build an explicit response so we can respond with HTTP/403 instead
                     // of HTTP/200.
                     $response = id(new AphrontDialogResponse())->setDialog($dialog)->setHTTPResponseCode(403);
                     return $response;
                 }
                 // return the file data without cache headers
                 $cache_response = false;
             } else {
                 if (!$file->getCanCDN()) {
                     // file cannot be served via cdn, and no token given
                     // redirect to the main domain to aquire a token
                     // This is marked as an "external" URI because it is fully qualified.
                     return id(new AphrontRedirectResponse())->setIsExternal(true)->setURI($acquire_token_uri);
                 }
             }
         }
     }
     $response = new AphrontFileResponse();
     if ($cache_response) {
         $response->setCacheDurationInSeconds(60 * 60 * 24 * 30);
     }
     $begin = null;
     $end = null;
     // NOTE: It's important to accept "Range" requests when playing audio.
     // If we don't, Safari has difficulty figuring out how long sounds are
     // and glitches when trying to loop them. In particular, Safari sends
     // an initial request for bytes 0-1 of the audio file, and things go south
     // if we can't respond with a 206 Partial Content.
     $range = $request->getHTTPHeader('range');
     if ($range) {
         $matches = null;
         if (preg_match('/^bytes=(\\d+)-(\\d+)$/', $range, $matches)) {
             // Note that the "Range" header specifies bytes differently than
             // we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
             $begin = (int) $matches[1];
             $end = (int) $matches[2] + 1;
             $response->setHTTPResponseCode(206);
             $response->setRange($begin, $end - 1);
         }
     } else {
         if (isset($validated_token)) {
             // We set this on the response, and the response deletes it after the
             // transfer completes. This allows transfers to be resumed, in theory.
             $response->setTemporaryFileToken($validated_token);
         }
     }
     $is_viewable = $file->isViewableInBrowser();
     $force_download = $request->getExists('download');
     if ($is_viewable && !$force_download) {
         $response->setMimeType($file->getViewableMimeType());
     } else {
         if (!$request->isHTTPPost() && !$alt_domain) {
             // NOTE: Require POST to download files from the primary domain. We'd
             // rather go full-bore and do a real CSRF check, but can't currently
             // authenticate users on the file domain. This should blunt any
             // attacks based on iframes, script tags, applet tags, etc., at least.
             // Send the user to the "info" page if they're using some other method.
             // This is marked as "external" because it is fully qualified.
             return id(new AphrontRedirectResponse())->setIsExternal(true)->setURI(PhabricatorEnv::getProductionURI($file->getBestURI()));
         }
         $response->setMimeType($file->getMimeType());
         $response->setDownload($file->getName());
     }
     $iterator = $file->getFileDataIterator($begin, $end);
     $response->setContentLength($file->getByteSize());
     $response->setContentIterator($iterator);
     return $response;
 }
 /**
  * Require high security, or prompt the user to enter high security.
  *
  * If the user's session is in high security, this method will return a
  * token. Otherwise, it will throw an exception which will eventually
  * be converted into a multi-factor authentication workflow.
  *
  * @param PhabricatorUser User whose session needs to be in high security.
  * @param AphrontReqeust  Current request.
  * @param string          URI to return the user to if they cancel.
  * @param bool            True to jump partial sessions directly into high
  *                        security instead of just upgrading them to full
  *                        sessions.
  * @return PhabricatorAuthHighSecurityToken Security token.
  * @task hisec
  */
 public function requireHighSecuritySession(PhabricatorUser $viewer, AphrontRequest $request, $cancel_uri, $jump_into_hisec = false)
 {
     if (!$viewer->hasSession()) {
         throw new Exception(pht('Requiring a high-security session from a user with no session!'));
     }
     $session = $viewer->getSession();
     // Check if the session is already in high security mode.
     $token = $this->issueHighSecurityToken($session);
     if ($token) {
         return $token;
     }
     // Load the multi-factor auth sources attached to this account.
     $factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere('userPHID = %s', $viewer->getPHID());
     // If the account has no associated multi-factor auth, just issue a token
     // without putting the session into high security mode. This is generally
     // easier for users. A minor but desirable side effect is that when a user
     // adds an auth factor, existing sessions won't get a free pass into hisec,
     // since they never actually got marked as hisec.
     if (!$factors) {
         return $this->issueHighSecurityToken($session, true);
     }
     // Check for a rate limit without awarding points, so the user doesn't
     // get partway through the workflow only to get blocked.
     PhabricatorSystemActionEngine::willTakeAction(array($viewer->getPHID()), new PhabricatorAuthTryFactorAction(), 0);
     $validation_results = array();
     if ($request->isHTTPPost()) {
         $request->validateCSRF();
         if ($request->getExists(AphrontRequest::TYPE_HISEC)) {
             // Limit factor verification rates to prevent brute force attacks.
             PhabricatorSystemActionEngine::willTakeAction(array($viewer->getPHID()), new PhabricatorAuthTryFactorAction(), 1);
             $ok = true;
             foreach ($factors as $factor) {
                 $id = $factor->getID();
                 $impl = $factor->requireImplementation();
                 $validation_results[$id] = $impl->processValidateFactorForm($factor, $viewer, $request);
                 if (!$impl->isFactorValid($factor, $validation_results[$id])) {
                     $ok = false;
                 }
             }
             if ($ok) {
                 // Give the user a credit back for a successful factor verification.
                 PhabricatorSystemActionEngine::willTakeAction(array($viewer->getPHID()), new PhabricatorAuthTryFactorAction(), -1);
                 if ($session->getIsPartial() && !$jump_into_hisec) {
                     // If we have a partial session and are not jumping directly into
                     // hisec, just issue a token without putting it in high security
                     // mode.
                     return $this->issueHighSecurityToken($session, true);
                 }
                 $until = time() + phutil_units('15 minutes in seconds');
                 $session->setHighSecurityUntil($until);
                 queryfx($session->establishConnection('w'), 'UPDATE %T SET highSecurityUntil = %d WHERE id = %d', $session->getTableName(), $until, $session->getID());
                 $log = PhabricatorUserLog::initializeNewLog($viewer, $viewer->getPHID(), PhabricatorUserLog::ACTION_ENTER_HISEC);
                 $log->save();
             } else {
                 $log = PhabricatorUserLog::initializeNewLog($viewer, $viewer->getPHID(), PhabricatorUserLog::ACTION_FAIL_HISEC);
                 $log->save();
             }
         }
     }
     $token = $this->issueHighSecurityToken($session);
     if ($token) {
         return $token;
     }
     throw id(new PhabricatorAuthHighSecurityRequiredException())->setCancelURI($cancel_uri)->setFactors($factors)->setFactorValidationResults($validation_results);
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $this->phid = $request->getURIData('phid');
     $this->key = $request->getURIData('key');
     $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
     $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
     $alt_uri = new PhutilURI($alt);
     $alt_domain = $alt_uri->getDomain();
     $req_domain = $request->getHost();
     $main_domain = id(new PhutilURI($base_uri))->getDomain();
     if (!strlen($alt) || $main_domain == $alt_domain) {
         // No alternate domain.
         $should_redirect = false;
         $is_alternate_domain = false;
     } else {
         if ($req_domain != $alt_domain) {
             // Alternate domain, but this request is on the main domain.
             $should_redirect = true;
             $is_alternate_domain = false;
         } else {
             // Alternate domain, and on the alternate domain.
             $should_redirect = false;
             $is_alternate_domain = true;
         }
     }
     $response = $this->loadFile();
     if ($response) {
         return $response;
     }
     $file = $this->getFile();
     if ($should_redirect) {
         return id(new AphrontRedirectResponse())->setIsExternal(true)->setURI($file->getCDNURI());
     }
     $response = new AphrontFileResponse();
     $response->setCacheDurationInSeconds(60 * 60 * 24 * 30);
     $response->setCanCDN($file->getCanCDN());
     $begin = null;
     $end = null;
     // NOTE: It's important to accept "Range" requests when playing audio.
     // If we don't, Safari has difficulty figuring out how long sounds are
     // and glitches when trying to loop them. In particular, Safari sends
     // an initial request for bytes 0-1 of the audio file, and things go south
     // if we can't respond with a 206 Partial Content.
     $range = $request->getHTTPHeader('range');
     if ($range) {
         $matches = null;
         if (preg_match('/^bytes=(\\d+)-(\\d+)$/', $range, $matches)) {
             // Note that the "Range" header specifies bytes differently than
             // we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
             $begin = (int) $matches[1];
             $end = (int) $matches[2] + 1;
             $response->setHTTPResponseCode(206);
             $response->setRange($begin, $end - 1);
         }
     }
     $is_viewable = $file->isViewableInBrowser();
     $force_download = $request->getExists('download');
     $request_type = $request->getHTTPHeader('X-Phabricator-Request-Type');
     $is_lfs = $request_type == 'git-lfs';
     if ($is_viewable && !$force_download) {
         $response->setMimeType($file->getViewableMimeType());
     } else {
         if (!$request->isHTTPPost() && !$is_alternate_domain && !$is_lfs) {
             // NOTE: Require POST to download files from the primary domain. We'd
             // rather go full-bore and do a real CSRF check, but can't currently
             // authenticate users on the file domain. This should blunt any
             // attacks based on iframes, script tags, applet tags, etc., at least.
             // Send the user to the "info" page if they're using some other method.
             // This is marked as "external" because it is fully qualified.
             return id(new AphrontRedirectResponse())->setIsExternal(true)->setURI(PhabricatorEnv::getProductionURI($file->getBestURI()));
         }
         $response->setMimeType($file->getMimeType());
         $response->setDownload($file->getName());
     }
     $iterator = $file->getFileDataIterator($begin, $end);
     $response->setContentLength($file->getByteSize());
     $response->setContentIterator($iterator);
     return $response;
 }