Optimize anonymous public sessions
This commit is contained in:
25
app/Http/Middleware/ConditionalShareErrorsFromSession.php
Normal file
25
app/Http/Middleware/ConditionalShareErrorsFromSession.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
|
||||
class ConditionalShareErrorsFromSession extends ShareErrorsFromSession
|
||||
{
|
||||
public function handle($request, Closure $next): mixed
|
||||
{
|
||||
if (! $request instanceof Request) {
|
||||
return parent::handle($request, $next);
|
||||
}
|
||||
|
||||
if ($request->attributes->get('skinbase.session_skipped') === true || ! $request->hasSession()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return parent::handle($request, $next);
|
||||
}
|
||||
}
|
||||
122
app/Http/Middleware/ConditionalStartSession.php
Normal file
122
app/Http/Middleware/ConditionalStartSession.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ConditionalStartSession extends StartSession
|
||||
{
|
||||
public function handle($request, Closure $next): mixed
|
||||
{
|
||||
if (! $request instanceof Request || ! config('skinbase-sessions.enabled', true)) {
|
||||
return parent::handle($request, $next);
|
||||
}
|
||||
|
||||
if ($this->shouldSkipSession($request)) {
|
||||
$request->attributes->set('skinbase.session_skipped', true);
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
if ($response instanceof Response && config('skinbase-sessions.debug_header', false)) {
|
||||
$response->headers->set('X-Skinbase-Session', 'skipped');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$request->attributes->set('skinbase.session_skipped', false);
|
||||
|
||||
$response = parent::handle($request, $next);
|
||||
|
||||
if ($response instanceof Response && config('skinbase-sessions.debug_header', false)) {
|
||||
$response->headers->set('X-Skinbase-Session', 'started');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function shouldSkipSession(Request $request): bool
|
||||
{
|
||||
if (! $this->isSafeReadMethod($request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->hasExistingSessionCookie($request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->matchesAnyPath($request, config('skinbase-sessions.always_session_paths', []))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->matchesAnyPath($request, config('skinbase-sessions.public_paths', []))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config('skinbase-sessions.skip_anonymous_public_get', true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return config('skinbase-sessions.skip_known_crawlers_on_public_get', true)
|
||||
&& $this->isKnownCrawler($request);
|
||||
}
|
||||
|
||||
protected function isSafeReadMethod(Request $request): bool
|
||||
{
|
||||
return in_array($request->getMethod(), ['GET', 'HEAD'], true);
|
||||
}
|
||||
|
||||
protected function hasExistingSessionCookie(Request $request): bool
|
||||
{
|
||||
$cookieName = config('session.cookie');
|
||||
|
||||
return is_string($cookieName)
|
||||
&& $cookieName !== ''
|
||||
&& $request->cookies->has($cookieName);
|
||||
}
|
||||
|
||||
protected function matchesAnyPath(Request $request, array $patterns): bool
|
||||
{
|
||||
foreach ($patterns as $pattern) {
|
||||
if (! is_string($pattern) || $pattern === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($pattern === '/' && $request->path() === '/') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$normalizedPattern = trim($pattern, '/');
|
||||
|
||||
if ($normalizedPattern !== '' && $request->is($normalizedPattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isKnownCrawler(Request $request): bool
|
||||
{
|
||||
$userAgent = strtolower((string) $request->userAgent());
|
||||
|
||||
if ($userAgent === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (config('skinbase-sessions.bot_user_agent_keywords', []) as $keyword) {
|
||||
$normalizedKeyword = strtolower((string) $keyword);
|
||||
|
||||
if ($normalizedKeyword !== '' && str_contains($userAgent, $normalizedKeyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
21
app/Http/Middleware/ConditionalValidateCsrfToken.php
Normal file
21
app/Http/Middleware/ConditionalValidateCsrfToken.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ConditionalValidateCsrfToken extends ValidateCsrfToken
|
||||
{
|
||||
public function handle($request, Closure $next): mixed
|
||||
{
|
||||
if ($request instanceof Request && $request->attributes->get('skinbase.session_skipped') === true) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return parent::handle($request, $next);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,15 @@ final class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
protected $rootView = 'upload';
|
||||
|
||||
protected function canReadSessionAuth(Request $request): bool
|
||||
{
|
||||
if ($request->attributes->get('skinbase.session_skipped') === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $request->hasSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the root Blade view based on route prefix.
|
||||
*/
|
||||
@@ -58,13 +67,16 @@ final class HandleInertiaRequests extends Middleware
|
||||
|
||||
public function share(Request $request): array
|
||||
{
|
||||
$canReadSessionAuth = $this->canReadSessionAuth($request);
|
||||
$user = $canReadSessionAuth ? $request->user() : null;
|
||||
|
||||
return array_merge(parent::share($request), [
|
||||
'auth' => [
|
||||
'user' => $request->user() ? [
|
||||
'id' => $request->user()->id,
|
||||
'name' => $request->user()->name,
|
||||
'is_admin' => $request->user()->isAdmin(),
|
||||
'is_moderator' => $request->user()->isModerator(),
|
||||
'user' => $user ? [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'is_admin' => $user->isAdmin(),
|
||||
'is_moderator' => $user->isModerator(),
|
||||
] : null,
|
||||
],
|
||||
'cdn' => [
|
||||
@@ -84,8 +96,8 @@ final class HandleInertiaRequests extends Middleware
|
||||
'group_assets' => (bool) config('features.group_assets', true),
|
||||
'group_activity_feed' => (bool) config('features.group_activity_feed', true),
|
||||
],
|
||||
'studio_groups' => $request->user()
|
||||
? app(GroupService::class)->studioOptionsForUser($request->user())
|
||||
'studio_groups' => $user
|
||||
? app(GroupService::class)->studioOptionsForUser($user)
|
||||
: [],
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user