Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php b/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php
index 0cac95f53d..f51f379d2b 100644
--- a/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php
+++ b/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php
@@ -1,209 +1,233 @@
<?php
final class PhabricatorAuthOneTimeLoginController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$link_type = $request->getURIData('type');
$key = $request->getURIData('key');
$email_id = $request->getURIData('emailID');
if ($request->getUser()->isLoggedIn()) {
return $this->renderError(
pht('You are already logged in.'));
}
$target_user = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIDs(array($id))
->executeOne();
if (!$target_user) {
return new Aphront404Response();
}
// NOTE: As a convenience to users, these one-time login URIs may also
// be associated with an email address which will be verified when the
// URI is used.
// This improves the new user experience for users receiving "Welcome"
// emails on installs that require verification: if we did not verify the
// email, they'd immediately get roadblocked with a "Verify Your Email"
// error and have to go back to their email account, wait for a
// "Verification" email, and then click that link to actually get access to
// their account. This is hugely unwieldy, and if the link was only sent
// to the user's email in the first place we can safely verify it as a
// side effect of login.
// The email hashed into the URI so users can't verify some email they
// do not own by doing this:
//
// - Add some address you do not own;
// - request a password reset;
// - change the URI in the email to the address you don't own;
// - login via the email link; and
// - get a "verified" address you don't control.
$target_email = null;
if ($email_id) {
$target_email = id(new PhabricatorUserEmail())->loadOneWhere(
'userPHID = %s AND id = %d',
$target_user->getPHID(),
$email_id);
if (!$target_email) {
return new Aphront404Response();
}
}
$engine = new PhabricatorAuthSessionEngine();
$token = $engine->loadOneTimeLoginKey(
$target_user,
$target_email,
$key);
if (!$token) {
return $this->newDialog()
->setTitle(pht('Unable to Log In'))
->setShortTitle(pht('Login Failure'))
->appendParagraph(
pht(
'The login link you clicked is invalid, out of date, or has '.
'already been used.'))
->appendParagraph(
pht(
'Make sure you are copy-and-pasting the entire link into '.
'your browser. Login links are only valid for 24 hours, and '.
'can only be used once.'))
->appendParagraph(
pht('You can try again, or request a new link via email.'))
->addCancelButton('/login/email/', pht('Send Another Email'));
}
if (!$target_user->canEstablishWebSessions()) {
return $this->newDialog()
->setTitle(pht('Unable to Establish Web Session'))
->setShortTitle(pht('Login Failure'))
->appendParagraph(
pht(
'You are trying to gain access to an account ("%s") that can not '.
'establish a web session.',
$target_user->getUsername()))
->appendParagraph(
pht(
'Special users like daemons and mailing lists are not permitted '.
'to log in via the web. Log in as a normal user instead.'))
->addCancelButton('/');
}
if ($request->isFormPost()) {
// If we have an email bound into this URI, verify email so that clicking
// the link in the "Welcome" email is good enough, without requiring users
// to go through a second round of email verification.
$editor = id(new PhabricatorUserEditor())
->setActor($target_user);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
// Nuke the token and all other outstanding password reset tokens.
// There is no particular security benefit to destroying them all, but
// it should reduce HackerOne reports of nebulous harm.
$editor->revokePasswordResetLinks($target_user);
if ($target_email) {
$editor->verifyEmail($target_user, $target_email);
}
unset($unguarded);
- $next = '/';
- if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
- $next = '/settings/panel/external/';
- } else {
-
- // We're going to let the user reset their password without knowing
- // the old one. Generate a one-time token for that.
- $key = Filesystem::readRandomCharacters(16);
- $password_type =
- PhabricatorAuthPasswordResetTemporaryTokenType::TOKENTYPE;
-
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- id(new PhabricatorAuthTemporaryToken())
- ->setTokenResource($target_user->getPHID())
- ->setTokenType($password_type)
- ->setTokenExpires(time() + phutil_units('1 hour in seconds'))
- ->setTokenCode(PhabricatorHash::weakDigest($key))
- ->save();
- unset($unguarded);
-
- $panel_uri = '/auth/password/';
-
- $next = (string)id(new PhutilURI($panel_uri))
- ->setQueryParams(
- array(
- 'key' => $key,
- ));
-
- $request->setTemporaryCookie(PhabricatorCookies::COOKIE_HISEC, 'yes');
- }
+ $next_uri = $this->getNextStepURI($target_user);
- PhabricatorCookies::setNextURICookie($request, $next, $force = true);
+ PhabricatorCookies::setNextURICookie($request, $next_uri, $force = true);
$force_full_session = false;
if ($link_type === PhabricatorAuthSessionEngine::ONETIME_RECOVER) {
$force_full_session = $token->getShouldForceFullSession();
}
return $this->loginUser($target_user, $force_full_session);
}
// NOTE: We need to CSRF here so attackers can't generate an email link,
// then log a user in to an account they control via sneaky invisible
// form submissions.
switch ($link_type) {
case PhabricatorAuthSessionEngine::ONETIME_WELCOME:
$title = pht('Welcome to Phabricator');
break;
case PhabricatorAuthSessionEngine::ONETIME_RECOVER:
$title = pht('Account Recovery');
break;
case PhabricatorAuthSessionEngine::ONETIME_USERNAME:
case PhabricatorAuthSessionEngine::ONETIME_RESET:
default:
$title = pht('Log in to Phabricator');
break;
}
$body = array();
$body[] = pht(
'Use the button below to log in as: %s',
phutil_tag('strong', array(), $target_user->getUsername()));
if ($target_email && !$target_email->getIsVerified()) {
$body[] = pht(
'Logging in will verify %s as an email address you own.',
phutil_tag('strong', array(), $target_email->getAddress()));
}
$body[] = pht(
'After logging in you should set a password for your account, or '.
'link your account to an external account that you can use to '.
'authenticate in the future.');
$dialog = $this->newDialog()
->setTitle($title)
->addSubmitButton(pht('Log In (%s)', $target_user->getUsername()))
->addCancelButton('/');
foreach ($body as $paragraph) {
$dialog->appendParagraph($paragraph);
}
return id(new AphrontDialogResponse())->setDialog($dialog);
}
+
+ private function getNextStepURI(PhabricatorUser $user) {
+ $request = $this->getRequest();
+
+ // If we have password auth, let the user set or reset their password after
+ // login.
+ $have_passwords = PhabricatorPasswordAuthProvider::getPasswordProvider();
+ if ($have_passwords) {
+ // We're going to let the user reset their password without knowing
+ // the old one. Generate a one-time token for that.
+ $key = Filesystem::readRandomCharacters(16);
+ $password_type =
+ PhabricatorAuthPasswordResetTemporaryTokenType::TOKENTYPE;
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ id(new PhabricatorAuthTemporaryToken())
+ ->setTokenResource($user->getPHID())
+ ->setTokenType($password_type)
+ ->setTokenExpires(time() + phutil_units('1 hour in seconds'))
+ ->setTokenCode(PhabricatorHash::weakDigest($key))
+ ->save();
+ unset($unguarded);
+
+ $panel_uri = '/auth/password/';
+
+ $request->setTemporaryCookie(PhabricatorCookies::COOKIE_HISEC, 'yes');
+
+ return (string)id(new PhutilURI($panel_uri))
+ ->setQueryParams(
+ array(
+ 'key' => $key,
+ ));
+ }
+
+ $providers = id(new PhabricatorAuthProviderConfigQuery())
+ ->setViewer($user)
+ ->withIsEnabled(true)
+ ->execute();
+
+ // If there are no configured providers and the user is an administrator,
+ // send them to Auth to configure a provider. This is probably where they
+ // want to go. You can end up in this state by accidentally losing your
+ // first session during initial setup, or after restoring exported data
+ // from a hosted instance.
+ if (!$providers && $user->getIsAdmin()) {
+ return '/auth/';
+ }
+
+ // If we didn't find anywhere better to send them, give up and just send
+ // them to the home page.
+ return '/';
+ }
+
}
diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php
index 626c80348f..1d21342e4e 100644
--- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php
+++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php
@@ -1,103 +1,77 @@
<?php
final class PhabricatorAuthProviderConfigQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $providerClasses;
-
- const STATUS_ALL = 'status:all';
- const STATUS_ENABLED = 'status:enabled';
-
- private $status = self::STATUS_ALL;
+ private $isEnabled;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
- public function withStatus($status) {
- $this->status = $status;
+ public function withProviderClasses(array $classes) {
+ $this->providerClasses = $classes;
return $this;
}
- public function withProviderClasses(array $classes) {
- $this->providerClasses = $classes;
+ public function withIsEnabled($is_enabled) {
+ $this->isEnabled = $is_enabled;
return $this;
}
- public static function getStatusOptions() {
- return array(
- self::STATUS_ALL => pht('All Providers'),
- self::STATUS_ENABLED => pht('Enabled Providers'),
- );
+ public function newResultObject() {
+ return new PhabricatorAuthProviderConfig();
}
protected function loadPage() {
- $table = new PhabricatorAuthProviderConfig();
- $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);
+ return $this->loadStandardPage($this->newResultObject());
}
- protected function buildWhereClause(AphrontDatabaseConnection $conn) {
- $where = array();
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->providerClasses !== null) {
$where[] = qsprintf(
$conn,
'providerClass IN (%Ls)',
$this->providerClasses);
}
- $status = $this->status;
- switch ($status) {
- case self::STATUS_ALL:
- break;
- case self::STATUS_ENABLED:
- $where[] = qsprintf(
- $conn,
- 'isEnabled = 1');
- break;
- default:
- throw new Exception(pht("Unknown status '%s'!", $status));
+ if ($this->isEnabled !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'isEnabled = %d',
+ (int)$this->isEnabled);
}
- $where[] = $this->buildPagingClause($conn);
-
- return $this->formatWhereClause($conn, $where);
+ return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorAuthApplication';
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 6, 1:58 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34030
Default Alt Text
(13 KB)

Event Timeline