.+\.(?:css|js))$'
=> 'CelerityResourceController',
),
);
}
public function buildRequest() {
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($_GET + $_POST);
$request->setApplicationConfiguration($this);
return $request;
}
public function handleException(Exception $ex) {
// Always log the unhandled exception.
phlog($ex);
$class = phutil_escape_html(get_class($ex));
$message = phutil_escape_html($ex->getMessage());
if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) {
$trace = $this->renderStackTrace($ex->getTrace());
} else {
$trace = null;
}
$content =
''.
'
'.$message.'
'.
$trace.
'
';
$user = $this->getRequest()->getUser();
if (!$user) {
// If we hit an exception very early, we won't have a user.
$user = new PhabricatorUser();
}
$dialog = new AphrontDialogView();
$dialog
->setTitle('Unhandled Exception ("'.$class.'")')
->setClass('aphront-exception-dialog')
->setUser($user)
->appendChild($content);
if ($this->getRequest()->isAjax()) {
$dialog->addCancelButton('/', 'Close');
}
$response = new AphrontDialogResponse();
$response->setDialog($dialog);
return $response;
}
public function willSendResponse(AphrontResponse $response) {
$request = $this->getRequest();
$response->setRequest($request);
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax()) {
$view = new PhabricatorStandardPageView();
$view->setRequest($request);
$view->appendChild(
''.
$response->buildResponseString().
'
');
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
} else {
return id(new AphrontAjaxResponse())
->setContent(array(
'dialog' => $response->buildResponseString(),
));
}
} else if ($response instanceof AphrontRedirectResponse) {
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
->setContent(
array(
'redirect' => $response->getURI(),
));
}
} else if ($response instanceof Aphront404Response) {
$failure = new AphrontRequestFailureView();
$failure->setHeader('404 Not Found');
$failure->appendChild(
'The page you requested was not found.
');
$view = new PhabricatorStandardPageView();
$view->setTitle('404 Not Found');
$view->setRequest($this->getRequest());
$view->appendChild($failure);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
$response->setHTTPResponseCode(404);
return $response;
}
return $response;
}
public function build404Controller() {
return array(new Phabricator404Controller($this->getRequest()), array());
}
public function buildRedirectController($uri) {
return array(
new PhabricatorRedirectController($this->getRequest()),
array(
'uri' => $uri,
));
}
private function renderStackTrace($trace) {
$libraries = PhutilBootloader::getInstance()->getAllLibraries();
// TODO: Make this configurable?
$host = 'https://secure.phabricator.com';
$browse = array(
'arcanist' =>
$host.'/diffusion/ARC/browse/origin:master/src/',
'phutil' =>
$host.'/diffusion/PHU/browse/origin:master/src/',
'phabricator' =>
$host.'/diffusion/P/browse/origin:master/src/',
);
$rows = array();
$depth = count($trace);
foreach ($trace as $part) {
$lib = null;
$file = idx($part, 'file');
$relative = $file;
foreach ($libraries as $library) {
$root = phutil_get_library_root($library);
if (Filesystem::isDescendant($file, $root)) {
$lib = $library;
$relative = Filesystem::readablePath($file, $root);
break;
}
}
$where = '';
if (isset($part['class'])) {
$where .= $part['class'].'::';
}
if (isset($part['function'])) {
$where .= $part['function'].'()';
}
if ($file) {
if (isset($browse[$lib])) {
$file_name = phutil_render_tag(
'a',
array(
'href' => $browse[$lib].$relative.'$'.$part['line'],
'title' => $file,
'target' => '_blank',
),
phutil_escape_html($relative));
} else {
$file_name = phutil_render_tag(
'span',
array(
'title' => $file,
),
phutil_escape_html($relative));
}
$file_name = $file_name.' : '.(int)$part['line'];
} else {
$file_name = '(Internal)';
}
$rows[] = array(
$depth--,
phutil_escape_html($lib),
$file_name,
phutil_escape_html($where),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Depth',
'Library',
'File',
'Where',
));
$table->setColumnClasses(
array(
'n',
'',
'',
'wide',
));
return
''.
''.
$table->render().
'
';
}
}
diff --git a/src/applications/auth/controller/emailtoken/PhabricatorEmailTokenController.php b/src/applications/auth/controller/emailtoken/PhabricatorEmailTokenController.php
index d93e0ae97e..2a875164cf 100644
--- a/src/applications/auth/controller/emailtoken/PhabricatorEmailTokenController.php
+++ b/src/applications/auth/controller/emailtoken/PhabricatorEmailTokenController.php
@@ -1,94 +1,100 @@
token = $data['token'];
}
public function processRequest() {
$request = $this->getRequest();
if (!PhabricatorEnv::getEnvConfig('auth.password-auth-enabled')) {
return new Aphront400Response();
}
if ($request->getUser()->getPHID()) {
$view = new AphrontRequestFailureView();
$view->setHeader('Already Logged In');
$view->appendChild(
'You are already logged in.
');
$view->appendChild(
'');
return $this->buildStandardPageResponse(
$view,
array(
'title' => 'Already Logged In',
));
}
$token = $this->token;
$email = $request->getStr('email');
$target_user = id(new PhabricatorUser())->loadOneWhere(
'email = %s',
$email);
if (!$target_user || !$target_user->validateEmailToken($token)) {
$view = new AphrontRequestFailureView();
$view->setHeader('Unable to Login');
$view->appendChild(
'The authentication information in the link you clicked is '.
'invalid or out of date. Make sure you are copy-and-pasting the '.
'entire link into your browser. You can try again, or request '.
'a new email.
');
$view->appendChild(
'');
return $this->buildStandardPageResponse(
$view,
array(
'title' => 'Email Sent',
));
}
$session_key = $target_user->establishSession('web');
$request->setCookie('phusr', $target_user->getUsername());
$request->setCookie('phsid', $session_key);
+ if (PhabricatorEnv::getEnvConfig('account.editable')) {
+ $next = '/settings/page/password/?token='.$token;
+ } else {
+ $next = '/';
+ }
+
$uri = new PhutilURI('/login/validate/');
$uri->setQueryParams(
array(
'phusr' => $target_user->getUsername(),
- 'next' => '/login/reset/',
+ 'next' => $next,
));
return id(new AphrontRedirectResponse())
->setURI((string)$uri);
}
}
diff --git a/src/applications/auth/controller/resetpassword/PhabricatorResetPasswordController.php b/src/applications/auth/controller/resetpassword/PhabricatorResetPasswordController.php
deleted file mode 100644
index a15adae465..0000000000
--- a/src/applications/auth/controller/resetpassword/PhabricatorResetPasswordController.php
+++ /dev/null
@@ -1,108 +0,0 @@
-getRequest();
- $user = $request->getUser();
-
- if (!PhabricatorEnv::getEnvConfig('auth.password-auth-enabled')) {
- return new Aphront400Response();
- }
-
- $errors = array();
-
- $e_pass = true;
- $e_confirm = true;
-
- if ($request->isFormPost()) {
- $e_pass = 'Error';
- $e_confirm = 'Error';
-
- $pass = $request->getStr('password');
- $confirm = $request->getStr('confirm');
-
- if (strlen($pass) < 3) {
- $errors[] = 'That password is ridiculously short.';
- }
-
- if ($pass !== $confirm) {
- $errors[] = "Passwords do not match.";
- }
-
- if (!$errors) {
-
- // The CSRF token depends on the user's password hash. When we change
- // it, we cause the CSRF check to fail. We don't need to do a CSRF
- // check here because we've already performed one in the isFormPost()
- // call earlier.
-
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- $user->setPassword($pass);
- $user->save();
- unset($unguarded);
-
- return id(new AphrontRedirectResponse())
- ->setURI('/');
- }
- }
-
- if ($errors) {
- $error_view = new AphrontErrorView();
- $error_view->setTitle('Password Reset Failed');
- $error_view->setErrors($errors);
- } else {
- $error_view = null;
- }
-
- $form = new AphrontFormView();
- $form
- ->setUser($user)
- ->setAction('/login/reset/')
- ->appendChild(
- id(new AphrontFormPasswordControl())
- ->setLabel('New Password')
- ->setName('password')
- ->setError($e_pass))
- ->appendChild(
- id(new AphrontFormPasswordControl())
- ->setLabel('Confirm Password')
- ->setName('confirm')
- ->setError($e_confirm))
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->setValue('Reset Password')
- ->addCancelButton('/', 'Skip'));
-
- $panel = new AphrontPanelView();
- $panel->setWidth(AphrontPanelView::WIDTH_FORM);
- $panel->setHeader('Reset Password');
- $panel->appendChild($form);
-
- return $this->buildStandardPageResponse(
- array(
- $error_view,
- $panel,
- ),
- array(
- 'title' => 'Reset Password',
- ));
- }
-
-}
diff --git a/src/applications/auth/controller/resetpassword/__init__.php b/src/applications/auth/controller/resetpassword/__init__.php
deleted file mode 100644
index 54d5e45e42..0000000000
--- a/src/applications/auth/controller/resetpassword/__init__.php
+++ /dev/null
@@ -1,23 +0,0 @@
-getRequest();
$user = $request->getUser();
$editable = $this->getAccountEditable();
// There's no sense in showing a change password panel if the user
// can't change their password
if (!$editable ||
!PhabricatorEnv::getEnvConfig('auth.password-auth-enabled')) {
return new Aphront400Response();
}
+ $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
+ $min_len = (int)$min_len;
+
+ // NOTE: To change your password, you need to prove you own the account,
+ // either by providing the old password or by carrying a token to
+ // the workflow from a password reset email.
+
+ $token = $request->getStr('token');
+ if ($token) {
+ $valid_token = $user->validateEmailToken($token);
+ } else {
+ $valid_token = false;
+ }
+
+ $e_old = true;
+ $e_new = true;
+ $e_conf = true;
+
$errors = array();
if ($request->isFormPost()) {
- if ($user->comparePassword($request->getStr('old_pw'))) {
- $pass = $request->getStr('new_pw');
- $conf = $request->getStr('conf_pw');
- if ($pass === $conf) {
- if (strlen($pass)) {
- $user->setPassword($pass);
- // This write is unguarded because the CSRF token has already
- // been checked in the call to $request->isFormPost() and
- // the CSRF token depends on the password hash, so when it
- // is changed here the CSRF token check will fail.
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- $user->save();
- unset($unguarded);
- return id(new AphrontRedirectResponse())
- ->setURI('/settings/page/password/?saved=true');
- } else {
- $errors[] = 'Your new password is too short.';
- }
+ if (!$valid_token) {
+ if (!$user->comparePassword($request->getStr('old_pw'))) {
+ $errors[] = 'The old password you entered is incorrect.';
+ $e_old = 'Invalid';
+ }
+ }
+
+ $pass = $request->getStr('new_pw');
+ $conf = $request->getStr('conf_pw');
+
+ if (strlen($pass) < $min_len) {
+ $errors[] = 'Your new password is too short.';
+ $e_new = 'Too Short';
+ }
+
+ if ($pass !== $conf) {
+ $errors[] = 'New password and confirmation do not match.';
+ $e_conf = 'Invalid';
+ }
+
+ if (!$errors) {
+ $user->setPassword($pass);
+ // This write is unguarded because the CSRF token has already
+ // been checked in the call to $request->isFormPost() and
+ // the CSRF token depends on the password hash, so when it
+ // is changed here the CSRF token check will fail.
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $user->save();
+ unset($unguarded);
+
+ if ($valid_token) {
+ // If this is a password set/reset, kick the user to the home page
+ // after we update their account.
+ $next = '/';
} else {
- $errors[] = 'New password and confirmation do not match.';
+ $next = '/settings/page/password/?saved=true';
}
- } else {
- $errors[] = 'The old password you entered is incorrect.';
+
+ return id(new AphrontRedirectResponse())->setURI($next);
}
}
$notice = null;
if (!$errors) {
if ($request->getStr('saved')) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('Changes Saved');
$notice->appendChild('Your password has been updated.
');
}
} else {
$notice = new AphrontErrorView();
$notice->setTitle('Error Changing Password');
$notice->setErrors($errors);
}
+ $len_caption = null;
+ if ($min_len) {
+ $len_caption = 'Minimum password length: '.$min_len.' characters.';
+ }
+
$form = new AphrontFormView();
$form
->setUser($user)
- ->appendChild(
+ ->addHiddenInput('token', $token);
+
+ if (!$valid_token) {
+ $form->appendChild(
id(new AphrontFormPasswordControl())
->setLabel('Old Password')
+ ->setError($e_old)
->setName('old_pw'));
+ }
+
$form
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel('New Password')
+ ->setError($e_new)
->setName('new_pw'));
$form
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel('Confirm Password')
+ ->setCaption($len_caption)
+ ->setError($e_conf)
->setName('conf_pw'));
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save'));
$panel = new AphrontPanelView();
$panel->setHeader('Change Password');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return id(new AphrontNullView())
->appendChild(
array(
$notice,
$panel,
));
}
}