OG Framework
OG Framework Documentação
Voltar para Documentação

Segurança

Autenticação & Autorização

O Auth System é responsável por verificar identidade (autenticação), permissões (autorização), e manter o estado do utilizador — incluindo suporte a impersonation para debugging.

Conceitos Chave

Conceito Pergunta Descrição
Autenticação "Quem é você?" Verificar identidade do utilizador
Autorização "O que pode fazer?" Verificar permissões para ações
Permissão Direito específico (ex: invoices:create)
Policy Classe que define regras de autorização
Impersonation Admin atuar como outro utilizador

Quick Start

// 1. OBTER o AuthManager
$auth = app('auth');

// 2. VERIFICAR se utilizador está logado
if ($auth->check()) {
    $user = $auth->user();
    echo "Olá, " . $user->getUsername();
}

// 3. VERIFICAR uma permissão
if ($auth->can('invoices:create')) {
    // Utilizador pode criar faturas
}

// 4. VERIFICAR múltiplas permissões
if ($auth->canAny(['sales:view', 'sales:edit'])) {
    // Tem pelo menos uma das permissões
}

// 5. OBTER ID do utilizador
$userId = $auth->id();

Padrão de Permissões

O formato segue módulo:ação:

entidades:clientes
stocks:artigos
vendas:facturas
invoices:create
invoices:admin
crm:edit

AuthManager

O AuthManager é o ponto central para toda a lógica de autenticação:

Verificação de Estado

$auth = app('auth');

// Está autenticado?
$auth->check(): bool

// Obter utilizador atual
$auth->user(): ?User

// Obter ID do utilizador
$auth->id(): ?int

// Está ativo?
$auth->isActive(): bool

Verificação de Permissões

// Verifica UMA permissão
$auth->can('stocks:artigos'): bool

// Verifica se tem ALGUMA
$auth->canAny(['view', 'edit']): bool

// Verifica se tem TODAS
$auth->canAll(['view', 'edit']): bool

Gestão de Utilizador

// Atualiza utilizador (sync legacy)
$auth->setUser(User $user): void

// Dados como array
$auth->getUserData(): array

Impersonation

// Em modo impersonation?
$auth->isImpersonating(): bool

// É admin developer?
$auth->isDeveloperAdmin(): bool

// ID do admin original
$auth->impersonatorId(): ?int

Policies (Autorização)

Policies encapsulam regras de autorização específicas para um recurso. Em vez de espalhar if statements pelo código, centralize a lógica:

use Og\Modules\Common\Auth\Authorization;

class InvoicePolicy extends Authorization
{
    // Pode visualizar a fatura?
    public function view($invoice): bool
    {
        return $this->user->checkPriv('invoices:view');
    }

    // Pode editar a fatura?
    public function edit($invoice): bool
    {
        // Criador OU admin
        return $invoice->created_by === $this->user->getId()
            || $this->user->checkPriv('invoices:admin');
    }

    // Pode eliminar a fatura?
    public function delete($invoice): bool
    {
        return $this->user->checkPriv('invoices:admin');
    }
}

Usando uma Policy

$policy = new InvoicePolicy($user, $app);

// Verificar manualmente
if ($policy->edit($invoice)) {
    // Pode editar
}

// Ou usar authorize() — lança exceção se falhar
try {
    $policy->authorize('edit', $invoice);
} catch (AuthorizationException $e) {
    return response()->error($e->getMessage(), 403);
}

Access Response

A classe Response encapsula decisões de acesso com mensagens e códigos HTTP personalizados:

Permitir

Response::allow();
Response::allow('OK', 'ACCESS_OK');

Negar

Response::deny('Sem permissão');
Response::denyWithStatus(403, 'Proibido');
Response::denyAsNotFound('Não encontrado');
class DocumentPolicy extends Authorization
{
    public function access($document): Response
    {
        if (!$this->user->checkPriv('documents:view')) {
            return Response::deny('Sem permissão');
        }

        if ($document->is_confidential && !$this->user->checkPriv('documents:confidential')) {
            return Response::denyWithStatus(403, 'Documento confidencial');
        }

        if ($document->status === 'deleted') {
            return Response::denyAsNotFound('Documento não encontrado');
        }

        return Response::allow();
    }
}

Impersonation

Permite que um administrador "atue como" outro utilizador para debugging ou suporte:

Admin ID: 1 (Real Identity) impersonates User ID: 42 (Current Identity) Visão do Sistema Auth::id() 42 Audit Log by Admin(1) check() true

Audit Log com Impersonation

class AuditService
{
    public function log(string $action, $resource): void
    {
        $auth = app('auth');

        $data = [
            'action' => $action,
            'user_id' => $auth->id(),
        ];

        // Se em impersonation, registar admin original
        if ($auth->isImpersonating()) {
            $data['impersonator_id'] = $auth->impersonatorId();
            $data['is_impersonated'] = true;
        }

        AuditLog::create($data);
    }
}

Exemplos Práticos

🎯 Proteger uma Action

class CreateInvoiceAction
{
    public function execute(array $data): Invoice
    {
        $auth = app('auth');

        if (!$auth->check()) {
            throw new AuthorizationException('Não autenticado');
        }

        if (!$auth->can('invoices:create')) {
            throw new AuthorizationException('Sem permissão');
        }

        return Invoice::create([
            ...$data,
            'created_by' => $auth->id(),
        ]);
    }
}

🎯 Middleware de Autorização

class RequirePermissionMiddleware
{
    public function handle(Request $request, string $permission): Response
    {
        $auth = app('auth');

        if (!$auth->check()) {
            return redirect('/login');
        }

        if (!$auth->can($permission)) {
            return response()->error('Acesso negado', 403);
        }

        return $next($request);
    }
}

// Uso na rota:
Route::get('/invoices', [InvoiceController::class, 'index'])
    ->middleware('permission:invoices:view');

Integração com Legacy

O AuthManager mantém sincronização bidirecional entre o sistema moderno e as variáveis globais legacy ($u).

// Código legacy usa $u global
global $u;
if ($u->checkPriv('stocks:artigos')) {
    // ...
}

// Código moderno usa AuthManager
$auth = app('auth');
if ($auth->can('stocks:artigos')) {
    // ...
}

// ✅ Ambos funcionam e estão sincronizados!

Boas Práticas

✅ Verificar Cedo

public function execute(): void
{
    // BOM — verificar no início
    if (!app('auth')->can('perm')) {
        throw new Exception();
    }
    // ... resto do código
}

✅ Usar Policies

// BOM — lógica centralizada
$policy->authorize('edit', $doc);

// MAU — lógica espalhada
if ($user->id === $doc->owner
    || $user->checkPriv('admin')) {
}

✅ Logar Acessos Negados

if (!$auth->can($perm)) {
    Funcs::log('security',
        "Negado: user={$auth->id()}"
    );
    throw new Exception();
}

❌ Confiar no Cliente

// MAU — confiar no request
$inv->user_id = $request->input('user_id');

// BOM — usar ID autenticado
$inv->user_id = app('auth')->id();