- Separa modelos em entidades individuais - Cria camada de serviços para acesso a dados - Implementa controladores para lógica de negócio - Organiza rotas em blueprints por funcionalidade - Adiciona documentação de arquitetura no README - Cria script para preparação da estrutura 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
127 lines
4.8 KiB
Python
127 lines
4.8 KiB
Python
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey
|
|
from sqlalchemy.orm import relationship, backref
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
from datetime import datetime, timedelta
|
|
from flask_login import UserMixin
|
|
import pyotp
|
|
|
|
from models.entities.base import Base
|
|
from models.entities.militante import Militante
|
|
|
|
class TipoUsuario(enum.Enum):
|
|
ADMIN = "admin"
|
|
CR_RESPONSAVEL = "cr_responsavel"
|
|
SETOR_RESPONSAVEL = "setor_responsavel"
|
|
USUARIO = "usuario"
|
|
|
|
class Usuario(Base, UserMixin):
|
|
__tablename__ = 'usuarios'
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
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'))
|
|
ativo = Column(Boolean, default=True)
|
|
is_admin = Column(Boolean, default=False)
|
|
ultimo_login = Column(DateTime)
|
|
ultimo_logout = Column(DateTime)
|
|
motivo_logout = Column(String(100))
|
|
cr_id = Column(Integer, ForeignKey('comites_regionais.id'))
|
|
celula_id = Column(Integer, ForeignKey('celulas.id'))
|
|
session_timeout = Column(Integer, default=30)
|
|
tipo = Column(String(17), nullable=False)
|
|
ultima_atividade = Column(DateTime, default=datetime.utcnow)
|
|
# Relacionamento com militante
|
|
militante_id = Column(Integer, ForeignKey('militantes.id'))
|
|
|
|
# Relacionamentos
|
|
roles = relationship("Role", secondary="user_roles", back_populates="users")
|
|
setor = relationship('Setor', back_populates='usuarios')
|
|
cr = relationship('ComiteRegional', back_populates='usuarios')
|
|
celula = relationship('Celula', back_populates='usuarios')
|
|
militante = relationship("Militante", backref=backref("usuario", uselist=False))
|
|
|
|
def __init__(self, username, email=None, is_admin=False, nome=None):
|
|
self.username = username
|
|
self.email = email
|
|
self.is_admin = is_admin
|
|
self.nome = nome
|
|
self.ativo = True
|
|
self.session_timeout = 30
|
|
self.tipo = "USUARIO"
|
|
self.ultima_atividade = datetime.utcnow()
|
|
|
|
def set_password(self, password):
|
|
self.password_hash = generate_password_hash(password)
|
|
|
|
def check_password(self, password):
|
|
return check_password_hash(self.password_hash, password)
|
|
|
|
def update_last_activity(self):
|
|
self.ultima_atividade = datetime.utcnow()
|
|
|
|
def is_session_expired(self):
|
|
if not self.ultima_atividade:
|
|
return True
|
|
time_diff = datetime.utcnow() - self.ultima_atividade
|
|
return time_diff.total_seconds() > (self.session_timeout * 60)
|
|
|
|
def check_session_timeout(self):
|
|
"""Verifica se a sessão do usuário expirou"""
|
|
if not self.ultima_atividade:
|
|
return True
|
|
time_diff = datetime.utcnow() - self.ultima_atividade
|
|
return time_diff.total_seconds() > (self.session_timeout * 60)
|
|
|
|
def has_permission(self, permission_name):
|
|
"""Verifica se o usuário tem uma permissão específica"""
|
|
if self.is_admin: # Se for admin, tem todas as permissões
|
|
return True
|
|
|
|
# Verifica se o usuário tem a permissão através de suas roles
|
|
for role in self.roles:
|
|
for permission in role.permissions:
|
|
if permission.nome == permission_name:
|
|
return True
|
|
return False
|
|
|
|
def has_role(self, role_nivel):
|
|
"""Verifica se o usuário tem um determinado nível de role"""
|
|
for role in self.roles:
|
|
if role.nivel == role_nivel:
|
|
return True
|
|
return False
|
|
|
|
def get_otp_uri(self):
|
|
"""Gera a URI para autenticação em duas etapas"""
|
|
if not self.otp_secret:
|
|
self.otp_secret = pyotp.random_base32()
|
|
return pyotp.totp.TOTP(self.otp_secret).provisioning_uri(
|
|
self.username,
|
|
issuer_name="Sistema de Controles"
|
|
)
|
|
|
|
def verify_otp(self, code):
|
|
"""Verifica se um código OTP é válido"""
|
|
if not self.otp_secret:
|
|
print(f"Erro: OTP secret não configurado para o usuário {self.username}")
|
|
return False
|
|
|
|
totp = pyotp.totp.TOTP(self.otp_secret)
|
|
is_valid = totp.verify(code)
|
|
return is_valid
|
|
|
|
def logout(self):
|
|
"""Registra o logout do usuário"""
|
|
self.ultimo_logout = datetime.utcnow()
|
|
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)
|