Files
Investidor-V2/outputs/investmoz-country02-https-20261011/codigo-corrigido.patch
T
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

113 lines
5.1 KiB
Diff

--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -42,6 +42,20 @@
public function boot(): void
{
$this->configureDefaults();
+
+ // Fortify calls this response AFTER invalidating/regenerating the server session.
+ // Clear the client's encryption key too, so Back must reauthorize private pages.
+ $this->app->bind(\Laravel\Fortify\Contracts\LogoutResponse::class, function () {
+ return new class extends \Laravel\Fortify\Http\Responses\LogoutResponse
+ {
+ public function toResponse($request)
+ {
+ \Inertia\Inertia::clearHistory();
+
+ return parent::toResponse($request);
+ }
+ };
+ });
Event::listen(ValidTwoFactorAuthenticationCodeProvided::class, function ($event): void {
// The authenticated installers also operate before the optional profile schema exists.
--- a/app/Http/Controllers/OpportunityResourceController.php
+++ b/app/Http/Controllers/OpportunityResourceController.php
@@ -17,6 +17,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\URL;
+use Illuminate\Support\Str;
use Inertia\Inertia;
use Inertia\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
@@ -41,16 +42,35 @@
public function organizationCountryPreview(Request $request, OrganizationCountryInstallation $service): JsonResponse|Response
{
- $plan = [...$service->preview($request->user()), 'actionUrl' => '/management/opportunity-resources-install/organization-country', 'extension' => true];
+ $plan = $service->preview($request->user());
+ $scope = $this->countryOperationScope($request, $plan);
+ $operation = $request->session()->get('portal.country_installation_operation');
+ if (! is_array($operation) || ($operation['scope'] ?? null) !== $scope) {
+ $operation = ['scope' => $scope, 'key' => (string) Str::uuid()];
+ $request->session()->put('portal.country_installation_operation', $operation);
+ }
+ $plan = [...$plan, 'operationKey' => $operation['key'], 'actionUrl' => '/management/opportunity-resources-install/organization-country', 'extension' => true];
return $request->expectsJson() ? response()->json($plan) : Inertia::render('management/opportunity-install', $plan);
}
public function organizationCountryApply(Request $request, OrganizationCountryInstallation $service): JsonResponse|RedirectResponse
{
+ // Preview authorizes the actor; the UUID remains stable until this scope changes or logout.
+ $plan = $service->preview($request->user());
+ $operation = $request->session()->get('portal.country_installation_operation');
+ $data = $request->validate(['idempotency_key' => 'required|uuid']);
+ abort_unless(is_array($operation) && ($operation['scope'] ?? null) === $this->countryOperationScope($request, $plan)
+ && hash_equals($operation['key'], $data['idempotency_key']), 409, 'A operação não corresponde a esta prévia. Atualize a página antes de tentar novamente.');
$result = $service->apply($request->user(), $request->all());
return $request->expectsJson() ? response()->json($result) : back()->with('success', 'País mestre instalado; nenhuma submissão anterior alterada.');
+ }
+
+ /** @param array<string, mixed> $plan */
+ private function countryOperationScope(Request $request, array $plan): string
+ {
+ return hash('sha256', json_encode([$request->user()->getAuthIdentifier(), $plan['target'], $plan['migration'], $plan['code_hash']], JSON_THROW_ON_ERROR));
}
public function index(int $id, OpportunityResources $service): Response
--- a/resources/js/pages/management/opportunity-install.tsx
+++ b/resources/js/pages/management/opportunity-install.tsx
@@ -12,6 +12,7 @@
actionUrl = '/management/opportunity-management-install',
imageProcessingAvailable,
extension = false,
+ operationKey,
}: {
installed: boolean;
ready: boolean;
@@ -24,11 +25,12 @@
actionUrl?: string;
imageProcessingAvailable?: boolean;
extension?: boolean;
+ operationKey?: string;
}) {
const form = useForm({
hash,
confirm: false,
- idempotency_key: crypto.randomUUID(),
+ idempotency_key: operationKey ?? '',
reason: '',
});
return (
@@ -113,6 +115,7 @@
installed ||
!ready ||
!form.data.confirm ||
+ (extension && !operationKey) ||
form.processing
}
>
@@ -120,6 +123,9 @@
? 'Aplicar extensão do país'
: 'Aplicar plano de gestão'}
</button>
+ {extension && !operationKey && (
+ <p role="alert">Atualize a prévia para obter a chave desta operação.</p>
+ )}
{!ready && (
<p>
Aplicação indisponível até comprovar QA,