File indexing completed on 2025-02-16 13:11:46
0001 // vi: ts=8 sts=4 sw=4 0002 /* 0003 This file is part of the KDE libraries 0004 SPDX-FileCopyrightText: 1998 Pietro Iglio <iglio@fub.it> 0005 SPDX-FileCopyrightText: 1999, 2000 Geert Jansen <jansen@kde.org> 0006 SPDX-FileCopyrightText: 2004, 2005 Andrew Coles <andrew_coles@yahoo.co.uk> 0007 SPDX-FileCopyrightText: 2007 Michaƫl Larouche <larouche@kde.org> 0008 SPDX-FileCopyrightText: 2009 Christoph Feck <cfeck@kde.org> 0009 SPDX-FileCopyrightText: 2015 Elvis Angelaccio <elvis.angelaccio@kde.org> 0010 0011 SPDX-License-Identifier: LGPL-2.0-only 0012 */ 0013 0014 #include "knewpasswordwidget.h" 0015 #include "ui_knewpasswordwidget.h" 0016 0017 class KNewPasswordWidgetPrivate 0018 { 0019 Q_DECLARE_TR_FUNCTIONS(KNewPasswordWidget) 0020 0021 public: 0022 KNewPasswordWidgetPrivate(KNewPasswordWidget *parent) 0023 : q(parent) 0024 { 0025 } 0026 0027 void init(); 0028 void passwordChanged(); 0029 void toggleEchoMode(); 0030 int effectivePasswordLength(QStringView password); 0031 void updatePasswordStatus(KNewPasswordWidget::PasswordStatus status); 0032 0033 KNewPasswordWidget *const q; 0034 0035 KNewPasswordWidget::PasswordStatus passwordStatus = KNewPasswordWidget::WeakPassword; 0036 int minimumPasswordLength = 0; 0037 int passwordStrengthWarningLevel = 1; 0038 int reasonablePasswordLength = 8; 0039 0040 QAction *toggleEchoModeAction = nullptr; 0041 QColor backgroundWarningColor; 0042 QColor defaultBackgroundColor; 0043 0044 Ui::KNewPasswordWidget ui; 0045 }; 0046 0047 void KNewPasswordWidgetPrivate::init() 0048 { 0049 ui.setupUi(q); 0050 0051 const QString strengthBarWhatsThis( 0052 tr("The password strength meter gives an indication of the security " 0053 "of the password you have entered. To improve the strength of the password, try:" 0054 "<ul><li>using a longer password;</li>" 0055 "<li>using a mixture of upper- and lower-case letters;</li>" 0056 "<li>using numbers or symbols, such as #, as well as letters.</li></ul>", 0057 "@info:whatsthis")); 0058 ui.labelStrengthMeter->setWhatsThis(strengthBarWhatsThis); 0059 ui.strengthBar->setWhatsThis(strengthBarWhatsThis); 0060 0061 QObject::connect(ui.linePassword, &KPasswordLineEdit::echoModeChanged, q, [this]() { 0062 toggleEchoMode(); 0063 }); 0064 0065 QObject::connect(ui.linePassword, &KPasswordLineEdit::passwordChanged, q, [this]() { 0066 passwordChanged(); 0067 }); 0068 QObject::connect(ui.lineVerifyPassword, &QLineEdit::textChanged, q, [this]() { 0069 passwordChanged(); 0070 }); 0071 0072 defaultBackgroundColor = q->palette().color(QPalette::Active, QPalette::Base); 0073 backgroundWarningColor = defaultBackgroundColor; 0074 0075 passwordChanged(); 0076 } 0077 0078 void KNewPasswordWidgetPrivate::passwordChanged() 0079 { 0080 const QString password = ui.linePassword->password(); 0081 const QString verification = ui.lineVerifyPassword->text(); 0082 const bool match = (password == verification); 0083 const bool partialMatch = password.startsWith(verification); 0084 const int minPasswordLength = q->minimumPasswordLength(); 0085 0086 QPalette palette = q->palette(); 0087 palette.setColor(QPalette::Active, QPalette::Base, (match || partialMatch) ? defaultBackgroundColor : backgroundWarningColor); 0088 ui.lineVerifyPassword->setPalette(palette); 0089 0090 // Password strength calculator 0091 int pwstrength = 0092 (20 * ui.linePassword->password().length() + 80 * effectivePasswordLength(ui.linePassword->password())) / qMax(reasonablePasswordLength, 2); 0093 ui.strengthBar->setValue(qBound(0, pwstrength, 100)); 0094 0095 // update the current password status 0096 if (match || ui.lineVerifyPassword->isHidden()) { 0097 if (!q->allowEmptyPasswords() && ui.linePassword->password().isEmpty()) { 0098 updatePasswordStatus(KNewPasswordWidget::EmptyPasswordNotAllowed); 0099 } else if (ui.linePassword->password().length() < minPasswordLength) { 0100 updatePasswordStatus(KNewPasswordWidget::PasswordTooShort); 0101 } else if (ui.strengthBar && ui.strengthBar->value() < passwordStrengthWarningLevel) { 0102 updatePasswordStatus(KNewPasswordWidget::WeakPassword); 0103 } else { 0104 updatePasswordStatus(KNewPasswordWidget::StrongPassword); 0105 } 0106 } else { 0107 updatePasswordStatus(KNewPasswordWidget::PasswordNotVerified); 0108 } 0109 } 0110 0111 void KNewPasswordWidgetPrivate::toggleEchoMode() 0112 { 0113 if (ui.linePassword->lineEdit()->echoMode() == QLineEdit::Normal) { 0114 ui.lineVerifyPassword->hide(); 0115 ui.labelVerifyPassword->hide(); 0116 } else if (ui.linePassword->lineEdit()->echoMode() == QLineEdit::Password) { 0117 ui.lineVerifyPassword->show(); 0118 ui.labelVerifyPassword->show(); 0119 } 0120 passwordChanged(); 0121 } 0122 0123 int KNewPasswordWidgetPrivate::effectivePasswordLength(QStringView password) 0124 { 0125 enum Category { 0126 Digit, 0127 Upper, 0128 Vowel, 0129 Consonant, 0130 Special, 0131 }; 0132 0133 Category previousCategory = Vowel; 0134 static const QLatin1String vowels("aeiou"); 0135 int count = 0; 0136 0137 const int len = password.length(); 0138 for (int i = 0; i < len; ++i) { 0139 const QChar currentChar = password.at(i); 0140 if (!password.left(i).contains(currentChar)) { 0141 Category currentCategory; 0142 switch (currentChar.category()) { 0143 case QChar::Letter_Uppercase: 0144 currentCategory = Upper; 0145 break; 0146 case QChar::Letter_Lowercase: 0147 if (vowels.contains(currentChar)) { 0148 currentCategory = Vowel; 0149 } else { 0150 currentCategory = Consonant; 0151 } 0152 break; 0153 case QChar::Number_DecimalDigit: 0154 currentCategory = Digit; 0155 break; 0156 default: 0157 currentCategory = Special; 0158 break; 0159 } 0160 switch (currentCategory) { 0161 case Vowel: 0162 if (previousCategory != Consonant) { 0163 ++count; 0164 } 0165 break; 0166 case Consonant: 0167 if (previousCategory != Vowel) { 0168 ++count; 0169 } 0170 break; 0171 default: 0172 if (previousCategory != currentCategory) { 0173 ++count; 0174 } 0175 break; 0176 } 0177 previousCategory = currentCategory; 0178 } 0179 } 0180 return count; 0181 } 0182 0183 void KNewPasswordWidgetPrivate::updatePasswordStatus(KNewPasswordWidget::PasswordStatus status) 0184 { 0185 if (passwordStatus == status) { 0186 return; 0187 } 0188 0189 passwordStatus = status; 0190 Q_EMIT q->passwordStatusChanged(); 0191 } 0192 0193 KNewPasswordWidget::KNewPasswordWidget(QWidget *parent) 0194 : QWidget(parent) 0195 , d(new KNewPasswordWidgetPrivate(this)) 0196 { 0197 d->init(); 0198 } 0199 0200 KNewPasswordWidget::~KNewPasswordWidget() = default; 0201 0202 KNewPasswordWidget::PasswordStatus KNewPasswordWidget::passwordStatus() const 0203 { 0204 return d->passwordStatus; 0205 } 0206 0207 bool KNewPasswordWidget::allowEmptyPasswords() const 0208 { 0209 return d->minimumPasswordLength == 0; 0210 } 0211 0212 int KNewPasswordWidget::minimumPasswordLength() const 0213 { 0214 return d->minimumPasswordLength; 0215 } 0216 0217 int KNewPasswordWidget::maximumPasswordLength() const 0218 { 0219 return d->ui.linePassword->lineEdit()->maxLength(); 0220 } 0221 0222 int KNewPasswordWidget::reasonablePasswordLength() const 0223 { 0224 return d->reasonablePasswordLength; 0225 } 0226 0227 int KNewPasswordWidget::passwordStrengthWarningLevel() const 0228 { 0229 return d->passwordStrengthWarningLevel; 0230 } 0231 0232 QColor KNewPasswordWidget::backgroundWarningColor() const 0233 { 0234 return d->backgroundWarningColor; 0235 } 0236 0237 bool KNewPasswordWidget::isPasswordStrengthMeterVisible() const 0238 { 0239 return d->ui.labelStrengthMeter->isVisible() && d->ui.strengthBar->isVisible(); 0240 } 0241 0242 bool KNewPasswordWidget::isRevealPasswordAvailable() const 0243 { 0244 return d->ui.linePassword->isRevealPasswordAvailable(); 0245 } 0246 0247 QString KNewPasswordWidget::password() const 0248 { 0249 return d->ui.linePassword->password(); 0250 } 0251 0252 void KNewPasswordWidget::setAllowEmptyPasswords(bool allowed) 0253 { 0254 setMinimumPasswordLength(allowed ? 0 : 1); 0255 d->passwordChanged(); 0256 } 0257 0258 void KNewPasswordWidget::setMinimumPasswordLength(int minLength) 0259 { 0260 d->minimumPasswordLength = minLength; 0261 d->passwordChanged(); 0262 } 0263 0264 void KNewPasswordWidget::setMaximumPasswordLength(int maxLength) 0265 { 0266 if (maxLength < minimumPasswordLength()) { 0267 maxLength = minimumPasswordLength(); 0268 } 0269 0270 d->ui.linePassword->lineEdit()->setMaxLength(maxLength); 0271 d->ui.lineVerifyPassword->setMaxLength(maxLength); 0272 } 0273 0274 // reasonable password length code contributed by Steffen Mthing 0275 0276 void KNewPasswordWidget::setReasonablePasswordLength(int reasonableLength) 0277 { 0278 d->reasonablePasswordLength = qBound(1, reasonableLength, maximumPasswordLength()); 0279 } 0280 0281 void KNewPasswordWidget::setPasswordStrengthWarningLevel(int warningLevel) 0282 { 0283 d->passwordStrengthWarningLevel = qBound(0, warningLevel, 99); 0284 } 0285 0286 void KNewPasswordWidget::setBackgroundWarningColor(const QColor &color) 0287 { 0288 d->backgroundWarningColor = color; 0289 update(); 0290 } 0291 0292 void KNewPasswordWidget::setPasswordStrengthMeterVisible(bool visible) 0293 { 0294 d->ui.labelStrengthMeter->setVisible(visible); 0295 d->ui.strengthBar->setVisible(visible); 0296 } 0297 0298 void KNewPasswordWidget::setRevealPasswordAvailable(bool reveal) 0299 { 0300 d->ui.linePassword->setRevealPasswordAvailable(reveal); 0301 } 0302 0303 #include "moc_knewpasswordwidget.cpp"