From 620abb96f2c2ce5bc499a5b2a6d30eb6201516dc Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sat, 4 Jan 2025 09:38:02 -0500 Subject: [PATCH] Implement Secure Input Field mode on macOS * Fixes #4738 * Also fixes flaky handling of caps lock detection events --- src/gui/PasswordWidget.cpp | 20 +++++++++++++------- src/gui/PasswordWidget.h | 5 ++--- src/gui/osutils/OSUtilsBase.h | 5 +++++ src/gui/osutils/macutils/MacUtils.cpp | 9 +++++++++ src/gui/osutils/macutils/MacUtils.h | 1 + src/gui/osutils/nixutils/NixUtils.cpp | 6 ++++++ src/gui/osutils/nixutils/NixUtils.h | 1 + src/gui/osutils/winutils/WinUtils.cpp | 6 ++++++ src/gui/osutils/winutils/WinUtils.h | 1 + 9 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/gui/PasswordWidget.cpp b/src/gui/PasswordWidget.cpp index 2b2d9057b3..03751c8b11 100644 --- a/src/gui/PasswordWidget.cpp +++ b/src/gui/PasswordWidget.cpp @@ -38,6 +38,7 @@ PasswordWidget::PasswordWidget(QWidget* parent) { m_ui->setupUi(this); setFocusProxy(m_ui->passwordEdit); + m_ui->passwordEdit->installEventFilter(this); const QIcon errorIcon = icons()->icon("dialog-error"); m_errorAction = m_ui->passwordEdit->addAction(errorIcon, QLineEdit::TrailingPosition); @@ -223,14 +224,19 @@ void PasswordWidget::updateRepeatStatus() } } -bool PasswordWidget::event(QEvent* event) +bool PasswordWidget::eventFilter(QObject* watched, QEvent* event) { - if (isVisible() - && (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease - || event->type() == QEvent::FocusIn)) { - checkCapslockState(); + if (watched == m_ui->passwordEdit) { + auto type = event->type(); + if (isVisible() && (type == QEvent::KeyPress || type == QEvent::KeyRelease || type == QEvent::FocusIn)) { + checkCapslockState(); + } + if (type == QEvent::FocusIn || type == QEvent::FocusOut) { + osUtils->setUserInputProtection(type == QEvent::FocusIn); + } } - return QWidget::event(event); + // Continue with normal operations + return false; } void PasswordWidget::checkCapslockState() @@ -306,4 +312,4 @@ void PasswordWidget::updatePasswordStrength(const QString& password) break; } -} \ No newline at end of file +} diff --git a/src/gui/PasswordWidget.h b/src/gui/PasswordWidget.h index 5ad1c4f5c9..6049d2908c 100644 --- a/src/gui/PasswordWidget.h +++ b/src/gui/PasswordWidget.h @@ -44,6 +44,8 @@ class PasswordWidget : public QWidget bool isPasswordVisible() const; QString text(); + bool eventFilter(QObject* watched, QEvent* event) override; + signals: void textChanged(QString text); @@ -57,9 +59,6 @@ public slots: void setEchoMode(QLineEdit::EchoMode mode); void setClearButtonEnabled(bool enabled); -protected: - bool event(QEvent* event) override; - private slots: void popupPasswordGenerator(); void updateRepeatStatus(); diff --git a/src/gui/osutils/OSUtilsBase.h b/src/gui/osutils/OSUtilsBase.h index 5b827b83be..11d739fde6 100644 --- a/src/gui/osutils/OSUtilsBase.h +++ b/src/gui/osutils/OSUtilsBase.h @@ -56,6 +56,11 @@ class OSUtilsBase : public QObject */ virtual bool isCapslockEnabled() = 0; + /** + * @param enable Toggle protection on user input (if available). + */ + virtual void setUserInputProtection(bool enable) = 0; + virtual void registerNativeEventFilter() = 0; virtual bool registerGlobalShortcut(const QString& name, diff --git a/src/gui/osutils/macutils/MacUtils.cpp b/src/gui/osutils/macutils/MacUtils.cpp index 15f55d94c7..893ec8fcc0 100644 --- a/src/gui/osutils/macutils/MacUtils.cpp +++ b/src/gui/osutils/macutils/MacUtils.cpp @@ -150,6 +150,15 @@ bool MacUtils::isCapslockEnabled() #endif } +void MacUtils::setUserInputProtection(bool enable) +{ + if (enable) { + EnableSecureEventInput(); + } else { + DisableSecureEventInput(); + } +} + /** * Toggle application state between foreground app and UIElement app. * Foreground apps have dock icons, UIElement apps do not. diff --git a/src/gui/osutils/macutils/MacUtils.h b/src/gui/osutils/macutils/MacUtils.h index 16cda54e24..5e0e121d5e 100644 --- a/src/gui/osutils/macutils/MacUtils.h +++ b/src/gui/osutils/macutils/MacUtils.h @@ -40,6 +40,7 @@ class MacUtils : public OSUtilsBase bool isLaunchAtStartupEnabled() const override; void setLaunchAtStartup(bool enable) override; bool isCapslockEnabled() override; + void setUserInputProtection(bool enable) override; WId activeWindow(); bool raiseWindow(WId pid); diff --git a/src/gui/osutils/nixutils/NixUtils.cpp b/src/gui/osutils/nixutils/NixUtils.cpp index c2ca1d4daa..30b7e21e4b 100644 --- a/src/gui/osutils/nixutils/NixUtils.cpp +++ b/src/gui/osutils/nixutils/NixUtils.cpp @@ -235,6 +235,12 @@ bool NixUtils::isCapslockEnabled() return false; } +void NixUtils::setUserInputProtection(bool enable) +{ + // Linux does not support this feature + Q_UNUSED(enable) +} + void NixUtils::registerNativeEventFilter() { qApp->installNativeEventFilter(this); diff --git a/src/gui/osutils/nixutils/NixUtils.h b/src/gui/osutils/nixutils/NixUtils.h index c3caca6b7f..9be835ff9f 100644 --- a/src/gui/osutils/nixutils/NixUtils.h +++ b/src/gui/osutils/nixutils/NixUtils.h @@ -35,6 +35,7 @@ class NixUtils : public OSUtilsBase, QAbstractNativeEventFilter bool isLaunchAtStartupEnabled() const override; void setLaunchAtStartup(bool enable) override; bool isCapslockEnabled() override; + void setUserInputProtection(bool enable) override; void registerNativeEventFilter() override; diff --git a/src/gui/osutils/winutils/WinUtils.cpp b/src/gui/osutils/winutils/WinUtils.cpp index 670b357e2e..a159769324 100644 --- a/src/gui/osutils/winutils/WinUtils.cpp +++ b/src/gui/osutils/winutils/WinUtils.cpp @@ -136,6 +136,12 @@ bool WinUtils::isCapslockEnabled() return GetKeyState(VK_CAPITAL) == 1; } +void WinUtils::setUserInputProtection(bool enable) +{ + // Windows does not support this feature + Q_UNUSED(enable) +} + bool WinUtils::isHighContrastMode() const { QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat); diff --git a/src/gui/osutils/winutils/WinUtils.h b/src/gui/osutils/winutils/WinUtils.h index 9e4492a5f5..9278c9d606 100644 --- a/src/gui/osutils/winutils/WinUtils.h +++ b/src/gui/osutils/winutils/WinUtils.h @@ -44,6 +44,7 @@ class WinUtils : public OSUtilsBase, QAbstractNativeEventFilter bool isLaunchAtStartupEnabled() const override; void setLaunchAtStartup(bool enable) override; bool isCapslockEnabled() override; + void setUserInputProtection(bool enable) override; bool isHighContrastMode() const; void registerNativeEventFilter() override;