mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 10:12:41 +01: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:
parent
477954a57e
commit
3c30ea41f1
6 changed files with 92 additions and 32 deletions
|
@ -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
|
||||
|
|
|
@ -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 -------------------------------------------------------------- //
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue