start work on persistent login
This commit is contained in:
@@ -1,8 +1,30 @@
|
||||
#include "db.h"
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sqlite3.h>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
|
||||
std::string generateSecureToken(size_t numBytes) {
|
||||
std::ifstream urandom("/dev/urandom", std::ios::binary);
|
||||
if (!urandom) {
|
||||
throw std::runtime_error("couldn't open /dev/urandom");
|
||||
}
|
||||
|
||||
std::vector<unsigned char> buf(numBytes);
|
||||
urandom.read(reinterpret_cast<char*>(buf.data()), numBytes);
|
||||
if (!urandom) {
|
||||
throw std::runtime_error("short read from /dev/urandom");
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for (unsigned char b : buf) {
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Database::Database(const std::string& path) {
|
||||
// init db
|
||||
@@ -25,6 +47,12 @@ Database::Database(const std::string& path) {
|
||||
timestamp INTEGER NOT NULL,
|
||||
likes INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
token TEXT PRIMARY KEY,
|
||||
userid INTEGER NOT NULL,
|
||||
expiry INTEGER NOT NULL
|
||||
);
|
||||
)";
|
||||
|
||||
char* errmsg = nullptr;
|
||||
@@ -227,6 +255,61 @@ std::optional<User> Database::getUserByName(const std::string& name) {
|
||||
return User(id, name, password);
|
||||
}
|
||||
|
||||
std::optional<std::string> Database::createNewToken(uint64_t id) {
|
||||
std::string token = generateSecureToken(32);
|
||||
int64_t expiry = static_cast<int64_t>(std::time(nullptr)) + 60 * 60 * 24 * 30; // 30 days
|
||||
|
||||
const char* sql = R"(
|
||||
INSERT INTO sessions (token, userid, expiry)
|
||||
VALUES (?, ?, ?);
|
||||
)";
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, token.c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 2, id);
|
||||
sqlite3_bind_int64(stmt, 3, expiry);
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
sqlite3_finalize(stmt);
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return token;
|
||||
}
|
||||
|
||||
std::optional<User> Database::getUserByToken(const std::string& token) {
|
||||
const char* sql = "SELECT userid, expiry FROM sessions WHERE token = ?;";
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
throw std::runtime_error("sqlite3 error: " + std::string(sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, token.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_DONE) {
|
||||
sqlite3_finalize(stmt);
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t userid = sqlite3_column_int64(stmt, 0);
|
||||
int64_t expiry = sqlite3_column_int64(stmt, 1);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (expiry < static_cast<int64_t>(std::time(nullptr))) {
|
||||
// expired; not bothering to delete it here, an expiry sweep
|
||||
// job would be a reasonable thing to add later
|
||||
return {};
|
||||
}
|
||||
|
||||
return getUser(userid);
|
||||
}
|
||||
|
||||
void Database::addUser(User& user) {
|
||||
const char* sql = R"(
|
||||
INSERT INTO users (name, password)
|
||||
|
||||
Reference in New Issue
Block a user