Plumbing for asking for passwords from QML
This change introduces a model to signal the UI what password "scenario" is applicable and provides the necessary plumbing to expose it QML.master
parent
4d966c3926
commit
f16eba4505
|
@ -40,6 +40,11 @@ namespace app
|
||||||
return new model::SimpleAccountListModel(storage(), this);
|
return new model::SimpleAccountListModel(storage(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model::PasswordRequest * Keysmith::passwordRequest(void)
|
||||||
|
{
|
||||||
|
return new model::PasswordRequest(storage()->secret(), this);
|
||||||
|
}
|
||||||
|
|
||||||
accounts::AccountStorage * Keysmith::storage(void)
|
accounts::AccountStorage * Keysmith::storage(void)
|
||||||
{
|
{
|
||||||
if (!m_storage) {
|
if (!m_storage) {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "../account/account.h"
|
#include "../account/account.h"
|
||||||
#include "../model/accounts.h"
|
#include "../model/accounts.h"
|
||||||
|
#include "../model/password.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ namespace app
|
||||||
virtual ~Keysmith();
|
virtual ~Keysmith();
|
||||||
Q_INVOKABLE void copyToClipboard(const QString &text);
|
Q_INVOKABLE void copyToClipboard(const QString &text);
|
||||||
Q_INVOKABLE model::SimpleAccountListModel * accountListModel(void);
|
Q_INVOKABLE model::SimpleAccountListModel * accountListModel(void);
|
||||||
|
Q_INVOKABLE model::PasswordRequest * passwordRequest(void);
|
||||||
private:
|
private:
|
||||||
accounts::AccountStorage * storage(void);
|
accounts::AccountStorage * storage(void);
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -32,6 +32,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||||
|
|
||||||
qmlRegisterUncreatableType<model::SimpleAccountListModel>("Keysmith.Models", 1, 0, "AccountListModel", "Use the Keysmith singleton to obtain an AccountListModel");
|
qmlRegisterUncreatableType<model::SimpleAccountListModel>("Keysmith.Models", 1, 0, "AccountListModel", "Use the Keysmith singleton to obtain an AccountListModel");
|
||||||
|
qmlRegisterUncreatableType<model::PasswordRequest>("Keysmith.Models", 1, 0, "PasswordRequestModel", "Use the Keysmith singleton to obtain an PasswordRequestModel");
|
||||||
qmlRegisterUncreatableType<model::AccountView>("Keysmith.Models", 1, 0, "Account", "Use an AccountListModel from the Keysmith singleton to obtain an Account");
|
qmlRegisterUncreatableType<model::AccountView>("Keysmith.Models", 1, 0, "Account", "Use an AccountListModel from the Keysmith singleton to obtain an Account");
|
||||||
qmlRegisterType<model::AccountNameValidator>("Keysmith.Validators", 1, 0, "AccountNameValidator");
|
qmlRegisterType<model::AccountNameValidator>("Keysmith.Validators", 1, 0, "AccountNameValidator");
|
||||||
qmlRegisterType<validators::Base32Validator>("Keysmith.Validators", 1, 0, "Base32SecretValidator");
|
qmlRegisterType<validators::Base32Validator>("Keysmith.Validators", 1, 0, "Base32SecretValidator");
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
set(model_SRCS
|
set(model_SRCS
|
||||||
accounts.cpp
|
accounts.cpp
|
||||||
|
password.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(model_lib STATIC ${model_SRCS})
|
add_library(model_lib STATIC ${model_SRCS})
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||||
|
*/
|
||||||
|
#include "password.h"
|
||||||
|
|
||||||
|
#include "../secrets/secrets.h"
|
||||||
|
#include "../logging_p.h"
|
||||||
|
|
||||||
|
KEYSMITH_LOGGER(logger, ".model.password")
|
||||||
|
|
||||||
|
namespace model
|
||||||
|
{
|
||||||
|
PasswordRequest::PasswordRequest(accounts::AccountSecret *secret, QObject *parent) :
|
||||||
|
QObject(parent), m_secret(secret), m_previous(false), m_haveKey(false), m_havePassword(false)
|
||||||
|
{
|
||||||
|
QObject::connect(m_secret, &accounts::AccountSecret::existingPasswordNeeded, this, &PasswordRequest::setPreviouslyDefined);
|
||||||
|
QObject::connect(m_secret, &accounts::AccountSecret::passwordAvailable, this, &PasswordRequest::setPasswordAvailable);
|
||||||
|
QObject::connect(m_secret, &accounts::AccountSecret::keyAvailable, this, &PasswordRequest::setKeyAvailable);
|
||||||
|
m_previous = secret->isExistingPasswordRequested();
|
||||||
|
m_haveKey = secret->isKeyAvailable();
|
||||||
|
m_havePassword = secret->isPasswordAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordRequest::previouslyDefined(void) const
|
||||||
|
{
|
||||||
|
return m_previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordRequest::keyAvailable(void) const
|
||||||
|
{
|
||||||
|
return m_haveKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordRequest::passwordProvided(void) const
|
||||||
|
{
|
||||||
|
return m_havePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordRequest::provideBothPasswords(QString password, QString other)
|
||||||
|
{
|
||||||
|
if (password != other || password.isEmpty()) {
|
||||||
|
qCDebug(logger) << "Not applying new password(s): passwords must match and must not be empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_previous) {
|
||||||
|
qCDebug(logger) << "Ignoring new password(s): function should not be used to unlock existing account secrets";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<secrets::KeyDerivationParameters> params = secrets::KeyDerivationParameters::create();
|
||||||
|
if (!params) {
|
||||||
|
qCDebug(logger) << "Unable apply new password(s): failed to create default key derivation parameters";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_secret->answerNewPassword(password, *params)) {
|
||||||
|
other.fill(QLatin1Char('*'), -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(logger) << "Failed to apply new password(s)";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PasswordRequest::providePassword(QString password)
|
||||||
|
{
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
qCDebug(logger) << "Not applying password: passwords must not be empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_previous) {
|
||||||
|
qCDebug(logger) << "Ignoring password: function should not be used to set up new account secrets";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_secret->answerExistingPassword(password)) {
|
||||||
|
password.fill(QLatin1Char('*'), -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(logger) << "Failed to apply password for existing account secrets";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordRequest::setKeyAvailable(void)
|
||||||
|
{
|
||||||
|
if (!m_haveKey) {
|
||||||
|
m_haveKey = true;
|
||||||
|
Q_EMIT derivedKey();
|
||||||
|
} else {
|
||||||
|
qCDebug(logger) << "Ignored signal: already marked key as available";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordRequest::setPasswordAvailable(void)
|
||||||
|
{
|
||||||
|
if (!m_havePassword) {
|
||||||
|
m_havePassword = true;
|
||||||
|
Q_EMIT passwordAccepted();
|
||||||
|
} else {
|
||||||
|
qCDebug(logger) << "Ignored signal: already marked password as available";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordRequest::setPreviouslyDefined(void)
|
||||||
|
{
|
||||||
|
if (!m_previous) {
|
||||||
|
m_previous = true;
|
||||||
|
Q_EMIT passwordExists();
|
||||||
|
} else {
|
||||||
|
qCDebug(logger) << "Ignored signal: already marked password for existing secrets";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
|
||||||
|
*/
|
||||||
|
#ifndef MODEL_PASSWORD_H
|
||||||
|
#define MODEL_PASSWORD_H
|
||||||
|
|
||||||
|
#include "../account/keys.h"
|
||||||
|
|
||||||
|
namespace model
|
||||||
|
{
|
||||||
|
class PasswordRequest: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool previouslyDefined READ previouslyDefined NOTIFY passwordExists)
|
||||||
|
Q_PROPERTY(bool keyAvailable READ keyAvailable NOTIFY derivedKey)
|
||||||
|
Q_PROPERTY(bool passwordProvided READ passwordProvided NOTIFY passwordAccepted)
|
||||||
|
public:
|
||||||
|
explicit PasswordRequest(accounts::AccountSecret *secret, QObject *parent = nullptr);
|
||||||
|
bool previouslyDefined(void) const;
|
||||||
|
bool keyAvailable(void) const;
|
||||||
|
bool passwordProvided(void) const;
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE bool providePassword(QString password);
|
||||||
|
Q_INVOKABLE bool provideBothPasswords(QString password, QString other);
|
||||||
|
Q_SIGNALS:
|
||||||
|
void passwordExists(void) const;
|
||||||
|
void passwordAccepted(void) const;
|
||||||
|
void derivedKey(void) const;
|
||||||
|
private Q_SLOTS:
|
||||||
|
void setKeyAvailable(void);
|
||||||
|
void setPasswordAvailable(void);
|
||||||
|
void setPreviouslyDefined(void);
|
||||||
|
private:
|
||||||
|
accounts::AccountSecret * m_secret;
|
||||||
|
private:
|
||||||
|
bool m_previous;
|
||||||
|
bool m_haveKey;
|
||||||
|
bool m_havePassword;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue