Files
Investidor-V2/tools/portal-owner-administration-preflight.php
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

125 lines
9.3 KiB
PHP

<?php
// Guarded READ ONLY destination check for the specifically authorized fixed revision.
ini_set('zend.exception_ignore_args', '1');
$root = dirname(__DIR__);
$out = $root.'/outputs/investmoz-internal-designation-20261011';
$private = $root.'/storage/app/private/investmoz-v2/portal-audit';
set_exception_handler(static function (Throwable $e) use ($out): void {
file_put_contents($out.'/disponibilizacao-preflight.json', json_encode([
'result'=>'stopped_before_application', 'at'=>gmdate('c'), 'type'=>$e::class,
'file'=>basename($e->getFile()), 'line'=>$e->getLine(), 'active_writes'=>0,
'reason'=>$e instanceof LogicException ? $e->getMessage() : 'Sanitized technical failure; no application performed.',
], JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
fwrite(STDERR, 'Preflight stopped before application; sanitized evidence retained.'.PHP_EOL);
exit(1);
});
$read = static fn ($path) => json_decode(file_get_contents($path), true, flags: JSON_THROW_ON_ERROR);
$require = static function (bool $test, string $message): void {
if (! $test) throw new LogicException($message);
};
$m = $read($out.'/manifesto.json');
$final = $read($out.'/verificacao-final.json');
$link = $read($private.'/internal-designation-target-backup-link.json');
$b = $read($link['path']);
$require($final['revision']==='2876cdfa900a' && $m['delta_sha256']===$final['code_revision'], 'Candidate correspondence differs.');
$require(hash_file('sha256',$out.'/manifesto.json')===$final['manifest_sha256']
&& hash_file('sha256',$out.'/'.$m['package'])===$final['package_sha256'], 'Package or manifest differs.');
$require($b['candidate_code_revision']===$m['delta_sha256'] && $b['candidate_zip_sha256']===$final['package_sha256']
&& $b['candidate_manifest_sha256']===$final['manifest_sha256'], 'Backup belongs to a different revision.');
$require($b['restore_verified'] && $b['files_verified'] && $b['sequences_verified']
&& empty($b['missing_files']), 'Restore evidence incomplete.');
// The backup receipt covers restoration only; QA/deployment reversal are distinct receipts.
$rehearsal=$read($out.'/ensaio-disponibilizacao-reversao.json');
$browser=$read($out.'/navegador-qa.json');
$require($final['QA_code_deployment_and_reversal_passed'] && $rehearsal['result']==='passed'
&& $browser['result']==='passed' && $browser['code_revision']===$m['delta_sha256'], 'Fixed QA or deployment-reversal evidence incomplete.');
$require(time()<strtotime($b['valid_until']), 'Backup recorded validity expired.');
$require(hash_file('sha256',$b['dump_path'])===$b['dump_sha256']
&& hash_file('sha256',$b['files_path'])===$b['files_sha256'], 'Protected backup artifact differs.');
foreach ($m['files'] as $item) {
$path=$root.'/'.$item['path'];
$require((is_file($path)?hash_file('sha256',$path):null)===$item['before_sha256'], 'Prepared destination baseline differs: '.$item['path']);
}
$require(hash_file('sha256',$root.'/public/build/manifest.json')===$b['active_manifest_sha256'], 'Active bundle baseline differs.');
$require(hash_file('sha256',$root.'/.env')===$b['active_files_manifest']['runtime/.env'], 'Destination configuration changed since backup.');
foreach ($b['active_files_manifest'] as $entry=>$hash) {
if (str_starts_with($entry,'private/')) {
$require(hash_file('sha256',$root.'/storage/app/'.$entry)===$hash, 'An original private document changed.');
}
}
require $root.'/vendor/autoload.php';
$app = require $root.'/bootstrap/app.php';
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);
(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
$c=config('database.connections.'.config('database.default'));
$require($c['driver']==='pgsql' && empty($c['url']) && in_array($c['host'],['127.0.0.1','localhost'],true)
&& (int)$c['port']===5432 && $c['database']==='investemoz', 'Effective destination refused.');
$require(config('app.url')==='https://portal-investidor.test'
&& config('portal.organization_country_enabled') && ! config('portal.profile_access_enabled')
&& ! config('portal.opportunity_dashboard_access_enabled') && ! config('portal.opportunity_specialized_drafts_enabled')
&& ! config('portal.profile_administration_enabled',false)
&& ! config('portal.notifications_enabled') && ! config('portal.opportunity_modular_profiles_qa_enabled')
&& ! config('portal.legacy_content_qa_enabled') && ! config('investor-path.enabled'), 'Destination flags do not match authorized scope.');
$require(! $app->configurationIsCached(), 'Cached configuration differs from rehearsed procedure.');
$pdo=new PDO('pgsql:host='.$c['host'].';port='.$c['port'].';dbname='.$c['database'],$c['username'],$c['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
$pdo->exec('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
$require($pdo->query('SELECT current_database()')->fetchColumn()==='investemoz'
&& (int)$pdo->query('SELECT inet_server_port()')->fetchColumn()===5432
&& $pdo->query("SELECT current_setting('transaction_read_only')")->fetchColumn()==='on', 'Actual destination identity refused.');
$actualTables=$pdo->query("SELECT schemaname,tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,tablename")->fetchAll(PDO::FETCH_ASSOC);
$changed=[];
foreach ($actualTables as $table) {
$name='"'.str_replace('"','""',$table['schemaname']).'"."'.str_replace('"','""',$table['tablename']).'"';
$rows=$pdo->query('SELECT row_to_json(t)::text FROM '.$name.' t ORDER BY row_to_json(t)::text')->fetchAll(PDO::FETCH_COLUMN);
if (!isset($b['tables'][$name]) || $b['tables'][$name]!==['count'=>count($rows),'sha256'=>hash('sha256',implode("\n",$rows))]) $changed[]=$name;
unset($rows);
}
$require(count($actualTables)===count($b['tables']), 'Destination schema changed since verified restore.');
$volatile=['"public"."cache"','"public"."cache_locks"','"public"."sessions"'];
$durableChanged=array_values(array_diff($changed,$volatile));
$require($durableChanged===[], 'Permanent destination data changed since backup; renew before release: '.implode(', ',$durableChanged));
$sequences=$pdo->query("SELECT * FROM pg_sequences WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,sequencename")->fetchAll(PDO::FETCH_ASSOC);
$require($sequences===$b['sequences'], 'Destination sequences changed since backup.');
$counts=[];
foreach(['users','organizations','memberships','portal_role_assignments','portal_profile_requests','jobs','failed_jobs'] as $table) {
$counts[$table]=(int)$pdo->query('SELECT count(*) FROM "'.$table.'"')->fetchColumn();
}
$pair=$pdo->query('SELECT first_user_id,second_user_id,version FROM portal_superuser_pair WHERE id=1')->fetch(PDO::FETCH_ASSOC);
$require(array_map('intval',[$pair['first_user_id'],$pair['second_user_id']])===[13,2], 'Initialized owner pair differs.');
$require((int)$pdo->query("SELECT count(*) FROM migrations WHERE migration IN ('2026_10_09_030000_extend_opportunity_resources','2026_10_10_040000_create_portal_profile_authorization','2026_10_10_050000_extend_organization_country')")->fetchColumn()===3, 'Existing prerequisite installations differ.');
$mfaReadable=0;
$keys=array_values(array_unique(array_filter([config('app.key'),...config('app.previous_keys',[])])));
foreach($pdo->query('SELECT two_factor_secret,two_factor_recovery_codes FROM users WHERE two_factor_secret IS NOT NULL')->fetchAll(PDO::FETCH_ASSOC) as $record) {
foreach(['two_factor_secret','two_factor_recovery_codes'] as $field) {
if($record[$field]===null) continue;
$readable=false;
foreach($keys as $key) {
try {
$crypt=new Illuminate\Encryption\Encrypter(str_starts_with($key,'base64:')?base64_decode(substr($key,7),true):$key,config('app.cipher'));
$plain=$crypt->decrypt($record[$field],false);
$readable=is_string($plain)&&$plain!=='';
unset($plain);
if($readable) break;
} catch(Illuminate\Contracts\Encryption\DecryptException) {}
}
$require($readable,'Existing encrypted MFA envelope unreadable.');
}
$mfaReadable++;
}
$pdo->exec('ROLLBACK');
$proof=[
'result'=>'ready_for_authorized_code_and_administration_only', 'at'=>gmdate('c'),
'revision'=>'2876cdfa900a','manifest_sha256'=>$final['manifest_sha256'],'package_sha256'=>$final['package_sha256'],
'database'=>'investemoz','port'=>5432,'read_only'=>true,'tables_unchanged'=>count($actualTables),
'sequences_unchanged'=>count($sequences),'counts'=>$counts,'pair'=>$pair,'encrypted_MFA_readable'=>$mfaReadable,
'backup_archive_hashes_verified'=>true,'backup_revision_corresponds'=>true,'backup_permanent_source_data_unchanged'=>true,
'volatile_tables_changed_since_snapshot'=>$changed,'permanent_tables_changed_since_snapshot'=>$durableChanged,
'backup_valid_until'=>$b['valid_until'],'existing_originals_unchanged'=>true,'source_hashes_match'=>true,
'COUNTRY02'=>true,'workflow'=>false,'administration_before'=>false,
'active_writes'=>0,'no_migration_required'=>true,'no_service_change_required'=>true,
'authorization'=>'User explicitly confirmed fixed revision 2876cdfa900a administration-only release in this turn',
];
file_put_contents($out.'/disponibilizacao-preflight.json',json_encode($proof,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR).PHP_EOL);
echo json_encode($proof,JSON_THROW_ON_ERROR).PHP_EOL;