Permissões
ACL por documento e por pasta com herança — GedPermissionService, níveis view/download/edit/manage.
Permissões
O GED tem dois níveis de ACL — por documento e por pasta — com herança:
uma permissão concedida numa pasta vale para tudo dentro dela (documentos e
subpastas), a não ser que exista uma permissão mais específica no próprio
documento. A resolução fica em App\Service\Ged\GedPermissionService.
Isto é diferente do row-scope de tenant e do escopo do agente de IA — é ACL de aplicação, dentro do módulo GED, não um mecanismo de isolamento multi-tenant nem do MCP.
Níveis
private static array $LEVEL_ORDER = ['view' => 1, 'download' => 2, 'edit' => 3, 'manage' => 4];
Os níveis são ordenados — manage inclui tudo que edit permite, que inclui
download, que inclui view. Ao calcular a permissão efetiva de um usuário,
o serviço sempre fica com o maior nível encontrado entre as fontes
aplicáveis (nunca soma, nunca restringe).
Como a permissão efetiva é calculada
GedPermissionService::getEffectivePermission(int $userId, int $documentId): ?string
GedPermissionService::hasAccess(int $userId, int $documentId, string $requiredLevel): bool
Ordem de resolução para um documento:
- Dono do documento (
document->created_by === $userId) →managedireto, sem consultar mais nada. - Administrador (
session('login') === 'admin') →managedireto. - Herdada da árvore de pastas — sobe de
folder_idemfolder_id(viaparent_id) até achar a primeira pasta com umaFolderPermissionpara o usuário ou um dos grupos dele. Para na primeira encontrada (não acumula múltiplos níveis de pastas diferentes). - Direta no documento —
DocumentPermissionespecífica daquele documento. - Resultado final = o maior nível entre a herdada (3) e a direta (4).
nullse nenhuma das duas existir (sem acesso).
if (GedPermissionService::hasAccess($userId, $documentId, 'edit')) {
// usuário pode editar (edit ou manage)
}
Para checar acesso a uma pasta diretamente (sem documento), use
getFolderAccess() — mesma lógica de subida na árvore, sem o passo de
permissão direta de documento (pastas não têm "dono" automático como
documentos).
Conceder e revogar
use App\Service\Ged\GedPermissionService;
// Documento
GedPermissionService::grantDocumentAccess($documentId, 'user', $userId, 'edit', $grantedBy);
GedPermissionService::grantDocumentAccess($documentId, 'group', $groupId, 'view', $grantedBy);
GedPermissionService::revokeDocumentAccess($documentId, 'user', $userId);
// Pasta (herdada por tudo dentro)
GedPermissionService::grantFolderAccess($folderId, 'group', $groupId, 'download', $grantedBy);
GedPermissionService::revokeFolderAccess($folderId, 'group', $groupId);
entity_type é sempre 'user' ou 'group'; entity_id é o id do usuário ou
do grupo. grantDocumentAccess/grantFolderAccess fazem upsert (atualizam o
nível se já existir uma permissão para a mesma combinação
documento/pasta + entidade) e nunca duplicam linha.
Cache de permissão por sessão
A resolução é razoavelmente cara (sobe a árvore de pastas a cada chamada), e
por isso é cacheada em session('ged_perm_cache') — não só durante o
request, mas persistente entre requests do mesmo usuário logado, até ser
invalidada. Sempre que você conceder/revogar uma permissão pelo seu próprio
código (fora dos métodos grant*/revoke* acima, que já limpam o cache
sozinhos), chame:
GedPermissionService::clearCache();
Caso contrário o usuário pode continuar vendo o nível antigo até a sessão expirar.
Limite real do mecanismo:
clearCache()ésession()->forget('ged_perm_cache')— limpa só a sessão de quem está executando o request. Um admin que revoga o acesso de outro usuário limpa a própria sessão, não a dele: o usuário afetado pode continuar com o nível antigo em cache até sua sessão expirar ou até ele mesmo disparar uma ação que chameclearCache(). Se o seu caso exige revogação imediata, não confie nesse cache — cheque a permissão sem ele no ponto crítico (ou invalide a sessão do usuário).
A aba "Compartilhamento" do DocumentDetail
A tela admin não chama grantDocumentAccess() diretamente — ela usa
<mad-db-blocks> apontando para o model DocumentPermission, com hooks que
populam a pivot a partir do form (escolha de usuário ou grupo via
mad-dbcombo-field, nível via select) e chamam clearCache() depois:
public function onAddPermission(DocumentPermission $pivot)
{
$data = (array) $this->form->getData();
$entityType = in_array($data['entity_type'] ?? '', ['user','group'], true) ? $data['entity_type'] : 'user';
$level = in_array($data['level'] ?? '', ['view','download','edit','manage'], true) ? $data['level'] : 'view';
$entityId = $entityType === 'user'
? (int) ($data['entity_id_user'] ?? 0)
: (int) ($data['entity_id_group'] ?? 0);
if (!$entityId) {
return false; // cancela o add
}
$pivot->entity_type = $entityType;
$pivot->entity_id = $entityId;
$pivot->level = $level;
$pivot->granted_by = (int) session('userid');
GedPermissionService::clearCache();
}
A mesma aba também lista as permissões herdadas das pastas pai
(DocumentDetail::getInheritedPermissions(), read-only — para revogar uma
herdada você edita a permissão na pasta, não no documento) e permite trocar o
nível de uma permissão direta inline, via um <select> que chama
onPermissionLevelChange(int $permId, string $level).
Ver também
- Visão geral do GED
- Compartilhamento público — acesso anônimo via link, sem usuário/grupo — um mecanismo complementar a este.
- mad-db-blocks — o componente por trás da lista editável de permissões e links.