mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-19 13:22:42 +01:00
Allow users to terminate login sessions
Summary: This is partly a good feature, and partly should reduce false positives on HackerOne reporting things vaguely related to this. Allow a user to terminate login sessions from the settings panel. Test Plan: - Terminated a session. - Terminated all sessions. - Tried to terminate all sessions again. - Logged in with two browsers, terminated the other browser's session, reloaded, got kicked out. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D8556
This commit is contained in:
parent
38cc38eaf6
commit
aea624118b
5 changed files with 131 additions and 3 deletions
|
@ -1226,6 +1226,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
|
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
|
||||||
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
|
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
|
||||||
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
|
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
|
||||||
|
'PhabricatorAuthTerminateSessionController' => 'applications/auth/controller/PhabricatorAuthTerminateSessionController.php',
|
||||||
'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php',
|
'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php',
|
||||||
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
|
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
|
||||||
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
|
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
|
||||||
|
@ -3910,6 +3911,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
|
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||||
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
|
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
|
||||||
|
'PhabricatorAuthTerminateSessionController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController',
|
'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
|
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
|
|
@ -86,6 +86,8 @@ final class PhabricatorApplicationAuth extends PhabricatorApplication {
|
||||||
=> 'PhabricatorAuthLinkController',
|
=> 'PhabricatorAuthLinkController',
|
||||||
'confirmlink/(?P<akey>[^/]+)/'
|
'confirmlink/(?P<akey>[^/]+)/'
|
||||||
=> 'PhabricatorAuthConfirmLinkController',
|
=> 'PhabricatorAuthConfirmLinkController',
|
||||||
|
'session/terminate/(?P<id>[^/]+)/'
|
||||||
|
=> 'PhabricatorAuthTerminateSessionController',
|
||||||
),
|
),
|
||||||
|
|
||||||
'/oauth/(?P<provider>\w+)/login/'
|
'/oauth/(?P<provider>\w+)/login/'
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorAuthTerminateSessionController
|
||||||
|
extends PhabricatorAuthController {
|
||||||
|
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->id = $data['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$is_all = ($this->id === 'all');
|
||||||
|
|
||||||
|
$query = id(new PhabricatorAuthSessionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIdentityPHIDs(array($viewer->getPHID()));
|
||||||
|
if (!$is_all) {
|
||||||
|
$query->withIDs(array($this->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_key = PhabricatorHash::digest(
|
||||||
|
$request->getCookie(PhabricatorCookies::COOKIE_SESSION));
|
||||||
|
|
||||||
|
$sessions = $query->execute();
|
||||||
|
foreach ($sessions as $key => $session) {
|
||||||
|
if ($session->getSessionKey() == $current_key) {
|
||||||
|
// Don't terminate the current login session.
|
||||||
|
unset($sessions[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$panel_uri = '/settings/panel/sessions/';
|
||||||
|
|
||||||
|
if (!$sessions) {
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setTitle(pht('No Matching Sessions'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht('There are no matching sessions to terminate.'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'(You can not terminate your current login session. To '.
|
||||||
|
'terminate it, log out.)'))
|
||||||
|
->addCancelButton($panel_uri);
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isDialogFormPost()) {
|
||||||
|
foreach ($sessions as $session) {
|
||||||
|
$session->delete();
|
||||||
|
}
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($panel_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_all) {
|
||||||
|
$title = pht('Terminate Sessions?');
|
||||||
|
$body = pht(
|
||||||
|
'Really terminate all sessions? (Your current login session will '.
|
||||||
|
'not be terminated.)');
|
||||||
|
} else {
|
||||||
|
$title = pht('Terminate Session?');
|
||||||
|
$body = pht(
|
||||||
|
'Really terminate session %s?',
|
||||||
|
phutil_tag('strong', array(), substr($session->getSessionKey(), 0, 6)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setTitle($title)
|
||||||
|
->appendParagraph($body)
|
||||||
|
->addSubmitButton(pht('Terminate'))
|
||||||
|
->addCancelButton($panel_uri);
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
final class PhabricatorAuthSessionQuery
|
final class PhabricatorAuthSessionQuery
|
||||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
|
private $ids;
|
||||||
private $identityPHIDs;
|
private $identityPHIDs;
|
||||||
private $sessionKeys;
|
private $sessionKeys;
|
||||||
private $sessionTypes;
|
private $sessionTypes;
|
||||||
|
@ -22,6 +23,11 @@ final class PhabricatorAuthSessionQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withIDs(array $ids) {
|
||||||
|
$this->ids = $ids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function loadPage() {
|
protected function loadPage() {
|
||||||
$table = new PhabricatorAuthSession();
|
$table = new PhabricatorAuthSession();
|
||||||
$conn_r = $table->establishConnection('r');
|
$conn_r = $table->establishConnection('r');
|
||||||
|
@ -62,6 +68,13 @@ final class PhabricatorAuthSessionQuery
|
||||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
|
if ($this->ids) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'id IN (%Ld)',
|
||||||
|
$this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->identityPHIDs) {
|
if ($this->identityPHIDs) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
|
|
|
@ -48,16 +48,31 @@ final class PhabricatorSettingsPanelSessions
|
||||||
foreach ($sessions as $session) {
|
foreach ($sessions as $session) {
|
||||||
if ($session->getSessionKey() == $current_key) {
|
if ($session->getSessionKey() == $current_key) {
|
||||||
$rowc[] = 'highlighted';
|
$rowc[] = 'highlighted';
|
||||||
|
$button = phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'class' => 'small grey button disabled',
|
||||||
|
),
|
||||||
|
pht('Current'));
|
||||||
} else {
|
} else {
|
||||||
$rowc[] = null;
|
$rowc[] = null;
|
||||||
|
$button = javelin_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '/auth/session/terminate/'.$session->getID().'/',
|
||||||
|
'class' => 'small grey button',
|
||||||
|
'sigil' => 'workflow',
|
||||||
|
),
|
||||||
|
pht('Terminate'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
$handles[$session->getUserPHID()]->renderLink(),
|
$handles[$session->getUserPHID()]->renderLink(),
|
||||||
substr($session->getSessionKey(), 0, 12),
|
substr($session->getSessionKey(), 0, 6),
|
||||||
$session->getType(),
|
$session->getType(),
|
||||||
phabricator_datetime($session->getSessionStart(), $viewer),
|
phabricator_datetime($session->getSessionStart(), $viewer),
|
||||||
phabricator_datetime($session->getSessionExpires(), $viewer),
|
phabricator_date($session->getSessionExpires(), $viewer),
|
||||||
|
$button,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +86,7 @@ final class PhabricatorSettingsPanelSessions
|
||||||
pht('Type'),
|
pht('Type'),
|
||||||
pht('Created'),
|
pht('Created'),
|
||||||
pht('Expires'),
|
pht('Expires'),
|
||||||
|
pht(''),
|
||||||
));
|
));
|
||||||
$table->setColumnClasses(
|
$table->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
|
@ -79,11 +95,23 @@ final class PhabricatorSettingsPanelSessions
|
||||||
'',
|
'',
|
||||||
'right',
|
'right',
|
||||||
'right',
|
'right',
|
||||||
|
'action',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
$terminate_icon = id(new PHUIIconView())
|
||||||
|
->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
|
||||||
|
->setSpriteIcon('warning');
|
||||||
|
$terminate_button = id(new PHUIButtonView())
|
||||||
|
->setText(pht('Terminate All Sessions'))
|
||||||
|
->setHref('/auth/session/terminate/all/')
|
||||||
|
->setTag('a')
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setIcon($terminate_icon);
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader(pht('Active Login Sessions'));
|
->setHeader(pht('Active Login Sessions'))
|
||||||
|
->addActionLink($terminate_button);
|
||||||
|
|
||||||
$panel = id(new PHUIObjectBoxView())
|
$panel = id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
|
|
Loading…
Reference in a new issue