48 lines
2.8 KiB
PHP
48 lines
2.8 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
// Existing synthetic QA provisioning only. Never change MFA or display QR bytes in tool output.
|
|
require dirname(__DIR__).'/vendor/autoload.php';
|
|
require __DIR__.'/portal-qa-environment.php';
|
|
$target = portalQaEnvironment();
|
|
$app = require dirname(__DIR__).'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
portalQaAssertTarget($target);
|
|
$connection = DB::connection();
|
|
$connection->statement('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
|
try {
|
|
$account = (int) ($argv[1] ?? 18);
|
|
abort_unless(in_array($account, [18, 21], true), 403);
|
|
$name = $account === 18 ? 'investor-mfa-qa' : 'promoter-qa21';
|
|
$local = json_decode(file_get_contents(storage_path('app/private/investmoz-v2/portal-audit/'.($account === 18 ? 'investor-mfa-qa-private.json' : 'profiles-v1-actors-private.json'))), true, 512, JSON_THROW_ON_ERROR);
|
|
if ($account === 21) {
|
|
$local = $local['promoter'];
|
|
}
|
|
$actor = User::findOrFail($account);
|
|
abort_unless($actor->email === $local['email'] && str_ends_with($actor->email, '@example.invalid') && $actor->two_factor_confirmed_at, 403);
|
|
abort_unless(hash_equals(app('encrypter')->decrypt($actor->two_factor_secret), $local['totp_secret'])
|
|
&& Hash::check($local['password'], $actor->password), 409);
|
|
$label = 'QA'.$account;
|
|
$email = htmlspecialchars($actor->email, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
$svg = $actor->twoFactorQrCodeSvg();
|
|
$file = storage_path('app/private/investmoz-v2/portal-audit/'.$name.'-2fas-local.html');
|
|
$html = '<!doctype html><html lang="pt"><meta charset="utf-8"><title>'.$label.' — configuração local 2FAS</title>'
|
|
.'<style>body{font:18px system-ui;max-width:650px;margin:32px auto;padding:16px}svg{width:300px;height:300px}</style>'
|
|
.'<h1>Conta '.$label.' no 2FAS Auth</h1><p>'.$email.' · servidor QA 127.0.0.1:18097</p>'
|
|
.'<p>Este QR recupera a configuração MFA existente. Não altera a conta, o segredo nem a base ativa.</p>'
|
|
.$svg.'<p>No 2FAS: adicionar serviço → ler este QR → identificar a entrada como InvestMoz '.$label.'.</p>'
|
|
.'<p>Ative a data e hora automáticas do telefone. Use o código desta entrada em /two-factor-challenge.</p>'
|
|
.'<p>Ficheiro privado: não o anexe, fotografe ou partilhe nesta conversa. Não remova as entradas antigas do portal.</p></html>';
|
|
file_put_contents($file, $html);
|
|
chmod($file, 0600);
|
|
unset($actor, $svg, $html, $local);
|
|
} finally {
|
|
$connection->statement('ROLLBACK');
|
|
}
|
|
echo json_encode(['prepared' => true, 'local_file' => $file, 'account_id' => $account, 'target' => $target,
|
|
'mfa_changed' => false, 'database_writes' => 0, 'active_writes' => 0, 'qr_or_secret_exported' => false], JSON_THROW_ON_ERROR);
|