Files
controles/docs/permission_strategy.md
LS 7399e0000e 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
2025-07-01 13:42:56 -03:00

10 KiB

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:

# 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:

{{ '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:

@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:

{% 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)

# 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)

<!-- 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)

// 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élulaSetorCRCC
  • 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

{% 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

{% 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

{% 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

{% 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

{% 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

{% 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

# 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

@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

{% 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.