1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-19 16:58:48 +02:00

Enable multiple web sessions

Summary:
Conduit already has multiple-session code, just move it to the main
establishSession() method and set a web session limit larger than 1.

NOTE: This will log everyone out since we no longer look for the "web" session,
only for "web-1", "web-2", ..., etc. Presumably this doesn't matter.

Test Plan:
Applied patch, was logged out. Logged in in Safari. Verified I was issued
"web-1". Logged in in Firefox. Verified I was issued "web-2".

Kept logging in and out until I got issued "web-5", then did it again and was
issued "web-1" with a new key.

Ran conduit methods and verified they work and correctly cycled session keys.

Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
Commenters: jungejason
CC: rm, fzamore, ola, aran, epriestley, jungejason, tuomaspelkonen
Differential Revision: 264
This commit is contained in:
epriestley 2011-05-11 04:52:32 -07:00
parent 477954a57e
commit 3c30ea41f1
6 changed files with 92 additions and 32 deletions

View file

@ -2,6 +2,11 @@ This is not a complete list of changes, just of API or workflow changes that may
break existing installs. Newer changes are listed at the top. If you pull new
changes and things stop working, check here first!
May 11 2011 - New session code
There's some new code which allows you to establish multiple web sessions.
When you update to it, all users will be logged out. This is expected, just
log in again and everything should work properly.
May 10 2011 - PhabricatorMailImplementationAdapter
The signatures of setFrom() and addReplyTo() have changed, and they now
accept a second "$name = ''" parameter. This represents a human-readable

View file

@ -183,6 +183,15 @@ return array(
// OAuth providers instead.
'auth.password-auth-enabled' => true,
// Maximum number of simultaneous web sessions each user is permitted to have.
// Setting this to "1" will prevent a user from logging in on more than one
// browser at the same time.
'auth.sessions.web' => 5,
// Maximum number of simultaneous Conduit sessions each user is permitted
// to have.
'auth.sessions.conduit' => 3,
// -- Accounts -------------------------------------------------------------- //

View file

@ -43,10 +43,10 @@ abstract class PhabricatorController extends AphrontController {
$info = queryfx_one(
$user->establishConnection('r'),
'SELECT u.* FROM %T u JOIN %T s ON u.phid = s.userPHID
AND s.type = %s AND s.sessionKey = %s',
AND s.type LIKE %> AND s.sessionKey = %s',
$user->getTableName(),
'phabricator_session',
'web',
'web-',
$phsid);
if ($info) {
$user->loadFromArray($info);

View file

@ -125,31 +125,7 @@ class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
if ($valid != $signature) {
throw new ConduitException('ERR-INVALID-CERTIFICATE');
}
$sessions = queryfx_all(
$user->establishConnection('r'),
'SELECT * FROM %T WHERE userPHID = %s AND type LIKE %>',
PhabricatorUser::SESSION_TABLE,
$user->getPHID(),
'conduit-');
$session_type = null;
$sessions = ipull($sessions, null, 'type');
for ($ii = 1; $ii <= 3; $ii++) {
if (empty($sessions['conduit-'.$ii])) {
$session_type = 'conduit-'.$ii;
break;
}
}
if (!$session_type) {
$sessions = isort($sessions, 'sessionStart');
$oldest = reset($sessions);
$session_type = $oldest['type'];
}
$session_key = $user->establishSession($session_type);
$session_key = $user->establishSession('conduit');
} else {
throw new ConduitException('ERR-NO-CERTIFICATE');
}

View file

@ -11,7 +11,6 @@ phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
phutil_require_module('phabricator', 'applications/conduit/storage/connectionlog');
phutil_require_module('phabricator', 'applications/people/storage/user');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phutil', 'utils');

View file

@ -134,12 +134,85 @@ class PhabricatorUser extends PhabricatorUserDAO {
return substr(sha1($vec), 0, $len);
}
/**
* 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');
$entropy = Filesystem::readRandomBytes(20);
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);
queryfx(
$conn_w,
'INSERT INTO %T '.
@ -151,11 +224,9 @@ class PhabricatorUser extends PhabricatorUserDAO {
'sessionStart = VALUES(sessionStart)',
self::SESSION_TABLE,
$this->getPHID(),
$session_type,
$establish_type,
$session_key);
$this->sessionKey = $session_key;
return $session_key;
}