feat(@desktop/general): (macos) Keychain manager added
LocalAuthentication class - used to authenticate OS' logged user (using Touch Id) Keychain class - able to store/read/remove item from the Keychain KeychainManager class - manages the flow of storing/reading/removing an item from the Keychain using own sync/async methods This change is required as part of the feature issue-2675
This commit is contained in:
parent
4d10692572
commit
5dc926f665
|
@ -1027,6 +1027,19 @@ DOS_API void dos_qsettings_delete(DosQSettings* vptr);
|
|||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region KeychainManager exposed methods
|
||||
|
||||
DOS_API DosKeychainManager* dos_keychainmanager_create(const char* service,
|
||||
const char* authenticationReason);
|
||||
DOS_API char* dos_keychainmanager_read_data_sync(DosKeychainManager* vptr,
|
||||
const char* key);
|
||||
DOS_API void dos_keychainmanager_read_data_async(DosKeychainManager* vptr,
|
||||
const char* key);
|
||||
DOS_API void dos_keychainmanager_store_data_async(DosKeychainManager* vptr,
|
||||
const char* key, const char* data);
|
||||
DOS_API void dos_keychainmanager_delete_data_async(DosKeychainManager* vptr,
|
||||
const char* key);
|
||||
DOS_API void dos_keychainmanager_delete(DosKeychainManager* vptr);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ typedef void DosEvent;
|
|||
/// A pointer to a os notification object which is actualy a QObject
|
||||
typedef DosQObject DosOSNotification;
|
||||
|
||||
/// A pointer to a keychain manager object which is actualy a QObject
|
||||
typedef DosQObject DosKeychainManager;
|
||||
|
||||
|
||||
/// A pixmap callback to be supplied to an image provider
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef KEYCHAIN_H
|
||||
#define KEYCHAIN_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Status
|
||||
{
|
||||
class Keychain : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Error {
|
||||
NoError=0,
|
||||
EntryNotFound,
|
||||
CouldNotDeleteEntry,
|
||||
AccessDeniedByUser,
|
||||
AccessDenied,
|
||||
NoBackendAvailable,
|
||||
NotImplemented,
|
||||
OtherError
|
||||
};
|
||||
|
||||
Keychain(const QString& service, QObject *parent = nullptr);
|
||||
|
||||
void readItem(const QString& key);
|
||||
void writeItem(const QString& key, const QString& data);
|
||||
void deleteItem(const QString& key);
|
||||
|
||||
signals:
|
||||
void success(QString data);
|
||||
void error(int error, const QString& errorString);
|
||||
|
||||
private:
|
||||
QString m_service;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef KEYCHAIN_MANAGER_H
|
||||
#define KEYCHAIN_MANAGER_H
|
||||
|
||||
#include "Keychain.h"
|
||||
#include "LocalAuthentication.h"
|
||||
|
||||
namespace Status
|
||||
{
|
||||
class KeychainManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Constructor defining name of the service for storing in the Keychain
|
||||
* and the reason for requesting authorisation via touch id.
|
||||
*
|
||||
* @param service Service name used in Keychain.
|
||||
* @param authenticationReason Reason for requestion touch id authorization.
|
||||
*/
|
||||
KeychainManager(const QString& service,
|
||||
const QString& authenticationReason, QObject* parent = nullptr);
|
||||
|
||||
/*!
|
||||
* Synchronously reads @data stored in the Keychain under the @key and
|
||||
* returns stored @data. In case of any error an empty string will be
|
||||
* returned and error signal will be emitted.
|
||||
*
|
||||
* @param key Key which is stored in the Keychain.
|
||||
*/
|
||||
QString readDataSync(const QString& key) const;
|
||||
|
||||
/*!
|
||||
* Asynchronously reads @data stored in the Keychain under the @key.
|
||||
* Onces it's read success signal will be emitted containing read data,
|
||||
* otherwise error signal will be emitted.
|
||||
*
|
||||
* @param key Key which is stored in the Keychain.
|
||||
*/
|
||||
void readDataAsync(const QString& key);
|
||||
/*!
|
||||
* Asynchronously stores @data under the @key in the Keychain.
|
||||
* Onces @data is stored success signal will be emitted, otherwise error
|
||||
* signal will be emitted.
|
||||
*
|
||||
* @param key Key which is stored in the Keychain.
|
||||
* @param data Data which is stored in the Keychain.
|
||||
*/
|
||||
void storeDataAsync(const QString& key, const QString& data);
|
||||
/*!
|
||||
* Asynchronously deletes @data stored in the Keychain under the @key.
|
||||
* Onces it's deleted success signal will be emitted, otherwise error
|
||||
* signal will be emitted.
|
||||
*
|
||||
* @param key Key which is stored in the Keychain.
|
||||
*/
|
||||
void deleteDataAsync(const QString& key);
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* Notifies that action was performed successfully and in case of asyc
|
||||
* read contains read @data, in other cases @data param is empty.
|
||||
*
|
||||
* @param data Data read from the Keychain.
|
||||
*/
|
||||
void success(QString data);
|
||||
/*!
|
||||
* Notifies that an error with @error code and @errorString description
|
||||
* occured.
|
||||
*
|
||||
* @param type Determins origin of the error ("authentication" or "keychain")
|
||||
* @param code Error code.
|
||||
* @param errorString Error description.
|
||||
*/
|
||||
void error(QString type, int code, const QString& errorString);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
private:
|
||||
void process(const std::function<void()> action);
|
||||
|
||||
QString readDataSyncMacOs(const QString& key) const;
|
||||
void readDataAsyncMacOs(const QString& key);
|
||||
void storeDataAsyncMacOs(const QString& key, const QString& data);
|
||||
void deleteDataAsyncMacOs(const QString& key);
|
||||
|
||||
private:
|
||||
QString m_authenticationReason;
|
||||
std::unique_ptr<LocalAuthentication> m_localAuth;
|
||||
std::unique_ptr<Keychain> m_keychain;
|
||||
QMetaObject::Connection m_actionConnection;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef LOCAL_AUTHENTICATION_H
|
||||
#define LOCAL_AUTHENTICATION_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Status
|
||||
{
|
||||
class LocalAuthentication : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Error {
|
||||
Domain=0,
|
||||
AppCanceled,
|
||||
SystemCanceled,
|
||||
UserCanceled,
|
||||
TouchIdNotAvailable,
|
||||
TouchIdNotConfigured,
|
||||
WrongCredentials,
|
||||
OtherError
|
||||
};
|
||||
|
||||
void runAuthentication(const QString& authenticationReason);
|
||||
|
||||
signals:
|
||||
void success();
|
||||
void error(int error, const QString& errorString);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -66,6 +66,7 @@
|
|||
#include "DOtherSide/Status/DockShowAppEvent.h"
|
||||
#include "DOtherSide/Status/OSThemeEvent.h"
|
||||
#include "DOtherSide/Status/OSNotification.h"
|
||||
#include "DOtherSide/Status/KeychainManager.h"
|
||||
#include "DOtherSide/DosSpellchecker.h"
|
||||
|
||||
namespace {
|
||||
|
@ -1459,6 +1460,60 @@ void dos_qsettings_delete(DosQSettings* vptr)
|
|||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region KeychainManager
|
||||
DosKeychainManager* dos_keychainmanager_create(const char* service,
|
||||
const char* authenticationReason)
|
||||
{
|
||||
return new Status::KeychainManager(QString(service), QString(authenticationReason));
|
||||
}
|
||||
|
||||
char* dos_keychainmanager_read_data_sync(DosKeychainManager* vptr,
|
||||
const char* key)
|
||||
{
|
||||
auto obj = static_cast<Status::KeychainManager*>(vptr);
|
||||
if(obj)
|
||||
{
|
||||
return convert_to_cstring(obj->readDataSync(QString(key)));
|
||||
}
|
||||
|
||||
return convert_to_cstring(QString());
|
||||
}
|
||||
|
||||
void dos_keychainmanager_read_data_async(DosKeychainManager* vptr,
|
||||
const char* key)
|
||||
{
|
||||
auto obj = static_cast<Status::KeychainManager*>(vptr);
|
||||
if(obj)
|
||||
obj->readDataAsync(QString(key));
|
||||
}
|
||||
|
||||
void dos_keychainmanager_store_data_async(DosKeychainManager* vptr,
|
||||
const char* key, const char* data)
|
||||
{
|
||||
auto obj = static_cast<Status::KeychainManager*>(vptr);
|
||||
if(obj)
|
||||
{
|
||||
obj->storeDataAsync(QString(key), QString(data));
|
||||
}
|
||||
}
|
||||
|
||||
void dos_keychainmanager_delete_data_async(DosKeychainManager* vptr,
|
||||
const char* key)
|
||||
{
|
||||
auto obj = static_cast<Status::KeychainManager*>(vptr);
|
||||
if(obj)
|
||||
obj->deleteDataAsync(QString(key));
|
||||
}
|
||||
|
||||
void dos_keychainmanager_delete(DosKeychainManager* vptr)
|
||||
{
|
||||
auto qobject = static_cast<QObject*>(vptr);
|
||||
if(qobject)
|
||||
qobject->deleteLater();
|
||||
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
char* dos_to_local_file(const char* fileUrl)
|
||||
{
|
||||
return convert_to_cstring(QUrl(QString::fromUtf8(fileUrl)).toLocalFile());
|
||||
|
@ -1467,4 +1522,4 @@ char* dos_to_local_file(const char* fileUrl)
|
|||
char* dos_from_local_file(const char* filePath)
|
||||
{
|
||||
return convert_to_cstring(QUrl::fromLocalFile(QString::fromUtf8(filePath)).toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#include "DOtherSide/Status/Keychain.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
using namespace Status;
|
||||
|
||||
struct ErrorDescription
|
||||
{
|
||||
Keychain::Error code;
|
||||
QString message;
|
||||
|
||||
ErrorDescription(Keychain::Error code, const QString &message)
|
||||
: code(code)
|
||||
, message(message)
|
||||
{}
|
||||
|
||||
static ErrorDescription fromStatus(OSStatus status)
|
||||
{
|
||||
switch(status) {
|
||||
case errSecSuccess:
|
||||
return ErrorDescription(Keychain::NoError,
|
||||
"No error");
|
||||
case errSecItemNotFound:
|
||||
return ErrorDescription(Keychain::EntryNotFound,
|
||||
"The specified item could not be found in the keychain");
|
||||
case errSecUserCanceled:
|
||||
return ErrorDescription(Keychain::AccessDeniedByUser,
|
||||
"User canceled the operation");
|
||||
case errSecInteractionNotAllowed:
|
||||
return ErrorDescription(Keychain::AccessDenied,
|
||||
"User interaction is not allowed");
|
||||
case errSecNotAvailable:
|
||||
return ErrorDescription(Keychain::AccessDenied,
|
||||
"No keychain is available. You may need to restart your computer");
|
||||
case errSecAuthFailed:
|
||||
return ErrorDescription(Keychain::AccessDenied,
|
||||
"The user name or passphrase you entered is not correct");
|
||||
case errSecVerifyFailed:
|
||||
return ErrorDescription(Keychain::AccessDenied,
|
||||
"A cryptographic verification failure has occurred");
|
||||
case errSecUnimplemented:
|
||||
return ErrorDescription(Keychain::NotImplemented,
|
||||
"Function or operation not implemented");
|
||||
case errSecIO:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"I/O error");
|
||||
case errSecOpWr:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"Already open with with write permission");
|
||||
case errSecParam:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"Invalid parameters passed to a function");
|
||||
case errSecAllocate:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"Failed to allocate memory");
|
||||
case errSecBadReq:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"Bad parameter or invalid state for operation");
|
||||
case errSecInternalComponent:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"An internal component failed");
|
||||
case errSecDuplicateItem:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"The specified item already exists in the keychain");
|
||||
case errSecDecode:
|
||||
return ErrorDescription(Keychain::OtherError,
|
||||
"Unable to decode the provided data");
|
||||
}
|
||||
|
||||
return ErrorDescription(Keychain::OtherError, "Unknown error");
|
||||
}
|
||||
};
|
||||
|
||||
Keychain::Keychain(const QString& service, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_service(service)
|
||||
{}
|
||||
|
||||
void Keychain::readItem(const QString& key)
|
||||
{
|
||||
NSDictionary *const query = @{
|
||||
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||
(__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(),
|
||||
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||
(__bridge id) kSecReturnData: @YES,
|
||||
};
|
||||
|
||||
CFTypeRef dataRef = nil;
|
||||
const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
QByteArray data;
|
||||
if (dataRef)
|
||||
data = QByteArray::fromCFData((CFDataRef) dataRef);
|
||||
|
||||
emit success(QString::fromUtf8(data));
|
||||
} else {
|
||||
const ErrorDescription ed = ErrorDescription::fromStatus(status);
|
||||
emit error(ed.code,
|
||||
QString("Could not retrieve private key from keystore: %1").arg(ed.message));
|
||||
}
|
||||
|
||||
if (dataRef)
|
||||
[dataRef release];
|
||||
}
|
||||
|
||||
void Keychain::writeItem(const QString& key, const QString& data)
|
||||
{
|
||||
NSDictionary *const query = @{
|
||||
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||
(__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(),
|
||||
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||
};
|
||||
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil);
|
||||
|
||||
QByteArray baData = data.toUtf8();
|
||||
if (status == errSecSuccess) {
|
||||
NSDictionary *const update = @{
|
||||
(__bridge id) kSecValueData: (__bridge NSData *) baData.toCFData(),
|
||||
};
|
||||
|
||||
status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update);
|
||||
} else {
|
||||
NSDictionary *const insert = @{
|
||||
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||
(__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(),
|
||||
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||
(__bridge id) kSecValueData: (__bridge NSData *) baData.toCFData(),
|
||||
};
|
||||
|
||||
status = SecItemAdd((__bridge CFDictionaryRef) insert, nil);
|
||||
}
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
emit success(QString());
|
||||
} else {
|
||||
const ErrorDescription ed = ErrorDescription::fromStatus(status);
|
||||
emit error(ed.code,
|
||||
QString("Could not store data in settings: %1").arg(ed.message));
|
||||
}
|
||||
}
|
||||
|
||||
void Keychain::deleteItem(const QString& key)
|
||||
{
|
||||
const NSDictionary *const query = @{
|
||||
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||
(__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(),
|
||||
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||
};
|
||||
|
||||
const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
emit success(QString());
|
||||
} else {
|
||||
const ErrorDescription ed = ErrorDescription::fromStatus(status);
|
||||
emit error(ed.code,
|
||||
QString("Could not remove private key from keystore: %1").arg(ed.message));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#include "DOtherSide/Status/KeychainManager.h"
|
||||
|
||||
using namespace Status;
|
||||
|
||||
KeychainManager::KeychainManager(const QString& service,
|
||||
const QString& authenticationReason, QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
m_authenticationReason = authenticationReason;
|
||||
m_localAuth = std::unique_ptr<LocalAuthentication>(new LocalAuthentication());
|
||||
m_keychain = std::unique_ptr<Keychain>(new Keychain(service));
|
||||
|
||||
connect(m_localAuth.get(), &LocalAuthentication::error, [this](int code, const QString& errorString){
|
||||
emit error("authentication", code, errorString);
|
||||
});
|
||||
|
||||
connect(m_keychain.get(), &Keychain::success, this, &KeychainManager::success);
|
||||
connect(m_keychain.get(), &Keychain::error, [this](int code, const QString& errorString){
|
||||
emit error("keychain", code, errorString);
|
||||
});
|
||||
#else
|
||||
// Marked params unused until we need them for Win/Linux.
|
||||
Q_UNUSED(authenticationReason);
|
||||
Q_UNUSED(service);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString KeychainManager::readDataSync(const QString& key) const
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
return readDataSyncMacOs(key);
|
||||
#endif
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void KeychainManager::readDataAsync(const QString& key)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
readDataAsyncMacOs(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeychainManager::storeDataAsync(const QString& key, const QString& data)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
storeDataAsyncMacOs(key, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeychainManager::deleteDataAsync(const QString& key)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
deleteDataAsyncMacOs(key);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
#include "DOtherSide/Status/KeychainManager.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
using namespace Status;
|
||||
|
||||
QString KeychainManager::readDataSyncMacOs(const QString& key) const
|
||||
{
|
||||
QString storedData;
|
||||
QEventLoop loop;
|
||||
|
||||
auto onAuthenticationSuccess = [this, &key](){
|
||||
m_keychain->readItem(key);
|
||||
};
|
||||
|
||||
auto onReadItemSuccess = [&loop, &storedData](QString data){
|
||||
storedData = data;
|
||||
loop.quit();
|
||||
};
|
||||
|
||||
connect(m_localAuth.get(), &LocalAuthentication::success, onAuthenticationSuccess);
|
||||
|
||||
connect(m_keychain.get(), &Keychain::success, onReadItemSuccess);
|
||||
|
||||
connect(m_localAuth.get(), &LocalAuthentication::error, [this, &loop](int code, const QString& errorString){
|
||||
Q_UNUSED(code)
|
||||
Q_UNUSED(errorString)
|
||||
loop.quit();
|
||||
});
|
||||
|
||||
connect(m_keychain.get(), &Keychain::error, [this, &loop](int code, const QString& errorString){
|
||||
Q_UNUSED(code)
|
||||
Q_UNUSED(errorString)
|
||||
loop.quit();
|
||||
});
|
||||
|
||||
m_localAuth->runAuthentication(m_authenticationReason);
|
||||
loop.exec();
|
||||
|
||||
return storedData;
|
||||
}
|
||||
|
||||
void KeychainManager::readDataAsyncMacOs(const QString& key)
|
||||
{
|
||||
auto readAction = [this, key](){
|
||||
m_keychain->readItem(key);
|
||||
};
|
||||
|
||||
process(readAction);
|
||||
}
|
||||
|
||||
void KeychainManager::storeDataAsyncMacOs(const QString& key, const QString& data)
|
||||
{
|
||||
auto writeAction = [this, key, data](){
|
||||
m_keychain->writeItem(key, data);
|
||||
};
|
||||
|
||||
process(writeAction);
|
||||
}
|
||||
|
||||
void KeychainManager::deleteDataAsyncMacOs(const QString& key)
|
||||
{
|
||||
auto deleteAction = [this, key](){
|
||||
m_keychain->deleteItem(key);
|
||||
};
|
||||
|
||||
process(deleteAction);
|
||||
}
|
||||
|
||||
void KeychainManager::process(const std::function<void()> action)
|
||||
{
|
||||
disconnect(m_actionConnection);
|
||||
m_actionConnection = connect(m_localAuth.get(), &LocalAuthentication::success, action);
|
||||
|
||||
m_localAuth->runAuthentication(m_authenticationReason);
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
#include "DOtherSide/Status/LocalAuthentication.h"
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#import <LocalAuthentication/LocalAuthentication.h>
|
||||
|
||||
using namespace Status;
|
||||
|
||||
struct ErrorDescription
|
||||
{
|
||||
LocalAuthentication::Error code;
|
||||
QString message;
|
||||
|
||||
ErrorDescription(LocalAuthentication::Error code, const QString &message)
|
||||
: code(code)
|
||||
, message(message)
|
||||
{}
|
||||
|
||||
static ErrorDescription fromLAError(NSInteger err)
|
||||
{
|
||||
NSProcessInfo *pInfo = [NSProcessInfo processInfo];
|
||||
NSOperatingSystemVersion info = [pInfo operatingSystemVersion];
|
||||
|
||||
#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
|
||||
#if defined MAC_OS_VERSION_11_2 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_2
|
||||
switch(err) {
|
||||
case LAErrorBiometryDisconnected: /* MacOs 11.2+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"The device supports biometry only using a removable accessory, but the paired accessory isn’t connected");
|
||||
case LAErrorBiometryNotPaired: /* MacOs 11.2+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"The device supports biometry only using a removable accessory, but no accessory is paired");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined MAC_OS_X_VERSION_10_15 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
|
||||
switch(err) {
|
||||
case LAErrorWatchNotAvailable: /* MacOs 10.15+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"An attempt to authenticate with Apple Watch failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined MAC_OS_X_VERSION_10_13 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
|
||||
switch(err) {
|
||||
case LAErrorBiometryLockout: /* MacOs 10.13+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"Biometry is locked because there were too many failed attempts");
|
||||
case LAErrorBiometryNotAvailable: /* MacOs 10.13+ */
|
||||
return ErrorDescription(LocalAuthentication::TouchIdNotAvailable,
|
||||
"Biometry is not available on the device");
|
||||
case LAErrorBiometryNotEnrolled: /* MacOs 10.13+ */
|
||||
return ErrorDescription(LocalAuthentication::TouchIdNotConfigured,
|
||||
"The user has no enrolled biometric identities");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined MAC_OS_X_VERSION_10_11 \
|
||||
&& defined MAC_OS_X_VERSION_10_13 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_13
|
||||
switch(err) {
|
||||
case LAErrorTouchIDLockout: /* MacOs 10.11 - 10.13 */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"Touch ID is locked because there were too many failed attempts");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined MAC_OS_X_VERSION_10_10 \
|
||||
&& defined MAC_OS_X_VERSION_10_13 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_13
|
||||
switch(err) {
|
||||
case LAErrorTouchIDNotAvailable: /* MacOs 10.10 - 10.13 */
|
||||
return ErrorDescription(LocalAuthentication::TouchIdNotAvailable,
|
||||
"Touch ID is not available on the device");
|
||||
case LAErrorTouchIDNotEnrolled: /* MacOs 10.10 - 10.13 */
|
||||
return ErrorDescription(LocalAuthentication::TouchIdNotConfigured,
|
||||
"The user has no enrolled Touch ID fingers");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined MAC_OS_X_VERSION_10_11 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11
|
||||
switch(err) {
|
||||
case LAErrorInvalidContext: /* MacOs 10.11+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"The context was previously invalidated");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined MAC_OS_X_VERSION_10_10 \
|
||||
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
|
||||
switch(err) {
|
||||
case LAErrorSystemCancel: /* MacOs 10.10+ */
|
||||
return ErrorDescription(LocalAuthentication::SystemCanceled,
|
||||
"The system canceled authentication");
|
||||
case LAErrorUserCancel: /* MacOs 10.10+ */
|
||||
return ErrorDescription(LocalAuthentication::UserCanceled,
|
||||
"The user tapped the cancel button in the authentication dialog");
|
||||
case LAErrorAuthenticationFailed: /* MacOs 10.10+ */
|
||||
return ErrorDescription(LocalAuthentication::WrongCredentials,
|
||||
"The user failed to provide valid credentials");
|
||||
case LAErrorNotInteractive: /* MacOs 10.10+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"Displaying the required authentication user interface is forbidden");
|
||||
case LAErrorPasscodeNotSet: /* MacOs 10.10+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"A passcode isn’t set on the device");
|
||||
case LAErrorUserFallback: /* MacOs 10.10+ */
|
||||
return ErrorDescription(LocalAuthentication::OtherError,
|
||||
"The user tapped the fallback button in the authentication dialog, but no fallback is available for the authentication policy");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
return ErrorDescription(LocalAuthentication::OtherError, "Unknown error");
|
||||
}
|
||||
};
|
||||
|
||||
void LocalAuthentication::runAuthentication(const QString& authenticationReason)
|
||||
{
|
||||
LAContext *laContext = [[LAContext alloc] init];
|
||||
NSError *authError = nil;
|
||||
NSString *localizedReasonString = authenticationReason.toNSString();
|
||||
|
||||
if ([laContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError])
|
||||
{
|
||||
[laContext evaluatePolicy:LAPolicyDeviceOwnerAuthentication
|
||||
localizedReason:localizedReasonString
|
||||
reply:^(BOOL authenticated, NSError *err)
|
||||
{
|
||||
if (authenticated)
|
||||
{
|
||||
emit success();
|
||||
}
|
||||
else
|
||||
{
|
||||
const ErrorDescription ed = ErrorDescription::fromLAError([err code]);
|
||||
emit error(ed.code, QString("User did not authenticate successfully: %1").arg(ed.message));
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
const ErrorDescription ed = ErrorDescription::fromLAError([authError code]);
|
||||
emit error(ed.code, QString("Could not evaluate policy: %1").arg(ed.message));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#include "DOtherSide/StatusNotification/OSNotification.h"
|
||||
#include "DOtherSide/Status/OSNotification.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <shellapi.h>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "DOtherSide/StatusNotification/OSNotification.h"
|
||||
#include "DOtherSide/Status/OSNotification.h"
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
|
||||
|
|
Loading…
Reference in New Issue