diff --git a/create_admin.py b/create_admin.py index ee162d4..6544061 100644 --- a/create_admin.py +++ b/create_admin.py @@ -2,6 +2,7 @@ from functions.database import init_database, Usuario, get_db_connection import qrcode import os from pathlib import Path +import pyotp def create_admin_user(): try: @@ -15,40 +16,86 @@ def create_admin_user(): admin = db_session.query(Usuario).filter_by(username="admin").first() if admin: - print("\n=== Detalhes do Usuário Admin ===") - print(f"Username: admin") - print(f"Email: {admin.email}") - print(f"OTP Secret: {admin.otp_secret}") - - # Configurar diretório para o QR Code - home = Path.home() - qr_dir = home / '.local' / 'share' / 'controles' / 'qrcodes' - qr_dir.mkdir(parents=True, exist_ok=True) - - # Gerar 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' - img = qr.make_image(fill_color="black", back_color="white") - img.save(str(qr_path)) - - print(f"\nQR Code salvo em: {qr_path}") - print("\nPasso a passo para configurar o OTP:") - print("1. Instale um aplicativo autenticador no seu celular") - print(" (Google Authenticator, Microsoft Authenticator, etc)") - print("2. Abra o aplicativo") - print("3. Selecione a opção para adicionar uma nova conta") - print("4. Escaneie o QR Code gerado") - print("\nOU, se preferir configuração manual:") - print(f"1. Use o segredo: {admin.otp_secret}") - print("2. Nome da conta: admin") - print("3. Tipo: Baseado em tempo (TOTP)") + print("\n=== Usuário Admin Encontrado ===") else: - print("ERRO: Falha ao criar usuário admin!") + 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"Senha: admin123") + print(f"Email: {admin.email}") + 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 + home = Path.home() + qr_dir = home / '.local' / 'share' / 'controles' / 'qrcodes' + qr_dir.mkdir(parents=True, exist_ok=True) + + # Gerar e salvar QR Code + 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.save(str(qr_path)) + + print("\n=== QR Code Gerado ===") + 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(" (Google Authenticator, Microsoft Authenticator, etc)") + print("2. Abra o aplicativo") + print("3. Selecione a opção para adicionar uma nova conta") + print("4. Escaneie o QR Code salvo em:", qr_path) + print("\nOU configure manualmente:") + print(f"- Nome da conta: {admin.username}") + print(f"- Segredo: {admin.otp_secret}") + print("- Tipo: Baseado em tempo (TOTP)") + print("- Algoritmo: SHA1") + 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: print(f"\nErro durante a execução: {e}") import traceback @@ -58,4 +105,10 @@ def create_admin_user(): db_session.close() 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() \ No newline at end of file diff --git a/functions/database.py b/functions/database.py index 56504d0..ebb9ce1 100644 --- a/functions/database.py +++ b/functions/database.py @@ -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.ext.declarative import declarative_base 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) username = Column(String(50), unique=True, nullable=False) password_hash = Column(String(255), nullable=False) - email = Column(String(100), unique=True, nullable=True) - otp_secret = Column(String(32), nullable=True) - role_id = Column(Integer, ForeignKey('roles.id'), nullable=True) - setor_id = Column(Integer, ForeignKey('setores.id'), nullable=True) + email = Column(String(100), unique=True, nullable=False) + 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)) + role = relationship("Role", back_populates="usuarios") setor = relationship("Setor", back_populates="usuarios") def __init__(self, username, password, is_admin=False): self.username = username - self.set_password(password) - self.otp_secret = pyotp.random_base32() + self.password_hash = generate_password_hash(password) self.is_admin = is_admin + self.otp_secret = pyotp.random_base32() # Gerar segredo OTP na criação self.ativo = True - def set_password(self, password): - self.password_hash = generate_password_hash(password) - def check_password(self, password): - result = check_password_hash(self.password_hash, password) - print(f"Verificação de senha para {self.username}: {'sucesso' if result else 'falha'}") - return result + return check_password_hash(self.password_hash, password) 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) - result = totp.verify(otp_code) - print(f"Verificação OTP para {self.username}") + is_valid = totp.verify(otp_code) + print(f"Verificando OTP para {self.username}") + print(f"Segredo: {self.otp_secret}") print(f"Código fornecido: {otp_code}") - print(f"Resultado da verificação: {'válido' if result else 'inválido'}") - return result + print(f"Resultado: {'válido' if is_valid else 'inválido'}") + return is_valid 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) - 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): __tablename__ = 'roles' diff --git a/templates/base.html b/templates/base.html index d2f91cc..0a4cf99 100644 --- a/templates/base.html +++ b/templates/base.html @@ -7,6 +7,7 @@ {{ bootstrap.load_css() }} + {% if 'user_id' in session %} + {% endif %}
{% block content %}{% endblock %}
{{ bootstrap.load_js() }} + + \ No newline at end of file