82 lines
6.4 KiB
PHP
82 lines
6.4 KiB
PHP
<?php
|
|
|
|
// Evaluation entry exists only on the local synthetic product host.
|
|
use App\Models\User;
|
|
use App\Shared\Testing\TestingDatabaseGuard;
|
|
use App\Shared\Context\CompanyContext;
|
|
use App\Modules\Identity\Services\Audit;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Route;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
$demoManifest = static function (Request $request): array {
|
|
abort_unless(app()->environment('browser-testing')
|
|
&& in_array($request->ip(), ['127.0.0.1', '::1'], true)
|
|
&& in_array($request->getHost(), ['127.0.0.1', 'localhost', '::1'], true), 404);
|
|
TestingDatabaseGuard::assertSafe();
|
|
$manifest = json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'), true, 64, JSON_THROW_ON_ERROR);
|
|
abort_unless(($manifest['product_demo']['version'] ?? '') === 'SGF-PRODUCT-DEMO-v1'
|
|
&& ($manifest['product_demo']['synthetic_only'] ?? false) === true, 404);
|
|
return $manifest;
|
|
};
|
|
|
|
Route::get('/avaliar', function (Request $request) use ($demoManifest) {
|
|
$demoManifest($request);
|
|
$csrf = htmlspecialchars(csrf_token(), ENT_QUOTES, 'UTF-8');
|
|
$html = '<!doctype html><html lang="pt"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Explorar SGF Tempo</title><style>body{font:18px system-ui;background:#f4f6f5;color:#183d35;margin:0}main{max-width:960px;margin:auto;padding:30px}section{background:white;padding:24px;border-radius:12px;margin:20px 0}button,a{font:inherit}button{padding:14px;border:0;border-radius:8px;background:#125a4b;color:white;cursor:pointer}form{display:inline-block;margin:8px}a{color:#125a4b}</style><main><h1>Explorar SGF Tempo</h1><p>Entrada de avaliação local, sem palavra-passe. Podes trocar de função sempre que quiseres, sem pedir concessões.</p><section><h2>Aplicação integrada</h2><p>Clientes, serviços, documentação, facturas de teste, recebimentos simulados, oficina, custos e orçamento industrial.</p>';
|
|
foreach (['proprietario' => 'Entrar como proprietário — todas as áreas desta instalação', 'preparador' => 'Preparar e gerir', 'validador' => 'Rever e validar', 'tecnico' => 'Registar trabalho de oficina', 'verificador' => 'Verificar trabalho de oficina'] as $role => $label) {
|
|
$html .= '<form method="post" action="/avaliar/entrar"><input type="hidden" name="_token" value="'.$csrf.'"><input type="hidden" name="role" value="'.$role.'"><button>'.$label.'</button></form>';
|
|
}
|
|
$html .= '</section><section><h2>Percursos em instalações separadas</h2><p><a href="http://127.0.0.1:8003">Controlo orçamental de Compras</a></p><p><a href="http://127.0.0.1:8006/demo">Obrigações a fornecedores no SGF</a></p><p><a href="http://127.0.0.1:8005/demo">Compras autónomo</a></p></section><p>Dados sintéticos persistentes e partilhados. A troca de função conserva os exemplos e o histórico. Esta entrada não concede administração da instalação real nem elimina as regras dos processos.</p></main></html>';
|
|
return response($html)->header('Cache-Control', 'no-store')->header('X-Robots-Tag', 'noindex, nofollow');
|
|
});
|
|
|
|
Route::post('/avaliar/entrar', function (Request $request) use ($demoManifest) {
|
|
$manifest = $demoManifest($request);
|
|
$data = $request->validate(['role' => 'required|in:proprietario,preparador,validador,tecnico,verificador']);
|
|
abort_if(array_diff(array_keys($request->all()), ['role', '_token']) !== [], 422);
|
|
if ($data['role'] === 'proprietario') {
|
|
$user = DB::transaction(function () use ($manifest) {
|
|
// Serialise provisioning, including first entry. Never touch customer accounts.
|
|
DB::select('select pg_advisory_xact_lock(?)', [80202910]);
|
|
$owner = User::firstOrCreate(['email' => 'proprietario-avaliacao@example.test'],
|
|
['name' => 'Proprietário — avaliação local', 'password' => bin2hex(random_bytes(32))]);
|
|
$actors = array_values($manifest['product_demo']['actors']);
|
|
$memberships = DB::table('company_memberships')->whereIn('user_id', $actors)->get(['tenant_id', 'company_id'])->unique('company_id');
|
|
$added = 0;
|
|
foreach ($memberships as $membership) {
|
|
DB::table('company_memberships')->insertOrIgnore(['user_id' => $owner->id, 'tenant_id' => $membership->tenant_id, 'company_id' => $membership->company_id]);
|
|
}
|
|
$grants = DB::table('permission_grants')->whereIn('user_id', $actors)->get(['tenant_id', 'company_id', 'permission', 'unit_id']);
|
|
foreach (['view', 'propose', 'approve'] as $action) {
|
|
$grants->push((object) ['tenant_id' => $manifest['tenant'], 'company_id' => $manifest['company'], 'permission' => 'catalogue_accounting.'.$action, 'unit_id' => null]);
|
|
}
|
|
foreach ($grants as $grant) {
|
|
$scope = ['user_id' => $owner->id, 'tenant_id' => $grant->tenant_id, 'company_id' => $grant->company_id, 'permission' => $grant->permission, 'unit_id' => $grant->unit_id];
|
|
if (!DB::table('permission_grants')->where($scope)->exists()) {
|
|
DB::table('permission_grants')->insert($scope + ['created_at' => now(), 'updated_at' => now()]);
|
|
$added++;
|
|
}
|
|
}
|
|
if ($added) {
|
|
Audit::record('demo.owner_access_prepared', $owner->id, $manifest['tenant'], $manifest['company'], $manifest['unit'], details: ['synthetic' => true, 'grants_added' => $added]);
|
|
}
|
|
return $owner;
|
|
});
|
|
} else {
|
|
$user = User::findOrFail($manifest['product_demo']['actors'][$data['role']]);
|
|
}
|
|
abort_unless(str_ends_with($user->email, '@example.test'), 403);
|
|
app(CompanyContext::class)->selectFor($user->id, $manifest['tenant'], $manifest['company'], $manifest['unit']);
|
|
Auth::logout();
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
Auth::login($user, false);
|
|
$request->session()->regenerate();
|
|
$request->session()->put('auth_version', (int) $user->auth_version);
|
|
$request->session()->put('company_context', ['tenant_id' => (int) $manifest['tenant'], 'company_id' => (int) $manifest['company'], 'unit_id' => (int) $manifest['unit'], 'token' => bin2hex(random_bytes(24))]);
|
|
Audit::record('demo.entered', $user->id, $manifest['tenant'], $manifest['company'], $manifest['unit'], details: ['role' => $data['role'], 'synthetic' => true]);
|
|
return redirect('/');
|
|
})->middleware('throttle:30,1');
|