From 4182e2d5a705272adc4ecc5e01218fd41abe75dd Mon Sep 17 00:00:00 2001 From: Digital Artifex <7929434+DigitalArtifex@users.noreply.github.com> Date: Sat, 15 Aug 2026 02:35:45 -0400 Subject: [PATCH] Added additional UserServices functionality --- KomplexHubPlugin/common/komplex_global.h | 9 +- KomplexHubPlugin/common/userservices.cpp | 209 ++++++++++++++++++++++- KomplexHubPlugin/common/userservices.h | 28 ++- 3 files changed, 233 insertions(+), 13 deletions(-) diff --git a/KomplexHubPlugin/common/komplex_global.h b/KomplexHubPlugin/common/komplex_global.h index 00ec803..ff4214d 100644 --- a/KomplexHubPlugin/common/komplex_global.h +++ b/KomplexHubPlugin/common/komplex_global.h @@ -69,14 +69,9 @@ inline const QString KOMPLEX_ENDPOINT_USER_AUTH = QStringLiteral(" inline const qsizetype nullsize = std::numeric_limits::min(); 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" -); - -inline const QString KOMPLEX_KEYCHAIN_TOKEN_KEY = QStringLiteral -( - "com.digitalartifex.komplex.keychain.token" + "com.digitalartifex.komplex" ); #endif // KOMPLEX_GLOBAL_H diff --git a/KomplexHubPlugin/common/userservices.cpp b/KomplexHubPlugin/common/userservices.cpp index 4d85e4e..79cad46 100644 --- a/KomplexHubPlugin/common/userservices.cpp +++ b/KomplexHubPlugin/common/userservices.cpp @@ -1,5 +1,11 @@ #include "userservices.h" +#include "common/komplex_global.h" #include "coreservices.h" +#include +#include +#include +#include +#include UserServices::UserServices(QObject *parent) : QObject(parent) { @@ -8,6 +14,10 @@ UserServices::UserServices(QObject *parent) : QObject(parent) UserServices::~UserServices() { + if(m_wallet) + { + m_wallet->deleteLater(); + } } auto UserServices::setUserCredentials(const UserCredentials &credentials) -> void @@ -24,12 +34,135 @@ auto UserServices::errorString() const -> const QString & auto UserServices::setErrorString(const QString &errorString) -> void { if (m_errorString == errorString) + { return; + } + m_errorString = errorString; emit errorStringChanged(); } -auto UserServices::instance() const -> QWeakPointer +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 +{ + return m_wallet->read(KOMPLEX_KEYCHAIN_SERVICE_NAME, username); +} + +auto UserServices::readToken(const QString &username) -> QFuture +{ + 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 + ( + ( + static_cast(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(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 { if(!s_pointer) { @@ -53,16 +186,19 @@ auto UserServices::login(const QString &username, const QByteArray &password) -> { return QtConcurrent::run ( - [this, username = std::move(username), password = std::move(password)] () -> UserCredentials + [this, username, password] () -> UserCredentials { QSharedPointer manager = CoreServices::networkAccessManager().toStrongRef(); if(!manager) { Q_EMIT loginFailed(); - return {}; + setErrorString(QStringLiteral("Network manager reference deleted")); + throw std::exception(); } + saveDefaultUsername(username); + QNetworkRequest request ( QUrl @@ -139,10 +275,77 @@ auto UserServices::login(const QString &username, const QByteArray &password) -> .expiry = QDateTime::currentDateTime() }; + savePassword(username, password); + saveToken(username, credentials.sessionToken, credentials.expiry); setUserCredentials(credentials); Q_EMIT loginComplete(); 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 +{ + 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); + } + ); } \ No newline at end of file diff --git a/KomplexHubPlugin/common/userservices.h b/KomplexHubPlugin/common/userservices.h index 63755b9..9250936 100644 --- a/KomplexHubPlugin/common/userservices.h +++ b/KomplexHubPlugin/common/userservices.h @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include + #include "komplex_global.h" #include "qwallet.h" @@ -32,23 +36,34 @@ struct UserCredentials !sessionToken.isEmpty() && sessionToken.isValidUtf8(); } }; +Q_DECLARE_METATYPE(UserCredentials) class KOMPLEX_EXPORT UserServices : public QObject { Q_OBJECT public: - auto instance() const -> QWeakPointer; + static auto instance() -> QWeakPointer; static auto userCredentials() -> const UserCredentials &; + + [[nodiscard]] auto login(const QString &username, const QByteArray &password) -> QFuture; auto errorString() const -> const QString &; + [[nodiscard]] + auto readPassword(const QString &username) -> QFuture; + + [[nodiscard]] + auto readToken(const QString &username) -> QFuture; + + [[nodiscard]] + auto defaultUsername() -> QFuture; + signals: auto loginComplete() -> void; auto loginFailed() -> void; auto userCredentialsChanged() -> void; - auto errorStringChanged() -> void; private: @@ -60,10 +75,17 @@ private: auto setUserCredentials(const UserCredentials &credentials) -> 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; QString m_errorString; inline static UserCredentials s_userCredentials; + + inline const static char PART_SEPARATOR = static_cast('\0x1f'); + Q_PROPERTY(UserCredentials userCredentials READ userCredentials NOTIFY userCredentialsChanged FINAL) Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL) -}; \ No newline at end of file +}; +Q_DECLARE_METATYPE(UserServices) \ No newline at end of file