diff --git a/lib/include/DOtherSide/DOtherSide.h b/lib/include/DOtherSide/DOtherSide.h index 75c414e..d8d4336 100644 --- a/lib/include/DOtherSide/DOtherSide.h +++ b/lib/include/DOtherSide/DOtherSide.h @@ -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 diff --git a/lib/include/DOtherSide/DOtherSideTypes.h b/lib/include/DOtherSide/DOtherSideTypes.h index ba46018..5e10c1e 100644 --- a/lib/include/DOtherSide/DOtherSideTypes.h +++ b/lib/include/DOtherSide/DOtherSideTypes.h @@ -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 diff --git a/lib/include/DOtherSide/Status/Keychain.h b/lib/include/DOtherSide/Status/Keychain.h new file mode 100644 index 0000000..3fd64c4 --- /dev/null +++ b/lib/include/DOtherSide/Status/Keychain.h @@ -0,0 +1,40 @@ +#ifndef KEYCHAIN_H +#define KEYCHAIN_H + +#include + +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 diff --git a/lib/include/DOtherSide/Status/KeychainManager.h b/lib/include/DOtherSide/Status/KeychainManager.h new file mode 100644 index 0000000..e02a2b5 --- /dev/null +++ b/lib/include/DOtherSide/Status/KeychainManager.h @@ -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 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 m_localAuth; + std::unique_ptr m_keychain; + QMetaObject::Connection m_actionConnection; +#endif + }; +} + +#endif diff --git a/lib/include/DOtherSide/Status/LocalAuthentication.h b/lib/include/DOtherSide/Status/LocalAuthentication.h new file mode 100644 index 0000000..a6c0523 --- /dev/null +++ b/lib/include/DOtherSide/Status/LocalAuthentication.h @@ -0,0 +1,33 @@ +#ifndef LOCAL_AUTHENTICATION_H +#define LOCAL_AUTHENTICATION_H + +#include + +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 diff --git a/lib/src/DOtherSide.cpp b/lib/src/DOtherSide.cpp index eb1476b..b32cc64 100644 --- a/lib/src/DOtherSide.cpp +++ b/lib/src/DOtherSide.cpp @@ -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(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(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(vptr); + if(obj) + { + obj->storeDataAsync(QString(key), QString(data)); + } +} + +void dos_keychainmanager_delete_data_async(DosKeychainManager* vptr, + const char* key) +{ + auto obj = static_cast(vptr); + if(obj) + obj->deleteDataAsync(QString(key)); +} + +void dos_keychainmanager_delete(DosKeychainManager* vptr) +{ + auto qobject = static_cast(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()); -} \ No newline at end of file +} diff --git a/lib/src/Status/Keychain.mm b/lib/src/Status/Keychain.mm new file mode 100644 index 0000000..c094e99 --- /dev/null +++ b/lib/src/Status/Keychain.mm @@ -0,0 +1,162 @@ +#include "DOtherSide/Status/Keychain.h" + +#import +#import + +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)); + } +} \ No newline at end of file diff --git a/lib/src/Status/KeychainManager.cpp b/lib/src/Status/KeychainManager.cpp new file mode 100644 index 0000000..d4bbb19 --- /dev/null +++ b/lib/src/Status/KeychainManager.cpp @@ -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(new LocalAuthentication()); + m_keychain = std::unique_ptr(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 +} \ No newline at end of file diff --git a/lib/src/Status/KeychainManager.mm b/lib/src/Status/KeychainManager.mm new file mode 100644 index 0000000..7083683 --- /dev/null +++ b/lib/src/Status/KeychainManager.mm @@ -0,0 +1,75 @@ +#include "DOtherSide/Status/KeychainManager.h" + +#include +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 action) +{ + disconnect(m_actionConnection); + m_actionConnection = connect(m_localAuth.get(), &LocalAuthentication::success, action); + + m_localAuth->runAuthentication(m_authenticationReason); +} \ No newline at end of file diff --git a/lib/src/Status/LocalAuthentication.mm b/lib/src/Status/LocalAuthentication.mm new file mode 100644 index 0000000..b423ada --- /dev/null +++ b/lib/src/Status/LocalAuthentication.mm @@ -0,0 +1,152 @@ +#include "DOtherSide/Status/LocalAuthentication.h" + +#include +#import + +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)); + } +} \ No newline at end of file diff --git a/lib/src/Status/OSNotification.cpp b/lib/src/Status/OSNotification.cpp index 4f88764..11fa779 100644 --- a/lib/src/Status/OSNotification.cpp +++ b/lib/src/Status/OSNotification.cpp @@ -1,4 +1,4 @@ -#include "DOtherSide/StatusNotification/OSNotification.h" +#include "DOtherSide/Status/OSNotification.h" #ifdef Q_OS_WIN #include diff --git a/lib/src/Status/OSNotification.mm b/lib/src/Status/OSNotification.mm index 68d7154..c81d78d 100644 --- a/lib/src/Status/OSNotification.mm +++ b/lib/src/Status/OSNotification.mm @@ -1,4 +1,4 @@ -#include "DOtherSide/StatusNotification/OSNotification.h" +#include "DOtherSide/Status/OSNotification.h" #ifdef Q_OS_MACOS