Implement Secure Input Field mode on macOS

* Fixes #4738
* Also fixes flaky handling of caps lock detection events
This commit is contained in:
Jonathan White 2025-01-04 09:38:02 -05:00
parent edab0faa94
commit 620abb96f2
9 changed files with 44 additions and 10 deletions

View File

@ -38,6 +38,7 @@ PasswordWidget::PasswordWidget(QWidget* parent)
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
setFocusProxy(m_ui->passwordEdit); setFocusProxy(m_ui->passwordEdit);
m_ui->passwordEdit->installEventFilter(this);
const QIcon errorIcon = icons()->icon("dialog-error"); const QIcon errorIcon = icons()->icon("dialog-error");
m_errorAction = m_ui->passwordEdit->addAction(errorIcon, QLineEdit::TrailingPosition); 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() if (watched == m_ui->passwordEdit) {
&& (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease auto type = event->type();
|| event->type() == QEvent::FocusIn)) { if (isVisible() && (type == QEvent::KeyPress || type == QEvent::KeyRelease || type == QEvent::FocusIn)) {
checkCapslockState(); checkCapslockState();
} }
return QWidget::event(event); if (type == QEvent::FocusIn || type == QEvent::FocusOut) {
osUtils->setUserInputProtection(type == QEvent::FocusIn);
}
}
// Continue with normal operations
return false;
} }
void PasswordWidget::checkCapslockState() void PasswordWidget::checkCapslockState()

View File

@ -44,6 +44,8 @@ public:
bool isPasswordVisible() const; bool isPasswordVisible() const;
QString text(); QString text();
bool eventFilter(QObject* watched, QEvent* event) override;
signals: signals:
void textChanged(QString text); void textChanged(QString text);
@ -57,9 +59,6 @@ public slots:
void setEchoMode(QLineEdit::EchoMode mode); void setEchoMode(QLineEdit::EchoMode mode);
void setClearButtonEnabled(bool enabled); void setClearButtonEnabled(bool enabled);
protected:
bool event(QEvent* event) override;
private slots: private slots:
void popupPasswordGenerator(); void popupPasswordGenerator();
void updateRepeatStatus(); void updateRepeatStatus();

View File

@ -56,6 +56,11 @@ public:
*/ */
virtual bool isCapslockEnabled() = 0; virtual bool isCapslockEnabled() = 0;
/**
* @param enable Toggle protection on user input (if available).
*/
virtual void setUserInputProtection(bool enable) = 0;
virtual void registerNativeEventFilter() = 0; virtual void registerNativeEventFilter() = 0;
virtual bool registerGlobalShortcut(const QString& name, virtual bool registerGlobalShortcut(const QString& name,

View File

@ -150,6 +150,15 @@ bool MacUtils::isCapslockEnabled()
#endif #endif
} }
void MacUtils::setUserInputProtection(bool enable)
{
if (enable) {
EnableSecureEventInput();
} else {
DisableSecureEventInput();
}
}
/** /**
* Toggle application state between foreground app and UIElement app. * Toggle application state between foreground app and UIElement app.
* Foreground apps have dock icons, UIElement apps do not. * Foreground apps have dock icons, UIElement apps do not.

View File

@ -40,6 +40,7 @@ public:
bool isLaunchAtStartupEnabled() const override; bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override; void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override; bool isCapslockEnabled() override;
void setUserInputProtection(bool enable) override;
WId activeWindow(); WId activeWindow();
bool raiseWindow(WId pid); bool raiseWindow(WId pid);

View File

@ -235,6 +235,12 @@ bool NixUtils::isCapslockEnabled()
return false; return false;
} }
void NixUtils::setUserInputProtection(bool enable)
{
// Linux does not support this feature
Q_UNUSED(enable)
}
void NixUtils::registerNativeEventFilter() void NixUtils::registerNativeEventFilter()
{ {
qApp->installNativeEventFilter(this); qApp->installNativeEventFilter(this);

View File

@ -35,6 +35,7 @@ public:
bool isLaunchAtStartupEnabled() const override; bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override; void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override; bool isCapslockEnabled() override;
void setUserInputProtection(bool enable) override;
void registerNativeEventFilter() override; void registerNativeEventFilter() override;

View File

@ -136,6 +136,12 @@ bool WinUtils::isCapslockEnabled()
return GetKeyState(VK_CAPITAL) == 1; return GetKeyState(VK_CAPITAL) == 1;
} }
void WinUtils::setUserInputProtection(bool enable)
{
// Windows does not support this feature
Q_UNUSED(enable)
}
bool WinUtils::isHighContrastMode() const bool WinUtils::isHighContrastMode() const
{ {
QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat); QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat);

View File

@ -44,6 +44,7 @@ public:
bool isLaunchAtStartupEnabled() const override; bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override; void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override; bool isCapslockEnabled() override;
void setUserInputProtection(bool enable) override;
bool isHighContrastMode() const; bool isHighContrastMode() const;
void registerNativeEventFilter() override; void registerNativeEventFilter() override;