src/eric7/WebBrowser/WebAuth/Fido2PinDialog.py

branch
eric7
changeset 10854
30c45bd597e6
child 10856
b19cefceca15
diff -r 1f651b204780 -r 30c45bd597e6 src/eric7/WebBrowser/WebAuth/Fido2PinDialog.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/WebBrowser/WebAuth/Fido2PinDialog.py	Fri Jul 19 18:06:48 2024 +0200
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2024 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to enter the current and potentially new PIN.
+"""
+
+import enum
+
+from PyQt6.QtCore import pyqtSlot
+from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QLineEdit
+
+from eric7.EricGui import EricPixmapCache
+
+from .Ui_Fido2PinDialog import Ui_Fido2PinDialog
+
+
+class Fido2PinDialogMode(enum.Enum):
+    """
+    Class defining the various PIN dialog mode.
+    """
+
+    GET = 0
+    SET = 1
+    CHANGE = 2
+
+
+class Fido2PinDialog(QDialog, Ui_Fido2PinDialog):
+    """
+    Class implementing a dialog to enter the current and potentially new PIN.
+    """
+
+    def __init__(self, mode, title, message, minLength, parent=None):
+        """
+        Constructor
+
+        @param mode mode of the dialog
+        @type Fido2PinDialogMode
+        @param title header title to be shown
+        @type str
+        @param message more decriptive text to be shown
+        @type str
+        @param minLength minimum PIN length
+        @type int
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+        self.pinButton.setIcon(EricPixmapCache.getIcon("showPassword"))
+        self.newPinButton.setIcon(EricPixmapCache.getIcon("showPassword"))
+
+        self.__minLength = minLength
+        self.__mode = mode
+
+        if title:
+            self.headerLabel.setText(f"<b>{title}</b>")
+        else:
+            self.headerLabel.setVisible(False)
+        if message:
+            self.descriptionLabel.setText(message)
+        else:
+            self.descriptionLabel.setVisible(False)
+        self.pinErrorLabel.setVisible(False)
+
+        if mode == Fido2PinDialogMode.GET:
+            self.newPinGroupBox.setVisible(False)
+        elif mode == Fido2PinDialogMode.SET:
+            self.pinLabel.setVisible(False)
+            self.pinEdit.setVisible(False)
+            self.pinButton.setVisible(False)
+        elif mode == Fido2PinDialogMode.CHANGE:
+            # all entries visible
+            pass
+
+        self.pinEdit.textEdited.connect(self.__checkPins)
+        self.newPinEdit.textEdited.connect(self.__checkPins)
+        self.confirmNewPinEdit.textEdited.connect(self.__checkPins)
+
+        self.__checkPins()
+
+    @pyqtSlot()
+    def __checkPins(self):
+        """
+        Private slot to check the entered PIN(s).
+
+        Appropriate error messages are shown in case of issues and the state of
+        the OK button is set accordingly.
+        """
+        messages = []
+
+        if (
+            self.__mode in (Fido2PinDialogMode.GET, Fido2PinDialogMode.CHANGE)
+            and not self.pinEdit.text()
+        ):
+            messages.append(self.tr("PIN must not be empty."))
+        if self.__mode in (Fido2PinDialogMode.SET, Fido2PinDialogMode.CHANGE):
+            if len(self.newPinEdit.text()) < self.__minLength:
+                messages.append(
+                    self.tr("New PIN is TOO short (minimum length: {0}).").format(
+                        self.__minLength
+                    )
+                )
+            if (
+                self.confirmNewPinEdit.isVisible()
+                and self.confirmNewPinEdit.text() != self.newPinEdit.text()
+            ):
+                messages.append("New PIN confirmation does not match.")
+        if (
+            self.__mode == Fido2PinDialogMode.CHANGE
+            and self.pinEdit.text() == self.newPinEdit.text()
+        ):
+            messages.append(self.tr("Old and new PIN must not be identical."))
+
+        self.__showPinErrors(messages)
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(
+            not bool(messages)
+        )
+
+    def __showPinErrors(self, errorMessages):
+        """
+        Private method to show some error messages.
+
+        @param errorMessages list of error messages
+        @type list of str
+        """
+        if not errorMessages:
+            self.pinErrorLabel.clear()
+            self.pinErrorLabel.setVisible(False)
+        else:
+            if len(errorMessages) == 1:
+                msg = errorMessages[0]
+            else:
+                msg = "<ul><li>{0}</li></ul>".format("</li><li>".join(errorMessages))
+            self.pinErrorLabel.setText(msg)
+            self.pinErrorLabel.setVisible(True)
+
+        self.adjustSize()
+
+    @pyqtSlot(bool)
+    def on_pinButton_toggled(self, checked):
+        """
+        Private slot to handle the toggling of the PIN visibility.
+
+        @param checked state of the PIN visibility button
+        @type bool
+        """
+        if checked:
+            self.pinButton.setIcon(EricPixmapCache.getIcon("hidePassword"))
+            self.pinEdit.setEchoMode(QLineEdit.EchoMode.Normal)
+        else:
+            self.pinButton.setIcon(EricPixmapCache.getIcon("showPassword"))
+            self.pinEdit.setEchoMode(QLineEdit.EchoMode.Password)
+
+    @pyqtSlot(bool)
+    def on_newPinButton_toggled(self, checked):
+        """
+        Private slot to handle the toggling of the new PIN visibility.
+
+        @param checked state of the new PIN visibility button
+        @type bool
+        """
+        if checked:
+            self.newPinButton.setIcon(EricPixmapCache.getIcon("hidePassword"))
+            self.newPinEdit.setEchoMode(QLineEdit.EchoMode.Normal)
+        else:
+            self.newPinButton.setIcon(EricPixmapCache.getIcon("showPassword"))
+            self.newPinEdit.setEchoMode(QLineEdit.EchoMode.Password)
+
+        self.confirmnewPinLabel.setVisible(not checked)
+        self.confirmPinnewEdit.setVisible(not checked)
+        self.on_newPinEdit_textEdited(self.newPinEdit.text())
+
+    def getPins(self):
+        """
+        Public method to get the entered PINs.
+
+        @return tuple containing the current and new PIN
+        @rtype tuple of (str, str)
+        """
+        if self.__mode == Fido2PinDialogMode.GET:
+            return self.pinEdit.text(), None
+        elif self.__mode == Fido2PinDialogMode.SET:
+            return None, self.newPinEdit.text()
+        elif self.__mode == Fido2PinDialogMode.GET:
+            return self.pinEdit.text(), self.newPinEdit.text()
+        else:
+            return None, None

eric ide

mercurial