summaryrefslogtreecommitdiff
path: root/src/user.cpp
blob: 4667ebf7cce022ee2649026e5632e8e839d54847 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "user.h"

#include <QCryptographicHash>
#include <QDebug>
#include <QRandomGenerator>
#include <QSqlError>
#include <QSqlQuery>

#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)
    : login{login}
    , salt{rsshit::generateSalt()}
    , passwordHash{hashPassword(password)}
{
    qDebug() << __func__ << "login:" << login;
    qDebug() << __func__ << "salt size:" << salt.size();
    qDebug() << __func__ << "passwordHash size:" << passwordHash.size();
}

int User::getDbId()
{
    if (id != rsshit::db::IdNotFound)
        return id;

    const auto db = rsshit::db::open();

    if (!db)
        return rsshit::db::IdNotFound;

    QSqlQuery selectQ{"select id from users where login=?"};
    selectQ.addBindValue(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<int>())
        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;
    }

    return result;
}

int User::createInDb()
{
    if (id != rsshit::db::IdNotFound)
        return id;

    const auto db = rsshit::db::open();

    if (!db)
        return rsshit::db::IdNotFound;

    if (login.isEmpty() || salt.isEmpty() || passwordHash.isEmpty())
        return rsshit::db::IdNotFound;

    QSqlQuery insertQ{"insert into users(login, salt, password_hash) values(?, ?, ?)"};
    insertQ.addBindValue(login);
    insertQ.addBindValue(salt);
    insertQ.addBindValue(passwordHash);

    if (!insertQ.exec()) {
        qWarning() << "cannot exec query" << insertQ.lastQuery() << ":"
                   << insertQ.lastError().text();

        return rsshit::db::IdNotFound;
    }

    return insertQ.lastInsertId().toInt();
}

int User::getOrInsertDbId()
{
    const auto id = getDbId();

    if (id != rsshit::db::IdNotFound)
        return id;

    return createInDb();
}

bool User::verifyPassword(const QString &password)
{
    return hashPassword(password) == passwordHash;
}

QByteArray User::hashPassword(const QString &password)
{
    return QCryptographicHash::hash(salt + password.toUtf8(), QCryptographicHash::Sha256);
}