# -*- coding: utf-8 -*- from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import ( QAction, QDialog, QGridLayout, QGroupBox, QLabel, QLineEdit, QProgressBar, QSizePolicy, QSpacerItem, ) from zxcvbn import zxcvbn from gridsync import resource from gridsync.gui.font import Font class PasswordDialog(QDialog): def __init__(self, parent=None, help_text="", show_stats=True): super(PasswordDialog, self).__init__(parent) self.setMinimumWidth(400) self.label = QLabel("Password:") self.label.setFont(Font(14)) self.label.setStyleSheet("color: gray") self.lineedit = QLineEdit(self) self.lineedit.setFont(Font(14)) self.lineedit.setEchoMode(QLineEdit.Password) self.action = QAction(QIcon(resource("eye.png")), "Toggle visibility") self.action.triggered.connect(self.toggle_visibility) self.lineedit.addAction(self.action, QLineEdit.TrailingPosition) self.lineedit.returnPressed.connect(self.accept) layout = QGridLayout(self) layout.addWidget(self.label, 1, 1) layout.addWidget(self.lineedit, 2, 1) if show_stats: self.progressbar = QProgressBar() self.progressbar.setMaximum(4) self.progressbar.setTextVisible(False) self.progressbar.setFixedHeight(5) self.progressbar.setStyleSheet( "QProgressBar { background-color: transparent }" "QProgressBar::chunk { background-color: gray }" ) self.rating_label = QLabel() self.rating_label.setAlignment(Qt.AlignRight) self.time_label = QLabel() self.time_label.setStyleSheet("color: gray") layout.addWidget(self.progressbar, 3, 1) layout.addWidget(self.time_label, 4, 1) layout.addWidget(self.rating_label, 4, 1) self.lineedit.textChanged.connect(self.update_stats) self.update_color("transparent") if help_text: gbox = QGroupBox() gbox_layout = QGridLayout() gbox_label = QLabel(help_text) gbox_label.setWordWrap(True) gbox_label.setAlignment(Qt.AlignCenter) gbox_label.setStyleSheet("color: gray") gbox_layout.addWidget(gbox_label) gbox.setLayout(gbox_layout) layout.addWidget(gbox, 5, 1) layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 6, 1) def update_color(self, color): self.rating_label.setStyleSheet("QLabel {{ color: {} }}".format(color)) self.progressbar.setStyleSheet( "QProgressBar {{ background-color: transparent }}" "QProgressBar::chunk {{ background-color: {} }}".format(color) ) def toggle_visibility(self): if self.lineedit.echoMode() == QLineEdit.Password: self.lineedit.setEchoMode(QLineEdit.Normal) else: self.lineedit.setEchoMode(QLineEdit.Password) def update_stats(self, text): # noqa: max-complexity=11 XXX if not text: self.time_label.setText("") self.rating_label.setText("") self.progressbar.setValue(0) return res = zxcvbn(text) t = res["crack_times_display"]["offline_slow_hashing_1e4_per_second"] self.time_label.setText("Time to crack: {}".format(t)) s = res["crack_times_seconds"]["offline_slow_hashing_1e4_per_second"] seconds = int(s) if seconds == 0: self.rating_label.setText("Very weak") self.update_color("lightgray") self.rating_label.setStyleSheet("QLabel { color: gray }") self.progressbar.setValue(1) elif seconds < 86400: # 1 day self.rating_label.setText("Weak") self.update_color("red") self.progressbar.setValue(1) elif seconds < 2592000: # 1 month self.rating_label.setText("Alright") self.update_color("orange") self.progressbar.setValue(2) elif seconds < 3153600000: # 100 years self.rating_label.setText("Good") self.update_color("#9CC259") self.progressbar.setValue(3) else: # > 100 years self.rating_label.setText("Excellent") self.update_color("#00B400") self.progressbar.setValue(4) warning = res["feedback"]["warning"] try: suggestion = "Suggestion: " + res["feedback"]["suggestions"][0] except IndexError: suggestion = None if warning and suggestion: self.rating_label.setToolTip(warning + "\n\n" + suggestion) elif warning: self.rating_label.setToolTip(warning) elif suggestion: self.rating_label.setToolTip(suggestion) else: self.rating_label.setToolTip(None) def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.reject() @staticmethod def get_password(parent=None, label=None, help_text="", show_stats=True): dialog = PasswordDialog(parent, help_text, show_stats) if label: dialog.label.setText(label) result = dialog.exec_() return (dialog.lineedit.text(), result)