adicionado timeout no login e botão de Sair

This commit is contained in:
LS
2025-03-24 16:34:38 -03:00
parent 0f4056fbff
commit 1367389619
3 changed files with 127 additions and 52 deletions

View File

@@ -2,6 +2,7 @@ from functions.database import init_database, Usuario, get_db_connection
import qrcode import qrcode
import os import os
from pathlib import Path from pathlib import Path
import pyotp
def create_admin_user(): def create_admin_user():
try: try:
@@ -15,39 +16,85 @@ def create_admin_user():
admin = db_session.query(Usuario).filter_by(username="admin").first() admin = db_session.query(Usuario).filter_by(username="admin").first()
if admin: if admin:
print("\n=== Detalhes do Usuário Admin ===") print("\n=== Usuário Admin Encontrado ===")
else:
print("\n=== Criando Novo Usuário Admin ===")
# Criar usuário admin com novo segredo OTP
admin = Usuario(
username="admin",
password="admin123",
is_admin=True
)
admin.email = "admin@example.com"
# Adicionar e fazer commit para obter o ID
db_session.add(admin)
db_session.commit()
# Recarregar o usuário do banco para garantir dados atualizados
admin = db_session.query(Usuario).filter_by(username="admin").first()
# Verificar e mostrar informações do OTP
print("\n=== Informações do Usuário Admin ===")
print(f"Username: admin") print(f"Username: admin")
print(f"Senha: admin123")
print(f"Email: {admin.email}") print(f"Email: {admin.email}")
print(f"OTP Secret: {admin.otp_secret}") print(f"Segredo OTP atual: {admin.otp_secret}")
# Gerar URI do OTP usando o segredo armazenado
totp = pyotp.TOTP(admin.otp_secret)
otp_uri = totp.provisioning_uri(
name=admin.username,
issuer_name="Sistema de Gestão"
)
# Configurar diretório para o QR Code # Configurar diretório para o QR Code
home = Path.home() home = Path.home()
qr_dir = home / '.local' / 'share' / 'controles' / 'qrcodes' qr_dir = home / '.local' / 'share' / 'controles' / 'qrcodes'
qr_dir.mkdir(parents=True, exist_ok=True) qr_dir.mkdir(parents=True, exist_ok=True)
# Gerar QR Code # Gerar e salvar QR Code
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(admin.get_otp_uri())
qr.make(fit=True)
# Salvar QR Code
qr_path = qr_dir / 'admin_qr.png' qr_path = qr_dir / 'admin_qr.png'
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(otp_uri)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white") img = qr.make_image(fill_color="black", back_color="white")
img.save(str(qr_path)) img.save(str(qr_path))
print(f"\nQR Code salvo em: {qr_path}") print("\n=== QR Code Gerado ===")
print("\nPasso a passo para configurar o OTP:") print(f"QR Code salvo em: {qr_path}")
print(f"URI do OTP: {otp_uri}")
# Gerar código atual para verificação
current_code = totp.now()
print("\n=== Verificação do OTP ===")
print(f"Código OTP atual: {current_code}")
print(f"Verificação do código: {totp.verify(current_code)}")
print("\n=== Instruções para Configuração ===")
print("1. Instale um aplicativo autenticador no seu celular") print("1. Instale um aplicativo autenticador no seu celular")
print(" (Google Authenticator, Microsoft Authenticator, etc)") print(" (Google Authenticator, Microsoft Authenticator, etc)")
print("2. Abra o aplicativo") print("2. Abra o aplicativo")
print("3. Selecione a opção para adicionar uma nova conta") print("3. Selecione a opção para adicionar uma nova conta")
print("4. Escaneie o QR Code gerado") print("4. Escaneie o QR Code salvo em:", qr_path)
print("\nOU, se preferir configuração manual:") print("\nOU configure manualmente:")
print(f"1. Use o segredo: {admin.otp_secret}") print(f"- Nome da conta: {admin.username}")
print("2. Nome da conta: admin") print(f"- Segredo: {admin.otp_secret}")
print("3. Tipo: Baseado em tempo (TOTP)") print("- Tipo: Baseado em tempo (TOTP)")
else: print("- Algoritmo: SHA1")
print("ERRO: Falha ao criar usuário admin!") print("- Dígitos: 6")
print("- Intervalo: 30 segundos")
# Verificação final
print("\n=== Teste de Verificação ===")
test_code = totp.now()
print(f"Código de teste: {test_code}")
is_valid = admin.verify_otp(test_code)
print(f"Verificação do código: {'Sucesso' if is_valid else 'Falha'}")
if not is_valid:
print("\nALERTA: Verificação do OTP falhou!")
print("Por favor, verifique se o segredo OTP está correto.")
except Exception as e: except Exception as e:
print(f"\nErro durante a execução: {e}") print(f"\nErro durante a execução: {e}")
@@ -58,4 +105,10 @@ def create_admin_user():
db_session.close() db_session.close()
if __name__ == "__main__": if __name__ == "__main__":
# Remover banco de dados existente para começar limpo
db_path = Path.home() / '.local' / 'share' / 'controles' / 'database.db'
if db_path.exists():
os.remove(db_path)
print("Banco de dados antigo removido.")
create_admin_user() create_admin_user()

View File

@@ -1,4 +1,4 @@
from sqlalchemy import create_engine, Column, Integer, String, Boolean, Numeric, Date, ForeignKey from sqlalchemy import create_engine, Column, Integer, String, Boolean, Numeric, Date, ForeignKey, DateTime
from sqlalchemy.orm import relationship, sessionmaker from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
@@ -190,42 +190,53 @@ class Usuario(Base):
id = Column(Integer, primary_key=True, autoincrement=True) id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(50), unique=True, nullable=False) username = Column(String(50), unique=True, nullable=False)
password_hash = Column(String(255), nullable=False) password_hash = Column(String(255), nullable=False)
email = Column(String(100), unique=True, nullable=True) email = Column(String(100), unique=True, nullable=False)
otp_secret = Column(String(32), nullable=True) otp_secret = Column(String(32))
role_id = Column(Integer, ForeignKey('roles.id'), nullable=True) role_id = Column(Integer, ForeignKey('roles.id'))
setor_id = Column(Integer, ForeignKey('setores.id'), nullable=True) setor_id = Column(Integer, ForeignKey('setores.id'))
ativo = Column(Boolean, default=True) ativo = Column(Boolean, default=True)
is_admin = Column(Boolean, default=False) is_admin = Column(Boolean, default=False)
ultimo_login = Column(DateTime)
ultimo_logout = Column(DateTime)
motivo_logout = Column(String(100))
role = relationship("Role", back_populates="usuarios") role = relationship("Role", back_populates="usuarios")
setor = relationship("Setor", back_populates="usuarios") setor = relationship("Setor", back_populates="usuarios")
def __init__(self, username, password, is_admin=False): def __init__(self, username, password, is_admin=False):
self.username = username self.username = username
self.set_password(password) self.password_hash = generate_password_hash(password)
self.otp_secret = pyotp.random_base32()
self.is_admin = is_admin self.is_admin = is_admin
self.otp_secret = pyotp.random_base32() # Gerar segredo OTP na criação
self.ativo = True self.ativo = True
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password): def check_password(self, password):
result = check_password_hash(self.password_hash, password) return check_password_hash(self.password_hash, password)
print(f"Verificação de senha para {self.username}: {'sucesso' if result else 'falha'}")
return result
def verify_otp(self, otp_code): def verify_otp(self, otp_code):
"""Verifica se o código OTP fornecido é válido"""
if not self.otp_secret:
print(f"Erro: Usuário {self.username} não tem segredo OTP configurado")
return False
totp = pyotp.TOTP(self.otp_secret) totp = pyotp.TOTP(self.otp_secret)
result = totp.verify(otp_code) is_valid = totp.verify(otp_code)
print(f"Verificação OTP para {self.username}") print(f"Verificando OTP para {self.username}")
print(f"Segredo: {self.otp_secret}")
print(f"Código fornecido: {otp_code}") print(f"Código fornecido: {otp_code}")
print(f"Resultado da verificação: {'válido' if result else 'inválido'}") print(f"Resultado: {'válido' if is_valid else 'inválido'}")
return result return is_valid
def get_otp_uri(self): def get_otp_uri(self):
"""Gera a URI para o QR code do OTP"""
if not self.otp_secret:
self.otp_secret = pyotp.random_base32()
totp = pyotp.TOTP(self.otp_secret) totp = pyotp.TOTP(self.otp_secret)
return totp.provisioning_uri(self.username, issuer_name="Sistema de Gestão") return totp.provisioning_uri(
name=self.username,
issuer_name="Sistema de Gestão"
)
class Role(Base): class Role(Base):
__tablename__ = 'roles' __tablename__ = 'roles'

View File

@@ -7,6 +7,7 @@
{{ bootstrap.load_css() }} {{ bootstrap.load_css() }}
</head> </head>
<body> <body>
{% if 'user_id' in session %}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container"> <div class="container">
<a class="navbar-brand" href="{{ url_for('home') }}">Sistema de Gestão</a> <a class="navbar-brand" href="{{ url_for('home') }}">Sistema de Gestão</a>
@@ -14,7 +15,7 @@
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav"> <ul class="navbar-nav me-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_militantes') }}">Militantes</a> <a class="nav-link" href="{{ url_for('listar_militantes') }}">Militantes</a>
</li> </li>
@@ -28,14 +29,24 @@
<a class="nav-link" href="{{ url_for('listar_materiais') }}">Materiais</a> <a class="nav-link" href="{{ url_for('listar_materiais') }}">Materiais</a>
</li> </li>
</ul> </ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">
<i class="fas fa-sign-out-alt"></i> Sair
</a>
</li>
</ul>
</div> </div>
</div> </div>
</nav> </nav>
{% endif %}
<div class="container mt-4"> <div class="container mt-4">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
{{ bootstrap.load_js() }} {{ bootstrap.load_js() }}
<!-- Adicionar Font Awesome para o ícone de Sair -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
</body> </body>
</html> </html>