Merge pull request #1366 from flarum/illuminate-session

Use Illuminate Session component instead of Symfony
This commit is contained in:
Franz Liedke 2018-03-24 20:42:58 +01:00 committed by GitHub
commit f42273e679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 128 additions and 47 deletions

View File

@ -35,6 +35,7 @@
"illuminate/filesystem": "5.5.*", "illuminate/filesystem": "5.5.*",
"illuminate/hashing": "5.5.*", "illuminate/hashing": "5.5.*",
"illuminate/mail": "5.5.*", "illuminate/mail": "5.5.*",
"illuminate/session": "5.5.*",
"illuminate/support": "5.5.*", "illuminate/support": "5.5.*",
"illuminate/validation": "5.5.*", "illuminate/validation": "5.5.*",
"illuminate/view": "5.5.*", "illuminate/view": "5.5.*",

View File

@ -62,7 +62,7 @@ abstract class AbstractOAuth2Controller implements ControllerInterface
if (! $code) { if (! $code) {
$authUrl = $this->provider->getAuthorizationUrl($this->getAuthorizationUrlOptions()); $authUrl = $this->provider->getAuthorizationUrl($this->getAuthorizationUrlOptions());
$session->set('oauth2state', $this->provider->getState()); $session->put('oauth2state', $this->provider->getState());
return new RedirectResponse($authUrl.'&display=popup'); return new RedirectResponse($authUrl.'&display=popup');
} elseif (! $state || $state !== $session->get('oauth2state')) { } elseif (! $state || $state !== $session->get('oauth2state')) {

View File

@ -102,7 +102,7 @@ class LogOutController implements ControllerInterface
// If a valid CSRF token hasn't been provided, show a view which will // If a valid CSRF token hasn't been provided, show a view which will
// allow the user to press a button to complete the log out process. // allow the user to press a button to complete the log out process.
$csrfToken = $session->get('csrf_token'); $csrfToken = $session->token();
if (array_get($request->getQueryParams(), 'token') !== $csrfToken) { if (array_get($request->getQueryParams(), 'token') !== $csrfToken) {
$return = array_get($request->getQueryParams(), 'return'); $return = array_get($request->getQueryParams(), 'return');

View File

@ -50,6 +50,6 @@ class ResetPasswordController extends AbstractHtmlController
return $this->view->make('flarum.forum::reset-password') return $this->view->make('flarum.forum::reset-password')
->with('passwordToken', $token->id) ->with('passwordToken', $token->id)
->with('csrfToken', $request->getAttribute('session')->get('csrf_token')); ->with('csrfToken', $request->getAttribute('session')->token());
} }
} }

View File

@ -33,6 +33,7 @@ use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Filesystem\FilesystemServiceProvider; use Illuminate\Filesystem\FilesystemServiceProvider;
use Illuminate\Hashing\HashServiceProvider; use Illuminate\Hashing\HashServiceProvider;
use Illuminate\Mail\MailServiceProvider; use Illuminate\Mail\MailServiceProvider;
use Illuminate\Session\SessionServiceProvider;
use Illuminate\Validation\ValidationServiceProvider; use Illuminate\Validation\ValidationServiceProvider;
use Illuminate\View\ViewServiceProvider; use Illuminate\View\ViewServiceProvider;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
@ -169,6 +170,7 @@ class Site
$app->register(FilesystemServiceProvider::class); $app->register(FilesystemServiceProvider::class);
$app->register(HashServiceProvider::class); $app->register(HashServiceProvider::class);
$app->register(MailServiceProvider::class); $app->register(MailServiceProvider::class);
$app->register(SessionServiceProvider::class);
$app->register(ViewServiceProvider::class); $app->register(ViewServiceProvider::class);
$app->register(ValidationServiceProvider::class); $app->register(ValidationServiceProvider::class);
@ -236,6 +238,11 @@ class Site
'root' => $app->publicPath().'/assets/avatars' 'root' => $app->publicPath().'/assets/avatars'
] ]
] ]
],
'session' => [
'lifetime' => 120,
'files' => $app->storagePath().'/sessions',
'cookie' => 'session'
] ]
]); ]);
} }

View File

@ -464,7 +464,7 @@ class FrontendView
return [ return [
'userId' => $actor->id, 'userId' => $actor->id,
'csrfToken' => $session->get('csrf_token') 'csrfToken' => $session->token()
]; ];
} }

View File

@ -13,10 +13,10 @@ namespace Flarum\Http\Middleware;
use Flarum\User\Guest; use Flarum\User\Guest;
use Flarum\User\User; use Flarum\User\User;
use Illuminate\Contracts\Session\Session;
use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class AuthenticateWithSession implements MiddlewareInterface class AuthenticateWithSession implements MiddlewareInterface
{ {
@ -33,7 +33,7 @@ class AuthenticateWithSession implements MiddlewareInterface
return $delegate->process($request); return $delegate->process($request);
} }
private function getActor(SessionInterface $session) private function getActor(Session $session)
{ {
$actor = User::find($session->get('user_id')) ?: new Guest; $actor = User::find($session->get('user_id')) ?: new Guest;

View File

@ -15,12 +15,30 @@ use Flarum\Http\AccessToken;
use Flarum\User\AuthToken; use Flarum\User\AuthToken;
use Flarum\User\EmailToken; use Flarum\User\EmailToken;
use Flarum\User\PasswordToken; use Flarum\User\PasswordToken;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use SessionHandlerInterface;
class CollectGarbage implements MiddlewareInterface class CollectGarbage implements MiddlewareInterface
{ {
/**
* @var SessionHandlerInterface
*/
protected $sessionHandler;
/**
* @var array
*/
protected $sessionConfig;
public function __construct(SessionHandlerInterface $handler, ConfigRepository $config)
{
$this->sessionHandler = $handler;
$this->sessionConfig = $config->get('session');
}
public function process(Request $request, DelegateInterface $delegate) public function process(Request $request, DelegateInterface $delegate)
{ {
$this->collectGarbageSometimes(); $this->collectGarbageSometimes();
@ -43,10 +61,17 @@ class CollectGarbage implements MiddlewareInterface
EmailToken::where('created_at', '<=', $earliestToKeep)->delete(); EmailToken::where('created_at', '<=', $earliestToKeep)->delete();
PasswordToken::where('created_at', '<=', $earliestToKeep)->delete(); PasswordToken::where('created_at', '<=', $earliestToKeep)->delete();
AuthToken::where('created_at', '<=', $earliestToKeep)->delete(); AuthToken::where('created_at', '<=', $earliestToKeep)->delete();
$this->sessionHandler->gc($this->getSessionLifetimeInSeconds());
} }
private function hit() private function hit()
{ {
return mt_rand(1, 100) <= 2; return mt_rand(1, 100) <= 2;
} }
private function getSessionLifetimeInSeconds()
{
return $this->sessionConfig['lifetime'] * 60;
}
} }

View File

@ -12,15 +12,29 @@
namespace Flarum\Http\Middleware; namespace Flarum\Http\Middleware;
use Flarum\Http\AccessToken; use Flarum\Http\AccessToken;
use Flarum\Http\CookieFactory;
use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
class RememberFromCookie implements MiddlewareInterface class RememberFromCookie implements MiddlewareInterface
{ {
/**
* @var CookieFactory
*/
protected $cookie;
/**
* @param CookieFactory $cookie
*/
public function __construct(CookieFactory $cookie)
{
$this->cookie = $cookie;
}
public function process(Request $request, DelegateInterface $delegate) public function process(Request $request, DelegateInterface $delegate)
{ {
$id = array_get($request->getCookieParams(), 'flarum_remember'); $id = array_get($request->getCookieParams(), $this->cookie->getName('remember'));
if ($id) { if ($id) {
$token = AccessToken::find($id); $token = AccessToken::find($id);
@ -28,8 +42,9 @@ class RememberFromCookie implements MiddlewareInterface
if ($token) { if ($token) {
$token->touch(); $token->touch();
/** @var \Illuminate\Contracts\Session\Session $session */
$session = $request->getAttribute('session'); $session = $request->getAttribute('session');
$session->set('user_id', $token->user_id); $session->put('user_id', $token->user_id);
} }
} }

View File

@ -13,72 +13,84 @@ namespace Flarum\Http\Middleware;
use Dflydev\FigCookies\FigResponseCookies; use Dflydev\FigCookies\FigResponseCookies;
use Flarum\Http\CookieFactory; use Flarum\Http\CookieFactory;
use Illuminate\Support\Str; use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Session\Session;
use Illuminate\Session\Store;
use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Symfony\Component\HttpFoundation\Session\Session; use SessionHandlerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class StartSession implements MiddlewareInterface class StartSession implements MiddlewareInterface
{ {
const COOKIE_NAME = 'session'; /**
* @var SessionHandlerInterface
*/
protected $handler;
/** /**
* @var CookieFactory * @var CookieFactory
*/ */
protected $cookie; protected $cookie;
/**
* @var array
*/
protected $config;
/** /**
* @param CookieFactory $cookie * @param CookieFactory $cookie
*/ */
public function __construct(CookieFactory $cookie) public function __construct(SessionHandlerInterface $handler, CookieFactory $cookie, ConfigRepository $config)
{ {
$this->handler = $handler;
$this->cookie = $cookie; $this->cookie = $cookie;
$this->config = $config->get('session');
} }
public function process(Request $request, DelegateInterface $delegate) public function process(Request $request, DelegateInterface $delegate)
{ {
$session = $this->startSession(); $request = $request->withAttribute(
'session',
$request = $request->withAttribute('session', $session); $session = $this->makeSession($request)
);
$session->start();
$response = $delegate->process($request); $response = $delegate->process($request);
$session->save();
$response = $this->withCsrfTokenHeader($response, $session); $response = $this->withCsrfTokenHeader($response, $session);
return $this->withSessionCookie($response, $session); return $this->withSessionCookie($response, $session);
} }
private function startSession() private function makeSession(Request $request)
{ {
$session = new Session; $cookieName = $this->cookie->getName($this->config['cookie']);
$session->setName($this->cookie->getName(self::COOKIE_NAME)); return new Store(
$session->start(); $cookieName,
$this->handler,
if (! $session->has('csrf_token')) { array_get($request->getCookieParams(), $cookieName)
$session->set('csrf_token', Str::random(40)); );
}
return $session;
} }
private function withCsrfTokenHeader(Response $response, SessionInterface $session) private function withCsrfTokenHeader(Response $response, Session $session)
{ {
if ($session->has('csrf_token')) { return $response->withHeader('X-CSRF-Token', $session->token());
$response = $response->withHeader('X-CSRF-Token', $session->get('csrf_token'));
}
return $response;
} }
private function withSessionCookie(Response $response, SessionInterface $session) private function withSessionCookie(Response $response, Session $session)
{ {
return FigResponseCookies::set( return FigResponseCookies::set(
$response, $response,
$this->cookie->make(self::COOKIE_NAME, $session->getId()) $this->cookie->make($session->getName(), $session->getId(), $this->getSessionLifetimeInSeconds())
); );
} }
private function getSessionLifetimeInSeconds()
{
return $this->config['lifetime'] * 60;
}
} }

View File

@ -11,25 +11,26 @@
namespace Flarum\Http; namespace Flarum\Http;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Illuminate\Contracts\Session\Session;
class SessionAuthenticator class SessionAuthenticator
{ {
/** /**
* @param SessionInterface $session * @param Session $session
* @param int $userId * @param int $userId
*/ */
public function logIn(SessionInterface $session, $userId) public function logIn(Session $session, $userId)
{ {
$session->migrate(); $session->regenerate(true);
$session->set('user_id', $userId); $session->put('user_id', $userId);
} }
/** /**
* @param SessionInterface $session * @param Session $session
*/ */
public function logOut(SessionInterface $session) public function logOut(Session $session)
{ {
$session->invalidate(); $session->invalidate();
$session->regenerateToken();
} }
} }

View File

@ -34,7 +34,7 @@ use Flarum\User\Event\PasswordChanged;
use Flarum\User\Event\Registered; use Flarum\User\Event\Registered;
use Flarum\User\Event\Renamed; use Flarum\User\Event\Renamed;
use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Contracts\Hashing\Hasher;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Illuminate\Contracts\Session\Session;
/** /**
* @property int $id * @property int $id
@ -698,7 +698,7 @@ class User extends AbstractModel
} }
/** /**
* @return SessionInterface * @return Session
*/ */
public function getSession() public function getSession()
{ {
@ -706,9 +706,9 @@ class User extends AbstractModel
} }
/** /**
* @param SessionInterface $session * @param Session $session
*/ */
public function setSession(SessionInterface $session) public function setSession(Session $session)
{ {
$this->session = $session; $this->session = $session;
} }

View File

@ -15,7 +15,9 @@ use Flarum\Event\ConfigureUserPreferences;
use Flarum\Event\GetPermission; use Flarum\Event\GetPermission;
use Flarum\Foundation\AbstractServiceProvider; use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;
use Illuminate\Session\FileSessionHandler;
use RuntimeException; use RuntimeException;
use SessionHandlerInterface;
class UserServiceProvider extends AbstractServiceProvider class UserServiceProvider extends AbstractServiceProvider
{ {
@ -23,6 +25,26 @@ class UserServiceProvider extends AbstractServiceProvider
* {@inheritdoc} * {@inheritdoc}
*/ */
public function register() public function register()
{
$this->registerSession();
$this->registerGate();
$this->registerAvatarsFilesystem();
}
protected function registerSession()
{
$this->app->singleton('session.handler', function ($app) {
return new FileSessionHandler(
$app['files'],
$app['config']['session.files'],
$app['config']['session.lifetime']
);
});
$this->app->alias('session.handler', SessionHandlerInterface::class);
}
protected function registerGate()
{ {
$this->app->singleton('flarum.gate', function ($app) { $this->app->singleton('flarum.gate', function ($app) {
return new Gate($app, function () { return new Gate($app, function () {
@ -32,8 +54,6 @@ class UserServiceProvider extends AbstractServiceProvider
$this->app->alias('flarum.gate', 'Illuminate\Contracts\Auth\Access\Gate'); $this->app->alias('flarum.gate', 'Illuminate\Contracts\Auth\Access\Gate');
$this->app->alias('flarum.gate', Gate::class); $this->app->alias('flarum.gate', Gate::class);
$this->registerAvatarsFilesystem();
} }
protected function registerAvatarsFilesystem() protected function registerAvatarsFilesystem()