mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-23 03:58:49 +00:00
feat: keychain available property (#17367)
* feat: Keychain availability check * fix: ifdef older systems * fix: typoe
This commit is contained in:
parent
65cc5a7c55
commit
9bebae3119
@ -12,6 +12,8 @@ Keychain {
|
||||
|
||||
service: "StatusStorybookMocked"
|
||||
|
||||
required property bool available
|
||||
|
||||
// shadowing Keychain's "loading" property
|
||||
readonly property alias loading: d.loading
|
||||
|
||||
@ -43,6 +45,10 @@ Keychain {
|
||||
}
|
||||
|
||||
function requestGetCredential(reason, account) {
|
||||
if (!root.available) {
|
||||
root.getCredentialRequestCompleted(Keychain.StatusUnavailable, "")
|
||||
return
|
||||
}
|
||||
d.loading = true
|
||||
d.key = account
|
||||
biometricsPopup.open()
|
||||
|
@ -1,9 +1,12 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQml 2.15
|
||||
|
||||
import QtQuick.Window 2.15
|
||||
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import StatusQ 0.1
|
||||
|
||||
import Storybook 1.0
|
||||
@ -15,11 +18,22 @@ Page {
|
||||
|
||||
Logs { id: logs }
|
||||
|
||||
Settings {
|
||||
property alias mockedKeychainAvailable: keychainAvailableCheckBox.checked
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property bool keychainAvailable: loader.item.available
|
||||
readonly property bool useMockedKeychain: root.isMac && !forceMockedKeychainCheckBox.checked
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
|
||||
sourceComponent: root.isMac && !forceMockedKeychainCheckBox.checked
|
||||
? nativeKeychainComponent : mockedKeychainComponent
|
||||
sourceComponent: d.useMockedKeychain ? nativeKeychainComponent
|
||||
: mockedKeychainComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
@ -35,6 +49,7 @@ Page {
|
||||
|
||||
KeychainMock {
|
||||
parent: root
|
||||
available: keychainAvailableCheckBox.checked
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +76,7 @@ Page {
|
||||
anchors.centerIn: parent
|
||||
|
||||
Text {
|
||||
text: `Is MacOS: ${root.isMac}`
|
||||
text: `Is MacOS: ${root.isMac ? "🍏" : "🙅"}, Keychain available: ${d.keychainAvailable ? "✅" : "❌"}`
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
@ -71,6 +86,12 @@ Page {
|
||||
text: `Force using mocked Keychain`
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: keychainAvailableCheckBox
|
||||
text: `Mocked keychain available`
|
||||
enabled: !d.useMockedKeychain
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Text {
|
||||
text: "Account"
|
||||
@ -94,6 +115,7 @@ Page {
|
||||
RowLayout {
|
||||
Button {
|
||||
text: "Save"
|
||||
enabled: d.keychainAvailable
|
||||
onClicked: {
|
||||
const status = loader.item.saveCredential(accountInput.text, passwordInput.text)
|
||||
logs.logEvent("SaveCredentials", ["status"], [status])
|
||||
@ -101,6 +123,7 @@ Page {
|
||||
}
|
||||
Button {
|
||||
text: "Delete"
|
||||
enabled: d.keychainAvailable
|
||||
onClicked: {
|
||||
const status = loader.item.deleteCredential(accountInput.text)
|
||||
logs.logEvent("DeleteCredential", ["status"], [status])
|
||||
@ -108,6 +131,7 @@ Page {
|
||||
}
|
||||
Button {
|
||||
text: "Get"
|
||||
enabled: d.keychainAvailable
|
||||
onClicked: {
|
||||
loader.item.requestGetCredential("Get reason",
|
||||
accountInput.text)
|
||||
@ -115,6 +139,7 @@ Page {
|
||||
}
|
||||
Button {
|
||||
text: "Has"
|
||||
enabled: d.keychainAvailable
|
||||
onClicked: {
|
||||
const status = loader.item.hasCredential(accountInput.text)
|
||||
logs.logEvent("HasCredential", ["status"], [status])
|
||||
@ -123,13 +148,14 @@ Page {
|
||||
}
|
||||
Button {
|
||||
text: "Cancel"
|
||||
enabled: d.keychainAvailable
|
||||
onClicked: {
|
||||
loader.item.cancelActiveRequest()
|
||||
}
|
||||
}
|
||||
BusyIndicator {
|
||||
Layout.preferredHeight: 40
|
||||
running: loader.item.loading
|
||||
running: loader.item.loading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ class Keychain : public QObject {
|
||||
|
||||
Q_PROPERTY(QString service READ service WRITE setService NOTIFY serviceChanged)
|
||||
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
|
||||
Q_PROPERTY(bool available READ available CONSTANT)
|
||||
|
||||
public:
|
||||
explicit Keychain(QObject *parent = nullptr);
|
||||
@ -33,6 +34,8 @@ public:
|
||||
QString service() const;
|
||||
void setService(const QString &service);
|
||||
|
||||
bool available() const;
|
||||
|
||||
bool loading() const;
|
||||
|
||||
Q_INVOKABLE Status saveCredential(const QString &account, const QString &password);
|
||||
@ -51,6 +54,7 @@ signals:
|
||||
private:
|
||||
QString m_service;
|
||||
bool m_loading = false;
|
||||
bool m_available = false;
|
||||
void setLoading(bool loading);
|
||||
|
||||
QFuture<void> m_future;
|
||||
@ -58,5 +62,6 @@ private:
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
Status getCredential(const QString &reason, const QString &account, QString *out);
|
||||
void reevaluateAvailability();
|
||||
#endif
|
||||
};
|
||||
|
@ -2,9 +2,6 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Keychain::Keychain(QObject *parent) : QObject(parent)
|
||||
{}
|
||||
|
||||
QString Keychain::service() const
|
||||
{
|
||||
return m_service;
|
||||
|
@ -9,7 +9,14 @@
|
||||
#include <LocalAuthentication/LocalAuthentication.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
const static auto authPolicy = LAPolicyDeviceOwnerAuthentication;
|
||||
const static auto authPolicy =
|
||||
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000
|
||||
LAPolicyDeviceOwnerAuthenticationWithBiometricsOrCompanion;
|
||||
#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101202
|
||||
LAPolicyDeviceOwnerAuthenticationWithBiometrics;
|
||||
#else
|
||||
LAPolicyDeviceOwnerAuthentication;
|
||||
#endif
|
||||
|
||||
static Keychain::Status convertStatus(OSStatus status)
|
||||
{
|
||||
@ -39,12 +46,23 @@ Keychain::Status convertError(NSError *error)
|
||||
}
|
||||
}
|
||||
|
||||
Keychain::Keychain(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
reevaluateAvailability();
|
||||
}
|
||||
|
||||
Keychain::~Keychain()
|
||||
{
|
||||
cancelActiveRequest();
|
||||
m_future.waitForFinished();
|
||||
}
|
||||
|
||||
bool Keychain::available() const
|
||||
{
|
||||
return m_available;
|
||||
}
|
||||
|
||||
Keychain::Status authenticate(const QString &reason, LAContext **context)
|
||||
{
|
||||
if (context == nullptr)
|
||||
@ -56,14 +74,6 @@ Keychain::Status authenticate(const QString &reason, LAContext **context)
|
||||
}
|
||||
|
||||
*context = [[LAContext alloc] init];
|
||||
NSError *authError = nil;
|
||||
|
||||
// Check if Biometrics Authentication is available
|
||||
if (![*context canEvaluatePolicy:authPolicy error:&authError]) {
|
||||
qWarning() << "biometric authentication not available:"
|
||||
<< QString::fromNSString(authError.localizedDescription);
|
||||
return convertError(authError);
|
||||
}
|
||||
|
||||
QEventLoop loop;
|
||||
auto loopPtr = &loop;
|
||||
@ -165,6 +175,10 @@ Keychain::Status Keychain::deleteCredential(const QString &account)
|
||||
|
||||
Keychain::Status Keychain::getCredential(const QString &reason, const QString &account, QString *out)
|
||||
{
|
||||
if (!m_available) {
|
||||
return StatusUnavailable;
|
||||
}
|
||||
|
||||
QScopedValueRollback<LAContext *> roolback(m_activeAuthContext, nullptr);
|
||||
const auto authStatus = authenticate(reason, &m_activeAuthContext);
|
||||
|
||||
@ -194,6 +208,19 @@ Keychain::Status Keychain::getCredential(const QString &reason, const QString &a
|
||||
return convertStatus(status);
|
||||
}
|
||||
|
||||
void Keychain::reevaluateAvailability()
|
||||
{
|
||||
auto context = [[LAContext alloc] init];
|
||||
NSError *authError = nil;
|
||||
|
||||
m_available = [context canEvaluatePolicy:authPolicy error:&authError];
|
||||
|
||||
if (!m_available) {
|
||||
const auto description = QString::fromNSString(authError.localizedDescription);
|
||||
qDebug() << "Keychain is not available" << description;
|
||||
}
|
||||
}
|
||||
|
||||
Keychain::Status Keychain::hasCredential(const QString &account) const
|
||||
{
|
||||
NSDictionary *query = @{
|
||||
|
@ -1,7 +1,14 @@
|
||||
#include "StatusQ/keychain.h"
|
||||
|
||||
Keychain::Keychain(QObject *parent) : QObject(parent) {}
|
||||
|
||||
Keychain::~Keychain() = default;
|
||||
|
||||
bool Keychain::available() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Keychain::Status Keychain::saveCredential(const QString &account, const QString &password)
|
||||
{
|
||||
Q_UNUSED(account);
|
||||
|
Loading…
x
Reference in New Issue
Block a user