mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 09:05:08 +00:00
6086821d6c
#1680 fixed the redirect rendered from an exception and said plainly what it did not cover: EnsureSetupIsComplete, EnsureAccountIsActive and EnforceTwoFactor answer before HandleInertiaRequests is ever entered, so a response they return never unwinds through Inertia's 302 to 303 upgrade either. Same 405, reached a different way — an account deactivated while its owner was part-way through a form, or one being made to enrol in two-factor. The rule now lives in one place rather than four. Three copies of "if the method is PUT, PATCH or DELETE" is how the fourth caller gets it wrong, and WriteSafeRedirect can carry the explanation of why 303 — which is worth more than the three lines it replaces, because nothing about a bare setStatusCode call says what a browser does with a 302. PUT /timezone is the route the setup test uses: it is one of only two writes a guest can reach and the only one that middleware does not exempt, so the case is real rather than defensive. All three new tests were run against the unfixed middleware and fail there. Extends the work of @denkfabrik-li, who found the gap and wrote it down.
55 lines
1.8 KiB
PHP
55 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Identity\Http\Middleware;
|
|
|
|
use App\Modules\Identity\TwoFactor\TwoFactorEnforcement;
|
|
use App\Modules\Platform\Settings\Setting;
|
|
use App\Modules\Platform\Settings\Settings;
|
|
use App\Support\WriteSafeRedirect;
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
/**
|
|
* When the installation enforces two-factor authentication for the
|
|
* user's type, an un-enrolled user can only reach the 2FA setup screen
|
|
* (and the exits: logout, locale) until they enable it.
|
|
*/
|
|
class EnforceTwoFactor
|
|
{
|
|
public function __construct(
|
|
private readonly Settings $settings,
|
|
) {}
|
|
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$user = $request->user();
|
|
|
|
if ($user === null || $user->hasTwoFactorEnabled()) {
|
|
return $next($request);
|
|
}
|
|
|
|
$value = $this->settings->get(Setting::TwoFactorEnforcement);
|
|
|
|
$enforcement = (is_string($value) ? TwoFactorEnforcement::tryFrom($value) : null)
|
|
?? TwoFactorEnforcement::None;
|
|
|
|
if (! $enforcement->appliesTo($user->type)) {
|
|
return $next($request);
|
|
}
|
|
|
|
// password.confirm is on this list because the two-factor mutation
|
|
// routes now require it: without the exemption, enrolling would
|
|
// redirect to the confirm-password screen, which this middleware
|
|
// would redirect straight back to two-factor.show — a loop that
|
|
// locks the user out of the only exit.
|
|
if ($request->routeIs('two-factor.*', 'password.confirm', 'logout', 'locale.update')) {
|
|
return $next($request);
|
|
}
|
|
|
|
return WriteSafeRedirect::apply($request, redirect()->route('two-factor.show')->with('two_factor_enforced_notice', true));
|
|
}
|
|
}
|