Fix tenant event form package selector so it no longer renders empty-value options, handles loading/empty
states, and pulls data from the authenticated /api/v1/tenant/packages endpoint.
(resources/js/admin/pages/EventFormPage.tsx, resources/js/admin/api.ts)
- Harden tenant-admin auth flow: prevent PKCE state loss, scope out StrictMode double-processing, add SPA
routes for /event-admin/login and /event-admin/logout, and tighten token/session clearing semantics (resources/js/admin/auth/{context,tokens}.tsx, resources/js/admin/pages/{AuthCallbackPage,LogoutPage}.tsx,
resources/js/admin/router.tsx, routes/web.php)
This commit is contained in:
154
app/Http/Middleware/ContentSecurityPolicy.php
Normal file
154
app/Http/Middleware/ContentSecurityPolicy.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ContentSecurityPolicy
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$scriptNonce = base64_encode(random_bytes(16));
|
||||
$styleNonce = null;
|
||||
|
||||
$request->attributes->set('csp_script_nonce', $scriptNonce);
|
||||
$request->attributes->set('csp_style_nonce', $styleNonce);
|
||||
|
||||
View::share('cspNonce', $scriptNonce);
|
||||
View::share('cspStyleNonce', $styleNonce);
|
||||
|
||||
Vite::useCspNonce($scriptNonce);
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
if (app()->environment('local') || config('app.debug')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($response->headers->has('Content-Security-Policy')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$matomoOrigin = $this->normaliseOrigin(config('services.matomo.url'));
|
||||
$scriptSources = [
|
||||
"'self'",
|
||||
"'nonce-{$scriptNonce}'",
|
||||
'https://js.stripe.com',
|
||||
'https://js.stripe.network',
|
||||
];
|
||||
|
||||
$styleSources = [
|
||||
"'self'",
|
||||
"'unsafe-inline'",
|
||||
'https:',
|
||||
];
|
||||
|
||||
$connectSources = [
|
||||
"'self'",
|
||||
'https://api.stripe.com',
|
||||
'https://api.stripe.network',
|
||||
];
|
||||
|
||||
$frameSources = [
|
||||
"'self'",
|
||||
'https://js.stripe.com',
|
||||
];
|
||||
|
||||
$imgSources = [
|
||||
"'self'",
|
||||
'data:',
|
||||
'blob:',
|
||||
'https:',
|
||||
];
|
||||
|
||||
$fontSources = [
|
||||
"'self'",
|
||||
'data:',
|
||||
'https:',
|
||||
];
|
||||
|
||||
$mediaSources = [
|
||||
"'self'",
|
||||
'data:',
|
||||
'blob:',
|
||||
'https:',
|
||||
];
|
||||
|
||||
if ($matomoOrigin) {
|
||||
$scriptSources[] = $matomoOrigin;
|
||||
$connectSources[] = $matomoOrigin;
|
||||
$imgSources[] = $matomoOrigin;
|
||||
}
|
||||
|
||||
if (app()->environment(['local', 'development']) || config('app.debug')) {
|
||||
$devHosts = [
|
||||
'http://localhost:5173',
|
||||
'http://127.0.0.1:5173',
|
||||
'https://localhost:5173',
|
||||
'https://127.0.0.1:5173',
|
||||
];
|
||||
$wsHosts = [
|
||||
'ws://localhost:5173',
|
||||
'ws://127.0.0.1:5173',
|
||||
'wss://localhost:5173',
|
||||
'wss://127.0.0.1:5173',
|
||||
];
|
||||
|
||||
$scriptSources = array_merge($scriptSources, $devHosts, ["'unsafe-inline'", "'unsafe-eval'"]);
|
||||
$styleSources = array_merge($styleSources, $devHosts, ["'unsafe-inline'"]);
|
||||
$connectSources = array_merge($connectSources, $devHosts, $wsHosts);
|
||||
$fontSources = array_merge($fontSources, $devHosts);
|
||||
$mediaSources = array_merge($mediaSources, $devHosts);
|
||||
}
|
||||
|
||||
$styleSources[] = 'data:';
|
||||
$connectSources[] = 'https:';
|
||||
$fontSources[] = 'https:';
|
||||
|
||||
$directives = [
|
||||
'default-src' => ["'self'"],
|
||||
'script-src' => array_unique($scriptSources),
|
||||
'style-src' => array_unique($styleSources),
|
||||
'img-src' => array_unique($imgSources),
|
||||
'font-src' => array_unique($fontSources),
|
||||
'connect-src' => array_unique($connectSources),
|
||||
'media-src' => array_unique($mediaSources),
|
||||
'frame-src' => array_unique($frameSources),
|
||||
'form-action' => ["'self'"],
|
||||
'base-uri' => ["'self'"],
|
||||
'object-src' => ["'none'"],
|
||||
];
|
||||
|
||||
$csp = collect($directives)
|
||||
->map(fn ($values, $directive) => $directive.' '.implode(' ', array_filter($values)))
|
||||
->implode('; ');
|
||||
|
||||
$response->headers->set('Content-Security-Policy', $csp);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function normaliseOrigin(?string $url): ?string
|
||||
{
|
||||
if (! $url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parsed = parse_url($url);
|
||||
if (! $parsed || ! isset($parsed['scheme'], $parsed['host'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$origin = strtolower($parsed['scheme'].'://'.$parsed['host']);
|
||||
|
||||
if (isset($parsed['port'])) {
|
||||
$origin .= ':'.$parsed['port'];
|
||||
}
|
||||
|
||||
return $origin;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class StripeCSP
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
$csp = "default-src 'self'; script-src 'self' 'unsafe-inline' https://js.stripe.com https://js.stripe.network; style-src 'self' 'unsafe-inline' data: https:; img-src 'self' data: https: blob:; font-src 'self' data: https:; connect-src 'self' https://api.stripe.com https://api.stripe.network wss://*.stripe.network; media-src 'self' data: blob:; frame-src 'self' https://js.stripe.com; object-src 'none'; base-uri 'self'; form-action 'self';";
|
||||
|
||||
$response->headers->set('Content-Security-Policy', $csp);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user