75 lines
5.1 KiB
Python
75 lines
5.1 KiB
Python
from pathlib import Path
|
|
import hashlib, json
|
|
|
|
R = Path(__file__).resolve().parent.parent
|
|
P = R / 'storage/app/private/investmoz-v2/portal-audit'
|
|
load = lambda p: json.loads(p.read_text(encoding='utf-8-sig'))
|
|
sha = lambda p: hashlib.sha256(p.read_bytes()).hexdigest()
|
|
candidate = load(P / 'consolidated-candidate-latest.json')
|
|
O = Path(candidate['output'])
|
|
manifest = load(O / 'manifesto.json')
|
|
assert manifest['revision'] == candidate['revision']
|
|
assert sha(O / 'manifesto.json') == candidate['manifest_sha256']
|
|
assert sha(O / candidate['zip']) == candidate['zip_sha256'] == manifest['zip_sha256']
|
|
assert manifest['code_files'] == 22 and manifest['production_assets'] == 159
|
|
for relative, expected in manifest['base_hashes'].items():
|
|
path = R / relative
|
|
assert (sha(path) if path.is_file() else None) == expected, relative
|
|
for relative, expected in manifest['unchanged_dependencies'].items():
|
|
assert sha(R / relative) == expected, relative
|
|
snapshot = load(P / 'workflow-execution-services-latest.json')
|
|
assert snapshot['active_services_changes'] == snapshot['active_acl_changes'] == 0
|
|
scope = {
|
|
'code_revision': manifest['code_revision'],
|
|
'active_base_hashes': manifest['base_hashes'],
|
|
'production_manifest_sha256': manifest['production_manifest_sha256'],
|
|
'candidate': candidate,
|
|
'resources_fingerprint': manifest['resources_fingerprint'],
|
|
'profile_fingerprint': manifest['profile_fingerprint'],
|
|
'services_snapshot': snapshot,
|
|
}
|
|
(P / 'workflow-execution-backup-scope.json').write_text(json.dumps(scope, indent=2), encoding='utf-8')
|
|
source = (R / 'tools/portal-workflow-upgrade-target-backup.php').read_text(encoding='utf-8')
|
|
source = source.replace('workflow-upgrade-target-backup-failure-20261011.json', 'workflow-execution-target-backup-failure.json')
|
|
source = source.replace('workflow-upgrade-code-20261011.json', 'workflow-execution-backup-scope.json')
|
|
source = source.replace("$c['driver'] === 'pgsql' && empty($c['url'])", "$c['driver'] === 'pgsql' && empty($c['url']) && in_array($c['host'], ['127.0.0.1','localhost'], true)")
|
|
source = source.replace("'workflow_target_'", "'workflow_consolidated_target_'").replace('^workflow_target_', '^workflow_consolidated_target_')
|
|
source = source.replace('workflow-target-backups/', 'workflow-execution-target-backups/')
|
|
source = source.replace('workflow-upgrade-target-backup-link-20261011.json', 'workflow-execution-target-backup-link.json')
|
|
source = source.replace('workflow-upgrade-target-backup-20261011.json', 'workflow-execution-target-backup.json')
|
|
needle = '$zip->close();'
|
|
addition = '''// Service assets are copied from an independent READ ONLY OS snapshot.
|
|
$services=$scope['services_snapshot'];
|
|
foreach($services['files'] as $serviceFile){
|
|
$name='services/'.$serviceFile['entry'];
|
|
$servicePath=$services['directory'].'/'.str_replace('\\\\','/',$serviceFile['entry']);
|
|
throw_unless(is_file($servicePath)&&hash_file('sha256',$servicePath)===$serviceFile['sha256'],RuntimeException::class,'Private service snapshot differs.');
|
|
if($serviceFile['source']!==null){throw_unless(is_file($serviceFile['source'])&&hash_file('sha256',$serviceFile['source'])===$serviceFile['sha256'],RuntimeException::class,'Service recovery source changed after snapshot.');}
|
|
$zip->addFile($servicePath,$name);$fileHashes[$name]=$serviceFile['sha256'];
|
|
}
|
|
$servicesMetadata=$services['directory'].'/snapshot.json';
|
|
$zip->addFile($servicesMetadata,'services/snapshot.json');$fileHashes['services/snapshot.json']=hash_file('sha256',$servicesMetadata);
|
|
'''
|
|
assert source.count(needle) == 1
|
|
source = source.replace(needle, addition + needle)
|
|
needle = "$proof['scope']='Read-only backup"
|
|
position = source.index(needle)
|
|
addition = '''$proof['candidate_revision']=$scope['candidate']['revision'];
|
|
$proof['candidate_zip_sha256']=$scope['candidate']['zip_sha256'];
|
|
$proof['candidate_manifest_sha256']=$scope['candidate']['manifest_sha256'];
|
|
$proof['resources_fingerprint']=$scope['resources_fingerprint'];$proof['profile_fingerprint']=$scope['profile_fingerprint'];
|
|
$proof['services_snapshot_at']=$services['at'];
|
|
$proof['service_asset_count']=count($services['files'])+1;
|
|
$proof['service_task_export_count']=count(array_filter($services['tasks'],fn($task)=>$task['exists']));
|
|
$proof['ACL_metadata_exported']=$services['acl_objects'];$proof['ACL_metadata_roundtrip']=$services['acl_metadata_roundtrip'];
|
|
$proof['ACL_restore_applied_on_destination']=false;
|
|
$proof['ACL_scope']=$services['acl_scope'];
|
|
$proof['signature_file_count']=$services['signature_files'];
|
|
$proof['source_files_stable_at_snapshot_completion']=true;
|
|
$proof['no_new_active_accounts_or_grants']=true;
|
|
'''
|
|
source = source[:position] + addition + source[position:]
|
|
source = source.replace("'files' => count($fileHashes),", "'files' => count($fileHashes), 'sequences' => count($sequences), 'MFA_readable' => $mfaReadable, 'originals' => count($documents), 'candidate' => substr($scope['candidate']['revision'],0,12),")
|
|
(R / 'tools/portal-workflow-execution-target-backup.php').write_text(source, encoding='utf-8')
|
|
print(json.dumps({'result': 'renewal_prepared', 'candidate': manifest['revision'][:12], 'code_revision': manifest['code_revision'][:12], 'service_assets': len(snapshot['files']) + 1, 'active_writes': 0}))
|