Compare commits
3 Commits
abd653ef1c
...
4182e2d5a7
| Author | SHA1 | Date | |
|---|---|---|---|
|
4182e2d5a7
|
|||
|
a56532a047
|
|||
|
ab0ab4b4fb
|
@@ -56,9 +56,12 @@ qt_add_qml_module(
|
|||||||
common/shaderpack.h
|
common/shaderpack.h
|
||||||
packcompiler.h
|
packcompiler.h
|
||||||
packcompiler.cpp
|
packcompiler.cpp
|
||||||
SOURCES common/userservices.h
|
common/userservices.h
|
||||||
SOURCES common/userservices.cpp
|
common/userservices.cpp
|
||||||
SOURCES common/qwallet.h common/qwallet.cpp
|
common/qwallet.h
|
||||||
|
common/qwallet.cpp
|
||||||
|
usermodel.h
|
||||||
|
usermodel.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
|
|||||||
@@ -69,14 +69,9 @@ inline const QString KOMPLEX_ENDPOINT_USER_AUTH = QStringLiteral("
|
|||||||
|
|
||||||
inline const qsizetype nullsize = std::numeric_limits<qsizetype>::min();
|
inline const qsizetype nullsize = std::numeric_limits<qsizetype>::min();
|
||||||
inline const std::chrono::milliseconds KOMPLEX_RATE_LIMIT(500);
|
inline const std::chrono::milliseconds KOMPLEX_RATE_LIMIT(500);
|
||||||
inline const QString KOMPLEX_KEYCHAIN_PASSWORD_KEY = QStringLiteral
|
inline const QString KOMPLEX_KEYCHAIN_SERVICE_NAME = QStringLiteral
|
||||||
(
|
(
|
||||||
"com.digitalartifex.komplex.keychain.password"
|
"com.digitalartifex.komplex"
|
||||||
);
|
|
||||||
|
|
||||||
inline const QString KOMPLEX_KEYCHAIN_TOKEN_KEY = QStringLiteral
|
|
||||||
(
|
|
||||||
"com.digitalartifex.komplex.keychain.token"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif // KOMPLEX_GLOBAL_H
|
#endif // KOMPLEX_GLOBAL_H
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
* @brief The QWallet class
|
* @brief The QWallet class
|
||||||
* QWallet is a QFuture wrapper for QtKeychain to make interaction with the keychain easier
|
* QWallet is a QFuture wrapper for QtKeychain to make interaction with the keychain easier
|
||||||
*/
|
*/
|
||||||
class KOMPLEX_EXPORT QWallet : QObject
|
class KOMPLEX_EXPORT QWallet : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
#include "userservices.h"
|
#include "userservices.h"
|
||||||
|
#include "common/komplex_global.h"
|
||||||
#include "coreservices.h"
|
#include "coreservices.h"
|
||||||
|
#include <QtConcurrent/qtconcurrentrun.h>
|
||||||
|
#include <exception>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qstandardpaths.h>
|
||||||
|
#include <qstringview.h>
|
||||||
|
|
||||||
UserServices::UserServices(QObject *parent) : QObject(parent)
|
UserServices::UserServices(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
@@ -8,6 +14,10 @@ UserServices::UserServices(QObject *parent) : QObject(parent)
|
|||||||
|
|
||||||
UserServices::~UserServices()
|
UserServices::~UserServices()
|
||||||
{
|
{
|
||||||
|
if(m_wallet)
|
||||||
|
{
|
||||||
|
m_wallet->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto UserServices::setUserCredentials(const UserCredentials &credentials) -> void
|
auto UserServices::setUserCredentials(const UserCredentials &credentials) -> void
|
||||||
@@ -24,12 +34,135 @@ auto UserServices::errorString() const -> const QString &
|
|||||||
auto UserServices::setErrorString(const QString &errorString) -> void
|
auto UserServices::setErrorString(const QString &errorString) -> void
|
||||||
{
|
{
|
||||||
if (m_errorString == errorString)
|
if (m_errorString == errorString)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_errorString = errorString;
|
m_errorString = errorString;
|
||||||
emit errorStringChanged();
|
emit errorStringChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto UserServices::instance() const -> QWeakPointer<UserServices>
|
auto UserServices::savePassword(const QString &username, const QByteArray &password) -> void
|
||||||
|
{
|
||||||
|
QFuture saveAttempt = m_wallet->write
|
||||||
|
(
|
||||||
|
KOMPLEX_KEYCHAIN_SERVICE_NAME,
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserServices::readPassword(const QString &username) -> QFuture<QByteArray>
|
||||||
|
{
|
||||||
|
return m_wallet->read(KOMPLEX_KEYCHAIN_SERVICE_NAME, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserServices::readToken(const QString &username) -> QFuture<UserCredentials>
|
||||||
|
{
|
||||||
|
return QtConcurrent::run
|
||||||
|
(
|
||||||
|
[this, username] () -> UserCredentials
|
||||||
|
{
|
||||||
|
UserCredentials credentials;
|
||||||
|
|
||||||
|
QFuture readAttempt = m_wallet->read
|
||||||
|
(
|
||||||
|
KOMPLEX_KEYCHAIN_SERVICE_NAME,
|
||||||
|
username + QStringLiteral("_token")
|
||||||
|
);
|
||||||
|
|
||||||
|
QString _username = username;
|
||||||
|
|
||||||
|
readAttempt
|
||||||
|
.then
|
||||||
|
(
|
||||||
|
[this, &credentials, _username] (const QByteArray &value) -> void
|
||||||
|
{
|
||||||
|
auto parts = value.split(PART_SEPARATOR);
|
||||||
|
QDateTime expiry;
|
||||||
|
|
||||||
|
if(parts.count() == 1)
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Token malformed"));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parts[0].length() != sizeof(qint64))
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Token expiry malformed"));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray numericData = parts[0];
|
||||||
|
qint64 numericConversion = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < sizeof(qint64); ++i)
|
||||||
|
{
|
||||||
|
numericConversion |=
|
||||||
|
(
|
||||||
|
static_cast<qint64>
|
||||||
|
(
|
||||||
|
(
|
||||||
|
static_cast<qint8>(numericData[i]) &
|
||||||
|
0xFF
|
||||||
|
)
|
||||||
|
<< (i * sizeof(qint8))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expiry = QDateTime::fromMSecsSinceEpoch(numericConversion);
|
||||||
|
|
||||||
|
if(parts[1].isEmpty() && parts.count() == 3)
|
||||||
|
{
|
||||||
|
parts[1] = parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
credentials = UserCredentials
|
||||||
|
{
|
||||||
|
.username = _username.toUtf8(),
|
||||||
|
.sessionToken = parts[1],
|
||||||
|
.expiry = expiry
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onFailed
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
setErrorString(m_wallet->errorString());
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
readAttempt.waitForFinished();
|
||||||
|
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserServices::saveToken(const QString &username, const QByteArray &token, const QDateTime &expiry) -> void
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
qint64 numericData = expiry.toMSecsSinceEpoch();
|
||||||
|
|
||||||
|
for(int i = 0; i < sizeof(qint64); ++i)
|
||||||
|
{
|
||||||
|
data[i] = 0xFF & static_cast<qint8>(numericData >> (i * sizeof(qint8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
data += PART_SEPARATOR + token;
|
||||||
|
|
||||||
|
QFuture saveAttempt = m_wallet->write
|
||||||
|
(
|
||||||
|
KOMPLEX_KEYCHAIN_SERVICE_NAME,
|
||||||
|
username + QString("_token"),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserServices::instance() -> QWeakPointer<UserServices>
|
||||||
{
|
{
|
||||||
if(!s_pointer)
|
if(!s_pointer)
|
||||||
{
|
{
|
||||||
@@ -53,16 +186,19 @@ auto UserServices::login(const QString &username, const QByteArray &password) ->
|
|||||||
{
|
{
|
||||||
return QtConcurrent::run
|
return QtConcurrent::run
|
||||||
(
|
(
|
||||||
[this, username = std::move(username), password = std::move(password)] () -> UserCredentials
|
[this, username, password] () -> UserCredentials
|
||||||
{
|
{
|
||||||
QSharedPointer<QNetworkAccessManager> manager = CoreServices::networkAccessManager().toStrongRef();
|
QSharedPointer<QNetworkAccessManager> manager = CoreServices::networkAccessManager().toStrongRef();
|
||||||
|
|
||||||
if(!manager)
|
if(!manager)
|
||||||
{
|
{
|
||||||
Q_EMIT loginFailed();
|
Q_EMIT loginFailed();
|
||||||
return {};
|
setErrorString(QStringLiteral("Network manager reference deleted"));
|
||||||
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveDefaultUsername(username);
|
||||||
|
|
||||||
QNetworkRequest request
|
QNetworkRequest request
|
||||||
(
|
(
|
||||||
QUrl
|
QUrl
|
||||||
@@ -139,10 +275,77 @@ auto UserServices::login(const QString &username, const QByteArray &password) ->
|
|||||||
.expiry = QDateTime::currentDateTime()
|
.expiry = QDateTime::currentDateTime()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
savePassword(username, password);
|
||||||
|
saveToken(username, credentials.sessionToken, credentials.expiry);
|
||||||
setUserCredentials(credentials);
|
setUserCredentials(credentials);
|
||||||
|
|
||||||
Q_EMIT loginComplete();
|
Q_EMIT loginComplete();
|
||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserServices::saveDefaultUsername(const QString &username) -> void
|
||||||
|
{
|
||||||
|
QDir configDirectory(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
||||||
|
|
||||||
|
if(!configDirectory.exists())
|
||||||
|
{
|
||||||
|
if(!configDirectory.mkpath(configDirectory.absolutePath()))
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Could not make config directory"));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile usernameFile(configDirectory.absoluteFilePath(QStringLiteral("uname")));
|
||||||
|
|
||||||
|
if(!usernameFile.open(QFile::ReadWrite | QFile::Truncate))
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Could not open uname file"));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data = username.toUtf8().toBase64();
|
||||||
|
|
||||||
|
if(usernameFile.write(data) != data.length())
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Could not write uname file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
usernameFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserServices::defaultUsername() -> QFuture<QString>
|
||||||
|
{
|
||||||
|
return QtConcurrent::run
|
||||||
|
(
|
||||||
|
[this] () -> QString
|
||||||
|
{
|
||||||
|
QDir configDirectory(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
||||||
|
|
||||||
|
if(!configDirectory.exists())
|
||||||
|
{
|
||||||
|
if(!configDirectory.mkpath(configDirectory.absolutePath()))
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Could not make config directory"));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile usernameFile(configDirectory.absoluteFilePath(QStringLiteral("uname")));
|
||||||
|
|
||||||
|
if(!usernameFile.open(QFile::ReadOnly))
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("Could not open uname file"));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data = QByteArray::fromBase64(usernameFile.readAll());
|
||||||
|
|
||||||
|
usernameFile.close();
|
||||||
|
|
||||||
|
return QString(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,10 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include "komplex_global.h"
|
#include "komplex_global.h"
|
||||||
#include "qwallet.h"
|
#include "qwallet.h"
|
||||||
|
|
||||||
@@ -32,23 +36,34 @@ struct UserCredentials
|
|||||||
!sessionToken.isEmpty() && sessionToken.isValidUtf8();
|
!sessionToken.isEmpty() && sessionToken.isValidUtf8();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Q_DECLARE_METATYPE(UserCredentials)
|
||||||
|
|
||||||
class KOMPLEX_EXPORT UserServices : public QObject
|
class KOMPLEX_EXPORT UserServices : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
auto instance() const -> QWeakPointer<UserServices>;
|
static auto instance() -> QWeakPointer<UserServices>;
|
||||||
static auto userCredentials() -> const UserCredentials &;
|
static auto userCredentials() -> const UserCredentials &;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
auto login(const QString &username, const QByteArray &password) -> QFuture<UserCredentials>;
|
auto login(const QString &username, const QByteArray &password) -> QFuture<UserCredentials>;
|
||||||
|
|
||||||
auto errorString() const -> const QString &;
|
auto errorString() const -> const QString &;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto readPassword(const QString &username) -> QFuture<QByteArray>;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto readToken(const QString &username) -> QFuture<UserCredentials>;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto defaultUsername() -> QFuture<QString>;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
auto loginComplete() -> void;
|
auto loginComplete() -> void;
|
||||||
auto loginFailed() -> void;
|
auto loginFailed() -> void;
|
||||||
auto userCredentialsChanged() -> void;
|
auto userCredentialsChanged() -> void;
|
||||||
|
|
||||||
auto errorStringChanged() -> void;
|
auto errorStringChanged() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -60,10 +75,17 @@ private:
|
|||||||
|
|
||||||
auto setUserCredentials(const UserCredentials &credentials) -> void;
|
auto setUserCredentials(const UserCredentials &credentials) -> void;
|
||||||
auto setErrorString(const QString &errorString) -> void;
|
auto setErrorString(const QString &errorString) -> void;
|
||||||
|
auto savePassword(const QString &username, const QByteArray &password) -> void;
|
||||||
|
auto saveToken(const QString &username, const QByteArray &token, const QDateTime &expiry) -> void;
|
||||||
|
auto saveDefaultUsername(const QString &username) -> void;
|
||||||
|
|
||||||
QWallet *m_wallet = nullptr;
|
QWallet *m_wallet = nullptr;
|
||||||
QString m_errorString;
|
QString m_errorString;
|
||||||
inline static UserCredentials s_userCredentials;
|
inline static UserCredentials s_userCredentials;
|
||||||
|
|
||||||
|
inline const static char PART_SEPARATOR = static_cast<char>('\0x1f');
|
||||||
|
|
||||||
Q_PROPERTY(UserCredentials userCredentials READ userCredentials NOTIFY userCredentialsChanged FINAL)
|
Q_PROPERTY(UserCredentials userCredentials READ userCredentials NOTIFY userCredentialsChanged FINAL)
|
||||||
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
|
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
|
||||||
};
|
};
|
||||||
|
Q_DECLARE_METATYPE(UserServices)
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
#include "usermodel.h"
|
||||||
|
#include "common/userservices.h"
|
||||||
|
#include <QtConcurrent/qtconcurrentrun.h>
|
||||||
|
#include <qdatetime.h>
|
||||||
|
#include <qfuture.h>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
#include <qstringview.h>
|
||||||
|
|
||||||
|
UserModel::UserModel(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
m_stateResetTimer = new QTimer(this);
|
||||||
|
m_stateResetTimer->setInterval(1000);
|
||||||
|
m_stateResetTimer->setSingleShot(true);
|
||||||
|
|
||||||
|
QObject::connect
|
||||||
|
(
|
||||||
|
m_stateResetTimer,
|
||||||
|
&QTimer::timeout,
|
||||||
|
this,
|
||||||
|
&UserModel::resetState
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(Busy);
|
||||||
|
|
||||||
|
QSharedPointer<UserServices> service = UserServices::instance();
|
||||||
|
|
||||||
|
if(service == nullptr)
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||||
|
setState(LoginFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<QString> usernameFuture = service->defaultUsername();
|
||||||
|
|
||||||
|
usernameFuture
|
||||||
|
.then
|
||||||
|
(
|
||||||
|
[this] (const QString username)
|
||||||
|
{
|
||||||
|
login(username);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onCanceled
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onFailed
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
service.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserModel::~UserModel()
|
||||||
|
{
|
||||||
|
if(m_stateResetTimer != nullptr)
|
||||||
|
{
|
||||||
|
m_stateResetTimer->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::login() -> void
|
||||||
|
{
|
||||||
|
QFuture aether = QtConcurrent::run
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_loginMutex);
|
||||||
|
setState(LoggingIn);
|
||||||
|
|
||||||
|
QSharedPointer<UserServices> service = UserServices::instance();
|
||||||
|
|
||||||
|
if(service == nullptr)
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||||
|
setState(LoginFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_loginAttempt = service->login(username(), password().toUtf8());
|
||||||
|
|
||||||
|
m_loginAttempt
|
||||||
|
.then
|
||||||
|
(
|
||||||
|
[this] (UserCredentials credentials) -> void
|
||||||
|
{
|
||||||
|
setState(LoggedIn);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onCanceled
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
setState(LoginFailure);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onFailed
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
setState(LoginCancelled);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::login(const QString username) -> void
|
||||||
|
{
|
||||||
|
if(username == m_username)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_username = username;
|
||||||
|
Q_EMIT usernameChanged();
|
||||||
|
|
||||||
|
QSharedPointer<UserServices> service = UserServices::instance();
|
||||||
|
|
||||||
|
if(service == nullptr)
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||||
|
setState(LoggedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<UserCredentials> tokenRead = service->readToken(username);
|
||||||
|
|
||||||
|
tokenRead
|
||||||
|
.then
|
||||||
|
(
|
||||||
|
[this] (const UserCredentials credentials) -> void
|
||||||
|
{
|
||||||
|
if(QDateTime::currentDateTime() <= credentials.expiry)
|
||||||
|
{
|
||||||
|
setState(LoggedIn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<UserServices> service = UserServices::instance();
|
||||||
|
|
||||||
|
if(service == nullptr)
|
||||||
|
{
|
||||||
|
setErrorString(QStringLiteral("UserServices object deleted"));
|
||||||
|
setState(LoggedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<QByteArray> passwordRead = service->readPassword(credentials.username);
|
||||||
|
|
||||||
|
passwordRead
|
||||||
|
.then
|
||||||
|
(
|
||||||
|
[this] (const QByteArray password)
|
||||||
|
{
|
||||||
|
if(password.isEmpty())
|
||||||
|
{
|
||||||
|
setState(LoggedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPassword(password);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
service.clear();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
service.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::cancelLogin() -> void
|
||||||
|
{
|
||||||
|
QFuture aether = QtConcurrent::run
|
||||||
|
(
|
||||||
|
[this] () -> void
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_loginMutex);
|
||||||
|
|
||||||
|
if(m_loginAttempt.isRunning())
|
||||||
|
{
|
||||||
|
m_loginAttempt.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::reset() -> void
|
||||||
|
{
|
||||||
|
resetState();
|
||||||
|
setUsername({});
|
||||||
|
setPassword({});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::setUsername(const QString &username) -> void
|
||||||
|
{
|
||||||
|
if(username == m_username)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_username = username;
|
||||||
|
Q_EMIT usernameChanged();
|
||||||
|
|
||||||
|
if(!password().isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(Busy);
|
||||||
|
|
||||||
|
QSharedPointer<UserServices> service = UserServices::instance();
|
||||||
|
|
||||||
|
if(service == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<QByteArray> passwordRead = service->readPassword(username);
|
||||||
|
passwordRead
|
||||||
|
.then
|
||||||
|
(
|
||||||
|
[this] (const QByteArray password) -> void
|
||||||
|
{
|
||||||
|
setPassword(password);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::setPassword(const QString &password) -> void
|
||||||
|
{
|
||||||
|
if(password == m_password)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_password = password;
|
||||||
|
Q_EMIT passwordChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::resetState() -> void
|
||||||
|
{
|
||||||
|
setState(Idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::setState(State state) -> void
|
||||||
|
{
|
||||||
|
if(state == m_state)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case LoginFailure:
|
||||||
|
case LoggedOut:
|
||||||
|
case LoginCancelled:
|
||||||
|
m_stateResetTimer->start();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = state;
|
||||||
|
Q_EMIT stateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto UserModel::setErrorString(const QString &string) -> void
|
||||||
|
{
|
||||||
|
if(string == m_errorString)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_errorString = string;
|
||||||
|
Q_EMIT errorStringChanged();
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#ifndef USERMODEL_H
|
||||||
|
#define USERMODEL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include "common/userservices.h"
|
||||||
|
|
||||||
|
class UserModel : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
LoggingIn,
|
||||||
|
LoggedIn,
|
||||||
|
LoginFailure,
|
||||||
|
LoginCancelled,
|
||||||
|
LoggedOut,
|
||||||
|
Busy
|
||||||
|
};
|
||||||
|
Q_ENUM(State)
|
||||||
|
|
||||||
|
explicit UserModel(QObject *parent = nullptr);
|
||||||
|
~UserModel();
|
||||||
|
|
||||||
|
Q_INVOKABLE auto login() -> void;
|
||||||
|
Q_INVOKABLE auto login(const QString username) -> void;
|
||||||
|
Q_INVOKABLE auto cancelLogin() -> void;
|
||||||
|
Q_INVOKABLE auto reset() -> void;
|
||||||
|
|
||||||
|
auto username() const -> const QString & { return m_username; }
|
||||||
|
auto setUsername(const QString &username) -> void;
|
||||||
|
|
||||||
|
auto password() const -> const QString & { return m_password; }
|
||||||
|
auto setPassword(const QString &password) -> void;
|
||||||
|
|
||||||
|
auto state() const -> State { return m_state; }
|
||||||
|
auto errorString() const -> QString { return m_errorString; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
auto loginSuccessful() -> void;
|
||||||
|
auto loginFailed() -> void;
|
||||||
|
auto stateChanged() -> void;
|
||||||
|
auto usernameChanged() -> void;
|
||||||
|
auto passwordChanged() -> void;
|
||||||
|
auto errorStringChanged() -> void;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
auto resetState() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto setState(State state) -> void;
|
||||||
|
auto setErrorString(const QString &string) -> void;
|
||||||
|
|
||||||
|
State m_state = Idle;
|
||||||
|
QString m_errorString;
|
||||||
|
QString m_username;
|
||||||
|
QString m_password;
|
||||||
|
QTimer *m_stateResetTimer = nullptr;
|
||||||
|
QMutex m_loginMutex;
|
||||||
|
QFuture<UserCredentials> m_loginAttempt;
|
||||||
|
|
||||||
|
Q_PROPERTY(State state READ state NOTIFY stateChanged FINAL)
|
||||||
|
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged FINAL)
|
||||||
|
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL)
|
||||||
|
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USERMODEL_H
|
||||||
Reference in New Issue
Block a user