fix: complete InvesteMoz validation and error corrections

This commit is contained in:
2026-10-29 21:47:33 +02:00
parent 63147312e2
commit daf8623441
718 changed files with 127308 additions and 20959 deletions
@@ -12,7 +12,7 @@ class EnsureDocumentManagement
public function handle(Request $request, Closure $next): Response
{
$actor = $request->user();
abort_unless($actor, 403);
abort_unless($actor !== null, 403);
$access = app(DocumentManagementAccess::class);
if ($access->full($actor)) {
return $next($request); // Preserve existing management controls.
@@ -1,37 +1,50 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/** Environment reservation is not a portal identity or a business permission. */
class EnsureHomologationAccess
{
public function handle(Request $request, Closure $next): Response
{
if (!config('portal.homologation_only', false)) return $next($request);
$mode=config('portal.homologation_access_mode', 'reviewers');
if ($mode==='perimeter') {
// Set only by the trusted server after its environment-access check.
// A client HTTP_INVESTMOZ_* header is deliberately not accepted.
abort_unless($request->server('INVESTMOZ_HOMOLOGATION_ACCESS')==='granted',403,
'Este ambiente de homologação tem acesso reservado à Tempo.');
$response=$next($request);
$response->headers->set('Cache-Control','private, no-store');
$response->headers->set('X-Robots-Tag','noindex, nofollow, noarchive');
return $response;
}
abort_unless($mode==='reviewers',503,'A reserva da homologação não está configurada.');
// Compatibility until an independent perimeter is explicitly configured.
if ($request->is('login','logout','register','forgot-password','reset-password','reset-password/*',
'email/verify','email/verify/*','email/verification-notification','two-factor-challenge',
'user/confirm-password','user/confirmed-password-status','user/two-factor-*',
'settings/security','settings/password','user/password','passkeys/*','user/passkeys/*','up')) return $next($request);
$actor=$request->user();
if (!$actor) return $request->expectsJson()
? response()->json(['message'=>'Entre na conta autorizada para homologação.'],401)
: redirect()->guest(route('login'));
abort_unless($actor->hasVerifiedEmail() && in_array(strtolower(trim($actor->email)),config('portal.homologation_reviewers',[]),true),403,
'Esta homologação está reservada às contas indicadas pela Tempo.');
$response=$next($request);$response->headers->set('Cache-Control','private, no-store');return $response;
}
public function handle(Request $request, Closure $next): Response
{
if (! config('portal.homologation_only', false)) {
return $next($request);
}
$mode = config('portal.homologation_access_mode', 'reviewers');
if ($mode === 'perimeter') {
// Set only by the trusted server after its environment-access check.
// A client HTTP_INVESTMOZ_* header is deliberately not accepted.
abort_unless($request->server('INVESTMOZ_HOMOLOGATION_ACCESS') === 'granted', 403,
'Este ambiente de homologação tem acesso reservado à Tempo.');
$response = $next($request);
$response->headers->set('Cache-Control', 'private, no-store');
$response->headers->set('X-Robots-Tag', 'noindex, nofollow, noarchive');
return $response;
}
abort_unless($mode === 'reviewers', 503, 'A reserva da homologação não está configurada.');
// Compatibility until an independent perimeter is explicitly configured.
if ($request->is('login', 'logout', 'register', 'forgot-password', 'reset-password', 'reset-password/*',
'email/verify', 'email/verify/*', 'email/verification-notification', 'two-factor-challenge',
'user/confirm-password', 'user/confirmed-password-status', 'user/two-factor-*',
'settings/security', 'settings/password', 'user/password', 'passkeys/*', 'user/passkeys/*', 'up')) {
return $next($request);
}
$actor = $request->user();
if (! $actor) {
return $request->expectsJson()
? response()->json(['message' => 'Entre na conta autorizada para homologação.'], 401)
: redirect()->guest(route('login'));
}
abort_unless($actor->hasVerifiedEmail() && in_array(strtolower(trim($actor->email)), config('portal.homologation_reviewers', []), true), 403,
'Esta homologação está reservada às contas indicadas pela Tempo.');
$response = $next($request);
$response->headers->set('Cache-Control', 'private, no-store');
return $response;
}
}
@@ -1,15 +1,19 @@
<?php
namespace App\Http\Middleware;
use App\Services\DocumentManagementAccess;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureScopedEditorial
{
public function handle(Request $request, Closure $next): Response
{
$actor = $request->user();
abort_unless($actor, 403);
$access = app(\App\Services\DocumentManagementAccess::class);
abort_unless($actor !== null, 403);
$access = app(DocumentManagementAccess::class);
if ($access->full($actor)) {
return $next($request);
}
@@ -20,6 +24,7 @@ class EnsureScopedEditorial
if ($request->route('id') !== null) {
$access->assertEditableResource($actor, (int) $request->route('id'));
}
return $next($request);
}
}
@@ -64,6 +64,7 @@ final class EnsureSimulationRecoveryCookie
false,
'lax',
));
return $response;
}
+15 -11
View File
@@ -3,10 +3,13 @@
namespace App\Http\Middleware;
use App\Models\ExistingBusiness;
use App\Services\ContentManagement;
use App\Services\InvestorPath;
use App\Services\LegacyContent;
use App\Services\OpportunityManagement;
use App\Services\PortalProfileAccess;
use App\Services\Procurement;
use App\Services\ScopedPortalAdministration;
use Illuminate\Http\Request;
use Inertia\Middleware;
@@ -48,15 +51,16 @@ class HandleInertiaRequests extends Middleware
*/
public function share(Request $request): array
{
$contentManagementAccess=app(\App\Services\ContentManagement::class)->access($request->user());
$contentManagementAccess = app(ContentManagement::class)->access($request->user());
return [
...parent::share($request),
'name' => config('app.name'),
'procurementEnabled' => app(\App\Services\Procurement::class)->available(),
'procurementEnabled' => app(Procurement::class)->available(),
'homologationReserved' => (bool) config('portal.homologation_only', false),
'profileAccess' => app(PortalProfileAccess::class)->navigation($request->user()),
'contentManagementHref' => $contentManagementAccess !== [] ? '/gestao-conteudos' : null,
'contentManagementAccess'=>$contentManagementAccess,
'contentManagementAccess' => $contentManagementAccess,
'portalOperator' => config('portal.operator_name'),
'portalInstitutionalContactEmail' => filter_var(config('portal.institutional_contact_email'), FILTER_VALIDATE_EMAIL) ?: null,
'legacyContentEnabled' => app(LegacyContent::class)->available(),
@@ -73,14 +77,14 @@ class HandleInertiaRequests extends Middleware
'installationHref' => '/management/opportunity-management-install',
] : null,
'flash' => ['success' => fn () => $request->session()->get('success'), 'warning' => fn () => $request->session()->get('warning'), 'uploaded_document_id' => fn () => $request->session()->get('uploaded_document_id')],
'scopedAdministration' => $request->user() && app(\App\Services\ScopedPortalAdministration::class)->administrative($request->user()) ? [
['label'=>'Zonas e parques','href'=>'/management/investment/zones'],
['label'=>'Referências económicas','href'=>'/management/economic-references/lot-01'],
['label'=>'Referências e formulários','href'=>'/management/investment/references'],
['label'=>'Educação e competências','href'=>'/management/education'],
['label'=>'Interesses atribuídos','href'=>'/management/interests'],
['label'=>'Atribuições de interesses','href'=>'/management/interests/atribuicoes'],
['label'=>'Atribuições do Balcão','href'=>'/management/balcao/atribuicoes'],
'scopedAdministration' => $request->user() && app(ScopedPortalAdministration::class)->administrative($request->user()) ? [
['label' => 'Zonas e parques', 'href' => '/management/investment/zones'],
['label' => 'Referências económicas', 'href' => '/management/economic-references/lot-01'],
['label' => 'Referências e formulários', 'href' => '/management/investment/references'],
['label' => 'Educação e competências', 'href' => '/management/education'],
['label' => 'Interesses atribuídos', 'href' => '/management/interests'],
['label' => 'Atribuições de interesses', 'href' => '/management/interests/atribuicoes'],
['label' => 'Atribuições do Balcão', 'href' => '/management/balcao/atribuicoes'],
] : [],
'auth' => [
'user' => $request->user(),
+6 -2
View File
@@ -13,15 +13,19 @@ class PortalProfileSession
{
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()) {
return $next($request);
}
$access = app(PortalProfileAccess::class);
$available = $access->governanceAvailable();
if ($request->user() && (config('portal.profile_access_enabled') || config('portal.profile_administration_enabled')) && ! $available) {
if ((config('portal.profile_access_enabled') || config('portal.profile_administration_enabled')) && ! $available) {
$recovery = $request->is('management/profile-install', 'management/profile-install/bootstrap')
|| ($request->is('logout') && $request->isMethod('POST'));
abort_if(! in_array($request->method(), ['GET', 'HEAD', 'OPTIONS'], true) && ! $recovery, 503,
'Autorizações indisponíveis: recuperar a consistência dos perfis antes de gravar. Pode terminar a sessão ou consultar o instalador.');
}
if ($request->user() && $available) {
if ($available) {
$revision = (int) DB::table('users')->where('id', $request->user()->id)->value('portal_access_revision');
$bound = $request->session()->get('portal.access_revision');
if (($bound === null && $revision > 0) || ($bound !== null && (int) $bound !== $revision)) {
@@ -13,15 +13,22 @@ class VerifiedOrSimpleParticipation
{
public function handle(Request $request, Closure $next): Response
{
if ($request->user()->hasVerifiedEmail()) return $next($request);
if ($request->isMethod('GET') && $request->is('investidor/manifestacoes', 'api/v1/investment/submissions')) return $next($request);
if ($request->user()->hasVerifiedEmail()) {
return $next($request);
}
if ($request->isMethod('GET') && $request->is('investidor/manifestacoes', 'api/v1/investment/submissions')) {
return $next($request);
}
$id = $request->route('id');
$show = $request->isMethod('GET') && $request->is('investidor/manifestacoes/'.$id, 'api/v1/investment/submissions/'.$id);
$message = $request->isMethod('POST') && $request->is('investidor/manifestacoes/'.$id.'/messages', 'api/v1/investment/submissions/'.$id.'/messages');
if ($id && ($show || $message)) {
$row = InvestmentSubmission::whereKey($id)->where('user_id', $request->user()->id)->firstOrFail();
if ($row->status === 'submitted' && ($row->payload['simple_participation'] ?? false) === true) return $next($request);
if ($row->status === 'submitted' && ($row->payload['simple_participation'] ?? false) === true) {
return $next($request);
}
}
return app(EnsureEmailIsVerified::class)->handle($request, $next);
}
}