Preserva base local aceite de identidade e acessos
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Context;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
trait BelongsToCompany
|
||||
{
|
||||
protected static function bootBelongsToCompany(): void
|
||||
{
|
||||
static::addGlobalScope('company', function (Builder $query): void {
|
||||
foreach (app(CompanyContext::class)->identifiers() as $column => $value) {
|
||||
$query->where($query->getModel()->qualifyColumn($column), $value);
|
||||
}
|
||||
});
|
||||
|
||||
static::saving(function (Model $model): void {
|
||||
foreach (app(CompanyContext::class)->identifiers() as $column => $value) {
|
||||
if ($model->getAttribute($column) !== null && (int) $model->getAttribute($column) !== $value) {
|
||||
throw new AccessDeniedHttpException('Contexto de empresa incompatível.');
|
||||
}
|
||||
$model->setAttribute($column, $value);
|
||||
}
|
||||
});
|
||||
|
||||
static::deleting(function (Model $model): void {
|
||||
foreach (app(CompanyContext::class)->identifiers() as $column => $value) {
|
||||
if ((int) $model->getAttribute($column) !== $value) {
|
||||
throw new AccessDeniedHttpException('Contexto de empresa incompatível.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Context;
|
||||
|
||||
use App\Modules\Identity\Models\Company;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use App\Modules\Identity\Services\Permissions;
|
||||
|
||||
final class CompanyContext
|
||||
{
|
||||
private ?array $identifiers = null;
|
||||
private ?int $userId = null;
|
||||
private ?int $unitId = null;
|
||||
|
||||
public function selectFor(int $userId, int $tenantId, int $companyId, ?int $unitId = null): void
|
||||
{
|
||||
$this->clear();
|
||||
$allowed = DB::table('company_memberships')
|
||||
->where('user_id', $userId)->where('tenant_id', $tenantId)
|
||||
->where('company_id', $companyId)->exists();
|
||||
|
||||
if (! $allowed || ! Company::query()->where('tenant_id', $tenantId)->whereKey($companyId)->exists()) {
|
||||
throw new AccessDeniedHttpException('Empresa não autorizada.');
|
||||
}
|
||||
|
||||
app(Permissions::class)->require($userId, $tenantId, $companyId, 'company.view');
|
||||
if ($unitId !== null) {
|
||||
app(Permissions::class)->require($userId, $tenantId, $companyId, 'unit.view', $unitId);
|
||||
abort_unless(DB::table('units')->where('tenant_id', $tenantId)->where('company_id', $companyId)->where('id', $unitId)->exists(), 403);
|
||||
}
|
||||
|
||||
$this->identifiers = ['tenant_id' => $tenantId, 'company_id' => $companyId];
|
||||
$this->userId = $userId;
|
||||
$this->unitId = $unitId;
|
||||
}
|
||||
|
||||
public function identifiers(): array
|
||||
{
|
||||
return $this->identifiers ?? throw new AccessDeniedHttpException('Contexto de empresa obrigatório.');
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->identifiers = null;
|
||||
$this->userId = null;
|
||||
$this->unitId = null;
|
||||
}
|
||||
|
||||
public function userId(): int { $this->identifiers(); return $this->userId; }
|
||||
public function unitId(): ?int { $this->identifiers(); return $this->unitId; }
|
||||
public function permittedUnitIds(): array
|
||||
{
|
||||
$ids = $this->identifiers();
|
||||
return app(Permissions::class)->unitIds($this->userId(), $ids['tenant_id'], $ids['company_id']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Testing;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use RuntimeException;
|
||||
|
||||
final class TestingDatabaseGuard
|
||||
{
|
||||
public static function assertSafe(?string $connection = null): void
|
||||
{
|
||||
$name = $connection ?: config('database.default');
|
||||
$config = config("database.connections.{$name}", []);
|
||||
$expected = [
|
||||
'driver' => 'pgsql', 'host' => '127.0.0.1', 'port' => '55432',
|
||||
'database' => 'sgf_tempo_test', 'username' => 'sgf_test_runner',
|
||||
];
|
||||
foreach ($expected as $key => $value) {
|
||||
if ((string) ($config[$key] ?? '') !== $value) {
|
||||
throw new RuntimeException('SGF_TEST_DATABASE_REFUSED: apenas sgf_tempo_test no cluster próprio é permitido.');
|
||||
}
|
||||
}
|
||||
if (! empty($config['url']) || ! empty($config['read']) || ! empty($config['write']) || ($config['search_path'] ?? 'public') !== 'public') {
|
||||
throw new RuntimeException('SGF_TEST_DATABASE_REFUSED: redireccionamento de ligação não permitido.');
|
||||
}
|
||||
$identity = DB::connection($name)->selectOne('SELECT current_database() AS db, current_user AS role');
|
||||
if ($identity->db !== 'sgf_tempo_test' || $identity->role !== 'sgf_test_runner') {
|
||||
throw new RuntimeException('SGF_TEST_DATABASE_REFUSED: destino efectivo inválido.');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user