2014-01-14 22:22:27 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorAuthSessionEngine extends Phobject {
|
|
|
|
|
|
|
|
public function loadUserForSession($session_type, $session_key) {
|
|
|
|
$session_table = new PhabricatorAuthSession();
|
|
|
|
$user_table = new PhabricatorUser();
|
2014-01-15 22:56:16 +01:00
|
|
|
$conn_r = $session_table->establishConnection('r');
|
|
|
|
|
|
|
|
// NOTE: We're being clever here because this happens on every page load,
|
|
|
|
// and by joining we can save a query.
|
2014-01-14 22:22:27 +01:00
|
|
|
|
|
|
|
$info = queryfx_one(
|
|
|
|
$conn_r,
|
2014-01-15 22:56:16 +01:00
|
|
|
'SELECT s.sessionExpires AS _sessionExpires, s.id AS _sessionID, u.*
|
|
|
|
FROM %T u JOIN %T s ON u.phid = s.userPHID
|
2014-01-16 02:27:59 +01:00
|
|
|
AND s.type = %s AND s.sessionKey = %s',
|
2014-01-14 22:22:27 +01:00
|
|
|
$user_table->getTableName(),
|
|
|
|
$session_table->getTableName(),
|
2014-01-16 02:27:59 +01:00
|
|
|
$session_type,
|
2014-01-14 22:22:27 +01:00
|
|
|
PhabricatorHash::digest($session_key));
|
|
|
|
|
|
|
|
if (!$info) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2014-01-15 22:56:16 +01:00
|
|
|
$expires = $info['_sessionExpires'];
|
|
|
|
$id = $info['_sessionID'];
|
|
|
|
unset($info['_sessionExpires']);
|
|
|
|
unset($info['_sessionID']);
|
|
|
|
|
|
|
|
$ttl = PhabricatorAuthSession::getSessionTypeTTL($session_type);
|
|
|
|
|
|
|
|
// If more than 20% of the time on this session has been used, refresh the
|
|
|
|
// TTL back up to the full duration. The idea here is that sessions are
|
|
|
|
// good forever if used regularly, but get GC'd when they fall out of use.
|
|
|
|
|
|
|
|
if (time() + (0.80 * $ttl) > $expires) {
|
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
|
|
|
$conn_w = $session_table->establishConnection('w');
|
|
|
|
queryfx(
|
|
|
|
$conn_w,
|
|
|
|
'UPDATE %T SET sessionExpires = UNIX_TIMESTAMP() + %d WHERE id = %d',
|
|
|
|
$session_table->getTableName(),
|
|
|
|
$ttl,
|
|
|
|
$id);
|
|
|
|
unset($unguarded);
|
|
|
|
}
|
|
|
|
|
2014-01-14 22:22:27 +01:00
|
|
|
return $user_table->loadFromArray($info);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Issue a new session key for a given identity. 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.
|
|
|
|
*
|
2014-01-14 22:22:34 +01:00
|
|
|
* @param const Session type constant (see
|
|
|
|
* @{class:PhabricatorAuthSession}).
|
2014-01-14 22:22:27 +01:00
|
|
|
* @param phid Identity to establish a session for, usually a user PHID.
|
|
|
|
* @return string Newly generated session key.
|
|
|
|
*/
|
|
|
|
public function establishSession($session_type, $identity_phid) {
|
|
|
|
$session_table = new PhabricatorAuthSession();
|
|
|
|
$conn_w = $session_table->establishConnection('w');
|
|
|
|
|
|
|
|
// Consume entropy to generate a new session key, forestalling the eventual
|
|
|
|
// heat death of the universe.
|
|
|
|
$session_key = Filesystem::readRandomCharacters(40);
|
|
|
|
|
2014-01-16 02:27:59 +01:00
|
|
|
// This has a side effect of validating the session type.
|
|
|
|
$session_ttl = PhabricatorAuthSession::getSessionTypeTTL($session_type);
|
2014-01-14 22:22:27 +01:00
|
|
|
|
2014-01-16 02:27:59 +01:00
|
|
|
// Logging-in users don't have CSRF stuff yet, so we have to unguard this
|
|
|
|
// write.
|
2014-01-14 22:22:27 +01:00
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
2014-01-16 02:27:59 +01:00
|
|
|
id(new PhabricatorAuthSession())
|
|
|
|
->setUserPHID($identity_phid)
|
|
|
|
->setType($session_type)
|
|
|
|
->setSessionKey(PhabricatorHash::digest($session_key))
|
|
|
|
->setSessionStart(time())
|
|
|
|
->setSessionExpires(time() + $session_ttl)
|
|
|
|
->save();
|
|
|
|
|
|
|
|
$log = PhabricatorUserLog::initializeNewLog(
|
|
|
|
null,
|
2014-01-14 22:22:27 +01:00
|
|
|
$identity_phid,
|
2014-01-16 02:27:59 +01:00
|
|
|
PhabricatorUserLog::ACTION_LOGIN);
|
|
|
|
$log->setDetails(
|
|
|
|
array(
|
|
|
|
'session_type' => $session_type,
|
|
|
|
));
|
|
|
|
$log->setSession($session_key);
|
|
|
|
$log->save();
|
|
|
|
unset($unguarded);
|
2014-01-14 22:22:27 +01:00
|
|
|
|
|
|
|
return $session_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|