Preserva base local aceite de identidade e acessos
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Identity\Http;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Modules\Identity\Services\Audit;
|
||||
use App\Modules\Identity\Services\IdentityWorkflow;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
final class IdentityController extends Controller
|
||||
{
|
||||
public function __construct(private IdentityWorkflow $workflow) {}
|
||||
|
||||
public function session(Request $request)
|
||||
{
|
||||
if ($request->user() && (int) $request->session()->get('auth_version', 0) !== (int) $request->user()->auth_version) {
|
||||
Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken();
|
||||
}
|
||||
return response()->json($this->workflow->snapshot($request))->header('Cache-Control', 'no-store');
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$data = $request->validate(['email' => 'required|email|max:255', 'password' => 'required|string|max:1024']);
|
||||
$email = mb_strtolower(trim($data['email']));
|
||||
if (strlen($data['password']) > 72) { throw ValidationException::withMessages(['email' => 'Email ou palavra-passe inválidos.']); }
|
||||
$key = 'sgf-login:'.hash('sha256', $email.'|'.$request->ip());
|
||||
$ipKey = 'sgf-login-ip:'.hash('sha256', (string) $request->ip());
|
||||
abort_if(RateLimiter::tooManyAttempts($key, config('identity.login_attempts')) || RateLimiter::tooManyAttempts($ipKey, config('identity.login_ip_attempts')), 429, 'Demasiadas tentativas. Aguarde antes de tentar novamente.');
|
||||
if (! Auth::attempt(['email' => $email, 'password' => $data['password']], false)) {
|
||||
RateLimiter::hit($key, config('identity.login_decay_seconds'));
|
||||
RateLimiter::hit($ipKey, config('identity.login_decay_seconds'));
|
||||
Audit::record('auth.failed', null);
|
||||
throw ValidationException::withMessages(['email' => 'Email ou palavra-passe inválidos.']);
|
||||
}
|
||||
RateLimiter::clear($key);
|
||||
$request->session()->regenerate();
|
||||
$request->session()->forget('company_context');
|
||||
$request->session()->put('auth_version', (int) $request->user()->auth_version);
|
||||
Audit::record('auth.login', $request->user()->id);
|
||||
return $this->session($request);
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
Audit::record('auth.logout', $request->user()->id);
|
||||
Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken();
|
||||
return response()->json(['csrf' => csrf_token()])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
|
||||
public function select(Request $request) { return response()->json($this->workflow->select($request)); }
|
||||
public function company(Request $request, int $company)
|
||||
{
|
||||
$ids = app(\App\Shared\Context\CompanyContext::class)->identifiers();
|
||||
abort_unless($company === $ids['company_id'], 403);
|
||||
return response()->json(\Illuminate\Support\Facades\DB::table('companies')->where('id', $company)->where('tenant_id', $ids['tenant_id'])->first(['id', 'name']));
|
||||
}
|
||||
public function unit(Request $request, int $unit)
|
||||
{
|
||||
$ids = app(\App\Shared\Context\CompanyContext::class)->identifiers();
|
||||
app(\App\Modules\Identity\Services\Permissions::class)->require($request->user()->id, $ids['tenant_id'], $ids['company_id'], 'unit.view', $unit);
|
||||
$record = \Illuminate\Support\Facades\DB::table('units')->where('id', $unit)->where('company_id', $ids['company_id'])->where('tenant_id', $ids['tenant_id'])->first(['id', 'name']);
|
||||
abort_unless($record, 403);
|
||||
return response()->json($record);
|
||||
}
|
||||
public function renameCompany(Request $request, int $company) { $this->workflow->renameCompany($request, $company); return response()->json(['ok' => true]); }
|
||||
public function renameUnit(Request $request, int $unit) { $this->workflow->renameUnit($request, $unit); return response()->json(['ok' => true]); }
|
||||
public function createUnit(Request $request) { return response()->json(['id' => $this->workflow->createUnit($request)], 201); }
|
||||
public function accesses(Request $request) { return response()->json($this->workflow->accessList($request)); }
|
||||
public function grant(Request $request) { $this->workflow->changeAccess($request, true); return response()->json(['ok' => true]); }
|
||||
public function revoke(Request $request) { $this->workflow->changeAccess($request, false); return response()->json(['ok' => true]); }
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Identity\Http;
|
||||
|
||||
use App\Shared\Context\CompanyContext;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class RequireCompanyContext
|
||||
{
|
||||
public function __construct(private CompanyContext $context) {}
|
||||
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$this->context->clear();
|
||||
try {
|
||||
$selection = $request->session()->get('company_context', []);
|
||||
$tenant = $selection['tenant_id'] ?? null;
|
||||
$company = $selection['company_id'] ?? null;
|
||||
abort_unless($request->user() && $tenant && $company, 403, 'Contexto de empresa obrigatório.');
|
||||
$this->context->selectFor((int) $request->user()->getKey(), $tenant, $company, $selection['unit_id'] ?? null);
|
||||
if (! $request->isMethodSafe()) {
|
||||
$token = $request->header('X-SGF-Context');
|
||||
abort_unless(is_string($token) && hash_equals($selection['token'] ?? '', $token), 409, 'O contexto mudou. Reabra o formulário antes de guardar.');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
} finally {
|
||||
$this->context->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Identity\Http;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
final class RequireValidSession
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
abort_unless($request->user(), 401);
|
||||
if ((int) $request->session()->get('auth_version', 0) !== (int) $request->user()->auth_version) {
|
||||
auth()->logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
abort(401, 'Sessão terminada. Entre novamente.');
|
||||
}
|
||||
$response = $next($request);
|
||||
$response->headers->set('Cache-Control', 'no-store');
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user