Initial release
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
from PyQt6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
||||
QPushButton, QProgressBar, QFrame,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QTimer
|
||||
from PyQt6.QtGui import QFont
|
||||
from app.core import mfa as mfa_core
|
||||
|
||||
|
||||
class MFADialog(QDialog):
|
||||
def __init__(self, secret: str, parent=None):
|
||||
super().__init__(parent)
|
||||
self._secret = secret
|
||||
self._verified = False
|
||||
self.setWindowTitle("WGSecure — Authentification MFA")
|
||||
self.setFixedWidth(340)
|
||||
self.setModal(True)
|
||||
self._build_ui()
|
||||
self._timer = QTimer(self)
|
||||
self._timer.timeout.connect(self._tick)
|
||||
self._timer.start(500)
|
||||
self._tick()
|
||||
|
||||
def _build_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(12)
|
||||
layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
title = QLabel("Vérification en deux étapes")
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
f = QFont()
|
||||
f.setPointSize(13)
|
||||
f.setBold(True)
|
||||
title.setFont(f)
|
||||
layout.addWidget(title)
|
||||
|
||||
sub = QLabel("Entrez le code à 6 chiffres de votre application d'authentification.")
|
||||
sub.setWordWrap(True)
|
||||
sub.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
sub.setStyleSheet("color: #555;")
|
||||
layout.addWidget(sub)
|
||||
|
||||
sep = QFrame()
|
||||
sep.setFrameShape(QFrame.Shape.HLine)
|
||||
sep.setStyleSheet("color: #ddd;")
|
||||
layout.addWidget(sep)
|
||||
|
||||
self._code_input = QLineEdit()
|
||||
self._code_input.setPlaceholderText("000 000")
|
||||
self._code_input.setMaxLength(7)
|
||||
self._code_input.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
f2 = QFont("Courier", 20)
|
||||
f2.setLetterSpacing(QFont.SpacingType.AbsoluteSpacing, 4)
|
||||
self._code_input.setFont(f2)
|
||||
self._code_input.setStyleSheet(
|
||||
"padding: 8px; border: 2px solid #3498db; border-radius: 6px;"
|
||||
)
|
||||
self._code_input.returnPressed.connect(self._verify)
|
||||
layout.addWidget(self._code_input)
|
||||
|
||||
self._error_label = QLabel("")
|
||||
self._error_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self._error_label.setStyleSheet("color: #e74c3c; font-weight: bold;")
|
||||
layout.addWidget(self._error_label)
|
||||
|
||||
self._progress = QProgressBar()
|
||||
self._progress.setRange(0, 30)
|
||||
self._progress.setTextVisible(False)
|
||||
self._progress.setFixedHeight(6)
|
||||
self._progress.setStyleSheet(
|
||||
"QProgressBar { border-radius: 3px; background: #ecf0f1; }"
|
||||
"QProgressBar::chunk { background: #3498db; border-radius: 3px; }"
|
||||
)
|
||||
layout.addWidget(self._progress)
|
||||
|
||||
self._timer_label = QLabel("")
|
||||
self._timer_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self._timer_label.setStyleSheet("color: #999; font-size: 11px;")
|
||||
layout.addWidget(self._timer_label)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
btn_cancel = QPushButton("Annuler")
|
||||
btn_cancel.setStyleSheet(
|
||||
"QPushButton { padding: 8px 16px; border-radius: 5px; "
|
||||
"background: #ecf0f1; } QPushButton:hover { background: #bdc3c7; }"
|
||||
)
|
||||
btn_cancel.clicked.connect(self.reject)
|
||||
|
||||
self._btn_ok = QPushButton("Vérifier")
|
||||
self._btn_ok.setDefault(True)
|
||||
self._btn_ok.setStyleSheet(
|
||||
"QPushButton { padding: 8px 20px; border-radius: 5px; "
|
||||
"background: #3498db; color: white; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #2980b9; }"
|
||||
)
|
||||
self._btn_ok.clicked.connect(self._verify)
|
||||
btn_row.addWidget(btn_cancel)
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(self._btn_ok)
|
||||
layout.addLayout(btn_row)
|
||||
|
||||
def _tick(self):
|
||||
remaining = mfa_core.time_remaining()
|
||||
self._progress.setValue(remaining)
|
||||
self._timer_label.setText(f"Code valide encore {remaining}s")
|
||||
|
||||
def _verify(self):
|
||||
raw = self._code_input.text().replace(" ", "").replace("-", "")
|
||||
if len(raw) != 6 or not raw.isdigit():
|
||||
self._error_label.setText("Entrez exactement 6 chiffres.")
|
||||
self._code_input.setStyleSheet(
|
||||
"padding: 8px; border: 2px solid #e74c3c; border-radius: 6px;"
|
||||
)
|
||||
return
|
||||
if mfa_core.verify_code(self._secret, raw):
|
||||
self._verified = True
|
||||
self.accept()
|
||||
else:
|
||||
self._error_label.setText("Code incorrect. Réessayez.")
|
||||
self._code_input.clear()
|
||||
self._code_input.setStyleSheet(
|
||||
"padding: 8px; border: 2px solid #e74c3c; border-radius: 6px;"
|
||||
)
|
||||
|
||||
def is_verified(self) -> bool:
|
||||
return self._verified
|
||||
|
||||
def closeEvent(self, event):
|
||||
self._timer.stop()
|
||||
super().closeEvent(event)
|
||||
Reference in New Issue
Block a user