Files
compras/integrations/sgf/access/AccessOverlay.php
T
2026-11-02 08:08:05 +02:00

58 lines
3.6 KiB
PHP

<?php
namespace Tempo\Purchasing\Delivery;
/** Builds a review overlay; never writes to the source host. */
final class AccessOverlay
{
public const LABELS = [
'purchasing_requisitions.view' => 'Consultar requisições', 'purchasing_requisitions.prepare' => 'Preparar e submeter requisições',
'purchasing_requisitions.configure' => 'Configurar Compras internas', 'purchasing_requisitions.approve.department_head' => 'Aprovar como responsável de departamento',
'purchasing_requisitions.approve.procurement' => 'Aprovar como responsável de Compras', 'purchasing_requisitions.approve.finance' => 'Aprovar como responsável financeiro',
'purchasing_budget.create' => 'Preparar dotações', 'purchasing_budget.approve' => 'Aprovar dotações', 'purchasing_budget.record' => 'Registar e libertar reservas orçamentais',
'purchasing_budget.consume' => 'Registar consumo orçamental', 'purchasing_budget.view' => 'Consultar orçamento',
'purchasing_budget.reinforcement.propose' => 'Propor reforços orçamentais', 'purchasing_budget.reinforcement.review' => 'Rever reforços orçamentais',
];
public const RIGHTS = [
'purchasing_requisitions.view', 'purchasing_requisitions.prepare', 'purchasing_requisitions.configure',
'purchasing_requisitions.approve.department_head', 'purchasing_requisitions.approve.procurement', 'purchasing_requisitions.approve.finance',
'purchasing_budget.create', 'purchasing_budget.approve', 'purchasing_budget.record', 'purchasing_budget.consume', 'purchasing_budget.view',
'purchasing_budget.reinforcement.propose', 'purchasing_budget.reinforcement.review',
];
public static function prepare(string $host): array
{
$paths = ['app/Modules/Identity/Services/Permissions.php', 'app/Modules/Identity/Services/IdentityWorkflow.php'];
$result = [];
foreach ($paths as $path) {
$source = file_get_contents($host.'/'.$path);
$before = hash('sha256', $source);
if (str_ends_with($path, '/Permissions.php')) {
$source = self::replace($source, "public const UNIT = ['unit.view', 'unit.edit'];", 'public const UNIT = '.var_export(['unit.view', 'unit.edit', ...self::RIGHTS], true).';');
} else {
// Expanding a catalogue must never expand creation-time grants.
$source = self::replace($source, 'foreach (Permissions::UNIT as $permission)', "foreach (['unit.view', 'unit.edit'] as \$permission)");
$source = self::replace($source,
"if (\$unit && \$data['permission'] === 'unit.edit')",
"if (\$unit && \$data['permission'] !== 'unit.view')");
$source = self::replace($source,
"if (\$this->permissions->has(\$target->id, \$tenant, \$company, 'unit.edit', \$unit)) { \$this->permissions->require(\$actor, \$tenant, \$company, 'unit.edit', \$unit); }",
"foreach ((clone \$query)->where('unit_id', \$unit)->whereIn('permission', Permissions::UNIT)->get() as \$existing) { \$this->permissions->require(\$actor, \$tenant, \$company, \$existing->permission, \$unit); }");
}
$result[$path] = ['before_sha256' => $before, 'after_sha256' => hash('sha256', $source), 'contents' => $source];
}
return $result;
}
private static function replace(string $source, string $old, string $new): string
{
if (substr_count($source, $old) !== 1) {
throw new \RuntimeException('Host divergiu: reconciliar a alteração de identidade antes de preparar a entrega.');
}
return str_replace($old, $new, $source);
}
}