#include "user.h" #include #include #include #include #include #include "constants.h" #include "rsshit_db.h" namespace rsshit { QByteArray generateSalt() { constexpr qsizetype hashSizeBytes{32}; QByteArray saltArray{hashSizeBytes, char{0}}; auto generator = QRandomGenerator::global(); generator->fillRange((uint32_t *) saltArray.data(), hashSizeBytes); return saltArray; } } // namespace rsshit // FIXME: use better password hashing algo User::User(const QString &login, const QString &password) : m_login{login} , m_salt{rsshit::generateSalt()} , m_passwordHash{hashPassword(password)} { qDebug() << __func__ << "login:" << login; qDebug() << __func__ << "salt size:" << m_salt.size(); qDebug() << __func__ << "passwordHash size:" << m_passwordHash.size(); } User::User(const QString &login) : m_login{login} { fetchFromDb(); } // TODO: rename to `fetchFromDb` and fill all data? int User::getDbId() { if (m_id != rsshit::db::IdNotFound) return m_id; const auto db = rsshit::db::open(); if (!db) return rsshit::db::IdNotFound; QSqlQuery selectQ{"select id from users where login=?"}; selectQ.addBindValue(m_login); if (!selectQ.exec()) { qWarning() << "cannot exec query" << selectQ.lastQuery() << ":" << selectQ.lastError().text(); return rsshit::db::IdNotFound; } if (!selectQ.next()) return rsshit::db::IdNotFound; const auto idVariant = selectQ.value(rsshit::db::idTag); if (!idVariant.isValid() || !idVariant.canConvert()) return rsshit::db::IdNotFound; bool ok{false}; const auto result = idVariant.toInt(&ok); if (!ok) { qWarning() << "got invalid id from db:" << idVariant; return rsshit::db::IdNotFound; } if (m_id != result) m_id = result; return result; } int User::fetchFromDb() { const auto db = rsshit::db::open(); if (!db) return rsshit::db::IdNotFound; // ignore local `id` if exists, fetch all fields QSqlQuery selectQ{"select id, login, salt, password_hash from users where login=?"}; selectQ.addBindValue(m_login); if (!selectQ.exec()) { qWarning() << "cannot exec query" << selectQ.lastQuery() << ":" << selectQ.lastError().text(); return rsshit::db::IdNotFound; } if (!selectQ.next()) return rsshit::db::IdNotFound; const auto idVariant = selectQ.value(rsshit::db::idTag); if (!idVariant.isValid() || !idVariant.canConvert()) return rsshit::db::IdNotFound; bool ok{false}; m_id = idVariant.toInt(&ok); if (!ok) { qWarning() << "got invalid id from db:" << idVariant; return rsshit::db::IdNotFound; } if (m_id == rsshit::db::IdNotFound) return m_id; m_salt = selectQ.value(rsshit::db::saltTag).toByteArray(); m_passwordHash = selectQ.value(rsshit::db::passwordHashTag).toByteArray(); return m_id; } bool User::existsInDb() { return getDbId() != rsshit::db::IdNotFound; } int User::createInDb() { if (m_id != rsshit::db::IdNotFound) return m_id; const auto db = rsshit::db::open(); if (!db) return rsshit::db::IdNotFound; if (m_login.isEmpty() || m_salt.isEmpty() || m_passwordHash.isEmpty()) return rsshit::db::IdNotFound; QSqlQuery insertQ{"insert into users(login, salt, password_hash) values(?, ?, ?)"}; insertQ.addBindValue(m_login); insertQ.addBindValue(m_salt); insertQ.addBindValue(m_passwordHash); if (!insertQ.exec()) { qWarning() << "cannot exec query" << insertQ.lastQuery() << ":" << insertQ.lastError().text(); return rsshit::db::IdNotFound; } m_id = insertQ.lastInsertId().toInt(); return m_id; } int User::getOrInsertDbId() { const auto id = getDbId(); if (id != rsshit::db::IdNotFound) return id; return createInDb(); } bool User::verifyPassword(const QString &password) { return (!m_passwordHash.isEmpty()) && (hashPassword(password) == m_passwordHash); } QByteArray User::hashPassword(const QString &password) { return QCryptographicHash::hash(m_salt + password.toUtf8(), QCryptographicHash::Sha256); }