types and db setup
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
build
|
||||||
|
.vscode
|
||||||
|
creds.hpp
|
||||||
27
CMakeLists.txt
Normal file
27
CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(plural++)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
|
# Add submodules
|
||||||
|
add_subdirectory(libs/pqxx)
|
||||||
|
add_subdirectory(libs/crow)
|
||||||
|
|
||||||
|
# Collect sources
|
||||||
|
add_executable(plural++
|
||||||
|
src/main.cpp
|
||||||
|
src/database.cpp
|
||||||
|
src/hashing.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
target_include_directories(plural++ PRIVATE
|
||||||
|
include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link libraries
|
||||||
|
target_link_libraries(plural++ PRIVATE
|
||||||
|
pqxx
|
||||||
|
Crow
|
||||||
|
crypt
|
||||||
|
)
|
||||||
12
include/database.hpp
Normal file
12
include/database.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
class PostgresDB {
|
||||||
|
private:
|
||||||
|
std::string conn_str;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PostgresDB(const std::string& connection) : conn_str(connection) {}
|
||||||
|
|
||||||
|
User GetUser(const int uid);
|
||||||
|
User GetUser(const std::string& username, const std::string& password);
|
||||||
|
};
|
||||||
7
include/hashing.hpp
Normal file
7
include/hashing.hpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace hashing {
|
||||||
|
std::string GenerateSetting(unsigned long cost = 0);
|
||||||
|
std::string HashPassword(const std::string& password, const std::string& setting);
|
||||||
|
bool VerifyPassword(const std::string& password, const std::string& stored_password);
|
||||||
|
}
|
||||||
55
include/types.hpp
Normal file
55
include/types.hpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "hashing.hpp"
|
||||||
|
|
||||||
|
struct AcessToken {
|
||||||
|
int uid;
|
||||||
|
std::string token;
|
||||||
|
|
||||||
|
const bool operator==(const AcessToken& other) const {
|
||||||
|
return uid == other.uid && token == other.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool operator==(const std::string& token) const {
|
||||||
|
return token == this->token;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Member {
|
||||||
|
int mid;
|
||||||
|
int sid;
|
||||||
|
std::string name;
|
||||||
|
std::string pronouns;
|
||||||
|
std::string description;
|
||||||
|
|
||||||
|
const bool operator==(const Member& other) const {
|
||||||
|
return mid == other.mid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct System {
|
||||||
|
int sid;
|
||||||
|
int uid;
|
||||||
|
std::vector<Member> members;
|
||||||
|
|
||||||
|
const bool operator==(const System& other) const {
|
||||||
|
return sid == other.sid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct User {
|
||||||
|
int uid;
|
||||||
|
std::string username;
|
||||||
|
std::string password_hash;
|
||||||
|
bool is_system;
|
||||||
|
System *system;
|
||||||
|
AcessToken *accesstoken;
|
||||||
|
|
||||||
|
const bool operator==(const User& other) const {
|
||||||
|
return uid == other.uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool CheckPassword(const std::string& password) const {
|
||||||
|
return hashing::VerifyPassword(password, password_hash);
|
||||||
|
}
|
||||||
|
};
|
||||||
87
src/database.cpp
Normal file
87
src/database.cpp
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#include <pqxx/pqxx>
|
||||||
|
#include "database.hpp"
|
||||||
|
|
||||||
|
User PostgresDB::GetUser(const int uid) {
|
||||||
|
pqxx::connection C(conn_str);
|
||||||
|
pqxx::work W(C);
|
||||||
|
|
||||||
|
pqxx::result R = W.exec("SELECT * FROM users WHERE uid = $1", pqxx::params{uid});
|
||||||
|
|
||||||
|
if (R.empty()) {
|
||||||
|
W.commit();
|
||||||
|
throw std::runtime_error("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const pqxx::row& row = R[0];
|
||||||
|
User user;
|
||||||
|
|
||||||
|
user.uid = row["uid"].as<int>();
|
||||||
|
user.username = row["username"].as<std::string>();
|
||||||
|
user.password_hash = row["password_hash"].as<std::string>();
|
||||||
|
user.is_system = row["is_system"].as<bool>();
|
||||||
|
|
||||||
|
if (!user.is_system) {
|
||||||
|
W.commit();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
pqxx::result R2 = W.exec("SELECT * FROM systems WHERE uid = $1", pqxx::params{user.uid});
|
||||||
|
|
||||||
|
if (R2.empty()) {
|
||||||
|
W.commit();
|
||||||
|
throw std::runtime_error("System not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const pqxx::row& row2 = R2[0];
|
||||||
|
user.system = new System;
|
||||||
|
user.system->sid = row2["sid"].as<int>();
|
||||||
|
user.system->uid = row2["uid"].as<int>();
|
||||||
|
|
||||||
|
pqxx::result R3 = W.exec("SELECT * FROM members WHERE sid = $1", pqxx::params{user.system->sid});
|
||||||
|
|
||||||
|
for (auto row : R3) {
|
||||||
|
Member member;
|
||||||
|
member.mid = row["mid"].as<int>();
|
||||||
|
member.sid = row["sid"].as<int>();
|
||||||
|
member.name = row["name"].as<std::string>();
|
||||||
|
member.pronouns = row["pronouns"].as<std::string>();
|
||||||
|
member.description = row["description"].as<std::string>();
|
||||||
|
user.system->members.push_back(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
pqxx::result R4 = W.exec("SELECT * FROM access_tokens WHERE uid = $1", pqxx::params{user.uid});
|
||||||
|
|
||||||
|
if (!R4.empty()) {
|
||||||
|
const pqxx::row& row4 = R4[0];
|
||||||
|
user.accesstoken = new AcessToken;
|
||||||
|
user.accesstoken->uid = row4["uid"].as<int>();
|
||||||
|
user.accesstoken->token = row4["token"].as<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
W.commit();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
User PostgresDB::GetUser(const std::string& username, const std::string& password) {
|
||||||
|
pqxx::connection C(conn_str);
|
||||||
|
pqxx::work W(C);
|
||||||
|
|
||||||
|
pqxx::result R = W.exec("SELECT * FROM users WHERE username = $1", pqxx::params{username});
|
||||||
|
|
||||||
|
if (R.size() == 0) {
|
||||||
|
W.commit();
|
||||||
|
throw std::runtime_error("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const pqxx::row& row = R[0];
|
||||||
|
User user;
|
||||||
|
|
||||||
|
user.password_hash = row["password_hash"].as<std::string>();
|
||||||
|
|
||||||
|
if (!user.CheckPassword(password)) {
|
||||||
|
W.commit();
|
||||||
|
throw std::runtime_error("Invalid password");
|
||||||
|
}
|
||||||
|
|
||||||
|
return PostgresDB::GetUser(row["uid"].as<int>());
|
||||||
|
}
|
||||||
27
src/hashing.cpp
Normal file
27
src/hashing.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include <crypt.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "hashing.hpp"
|
||||||
|
|
||||||
|
std::string hashing::GenerateSetting(unsigned long cost) {
|
||||||
|
// "$y$" is the yescrypt prefix
|
||||||
|
const char* setting = crypt_gensalt("$y$", cost, nullptr, 0);
|
||||||
|
if (!setting)
|
||||||
|
throw std::runtime_error("crypt_gensalt() failed – yescrypt may not be supported");
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hashing::HashPassword(const std::string& password, const std::string& setting) {
|
||||||
|
crypt_data data{};
|
||||||
|
const char* hash = crypt_r(password.c_str(), setting.c_str(), &data);
|
||||||
|
if (!hash || hash[0] == '*')
|
||||||
|
throw std::runtime_error("crypt_r() failed");
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hashing::VerifyPassword(const std::string& password, const std::string& stored_hash) {
|
||||||
|
crypt_data data{};
|
||||||
|
const char* result = crypt_r(password.c_str(), stored_hash.c_str(), &data);
|
||||||
|
if (!result || result[0] == '*')
|
||||||
|
return false;
|
||||||
|
return stored_hash == result;
|
||||||
|
}
|
||||||
13
src/main.cpp
Normal file
13
src/main.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "database.hpp"
|
||||||
|
#include "creds.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
PostgresDB db(DB_CREDS);
|
||||||
|
|
||||||
|
User user = db.GetUser("toaster", "password");
|
||||||
|
|
||||||
|
std::cout << "User: " << user.username << std::endl;
|
||||||
|
std::cout << "System id: " << user.system->sid << std::endl;
|
||||||
|
std::cout << "Member 0 Name: " << user.system->members[0].name << std::endl;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user