adicionado timeout no login e botão de Sair
This commit is contained in:
117
create_admin.py
117
create_admin.py
@@ -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 ===")
|
||||||
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)")
|
|
||||||
else:
|
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:
|
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()
|
||||||
@@ -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'
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user