From e43b089155c26d73d9f4a05d216ea2bdf3b1875f Mon Sep 17 00:00:00 2001 From: andersonid Date: Sun, 13 Apr 2025 22:30:05 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20Corre=C3=A7=C3=B5es=20na=20p=C3=A1gina?= =?UTF-8?q?=20de=20administra=C3=A7=C3=A3o=20e=20suas=20depend=C3=AAncias?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 214 ++++++++++++++++++++++++++++----- create_admin.py | 9 ++ functions/database.py | 9 +- functions/decorators.py | 54 +++++++-- templates/base.html | 7 +- templates/dashboard_admin.html | 211 +++++++++++++++++++++++++------- 6 files changed, 418 insertions(+), 86 deletions(-) diff --git a/app.py b/app.py index 531bb7a..8e36537 100644 --- a/app.py +++ b/app.py @@ -128,7 +128,30 @@ def create_app(): if 'user_id' not in session: flash('Por favor, faça login para acessar esta página.', 'warning') return redirect(url_for('login')) - return f(*args, **kwargs) + + from sqlalchemy.orm import Session + db = get_db_connection() + try: + # Carregar o usuário com suas roles e permissões + user = db.query(Usuario).options( + joinedload(Usuario.roles).joinedload(Role.permissions), + joinedload(Usuario.militante), + joinedload(Usuario.cr), + joinedload(Usuario.setor), + joinedload(Usuario.celula) + ).get(session['user_id']) + + if not user: + flash('Usuário não encontrado.', 'danger') + return redirect(url_for('login')) + + # Atualiza timestamp da última atividade + user.update_last_activity() + db.commit() + + return f(*args, **kwargs) + finally: + db.close() return decorated_function # Decorator para verificar se a sessão expirou @@ -222,6 +245,7 @@ def create_app(): session['user_id'] = user.id session['username'] = user.username session['is_admin'] = user.is_admin + print(f"Login realizado: user_id={user.id}, username={user.username}, is_admin={user.is_admin}") # Redirecionar para home return redirect(url_for("home")) @@ -1361,34 +1385,36 @@ def create_app(): @app.route('/usuarios//toggle_status', methods=['POST']) @require_login def toggle_user_status(user_id): - user = db_session.query(Usuario).get_or_404(user_id) - - # Verificar permissões baseado na hierarquia - if not current_user.has_permission('system_config'): - if current_user.has_permission('manage_cr_sectors'): - # Secretário de CR só pode gerenciar membros do seu CR - if user.cr_id != current_user.cr_id: - flash('Você não tem permissão para gerenciar este usuário.', 'danger') - return redirect(url_for('dashboard_admin')) - elif current_user.has_permission('manage_sector_cells'): - # Secretário de Setor só pode gerenciar membros do seu setor - if user.setor_id != current_user.setor_id: - flash('Você não tem permissão para gerenciar este usuário.', 'danger') - return redirect(url_for('dashboard_admin')) - elif current_user.has_permission('manage_cell_members'): - # Secretário de Célula só pode gerenciar membros da sua célula - if user.celula_id != current_user.celula_id: - flash('Você não tem permissão para gerenciar este usuário.', 'danger') - return redirect(url_for('dashboard_admin')) - else: - # Militante básico não pode gerenciar ninguém - flash('Você não tem permissão para gerenciar usuários.', 'danger') - return redirect(url_for('dashboard_admin')) - - user.ativo = not user.ativo - db_session.commit() - - return jsonify({'success': True, 'message': 'Status do usuário alterado com sucesso!'}) + if not current_user.is_admin: + return jsonify({ + 'success': False, + 'error': 'Você não tem permissão para alterar o status de usuários.' + }), 403 + + db = get_db_connection() + try: + usuario = db.query(Usuario).get(user_id) + if not usuario: + return jsonify({ + 'success': False, + 'error': 'Usuário não encontrado.' + }), 404 + + usuario.ativo = not usuario.ativo + db.commit() + + return jsonify({ + 'success': True, + 'message': f'Usuário {"ativado" if usuario.ativo else "desativado"} com sucesso!' + }) + except Exception as e: + db.rollback() + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + finally: + db.close() @app.route('/usuarios//alterar_nivel', methods=['POST']) @require_login @@ -1619,6 +1645,136 @@ def create_app(): finally: db.close() + @app.route('/dashboard_admin') + @login_required + def dashboard_admin(): + """Rota para o dashboard administrativo""" + if not current_user.is_admin: + flash('Você não tem permissão para acessar esta página.', 'danger') + return redirect(url_for('home')) + + db = get_db_connection() + try: + # Busca usuários + usuarios = db.query(Usuario).all() + + usuarios_data = [] + for usuario in usuarios: + user_data = { + 'id': usuario.id, + 'username': usuario.username, + 'email': usuario.email, + 'nome': usuario.username, # Usar username como fallback + 'ativo': usuario.ativo, + 'is_admin': usuario.is_admin, + 'last_login': usuario.ultimo_login.strftime('%d/%m/%Y %H:%M') if usuario.ultimo_login else 'Nunca', + 'nivel': 'Administrador' if usuario.is_admin else 'Usuário' + } + usuarios_data.append(user_data) + + return render_template( + 'dashboard_admin.html', + usuarios=usuarios_data + ) + except Exception as e: + import traceback + print(f"Erro no dashboard_admin: {traceback.format_exc()}") + flash('Erro ao carregar dados dos usuários. Por favor, tente novamente.', 'danger') + return redirect(url_for('home')) + finally: + db.close() + + @app.route('/reset_otp/', methods=['POST']) + @login_required + def reset_otp(user_id): + if not current_user.is_admin: + return jsonify({ + 'success': False, + 'error': 'Você não tem permissão para resetar OTP.' + }), 403 + + db = get_db_connection() + try: + usuario = db.query(Usuario).get(user_id) + if not usuario: + return jsonify({ + 'success': False, + 'error': 'Usuário não encontrado.' + }), 404 + + usuario.otp_secret = pyotp.random_base32() + db.commit() + + return jsonify({ + 'success': True + }) + except Exception as e: + db.rollback() + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + finally: + db.close() + + @app.route('/reset_password/', methods=['POST']) + @login_required + def reset_password(user_id): + if not current_user.is_admin: + return jsonify({ + 'success': False, + 'error': 'Você não tem permissão para resetar senhas.' + }), 403 + + db = get_db_connection() + try: + usuario = db.query(Usuario).get(user_id) + if not usuario: + return jsonify({ + 'success': False, + 'error': 'Usuário não encontrado.' + }), 404 + + nova_senha = ''.join(random.choices(string.ascii_letters + string.digits, k=12)) + usuario.set_password(nova_senha) + + try: + msg = Message( + 'Nova Senha - Sistema de Controles', + recipients=[usuario.email] + ) + msg.body = f'''Olá {usuario.nome}, + +Sua senha foi resetada por um administrador. Sua nova senha é: + +{nova_senha} + +Por favor, altere esta senha no seu próximo login. + +Atenciosamente, +Sistema de Controles''' + + mail.send(msg) + except Exception as e: + print(f"Erro ao enviar email: {str(e)}") + return jsonify({ + 'success': False, + 'error': 'Erro ao enviar email com a nova senha.' + }), 500 + + db.commit() + return jsonify({ + 'success': True + }) + except Exception as e: + db.rollback() + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + finally: + db.close() + return app def init_system(): diff --git a/create_admin.py b/create_admin.py index 9d70e6d..b53fa93 100644 --- a/create_admin.py +++ b/create_admin.py @@ -70,6 +70,15 @@ def create_admin_user(): admin.set_password("admin123") admin.generate_otp_secret() + # Buscar ou criar role de admin + admin_role = db.query(Role).filter_by(nome="admin").first() + if not admin_role: + admin_role = Role(nome="admin", nivel=0) # Nível 0 é o mais alto + db.add(admin_role) + + # Adicionar role ao usuário + admin.roles.append(admin_role) + # Adicionar e fazer commit db.add(admin) db.commit() diff --git a/functions/database.py b/functions/database.py index 54e0d1c..4f09e71 100644 --- a/functions/database.py +++ b/functions/database.py @@ -441,6 +441,7 @@ class Usuario(Base, UserMixin): username = Column(String(50), unique=True, nullable=False) password_hash = Column(String(255), nullable=False) email = Column(String(100), unique=True, nullable=False) + nome = Column(String(100)) # Nome completo do usuário otp_secret = Column(String(32)) role_id = Column(Integer, ForeignKey('roles.id')) setor_id = Column(Integer, ForeignKey('setores.id')) @@ -464,11 +465,11 @@ class Usuario(Base, UserMixin): cr = relationship('ComiteRegional', back_populates='usuarios') celula = relationship('Celula', back_populates='usuarios') - def __init__(self, username, email=None, is_admin=False): + def __init__(self, username, email=None, is_admin=False, nome=None): self.username = username self.email = email self.is_admin = is_admin - self.email = email + self.nome = nome self.ativo = True self.session_timeout = 30 self.tipo = "USUARIO" @@ -549,6 +550,10 @@ class Usuario(Base, UserMixin): self.motivo_logout = "Logout manual" self.ultima_atividade = None + def is_admin_user(self): + """Verifica se o usuário é admin""" + return self.is_admin or any(role.nome == "admin" for role in self.roles) + class PagamentoCelula(Base): __tablename__ = 'pagamentos_celula' diff --git a/functions/decorators.py b/functions/decorators.py index b2ec0dc..1483835 100644 --- a/functions/decorators.py +++ b/functions/decorators.py @@ -2,7 +2,7 @@ from functools import wraps from flask import session, redirect, url_for, flash from flask_login import current_user, login_required from sqlalchemy.orm import joinedload -from .database import get_db_connection, Usuario +from .database import get_db_connection, Usuario, Role from .rbac import Permission def require_login(f): @@ -15,9 +15,13 @@ def require_login(f): db = get_db_connection() try: - # Carregar o usuário com suas roles + # Carregar o usuário com suas roles e permissões user = db.query(Usuario).options( - joinedload(Usuario.roles) + joinedload(Usuario.roles).joinedload(Role.permissions), + joinedload(Usuario.militante), + joinedload(Usuario.cr), + joinedload(Usuario.setor), + joinedload(Usuario.celula) ).get(current_user.id) if not user: @@ -28,7 +32,15 @@ def require_login(f): user.update_last_activity() db.commit() + # Substituir o current_user pelo usuário carregado + setattr(current_user, '_get_current_object', lambda: user) + + # Executar a função com o usuário carregado return f(*args, **kwargs) + except Exception as e: + db.rollback() + flash('Erro ao carregar dados do usuário.', 'danger') + return redirect(url_for('login')) finally: db.close() return decorated_function @@ -39,14 +51,38 @@ def require_permission(permission_name): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: - flash('Por favor, faça login para acessar esta página.', 'danger') + flash('Você precisa estar logado para acessar esta página.', 'error') return redirect(url_for('login')) - if not current_user.has_permission(permission_name): - flash('Você não tem permissão para acessar esta página.', 'danger') - return redirect(url_for('home')) - - return f(*args, **kwargs) + db = get_db_connection() + try: + # Carregar o usuário com suas roles e permissões + user = db.query(Usuario).options( + joinedload(Usuario.roles).joinedload(Role.permissions), + joinedload(Usuario.militante), + joinedload(Usuario.cr), + joinedload(Usuario.setor), + joinedload(Usuario.celula) + ).get(current_user.id) + + if not user: + flash('Usuário não encontrado.', 'error') + return redirect(url_for('login')) + + if not user.has_permission(permission_name): + flash('Você não tem permissão para acessar esta página.', 'error') + return redirect(url_for('index')) + + # Atualiza timestamp da última atividade + user.update_last_activity() + db.commit() + + # Substituir o current_user pelo usuário carregado + setattr(current_user, '_get_current_object', lambda: user) + + return f(*args, **kwargs) + finally: + db.close() return decorated_function return decorator diff --git a/templates/base.html b/templates/base.html index 9327f54..c6f072b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -10,7 +10,7 @@ - + @@ -599,6 +599,11 @@ Novo Usuário +
  • + + Administração + +
  • {% endif %}
  • diff --git a/templates/dashboard_admin.html b/templates/dashboard_admin.html index d9c539a..170360d 100644 --- a/templates/dashboard_admin.html +++ b/templates/dashboard_admin.html @@ -1,63 +1,75 @@ -{% extends 'base.html' %} +{% extends "base.html" %} {% block title %}Dashboard Administrativo{% endblock %} {% block content %} -
    -

    Dashboard Administrativo

    - - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} -
    {{ message }}
    - {% endfor %} - {% endif %} - {% endwith %} - -
    -
    -
    Gerenciamento de Usuários
    +
    +
    +
    +

    Administração de Usuários

    + +
    - - +
    + - - - + + + + {% for usuario in usuarios %} - + + + - {% endfor %} @@ -66,18 +78,127 @@ - -
    -
    -
    Ações Rápidas
    -
    -
    - + + + + +{% endblock %} + +{% block scripts %} + {% endblock %} \ No newline at end of file
    ID Usuário EmailAdminOTP ConfiguradoNomeÚltimo AcessoStatusNível Ações
    {{ usuario.id }} {{ usuario.username }} {{ usuario.email }}{{ usuario.nome }}{{ usuario.last_login }} + + {{ "Ativo" if usuario.ativo else "Inativo" }} + + {% if usuario.is_admin %} - Sim + Administrador {% else %} - Não + {{ usuario.nivel }} {% endif %} - {% if usuario.otp_secret %} - Sim - {% else %} - Não - {% endif %} - -
    - -
    + + + + {% if not usuario.is_admin %} + + {% endif %} +