*/
class CookieSessionProvider extends SessionProvider {
- protected $params = array();
- protected $cookieOptions = array();
+ protected $params = [];
+ protected $cookieOptions = [];
/**
* @param array $params Keys include:
* - secure: Cookie secure flag, defaults to $wgCookieSecure
* - httpOnly: Cookie httpOnly flag, defaults to $wgCookieHttpOnly
*/
- public function __construct( $params = array() ) {
+ public function __construct( $params = [] ) {
parent::__construct();
- $params += array(
- 'cookieOptions' => array(),
+ $params += [
+ 'cookieOptions' => [],
// @codeCoverageIgnoreStart
- );
+ ];
// @codeCoverageIgnoreEnd
if ( !isset( $params['priority'] ) ) {
parent::setConfig( $config );
// @codeCoverageIgnoreStart
- $this->params += array(
+ $this->params += [
// @codeCoverageIgnoreEnd
'callUserSetCookiesHook' => false,
'sessionName' =>
$config->get( 'SessionName' ) ?: $config->get( 'CookiePrefix' ) . '_session',
- );
+ ];
// @codeCoverageIgnoreStart
- $this->cookieOptions += array(
+ $this->cookieOptions += [
// @codeCoverageIgnoreEnd
'prefix' => $config->get( 'CookiePrefix' ),
'path' => $config->get( 'CookiePath' ),
'domain' => $config->get( 'CookieDomain' ),
'secure' => $config->get( 'CookieSecure' ),
'httpOnly' => $config->get( 'CookieHttpOnly' ),
- );
+ ];
}
public function provideSessionInfo( WebRequest $request ) {
- $info = array(
- 'id' => $this->getCookie( $request, $this->params['sessionName'], '' )
- );
- if ( !SessionManager::validateSessionId( $info['id'] ) ) {
- unset( $info['id'] );
+ $sessionId = $this->getCookie( $request, $this->params['sessionName'], '' );
+ $info = [
+ 'provider' => $this,
+ 'forceHTTPS' => $this->getCookie( $request, 'forceHTTPS', '', false )
+ ];
+ if ( SessionManager::validateSessionId( $sessionId ) ) {
+ $info['id'] = $sessionId;
+ $info['persisted'] = true;
}
list( $userId, $userName, $token ) = $this->getUserInfoFromCookies( $request );
// Sanity check
if ( $userName !== null && $userInfo->getName() !== $userName ) {
+ $this->logger->warning(
+ 'Session "{session}" requested with mismatched UserID and UserName cookies.',
+ [
+ 'session' => $sessionId,
+ 'mismatch' => [
+ 'userid' => $userId,
+ 'cookie_username' => $userName,
+ 'username' => $userInfo->getName(),
+ ],
+ ] );
return null;
}
if ( $token !== null ) {
if ( !hash_equals( $userInfo->getToken(), $token ) ) {
+ $this->logger->warning(
+ 'Session "{session}" requested with invalid Token cookie.',
+ [
+ 'session' => $sessionId,
+ 'userid' => $userId,
+ 'username' => $userInfo->getName(),
+ ] );
return null;
}
$info['userInfo'] = $userInfo->verified();
- } elseif ( isset( $info['id'] ) ) { // No point if no session ID
+ $info['persisted'] = true; // If we have user+token, it should be
+ } elseif ( isset( $info['id'] ) ) {
$info['userInfo'] = $userInfo;
+ } else {
+ // No point in returning, loadSessionInfoFromStore() will
+ // reject it anyway.
+ return null;
}
- }
-
- if ( !$info ) {
+ } elseif ( isset( $info['id'] ) ) {
+ // No UserID cookie, so insist that the session is anonymous.
+ // Note: this event occurs for several normal activities:
+ // * anon visits Special:UserLogin
+ // * anon browsing after seeing Special:UserLogin
+ // * anon browsing after edit or preview
+ $this->logger->debug(
+ 'Session "{session}" requested without UserID cookie',
+ [
+ 'session' => $info['id'],
+ ] );
+ $info['userInfo'] = UserInfo::newAnonymous();
+ } else {
+ // No session ID and no user is the same as an empty session, so
+ // there's no point.
return null;
}
- $info += array(
- 'provider' => $this,
- 'persisted' => isset( $info['id'] ),
- 'forceHTTPS' => $this->getCookie( $request, 'forceHTTPS', '', false )
- );
-
return new SessionInfo( $this->priority, $info );
}
// Legacy hook
if ( $this->params['callUserSetCookiesHook'] && !$user->isAnon() ) {
- \Hooks::run( 'UserSetCookies', array( $user, &$sessionData, &$cookies ) );
+ \Hooks::run( 'UserSetCookies', [ $user, &$sessionData, &$cookies ] );
}
$options = $this->cookieOptions;
}
$response->setCookie( $this->params['sessionName'], $session->getId(), null,
- array( 'prefix' => '' ) + $options
+ [ 'prefix' => '' ] + $options
);
- $extendedCookies = $this->config->get( 'ExtendedLoginCookies' );
- $extendedExpiry = $this->config->get( 'ExtendedLoginCookieExpiration' );
-
foreach ( $cookies as $key => $value ) {
if ( $value === false ) {
$response->clearCookie( $key, $options );
} else {
- if ( $extendedExpiry !== null && in_array( $key, $extendedCookies ) ) {
- $expiry = time() + (int)$extendedExpiry;
- } else {
- $expiry = 0; // Default cookie expiration
- }
- $response->setCookie( $key, (string)$value, $expiry, $options );
+ $expirationDuration = $this->getLoginCookieExpiration( $key, $session->shouldRememberUser() );
+ $expiration = $expirationDuration ? $expirationDuration + time() : null;
+ $response->setCookie( $key, (string)$value, $expiration, $options );
}
}
return;
}
- $cookies = array(
+ $cookies = [
'UserID' => false,
'Token' => false,
- );
+ ];
$response->clearCookie(
- $this->params['sessionName'], array( 'prefix' => '' ) + $this->cookieOptions
+ $this->params['sessionName'], [ 'prefix' => '' ] + $this->cookieOptions
);
foreach ( $cookies as $key => $value ) {
) {
$response = $request->response();
if ( $set ) {
- $response->setCookie( 'forceHTTPS', 'true', $backend->shouldRememberUser() ? 0 : null,
- array( 'prefix' => '', 'secure' => false ) + $this->cookieOptions );
+ if ( $backend->shouldRememberUser() ) {
+ $expirationDuration = $this->getLoginCookieExpiration(
+ 'forceHTTPS',
+ true
+ );
+ $expiration = $expirationDuration ? $expirationDuration + time() : null;
+ } else {
+ $expiration = null;
+ }
+ $response->setCookie( 'forceHTTPS', 'true', $expiration,
+ [ 'prefix' => '', 'secure' => false ] + $this->cookieOptions );
} else {
$response->clearCookie( 'forceHTTPS',
- array( 'prefix' => '', 'secure' => false ) + $this->cookieOptions );
+ [ 'prefix' => '', 'secure' => false ] + $this->cookieOptions );
}
}
}
public function getVaryCookies() {
- return array(
+ return [
// Vary on token and session because those are the real authn
// determiners. UserID and UserName don't matter without those.
$this->cookieOptions['prefix'] . 'Token',
$this->cookieOptions['prefix'] . 'LoggedOut',
$this->params['sessionName'],
'forceHTTPS',
- );
+ ];
}
public function suggestLoginUsername( WebRequest $request ) {
*/
protected function getUserInfoFromCookies( $request ) {
$prefix = $this->cookieOptions['prefix'];
- return array(
+ return [
$this->getCookie( $request, 'UserID', $prefix ),
$this->getCookie( $request, 'UserName', $prefix ),
$this->getCookie( $request, 'Token', $prefix ),
- );
+ ];
}
/**
*/
protected function cookieDataToExport( $user, $remember ) {
if ( $user->isAnon() ) {
- return array(
+ return [
'UserID' => false,
'Token' => false,
- );
+ ];
} else {
- return array(
+ return [
'UserID' => $user->getId(),
'UserName' => $user->getName(),
'Token' => $remember ? (string)$user->getToken() : false,
- );
+ ];
}
}
// If we're calling the legacy hook, we should populate $session
// like User::setCookies() did.
if ( !$user->isAnon() && $this->params['callUserSetCookiesHook'] ) {
- return array(
+ return [
'wsUserID' => $user->getId(),
'wsToken' => $user->getToken(),
'wsUserName' => $user->getName(),
- );
+ ];
}
- return array();
+ return [];
}
public function whyNoSession() {
return wfMessage( 'sessionprovider-nocookies' );
}
+ public function getRememberUserDuration() {
+ return min( $this->getLoginCookieExpiration( 'UserID', true ),
+ $this->getLoginCookieExpiration( 'Token', true ) ) ?: null;
+ }
+
+ /**
+ * Gets the list of cookies that must be set to the 'remember me' duration,
+ * if $wgExtendedLoginCookieExpiration is in use.
+ *
+ * @return string[] Array of unprefixed cookie keys
+ */
+ protected function getExtendedLoginCookies() {
+ return [ 'UserID', 'UserName', 'Token' ];
+ }
+
+ /**
+ * Returns the lifespan of the login cookies, in seconds. 0 means until the end of the session.
+ *
+ * Cookies that are session-length do not call this function.
+ *
+ * @param string $cookieName
+ * @param bool $shouldRememberUser Whether the user should be remembered
+ * long-term
+ * @return int Cookie expiration time in seconds; 0 for session cookies
+ */
+ protected function getLoginCookieExpiration( $cookieName, $shouldRememberUser ) {
+ $extendedCookies = $this->getExtendedLoginCookies();
+ $normalExpiration = $this->config->get( 'CookieExpiration' );
+
+ if ( $shouldRememberUser && in_array( $cookieName, $extendedCookies, true ) ) {
+ $extendedExpiration = $this->config->get( 'ExtendedLoginCookieExpiration' );
+
+ return ( $extendedExpiration !== null ) ? (int)$extendedExpiration : (int)$normalExpiration;
+ } else {
+ return (int)$normalExpiration;
+ }
+ }
}