|
1 # -*- coding: utf-8 -*- |
|
2 # Copyright (c) 2024 Detlev Offenbach <detlev@die-offenbachs.de> |
|
3 # |
|
4 |
|
5 """ |
|
6 Module implementing a dialog to enter the current and potentially new PIN. |
|
7 """ |
|
8 |
|
9 import enum |
|
10 |
|
11 from PyQt6.QtCore import pyqtSlot |
|
12 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QLineEdit |
|
13 |
|
14 from eric7.EricGui import EricPixmapCache |
|
15 |
|
16 from .Ui_Fido2PinDialog import Ui_Fido2PinDialog |
|
17 |
|
18 |
|
19 class Fido2PinDialogMode(enum.Enum): |
|
20 """ |
|
21 Class defining the various PIN dialog mode. |
|
22 """ |
|
23 |
|
24 GET = 0 |
|
25 SET = 1 |
|
26 CHANGE = 2 |
|
27 |
|
28 |
|
29 class Fido2PinDialog(QDialog, Ui_Fido2PinDialog): |
|
30 """ |
|
31 Class implementing a dialog to enter the current and potentially new PIN. |
|
32 """ |
|
33 |
|
34 def __init__(self, mode, title, message, minLength, parent=None): |
|
35 """ |
|
36 Constructor |
|
37 |
|
38 @param mode mode of the dialog |
|
39 @type Fido2PinDialogMode |
|
40 @param title header title to be shown |
|
41 @type str |
|
42 @param message more decriptive text to be shown |
|
43 @type str |
|
44 @param minLength minimum PIN length |
|
45 @type int |
|
46 @param parent reference to the parent widget (defaults to None) |
|
47 @type QWidget (optional) |
|
48 """ |
|
49 super().__init__(parent) |
|
50 self.setupUi(self) |
|
51 |
|
52 self.pinButton.setIcon(EricPixmapCache.getIcon("showPassword")) |
|
53 self.newPinButton.setIcon(EricPixmapCache.getIcon("showPassword")) |
|
54 |
|
55 self.__minLength = minLength |
|
56 self.__mode = mode |
|
57 |
|
58 if title: |
|
59 self.headerLabel.setText(f"<b>{title}</b>") |
|
60 else: |
|
61 self.headerLabel.setVisible(False) |
|
62 if message: |
|
63 self.descriptionLabel.setText(message) |
|
64 else: |
|
65 self.descriptionLabel.setVisible(False) |
|
66 self.pinErrorLabel.setVisible(False) |
|
67 |
|
68 if mode == Fido2PinDialogMode.GET: |
|
69 self.newPinGroupBox.setVisible(False) |
|
70 elif mode == Fido2PinDialogMode.SET: |
|
71 self.pinLabel.setVisible(False) |
|
72 self.pinEdit.setVisible(False) |
|
73 self.pinButton.setVisible(False) |
|
74 elif mode == Fido2PinDialogMode.CHANGE: |
|
75 # all entries visible |
|
76 pass |
|
77 |
|
78 self.pinEdit.textEdited.connect(self.__checkPins) |
|
79 self.newPinEdit.textEdited.connect(self.__checkPins) |
|
80 self.confirmNewPinEdit.textEdited.connect(self.__checkPins) |
|
81 |
|
82 self.__checkPins() |
|
83 |
|
84 @pyqtSlot() |
|
85 def __checkPins(self): |
|
86 """ |
|
87 Private slot to check the entered PIN(s). |
|
88 |
|
89 Appropriate error messages are shown in case of issues and the state of |
|
90 the OK button is set accordingly. |
|
91 """ |
|
92 messages = [] |
|
93 |
|
94 if ( |
|
95 self.__mode in (Fido2PinDialogMode.GET, Fido2PinDialogMode.CHANGE) |
|
96 and not self.pinEdit.text() |
|
97 ): |
|
98 messages.append(self.tr("PIN must not be empty.")) |
|
99 if self.__mode in (Fido2PinDialogMode.SET, Fido2PinDialogMode.CHANGE): |
|
100 if len(self.newPinEdit.text()) < self.__minLength: |
|
101 messages.append( |
|
102 self.tr("New PIN is TOO short (minimum length: {0}).").format( |
|
103 self.__minLength |
|
104 ) |
|
105 ) |
|
106 if ( |
|
107 self.confirmNewPinEdit.isVisible() |
|
108 and self.confirmNewPinEdit.text() != self.newPinEdit.text() |
|
109 ): |
|
110 messages.append("New PIN confirmation does not match.") |
|
111 if ( |
|
112 self.__mode == Fido2PinDialogMode.CHANGE |
|
113 and self.pinEdit.text() == self.newPinEdit.text() |
|
114 ): |
|
115 messages.append(self.tr("Old and new PIN must not be identical.")) |
|
116 |
|
117 self.__showPinErrors(messages) |
|
118 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( |
|
119 not bool(messages) |
|
120 ) |
|
121 |
|
122 def __showPinErrors(self, errorMessages): |
|
123 """ |
|
124 Private method to show some error messages. |
|
125 |
|
126 @param errorMessages list of error messages |
|
127 @type list of str |
|
128 """ |
|
129 if not errorMessages: |
|
130 self.pinErrorLabel.clear() |
|
131 self.pinErrorLabel.setVisible(False) |
|
132 else: |
|
133 if len(errorMessages) == 1: |
|
134 msg = errorMessages[0] |
|
135 else: |
|
136 msg = "<ul><li>{0}</li></ul>".format("</li><li>".join(errorMessages)) |
|
137 self.pinErrorLabel.setText(msg) |
|
138 self.pinErrorLabel.setVisible(True) |
|
139 |
|
140 self.adjustSize() |
|
141 |
|
142 @pyqtSlot(bool) |
|
143 def on_pinButton_toggled(self, checked): |
|
144 """ |
|
145 Private slot to handle the toggling of the PIN visibility. |
|
146 |
|
147 @param checked state of the PIN visibility button |
|
148 @type bool |
|
149 """ |
|
150 if checked: |
|
151 self.pinButton.setIcon(EricPixmapCache.getIcon("hidePassword")) |
|
152 self.pinEdit.setEchoMode(QLineEdit.EchoMode.Normal) |
|
153 else: |
|
154 self.pinButton.setIcon(EricPixmapCache.getIcon("showPassword")) |
|
155 self.pinEdit.setEchoMode(QLineEdit.EchoMode.Password) |
|
156 |
|
157 @pyqtSlot(bool) |
|
158 def on_newPinButton_toggled(self, checked): |
|
159 """ |
|
160 Private slot to handle the toggling of the new PIN visibility. |
|
161 |
|
162 @param checked state of the new PIN visibility button |
|
163 @type bool |
|
164 """ |
|
165 if checked: |
|
166 self.newPinButton.setIcon(EricPixmapCache.getIcon("hidePassword")) |
|
167 self.newPinEdit.setEchoMode(QLineEdit.EchoMode.Normal) |
|
168 else: |
|
169 self.newPinButton.setIcon(EricPixmapCache.getIcon("showPassword")) |
|
170 self.newPinEdit.setEchoMode(QLineEdit.EchoMode.Password) |
|
171 |
|
172 self.confirmnewPinLabel.setVisible(not checked) |
|
173 self.confirmPinnewEdit.setVisible(not checked) |
|
174 self.on_newPinEdit_textEdited(self.newPinEdit.text()) |
|
175 |
|
176 def getPins(self): |
|
177 """ |
|
178 Public method to get the entered PINs. |
|
179 |
|
180 @return tuple containing the current and new PIN |
|
181 @rtype tuple of (str, str) |
|
182 """ |
|
183 if self.__mode == Fido2PinDialogMode.GET: |
|
184 return self.pinEdit.text(), None |
|
185 elif self.__mode == Fido2PinDialogMode.SET: |
|
186 return None, self.newPinEdit.text() |
|
187 elif self.__mode == Fido2PinDialogMode.GET: |
|
188 return self.pinEdit.text(), self.newPinEdit.text() |
|
189 else: |
|
190 return None, None |