feat: Implementar arquitetura de permissões no nível de dados
BREAKING CHANGES: - Sistema de permissões movido do nível de template para nível de dados - Menus sempre visíveis, controle transparente no backend - Templates nunca quebram, sempre renderizam com dados filtrados Features: - ✅ Arquitetura MVC completa implementada - ✅ Controllers com filtragem hierárquica de dados - ✅ Template helpers simplificados (user_can sempre True) - ✅ Controle de acesso baseado na hierarquia organizacional - ✅ Regra especial para tesoureiros (acesso completo) - ✅ Tratamento robusto de erros em todos os controllers Controllers implementados: - militante_controller.py - Filtragem por célula/setor/CR/CC - cota_controller.py - Controle baseado em permissões - material_controller.py - Acesso flexível por nível - pagamento_controller.py - Filtragem organizacional - auth_controller.py - Autenticação com OTP - home_controller.py - Dashboard com estatísticas - usuario_controller.py - Gestão de usuários Templates corrigidos: - listar_cotas.html - URLs corrigidas (nova_cota → cota.nova) - listar_tipos_materiais.html - Variáveis ajustadas (tipos → tipos_materiais) - base.html - Menus sempre visíveis - Diversos templates com correções de URLs e referências Services implementados: - auth_service.py - Lógica de autenticação - dashboard_service.py - Estatísticas do dashboard - cache_service.py - Integração com Redis - celula_service.py - Operações de células Models implementados: - militante_model.py - Operações de militantes - pagamento_model.py - Operações de pagamentos Documentação: - docs/permission_fixes_summary.md - Resumo completo das correções - docs/architecture_summary.md - Arquitetura MVC - docs/mvc_refactoring.md - Detalhes da refatoração - docs/permission_strategy.md - Estratégia de permissões - docs/redis_cache_setup.md - Setup do cache Redis - README.md atualizado com nova arquitetura Testes: - test_menu_navigation.py - Testes unitários de navegação - test_integration_menu.py - Testes de integração com Selenium Status dos testes: ✅ Funcionais: /, /dashboard, /pagamentos, /materiais ❌ Com problemas: /militantes, /cotas, /tipos-materiais, /admin/dashboard Hierarquia de permissões implementada: Admin → Acesso total CC → Acesso total CR → Dados do CR Setor → Dados do setor Célula → Dados da célula Próximos passos identificados: - Corrigir referências a Militante indefinido nos templates - Resolver problemas de campos inexistentes - Corrigir roteamento admin
This commit is contained in:
365
docs/permission_strategy.md
Normal file
365
docs/permission_strategy.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# Estratégia de Controle de Permissões Granular
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Esta documentação descreve a estratégia implementada para controle de permissões granular no sistema, permitindo que usuários vejam apenas dados e elementos para os quais têm autorização, sem quebrar templates ou causar erros.
|
||||
|
||||
## Arquitetura da Solução
|
||||
|
||||
### 1. Context Processors
|
||||
|
||||
**Arquivo**: `functions/template_helpers.py`
|
||||
|
||||
Os context processors disponibilizam automaticamente as permissões do usuário em todos os templates:
|
||||
|
||||
```python
|
||||
# Disponível em todos os templates
|
||||
user_can('permission_name') # Verifica permissão específica
|
||||
user_has_role('role_name') # Verifica role específica
|
||||
is_admin # Booleano se é admin
|
||||
current_user_data # Dados completos do usuário
|
||||
```
|
||||
|
||||
### 2. Template Filters
|
||||
|
||||
Filtros Jinja2 para uso direto nos templates:
|
||||
|
||||
```jinja2
|
||||
{{ 'view_cell_data' | has_permission }}
|
||||
{{ militantes | safe_data('view_cell_data', []) }}
|
||||
{{ 'militante' | can_manage }}
|
||||
```
|
||||
|
||||
### 3. Safe Data Controllers
|
||||
|
||||
Decorators que retornam dados vazios em caso de falta de permissão:
|
||||
|
||||
```python
|
||||
@safe_data_controller(Permission.VIEW_CELL_DATA, empty_data={'militantes': []})
|
||||
def listar():
|
||||
# Lógica normal do controller
|
||||
return render_template('template.html', militantes=militantes)
|
||||
```
|
||||
|
||||
### 4. Template Macros
|
||||
|
||||
Componentes reutilizáveis para elementos condicionais:
|
||||
|
||||
```jinja2
|
||||
{% from 'components/permission_wrapper.html' import permission_button %}
|
||||
{{ permission_button('create_cell_member', url_for('militante.novo'), 'Novo Militante') }}
|
||||
```
|
||||
|
||||
## Implementação por Camadas
|
||||
|
||||
### Camada 1: Controllers (Backend)
|
||||
|
||||
```python
|
||||
# Filtragem de dados baseada em permissões
|
||||
if current_user.is_admin:
|
||||
# Admin vê todos os dados
|
||||
militantes = query.all()
|
||||
elif current_user.has_permission(Permission.VIEW_CR_REPORTS):
|
||||
# CR vê apenas do seu CR
|
||||
militantes = query.filter(cr_id=current_user.cr_id).all()
|
||||
else:
|
||||
# Sem permissão - lista vazia
|
||||
militantes = []
|
||||
```
|
||||
|
||||
### Camada 2: Templates (Frontend)
|
||||
|
||||
```jinja2
|
||||
<!-- Menu só aparece se tiver permissão -->
|
||||
{% if user_can('view_cell_data') %}
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('militante.listar') }}">Militantes</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<!-- Dados condicionais -->
|
||||
{% if user_can('view_cell_data') %}
|
||||
{% if militantes %}
|
||||
<!-- Exibir tabela -->
|
||||
{% else %}
|
||||
<div class="alert alert-info">Nenhum dado disponível</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning">Sem permissão para visualizar</div>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### Camada 3: JavaScript (Interação)
|
||||
|
||||
```javascript
|
||||
// Verificações no frontend
|
||||
if (userPermissions.includes('manage_cell_members')) {
|
||||
// Habilitar funcionalidades de edição
|
||||
enableEditFeatures();
|
||||
}
|
||||
```
|
||||
|
||||
## Níveis de Permissão
|
||||
|
||||
### 1. Visualização de Dados
|
||||
|
||||
- `view_own_data`: Apenas próprios dados
|
||||
- `view_cell_data`: Dados da célula
|
||||
- `view_sector_reports`: Dados do setor
|
||||
- `view_cr_reports`: Dados do CR
|
||||
- `view_cc_reports`: Dados nacionais
|
||||
|
||||
### 2. Gerenciamento
|
||||
|
||||
- `manage_cell_members`: Gerenciar membros da célula
|
||||
- `manage_sector_cells`: Gerenciar células do setor
|
||||
- `create_cell_member`: Criar novos membros
|
||||
- `register_cell_payment`: Registrar pagamentos
|
||||
|
||||
### 3. Administração
|
||||
|
||||
- `system_config`: Configurações do sistema
|
||||
- `manage_cc_crs`: Gerenciar CRs
|
||||
- `create_cc_cr`: Criar novos CRs
|
||||
|
||||
## Regras Especiais de Permissão
|
||||
|
||||
### 1. Administrador (Admin)
|
||||
- **Acesso Total**: Tem todas as permissões do sistema
|
||||
- **Bypass de Verificações**: Sempre retorna `true` para qualquer verificação de permissão
|
||||
- **Acesso a Configurações**: Pode configurar o sistema e gerenciar usuários
|
||||
|
||||
### 2. Tesoureiro
|
||||
- **Regra Especial**: Tesoureiro pode fazer tudo que o secretário da instância pode fazer
|
||||
- **Permissões Automáticas**: Quando um militante tem responsabilidade de `TESOUREIRO`, automaticamente recebe:
|
||||
- `view_cell_data`: Visualizar dados da célula
|
||||
- `manage_cell_members`: Gerenciar membros da célula
|
||||
- `create_cell_member`: Criar novos membros
|
||||
- `view_cell_reports`: Visualizar relatórios da célula
|
||||
- `manage_cell_reports`: Gerenciar relatórios da célula
|
||||
- `register_cell_payment`: Registrar pagamentos da célula
|
||||
|
||||
### 3. Hierarquia de Instâncias
|
||||
- **Célula** → **Setor** → **CR** → **CC**
|
||||
- Usuários de níveis superiores têm acesso aos dados dos níveis inferiores
|
||||
- Secretários podem gerenciar todas as instâncias de seu nível e abaixo
|
||||
|
||||
## Padrões de Uso
|
||||
|
||||
### 1. Menus Condicionais
|
||||
|
||||
```jinja2
|
||||
{% if user_can('view_cell_data') %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">
|
||||
Militantes
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('militante.listar') }}">Listar</a></li>
|
||||
{% if user_can('create_cell_member') %}
|
||||
<li><a class="dropdown-item" href="{{ url_for('militante.novo') }}">Novo</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 2. Botões Condicionais
|
||||
|
||||
```jinja2
|
||||
{% if user_can('create_cell_member') %}
|
||||
<a href="{{ url_for('militante.novo') }}" class="btn btn-success">
|
||||
<i class="fas fa-plus me-2"></i>Novo Militante
|
||||
</a>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 3. Dados Filtrados
|
||||
|
||||
```jinja2
|
||||
{% if user_can('view_cell_data') %}
|
||||
{% if militantes %}
|
||||
<table class="table">
|
||||
<!-- Tabela com dados -->
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-info">Nenhum militante encontrado</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-lock me-2"></i>
|
||||
Você não tem permissão para visualizar estes dados
|
||||
</div>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 4. Formulários Condicionais
|
||||
|
||||
```jinja2
|
||||
{% if user_can('create_cell_member') %}
|
||||
<form method="POST" action="{{ url_for('militante.criar') }}">
|
||||
<!-- Campos do formulário -->
|
||||
<button type="submit" class="btn btn-primary">Salvar</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
Você não tem permissão para criar militantes
|
||||
</div>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
## Tratamento de Erros
|
||||
|
||||
### 1. Dados Não Encontrados
|
||||
|
||||
```jinja2
|
||||
{% if user_can('view_cell_data') %}
|
||||
{% if data %}
|
||||
<!-- Exibir dados -->
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
Nenhum registro encontrado
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 2. Permissão Negada
|
||||
|
||||
```jinja2
|
||||
{% if not user_can('required_permission') %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-lock me-2"></i>
|
||||
Você não tem permissão para acessar esta funcionalidade
|
||||
</div>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 3. Fallbacks Graceful
|
||||
|
||||
```python
|
||||
# Controller com fallback
|
||||
@safe_data_controller('view_cell_data', empty_data={'militantes': []})
|
||||
def listar():
|
||||
# Se não tiver permissão, retorna lista vazia
|
||||
# Template não quebra, apenas não mostra dados
|
||||
pass
|
||||
```
|
||||
|
||||
## Vantagens da Estratégia
|
||||
|
||||
### 1. **Segurança por Camadas**
|
||||
- Verificação no backend (controllers)
|
||||
- Verificação no frontend (templates)
|
||||
- Verificação no JavaScript (interação)
|
||||
|
||||
### 2. **Graceful Degradation**
|
||||
- Templates nunca quebram
|
||||
- Dados vazios em vez de erros
|
||||
- Mensagens informativas para usuário
|
||||
|
||||
### 3. **Flexibilidade**
|
||||
- Permissões granulares
|
||||
- Fácil de estender
|
||||
- Reutilização de componentes
|
||||
|
||||
### 4. **Manutenibilidade**
|
||||
- Lógica centralizada
|
||||
- Padrões consistentes
|
||||
- Fácil debugging
|
||||
|
||||
### 5. **UX Melhorada**
|
||||
- Interface adapta-se às permissões
|
||||
- Sem elementos inacessíveis visíveis
|
||||
- Feedback claro sobre limitações
|
||||
|
||||
## Exemplo Completo de Implementação
|
||||
|
||||
### Controller
|
||||
|
||||
```python
|
||||
@militante_bp.route("/militantes")
|
||||
@require_login
|
||||
@safe_data_controller(Permission.VIEW_CELL_DATA, empty_data={'militantes': []})
|
||||
def listar():
|
||||
# Filtragem baseada em permissões
|
||||
if current_user.is_admin:
|
||||
militantes = query.all()
|
||||
elif current_user.has_permission(Permission.VIEW_CELL_DATA):
|
||||
militantes = query.filter(celula_id=current_user.celula_id).all()
|
||||
else:
|
||||
militantes = []
|
||||
|
||||
return render_template('militantes.html', militantes=militantes)
|
||||
```
|
||||
|
||||
### Template
|
||||
|
||||
```jinja2
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Militantes</h2>
|
||||
|
||||
{% if user_can('create_cell_member') %}
|
||||
<a href="{{ url_for('militante.novo') }}" class="btn btn-success">
|
||||
<i class="fas fa-plus me-2"></i>Novo Militante
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user_can('view_cell_data') %}
|
||||
{% if militantes %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Email</th>
|
||||
{% if user_can('manage_cell_members') %}
|
||||
<th>Ações</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for militante in militantes %}
|
||||
<tr>
|
||||
<td>{{ militante.nome }}</td>
|
||||
<td>{{ militante.email }}</td>
|
||||
{% if user_can('manage_cell_members') %}
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary">Editar</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
Nenhum militante encontrado
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-lock me-2"></i>
|
||||
Você não tem permissão para visualizar militantes
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
## Conclusão
|
||||
|
||||
Esta estratégia garante que:
|
||||
|
||||
1. **Nunca há erros de template** - dados sempre estão disponíveis (mesmo que vazios)
|
||||
2. **Segurança é mantida** - usuários só veem o que podem
|
||||
3. **UX é preservada** - interface clara sobre limitações
|
||||
4. **Código é limpo** - padrões reutilizáveis e consistentes
|
||||
5. **Manutenção é fácil** - lógica centralizada e bem documentada
|
||||
6. **Tesoureiros têm poder adequado** - podem fazer tudo que secretários fazem
|
||||
|
||||
A implementação permite desenvolvimento ágil sem comprometer segurança ou experiência do usuário.
|
||||
Reference in New Issue
Block a user