macOS: Check for Auto-Type permissions on use instead of at launch

* Fix #3689 - link the use of Auto-Type with the permissions required to use it
This commit is contained in:
Jonathan White 2019-11-03 23:00:03 -05:00 committed by Janek Bevendorff
parent 440331d319
commit 7ba9fcc0e5
7 changed files with 76 additions and 14 deletions

View File

@ -218,6 +218,15 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
if (hideWindow) {
#if defined(Q_OS_MACOS)
// Check for accessibility permission
if (!macUtils()->enableAccessibility()) {
MessageBox::information(nullptr,
tr("Permission Required"),
tr("KeePassXC requires the Accessibility permission in order to perform entry "
"level Auto-Type. If you already granted permission, you may have to restart "
"KeePassXC."));
return;
}
macUtils()->raiseLastActiveWindow();
m_plugin->hideOwnWindow();
#else

View File

@ -18,6 +18,7 @@
#include "AutoTypeMac.h"
#include "gui/macutils/MacUtils.h"
#include "gui/MessageBox.h"
#include <ApplicationServices/ApplicationServices.h>
@ -25,6 +26,10 @@
#define MAX_WINDOW_TITLE_LENGTH 1024
#define INVALID_KEYCODE 0xFFFF
namespace {
bool accessibilityChecked = false;
}
AutoTypePlatformMac::AutoTypePlatformMac()
: m_hotkeyRef(nullptr)
, m_hotkeyId({ 'kpx2', HOTKEY_ID })
@ -33,14 +38,18 @@ AutoTypePlatformMac::AutoTypePlatformMac()
eventSpec.eventClass = kEventClassKeyboard;
eventSpec.eventKind = kEventHotKeyPressed;
MessageBox::initializeButtonDefs();
::InstallApplicationEventHandler(AutoTypePlatformMac::hotkeyHandler, 1, &eventSpec, this, nullptr);
}
//
// Keepassx requires mac os 10.7
//
/**
* Determine if Auto-Type is available
*
* @return always return true
*/
bool AutoTypePlatformMac::isAvailable()
{
// Accessibility permissions are requested upon first use, instead of on load
return true;
}
@ -470,6 +479,21 @@ OSStatus AutoTypePlatformMac::hotkeyHandler(EventHandlerCallRef nextHandler, Eve
{
Q_UNUSED(nextHandler);
// Determine if the user has given proper permissions to KeePassXC to perform Auto-Type
if (!accessibilityChecked) {
if (macUtils()->enableAccessibility() && macUtils()->enableScreenRecording()) {
accessibilityChecked = true;
} else {
// Does not have required permissions to Auto-Type, ignore the keypress
MessageBox::information(nullptr,
tr("Permission Required"),
tr("KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global "
"Auto-Type. Screen Recording is necessary to use the window title to find entries. If you "
"already granted permission, you may have to restart KeePassXC."));
return noErr;
}
}
AutoTypePlatformMac* self = static_cast<AutoTypePlatformMac*>(userData);
EventHotKeyID hotkeyId;

View File

@ -38,6 +38,7 @@ public:
bool isHidden(pid_t pid);
bool isDarkMode();
bool enableAccessibility();
bool enableScreenRecording();
signals:
void lockDatabases();

View File

@ -37,5 +37,6 @@
- (bool) isDarkMode;
- (void) userSwitchHandler:(NSNotification*) notification;
- (bool) enableAccessibility;
- (bool) enableScreenRecording;
@end

View File

@ -19,6 +19,7 @@
#import "AppKitImpl.h"
#import <AppKit/NSWorkspace.h>
#import <CoreVideo/CVPixelBuffer.h>
#import <Availability.h>
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
@ -128,21 +129,36 @@ static const NSEventMask NSEventMaskKeyDown = NSKeyDownMask;
//
- (bool) enableAccessibility
{
// Request a 1 pixel screenshot to trigger the permissions
// required for screen reader access. These are necessary
// for Auto-Type to find the window titles in macOS 10.15+
CGImageRef screenshot = CGWindowListCreateImage(
CGRectMake(0, 0, 1, 1),
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID,
kCGWindowImageDefault);
CFRelease(screenshot);
// Request accessibility permissions for Auto-Type type on behalf of the user
NSDictionary* opts = @{static_cast<id>(kAXTrustedCheckOptionPrompt): @YES};
return AXIsProcessTrustedWithOptions(static_cast<CFDictionaryRef>(opts));
}
//
// Check if screen recording is enabled, may show an popup asking for permissions
//
- (bool) enableScreenRecording
{
if (@available(macOS 10.15, *)) {
// Request screen recording permission on macOS 10.15+
// This is necessary to get the current window title
CGDisplayStreamRef stream = CGDisplayStreamCreate(CGMainDisplayID(), 1, 1, kCVPixelFormatType_32BGRA, nil,
^(CGDisplayStreamFrameStatus status, uint64_t displayTime,
IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
Q_UNUSED(status);
Q_UNUSED(displayTime);
Q_UNUSED(frameSurface);
Q_UNUSED(updateRef);
});
if (stream) {
CFRelease(stream);
} else {
return NO;
}
}
return YES;
}
@end
//
@ -198,4 +214,9 @@ bool AppKit::isDarkMode()
bool AppKit::enableAccessibility()
{
return [static_cast<id>(self) enableAccessibility];
}
}
bool AppKit::enableScreenRecording()
{
return [static_cast<id>(self) enableScreenRecording];
}

View File

@ -80,3 +80,8 @@ bool MacUtils::enableAccessibility()
{
return m_appkit->enableAccessibility();
}
bool MacUtils::enableScreenRecording()
{
return m_appkit->enableScreenRecording();
}

View File

@ -39,6 +39,7 @@ public:
bool isHidden();
bool isDarkMode();
bool enableAccessibility();
bool enableScreenRecording();
signals:
void lockDatabases();