diff --git a/framework/core/composer.json b/framework/core/composer.json index cc9151b2d..27c91e627 100644 --- a/framework/core/composer.json +++ b/framework/core/composer.json @@ -35,6 +35,7 @@ "illuminate/filesystem": "5.5.*", "illuminate/hashing": "5.5.*", "illuminate/mail": "5.5.*", + "illuminate/session": "5.5.*", "illuminate/support": "5.5.*", "illuminate/validation": "5.5.*", "illuminate/view": "5.5.*", diff --git a/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php b/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php index 52f8c1e17..ed0946dda 100644 --- a/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php +++ b/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php @@ -62,7 +62,7 @@ abstract class AbstractOAuth2Controller implements ControllerInterface if (! $code) { $authUrl = $this->provider->getAuthorizationUrl($this->getAuthorizationUrlOptions()); - $session->set('oauth2state', $this->provider->getState()); + $session->put('oauth2state', $this->provider->getState()); return new RedirectResponse($authUrl.'&display=popup'); } elseif (! $state || $state !== $session->get('oauth2state')) { diff --git a/framework/core/src/Forum/Controller/LogOutController.php b/framework/core/src/Forum/Controller/LogOutController.php index c819fec7d..86bb45480 100644 --- a/framework/core/src/Forum/Controller/LogOutController.php +++ b/framework/core/src/Forum/Controller/LogOutController.php @@ -102,7 +102,7 @@ class LogOutController implements ControllerInterface // 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. - $csrfToken = $session->get('csrf_token'); + $csrfToken = $session->token(); if (array_get($request->getQueryParams(), 'token') !== $csrfToken) { $return = array_get($request->getQueryParams(), 'return'); diff --git a/framework/core/src/Forum/Controller/ResetPasswordController.php b/framework/core/src/Forum/Controller/ResetPasswordController.php index 3752ead21..a95dd07cc 100644 --- a/framework/core/src/Forum/Controller/ResetPasswordController.php +++ b/framework/core/src/Forum/Controller/ResetPasswordController.php @@ -50,6 +50,6 @@ class ResetPasswordController extends AbstractHtmlController return $this->view->make('flarum.forum::reset-password') ->with('passwordToken', $token->id) - ->with('csrfToken', $request->getAttribute('session')->get('csrf_token')); + ->with('csrfToken', $request->getAttribute('session')->token()); } } diff --git a/framework/core/src/Foundation/Site.php b/framework/core/src/Foundation/Site.php index a86f7d6e1..9f17b4a27 100644 --- a/framework/core/src/Foundation/Site.php +++ b/framework/core/src/Foundation/Site.php @@ -33,6 +33,7 @@ use Illuminate\Config\Repository as ConfigRepository; use Illuminate\Filesystem\FilesystemServiceProvider; use Illuminate\Hashing\HashServiceProvider; use Illuminate\Mail\MailServiceProvider; +use Illuminate\Session\SessionServiceProvider; use Illuminate\Validation\ValidationServiceProvider; use Illuminate\View\ViewServiceProvider; use Monolog\Formatter\LineFormatter; @@ -169,6 +170,7 @@ class Site $app->register(FilesystemServiceProvider::class); $app->register(HashServiceProvider::class); $app->register(MailServiceProvider::class); + $app->register(SessionServiceProvider::class); $app->register(ViewServiceProvider::class); $app->register(ValidationServiceProvider::class); @@ -236,6 +238,11 @@ class Site 'root' => $app->publicPath().'/assets/avatars' ] ] + ], + 'session' => [ + 'lifetime' => 120, + 'files' => $app->storagePath().'/sessions', + 'cookie' => 'session' ] ]); } diff --git a/framework/core/src/Frontend/FrontendView.php b/framework/core/src/Frontend/FrontendView.php index 45d36d12c..0063384d3 100644 --- a/framework/core/src/Frontend/FrontendView.php +++ b/framework/core/src/Frontend/FrontendView.php @@ -464,7 +464,7 @@ class FrontendView return [ 'userId' => $actor->id, - 'csrfToken' => $session->get('csrf_token') + 'csrfToken' => $session->token() ]; } diff --git a/framework/core/src/Http/Middleware/AuthenticateWithSession.php b/framework/core/src/Http/Middleware/AuthenticateWithSession.php index 6c6729f16..4c780925b 100644 --- a/framework/core/src/Http/Middleware/AuthenticateWithSession.php +++ b/framework/core/src/Http/Middleware/AuthenticateWithSession.php @@ -13,10 +13,10 @@ namespace Flarum\Http\Middleware; use Flarum\User\Guest; use Flarum\User\User; +use Illuminate\Contracts\Session\Session; use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface as Request; -use Symfony\Component\HttpFoundation\Session\SessionInterface; class AuthenticateWithSession implements MiddlewareInterface { @@ -33,7 +33,7 @@ class AuthenticateWithSession implements MiddlewareInterface return $delegate->process($request); } - private function getActor(SessionInterface $session) + private function getActor(Session $session) { $actor = User::find($session->get('user_id')) ?: new Guest; diff --git a/framework/core/src/Http/Middleware/CollectGarbage.php b/framework/core/src/Http/Middleware/CollectGarbage.php index 4754f2300..f4882c9fb 100644 --- a/framework/core/src/Http/Middleware/CollectGarbage.php +++ b/framework/core/src/Http/Middleware/CollectGarbage.php @@ -15,12 +15,30 @@ use Flarum\Http\AccessToken; use Flarum\User\AuthToken; use Flarum\User\EmailToken; use Flarum\User\PasswordToken; +use Illuminate\Contracts\Config\Repository as ConfigRepository; use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface as Request; +use SessionHandlerInterface; 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) { $this->collectGarbageSometimes(); @@ -43,10 +61,17 @@ class CollectGarbage implements MiddlewareInterface EmailToken::where('created_at', '<=', $earliestToKeep)->delete(); PasswordToken::where('created_at', '<=', $earliestToKeep)->delete(); AuthToken::where('created_at', '<=', $earliestToKeep)->delete(); + + $this->sessionHandler->gc($this->getSessionLifetimeInSeconds()); } private function hit() { return mt_rand(1, 100) <= 2; } + + private function getSessionLifetimeInSeconds() + { + return $this->sessionConfig['lifetime'] * 60; + } } diff --git a/framework/core/src/Http/Middleware/RememberFromCookie.php b/framework/core/src/Http/Middleware/RememberFromCookie.php index 96cf1e289..335c43198 100644 --- a/framework/core/src/Http/Middleware/RememberFromCookie.php +++ b/framework/core/src/Http/Middleware/RememberFromCookie.php @@ -12,15 +12,29 @@ namespace Flarum\Http\Middleware; use Flarum\Http\AccessToken; +use Flarum\Http\CookieFactory; use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Http\Message\ServerRequestInterface as Request; 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) { - $id = array_get($request->getCookieParams(), 'flarum_remember'); + $id = array_get($request->getCookieParams(), $this->cookie->getName('remember')); if ($id) { $token = AccessToken::find($id); @@ -28,8 +42,9 @@ class RememberFromCookie implements MiddlewareInterface if ($token) { $token->touch(); + /** @var \Illuminate\Contracts\Session\Session $session */ $session = $request->getAttribute('session'); - $session->set('user_id', $token->user_id); + $session->put('user_id', $token->user_id); } } diff --git a/framework/core/src/Http/Middleware/StartSession.php b/framework/core/src/Http/Middleware/StartSession.php index 239fed9c1..7447aceea 100644 --- a/framework/core/src/Http/Middleware/StartSession.php +++ b/framework/core/src/Http/Middleware/StartSession.php @@ -13,72 +13,84 @@ namespace Flarum\Http\Middleware; use Dflydev\FigCookies\FigResponseCookies; 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\MiddlewareInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; -use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\SessionInterface; +use SessionHandlerInterface; class StartSession implements MiddlewareInterface { - const COOKIE_NAME = 'session'; + /** + * @var SessionHandlerInterface + */ + protected $handler; /** * @var CookieFactory */ protected $cookie; + /** + * @var array + */ + protected $config; + /** * @param CookieFactory $cookie */ - public function __construct(CookieFactory $cookie) + public function __construct(SessionHandlerInterface $handler, CookieFactory $cookie, ConfigRepository $config) { + $this->handler = $handler; $this->cookie = $cookie; + $this->config = $config->get('session'); } public function process(Request $request, DelegateInterface $delegate) { - $session = $this->startSession(); - - $request = $request->withAttribute('session', $session); + $request = $request->withAttribute( + 'session', + $session = $this->makeSession($request) + ); + $session->start(); $response = $delegate->process($request); + $session->save(); $response = $this->withCsrfTokenHeader($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)); - $session->start(); - - if (! $session->has('csrf_token')) { - $session->set('csrf_token', Str::random(40)); - } - - return $session; + return new Store( + $cookieName, + $this->handler, + array_get($request->getCookieParams(), $cookieName) + ); } - private function withCsrfTokenHeader(Response $response, SessionInterface $session) + private function withCsrfTokenHeader(Response $response, Session $session) { - if ($session->has('csrf_token')) { - $response = $response->withHeader('X-CSRF-Token', $session->get('csrf_token')); - } - - return $response; + return $response->withHeader('X-CSRF-Token', $session->token()); } - private function withSessionCookie(Response $response, SessionInterface $session) + private function withSessionCookie(Response $response, Session $session) { return FigResponseCookies::set( $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; + } } diff --git a/framework/core/src/Http/SessionAuthenticator.php b/framework/core/src/Http/SessionAuthenticator.php index 8ccb06524..8e23027a0 100644 --- a/framework/core/src/Http/SessionAuthenticator.php +++ b/framework/core/src/Http/SessionAuthenticator.php @@ -11,25 +11,26 @@ namespace Flarum\Http; -use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Illuminate\Contracts\Session\Session; class SessionAuthenticator { /** - * @param SessionInterface $session + * @param Session $session * @param int $userId */ - public function logIn(SessionInterface $session, $userId) + public function logIn(Session $session, $userId) { - $session->migrate(); - $session->set('user_id', $userId); + $session->regenerate(true); + $session->put('user_id', $userId); } /** - * @param SessionInterface $session + * @param Session $session */ - public function logOut(SessionInterface $session) + public function logOut(Session $session) { $session->invalidate(); + $session->regenerateToken(); } } diff --git a/framework/core/src/User/User.php b/framework/core/src/User/User.php index 3a695aa78..fa30ec01a 100644 --- a/framework/core/src/User/User.php +++ b/framework/core/src/User/User.php @@ -34,7 +34,7 @@ use Flarum\User\Event\PasswordChanged; use Flarum\User\Event\Registered; use Flarum\User\Event\Renamed; use Illuminate\Contracts\Hashing\Hasher; -use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Illuminate\Contracts\Session\Session; /** * @property int $id @@ -698,7 +698,7 @@ class User extends AbstractModel } /** - * @return SessionInterface + * @return Session */ 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; } diff --git a/framework/core/src/User/UserServiceProvider.php b/framework/core/src/User/UserServiceProvider.php index 03f3cbf55..a52d128ad 100644 --- a/framework/core/src/User/UserServiceProvider.php +++ b/framework/core/src/User/UserServiceProvider.php @@ -15,7 +15,9 @@ use Flarum\Event\ConfigureUserPreferences; use Flarum\Event\GetPermission; use Flarum\Foundation\AbstractServiceProvider; use Illuminate\Contracts\Container\Container; +use Illuminate\Session\FileSessionHandler; use RuntimeException; +use SessionHandlerInterface; class UserServiceProvider extends AbstractServiceProvider { @@ -23,6 +25,26 @@ class UserServiceProvider extends AbstractServiceProvider * {@inheritdoc} */ 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) { 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', Gate::class); - - $this->registerAvatarsFilesystem(); } protected function registerAvatarsFilesystem()