From ed434b7f68afe574e183a86f826ae06e15b901a9 Mon Sep 17 00:00:00 2001 From: Relintai Date: Sun, 22 Aug 2021 21:46:24 +0200 Subject: [PATCH] Started moving things out from the User and removed the UserManager. --- modules/users/user.cpp | 321 ++------------------------------- modules/users/user.h | 15 -- modules/users/user_manager.cpp | 103 ----------- modules/users/user_manager.h | 39 ---- modules/users/user_model.cpp | 304 ++++++++++++++++++++++++++++++- modules/users/user_model.h | 26 ++- 6 files changed, 345 insertions(+), 463 deletions(-) delete mode 100644 modules/users/user_manager.cpp delete mode 100644 modules/users/user_manager.h diff --git a/modules/users/user.cpp b/modules/users/user.cpp index d728b4f..4fdc1f8 100644 --- a/modules/users/user.cpp +++ b/modules/users/user.cpp @@ -4,7 +4,6 @@ #include "rapidjson/filewritestream.h" #include "rapidjson/rapidjson.h" #include "rapidjson/stringbuffer.h" -#include "user_manager.h" #include #include #include @@ -14,7 +13,6 @@ #include "core/database/query_result.h" #include "core/database/table_builder.h" -#include "core/hash/sha256.h" #include "core/html/form_validator.h" #include "core/html/html_builder.h" #include "core/http/cookie.h" @@ -22,242 +20,7 @@ #include "core/http/request.h" #include "core/http/session_manager.h" #include "core/utils.h" -#include "user_manager.h" - -void User::save() { - - QueryBuilder *b = DatabaseManager::get_singleton()->ddb->get_query_builder(); - - if (get_id() == 0) { - b->insert(_table_name, "username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked"); - - b->values(); - b->eval(name_user_input); - b->eval(email_user_input); - b->val(rank); - b->val(pre_salt); - b->val(post_salt); - b->val(password_hash); - b->val(banned); - b->val(password_reset_token); - b->val(locked); - b->cvalues(); - - b->end_command(); - b->select_last_insert_id(); - - QueryResult *r = b->run(); - - set_id(r->get_last_insert_rowid()); - - delete r; - - } else { - b->udpate(_table_name); - b->set(); - b->esetp("username", name_user_input); - b->esetp("email", email_user_input); - b->setp("rank", rank); - b->setp("pre_salt", pre_salt); - b->setp("post_salt", post_salt); - b->setp("password_hash", password_hash); - b->setp("banned", banned); - b->setp("password_reset_token", password_reset_token); - b->setp("locked", locked); - b->cset(); - b->where()->wp("id", get_id()); - - //b->print(); - - b->run_query(); - } - - if (get_id() == 0) { - return; - } - - b->reset(); - - b->del(_table_name + "_sessions")->where()->wp("user_id", get_id())->end_command(); - //b->print(); - - b->end_command(); - b->run_query(); - - b->reset(); - - for (int i = 0; i < sessions.size(); ++i) { - b->insert(_table_name + "_sessions")->values()->val(get_id())->val(sessions[i])->cvalues()->end_command(); - } - - //b->print(); - - b->run_query(); - - delete b; -} - -void User::load() { - - unregister_sessions(); - - if (get_id() == 0) { - return; - } - - QueryBuilder *b = DatabaseManager::get_singleton()->ddb->get_query_builder(); - - b->select("username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked"); - b->from(_table_name); - - b->where()->wp("id", get_id()); - - b->end_command(); - - QueryResult *r = b->run(); - - if (r->next_row()) { - name_user_input = r->get_cell(0); - email_user_input = r->get_cell(1); - rank = r->get_cell_int(2); - pre_salt = r->get_cell(3); - post_salt = r->get_cell(4); - password_hash = r->get_cell(5); - banned = r->get_cell_bool(6); - password_reset_token = r->get_cell(7); - locked = r->get_cell_bool(8); - } - - delete r; - - b->query_result = ""; - - b->select("session_id"); - b->from(_table_name + "_sessions"); - b->where()->wp("user_id", get_id()); - b->end_command(); - - r = b->run(); - - while (r->next_row()) { - sessions.push_back(r->get_cell(0)); - } - - delete r; - - delete b; - - register_sessions(); -} - -void User::load(const std::string &p_name) { - //name = p_name; - - //load(); -} - -void User::load(const int p_id) { - set_id(p_id); - - load(); -} - -void User::changed() { - save(); -} - -void User::update() { -} - - -void User::migrate() { - TableBuilder *tb = DatabaseManager::get_singleton()->ddb->get_table_builder(); - - tb->drop_table_if_exists(_table_name)->run_query(); - tb->drop_table_if_exists(_table_name + "_sessions")->run_query(); - //tb->print(); - - tb->result = ""; - - tb->create_table(_table_name); - tb->integer("id")->auto_increment()->next_row(); - tb->varchar("username", 60)->not_null()->next_row(); - tb->varchar("email", 100)->not_null()->next_row(); - tb->integer("rank")->not_null()->next_row(); - tb->varchar("pre_salt", 100)->next_row(); - tb->varchar("post_salt", 100)->next_row(); - tb->varchar("password_hash", 100)->next_row(); - tb->integer("banned")->next_row(); - tb->varchar("password_reset_token", 100)->next_row(); - tb->integer("locked")->next_row(); - tb->primary_key("id"); - tb->ccreate_table(); - tb->run_query(); - //tb->print(); - - tb->result = ""; - - tb->create_table(_table_name + "_sessions"); - tb->integer("user_id")->not_null()->next_row(); - tb->varchar("session_id", 100)->next_row(); - tb->foreign_key("user_id"); - tb->references("user", "id"); - tb->ccreate_table(); - //tb->print(); - tb->run_query(); - - delete tb; -} - -void User::db_load_all() { - QueryBuilder *b = DatabaseManager::get_singleton()->ddb->get_query_builder(); - - b->select("id"); - b->from(_table_name); - b->end_command(); - b->print(); - - QueryResult *r = b->run(); - - while (r->next_row()) { - User *u = new User(); - u->set_id(r->get_cell_int(0)); - u->load(); - - //printf("%s\n", u->to_json().c_str()); - - UserManager::get_singleton()->add_user(u); - } - - delete r; - - delete b; -} - - -bool User::check_password(const std::string &p_password) { - return hash_password(p_password) == password_hash; -} - -void User::create_password(const std::string &p_password) { - //todo improve a bit - pre_salt = hash_password(name_user_input + email_user_input); - post_salt = hash_password(email_user_input + name_user_input); - - password_hash = hash_password(p_password); -} - -std::string User::hash_password(const std::string &p_password) { - SHA256 *s = SHA256::get(); - - std::string p = pre_salt + p_password + post_salt; - - std::string c = s->compute(p); - - delete s; - - return c; -} +#include "user_model.h" void User::register_sessions() { if (sessions.size() == 0) { @@ -347,19 +110,16 @@ void User::handle_login_request_default(Request *request) { uname_val = request->get_parameter("username"); pass_val = request->get_parameter("password"); - User *user = UserManager::get_singleton()->get_user(uname_val); + Ref user = UserModel::get_singleton()->get_user(uname_val); - if (user) { - if (!user->check_password(pass_val)) { + if (user.is_valid()) { + if (!UserModel::get_singleton()->check_password(user, pass_val)) { error_str += "Invalid username or password!"; } else { HTTPSession *session = request->get_or_create_session(); - session->add_object("user", user); - - user->sessions.push_back(session->session_id); - - user->save(); + session->add_int("user", user->id); + //session->save(); request->add_cookie(::Cookie("session_id", session->session_id)); @@ -440,30 +200,11 @@ void User::handle_register_request_default(Request *request) { //todo username length etc check //todo pw length etc check - User *user = UserManager::get_singleton()->get_user(uname_val); - - if (user) { + if (UserModel::get_singleton()->is_username_taken(uname_val)) { error_str += "Username already taken!
"; } - UserManager *um = UserManager::get_singleton(); - - bool email_found = false; - - for (int i = 0; i < um->_users_vec.size(); ++i) { - User *u = um->_users_vec[i]; - - if (!u) { - continue; - } - - if (u->email_user_input == email_val) { - email_found = true; - break; - } - } - - if (email_found) { + if (UserModel::get_singleton()->is_email_taken(email_val)) { error_str += "Email already in use!
"; } @@ -472,16 +213,15 @@ void User::handle_register_request_default(Request *request) { } if (error_str.size() == 0) { - user = UserManager::get_singleton()->create_user(); + Ref user; + user.instance(); user->name_user_input = uname_val; user->email_user_input = email_val; //todo user->rank = 1; - user->create_password(pass_val); - user->save(); - - UserManager::get_singleton()->add_user(user); + UserModel::get_singleton()->create_password(user, pass_val); + UserModel::get_singleton()->save_user(user); HTMLBuilder b; @@ -621,9 +361,7 @@ void User::handle_settings_request(Request *request) { } if (uname_val != "") { - User *user = UserManager::get_singleton()->get_user(uname_val); - - if (user) { + if (UserModel::get_singleton()->is_username_taken(uname_val)) { error_str += "Username already taken!
"; } else { //todo sanitize for html special chars! @@ -634,29 +372,7 @@ void User::handle_settings_request(Request *request) { } if (email_val != "") { - UserManager *um = UserManager::get_singleton(); - - bool email_found = false; - - //todo better way + should be thread safe - for (int i = 0; i < um->_users_vec.size(); ++i) { - User *u = um->_users_vec[i]; - - if (!u) { - continue; - } - - if (u == this) { - continue; - } - - if (u->email_user_input == email_val) { - email_found = true; - break; - } - } - - if (email_found) { + if (UserModel::get_singleton()->is_email_taken(email_val)) { error_str += "Email already in use!
"; } else { //todo sanitize for html special chars! @@ -671,7 +387,8 @@ void User::handle_settings_request(Request *request) { if (pass_val != pass_check_val) { error_str += "The passwords did not match!
"; } else { - create_password(pass_val); + //todo + //create_password(pass_val); changed = true; } } @@ -839,6 +556,7 @@ std::string User::file_get_base_path() { } void User::file_load_all() { + /* tinydir_dir dir; if (tinydir_open(&dir, _path.c_str()) == -1) { return; @@ -865,6 +583,7 @@ void User::file_load_all() { } tinydir_close(&dir); + */ } std::string User::file_get_path() { @@ -892,7 +611,7 @@ std::string User::to_json(rapidjson::Document *into) { document->SetObject(); - document->AddMember("id", get_id(), document->GetAllocator()); + document->AddMember("id", id, document->GetAllocator()); document->AddMember("name", rapidjson::Value(name_user_input.c_str(), document->GetAllocator()), document->GetAllocator()); document->AddMember("email", rapidjson::Value(email_user_input.c_str(), document->GetAllocator()), document->GetAllocator()); @@ -934,7 +653,7 @@ void User::from_json(const std::string &p_data) { rapidjson::Value uobj = data.GetObject(); - set_id(uobj["id"].GetInt()); + id = uobj["id"].GetInt(); name_user_input = uobj["name"].GetString(); email_user_input = uobj["email"].GetString(); rank = uobj["rank"].GetInt(); diff --git a/modules/users/user.h b/modules/users/user.h index 98850c5..25a6ee4 100644 --- a/modules/users/user.h +++ b/modules/users/user.h @@ -25,21 +25,6 @@ public: std::string password_reset_token; bool locked; - virtual void save(); - virtual void load(); - virtual void load(const std::string &p_name); - virtual void load(const int p_id); - virtual void changed(); - virtual void update(); - - void migrate(); - - static void db_load_all(); - - virtual bool check_password(const std::string &p_password); - virtual void create_password(const std::string &p_password); - virtual std::string hash_password(const std::string &p_password); - static void handle_request_default(Request *request); static void handle_login_request_default(Request *request); diff --git a/modules/users/user_manager.cpp b/modules/users/user_manager.cpp deleted file mode 100644 index a304de4..0000000 --- a/modules/users/user_manager.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "user_manager.h" - -#include "core/http/http_session.h" -#include "core/http/session_manager.h" - -#include "user.h" -#include - -void UserManager::add_user(User *user) { - if (!user) { - printf("UserManager::add_user: ERROR, user is null!\n"); - return; - } - - std::lock_guard lock(_mutex); - - _users_vec.push_back(user); - _users[user->name_user_input] = user; -} - -void UserManager::remove_user(User *user) { - if (!user) { - printf("UserManager::remove_user: ERROR, user is null!\n"); - return; - } - - std::lock_guard lock(_mutex); - - _users.erase(user->name_user_input); - - for (int i = 0; i < _users_vec.size(); ++i) { - if (_users_vec[i] == user) { - _users_vec[i] = _users_vec[_users_vec.size() - 1]; - _users_vec.pop_back(); - break; - } - } -} - -User *UserManager::get_user(const std::string &user_name) { - return _users[user_name]; -} - -User *UserManager::create_user() { - User *u = new User(); - - return u; -} - -void UserManager::load_all() { - User::db_load_all(); -} - -void UserManager::set_table_name(const std::string &path) { - //DBBasedUser::set_path(path); -} - -void UserManager::migrate() { - User u; - u.migrate(); -} - - -void UserManager::clear() { - SessionManager *sm = SessionManager::get_singleton(); - - std::lock_guard lock(_mutex); - - for (int i = 0; i < _users_vec.size(); ++i) { - delete _users_vec[i]; - } - - _users.clear(); - _users_vec.clear(); -} - -UserManager *UserManager::get_singleton() { - return _self; -} - -UserManager::UserManager() : - Object() { - - if (_self) { - printf("UserManager::UserManager(): Error! self is not null!/n"); - } - - _self = this; - - printf("Using UserManager.\n"); - - User::create_validators(); -} - -UserManager::~UserManager() { - clear(); - - if (_self == this) { - _self = nullptr; - } -} - -UserManager *UserManager::_self = nullptr; \ No newline at end of file diff --git a/modules/users/user_manager.h b/modules/users/user_manager.h deleted file mode 100644 index bfcc0f9..0000000 --- a/modules/users/user_manager.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef USER_MANAGER_H -#define USER_MANAGER_H - -#include "core/object.h" - -#include -#include -#include -#include - -class User; - -class UserManager : public Object { -public: - void add_user(User *user); - void remove_user(User *user); - User *get_user(const std::string &user_name); - virtual User *create_user(); - virtual void load_all(); - - void set_table_name(const std::string &name); - virtual void migrate(); - - void clear(); - - static UserManager *get_singleton(); - - UserManager(); - ~UserManager(); - - std::map _users; - std::vector _users_vec; - std::mutex _mutex; - -protected: - static UserManager *_self; -}; - -#endif \ No newline at end of file diff --git a/modules/users/user_model.cpp b/modules/users/user_model.cpp index 31f336f..f8fb84b 100644 --- a/modules/users/user_model.cpp +++ b/modules/users/user_model.cpp @@ -1,6 +1,303 @@ #include "user_model.h" -#include "user.h" +#include "core/database/database.h" +#include "core/database/database_manager.h" +#include "core/database/query_builder.h" +#include "core/database/query_result.h" +#include "core/database/table_builder.h" + +#include "core/hash/sha256.h" + +Ref UserModel::get_user(const int id) { + if (id == 0) { + return Ref(); + } + + Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + + b->select("username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked"); + b->from(_table_name); + + b->where()->wp("id", id); + + b->end_command(); + + Ref r = b->run(); + + if (!r->next_row()) { + return Ref(); + } + + Ref user; + user.instance(); + + user->id = id; + user->name_user_input = r->get_cell(0); + user->email_user_input = r->get_cell(1); + user->rank = r->get_cell_int(2); + user->pre_salt = r->get_cell(3); + user->post_salt = r->get_cell(4); + user->password_hash = r->get_cell(5); + user->banned = r->get_cell_bool(6); + user->password_reset_token = r->get_cell(7); + user->locked = r->get_cell_bool(8); + + b->query_result = ""; + + //todo remove this, sessions should have their own model + b->select("session_id"); + b->from(_table_name + "_sessions"); + b->where()->wp("user_id", id); + b->end_command(); + + r = b->run(); + + while (r->next_row()) { + user->sessions.push_back(r->get_cell(0)); + } + + user->register_sessions(); + + return user; +} + +Ref UserModel::get_user(const std::string &user_name_input) { + if (user_name_input == "") { + return Ref(); + } + + Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + + b->select("id, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked"); + b->from(_table_name); + b->where()->ewp("name", user_name_input); + b->end_command(); + + Ref r = b->run(); + + if (!r->next_row()) { + return Ref(); + } + + Ref user; + user.instance(); + + user->id = r->get_cell_int(0); + user->name_user_input = user_name_input; + user->email_user_input = r->get_cell(1); + user->rank = r->get_cell_int(2); + user->pre_salt = r->get_cell(3); + user->post_salt = r->get_cell(4); + user->password_hash = r->get_cell(5); + user->banned = r->get_cell_bool(6); + user->password_reset_token = r->get_cell(7); + user->locked = r->get_cell_bool(8); + + b->query_result = ""; + + //todo remove this, sessions should have their own model + b->select("session_id"); + b->from(_table_name + "_sessions"); + b->where()->wp("user_id", user->id); + b->end_command(); + + r = b->run(); + + while (r->next_row()) { + user->sessions.push_back(r->get_cell(0)); + } + + user->register_sessions(); + + return user; +} + +void UserModel::save_user(Ref &user) { + Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + + if (user->id == 0) { + b->insert(_table_name, "username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked"); + + b->values(); + b->eval(user->name_user_input); + b->eval(user->email_user_input); + b->val(user->rank); + b->val(user->pre_salt); + b->val(user->post_salt); + b->val(user->password_hash); + b->val(user->banned); + b->val(user->password_reset_token); + b->val(user->locked); + b->cvalues(); + + b->end_command(); + b->select_last_insert_id(); + + QueryResult *r = b->run(); + + user->id = r->get_last_insert_rowid(); + + delete r; + + } else { + b->udpate(_table_name); + b->set(); + b->esetp("username", user->name_user_input); + b->esetp("email", user->email_user_input); + b->setp("rank", user->rank); + b->setp("pre_salt", user->pre_salt); + b->setp("post_salt", user->post_salt); + b->setp("password_hash", user->password_hash); + b->setp("banned", user->banned); + b->setp("password_reset_token", user->password_reset_token); + b->setp("locked", user->locked); + b->cset(); + b->where()->wp("id", user->id); + + //b->print(); + + b->run_query(); + } + + if (user->id == 0) { + return; + } + + b->reset(); + + b->del(_table_name + "_sessions")->where()->wp("user_id", user->id)->end_command(); + //b->print(); + + b->end_command(); + b->run_query(); + + b->reset(); + + for (int i = 0; i < user->sessions.size(); ++i) { + b->insert(_table_name + "_sessions")->values()->val(user->id)->val(user->sessions[i])->cvalues()->end_command(); + } + + //b->print(); + + b->run_query(); +} + +std::vector > UserModel::get_all() { + Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + + b->select("id"); + b->from(_table_name); + b->end_command(); + b->print(); + + std::vector > users; + + Ref r = b->run(); + + /* +todo + while (r->next_row()) { + User *u = new User(); + u->id = r->get_cell_int(0); + u->load(); + } +*/ + + return users; +} + +bool UserModel::is_username_taken(const std::string &user_name_input) { + Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + + b->select("id")->from(_table_name)->where("name")->elike(user_name_input)->end_command(); + + Ref r = b->run(); + + return r->next_row(); +} +bool UserModel::is_email_taken(const std::string &email_input) { + Ref b = DatabaseManager::get_singleton()->ddb->get_query_builder(); + + b->select("id")->from(_table_name)->where("name")->elike(email_input)->end_command(); + + Ref r = b->run(); + + return r->next_row(); +} + +bool UserModel::check_password(const Ref &user, const std::string &p_password) { + return hash_password(user, p_password) == user->password_hash; +} + +void UserModel::create_password(Ref &user, const std::string &p_password) { + if (!user.is_valid()) { + printf("Error UserModel::create_password !user.is_valid()!\n"); + return; + } + + //todo improve a bit + user->pre_salt = hash_password(user, user->name_user_input + user->email_user_input); + user->post_salt = hash_password(user, user->email_user_input + user->name_user_input); + + user->password_hash = hash_password(user, p_password); +} + +std::string UserModel::hash_password(const Ref &user, const std::string &p_password) { + if (!user.is_valid()) { + printf("Error UserModel::hash_password !user.is_valid()!\n"); + return ""; + } + + Ref s = SHA256::get(); + + std::string p = user->pre_salt + p_password + user->post_salt; + + std::string c = s->compute(p); + + return c; +} + +void UserModel::create_table() { + Ref tb = DatabaseManager::get_singleton()->ddb->get_table_builder(); + + tb->create_table(_table_name); + tb->integer("id")->auto_increment()->next_row(); + tb->varchar("username", 60)->not_null()->next_row(); + tb->varchar("email", 100)->not_null()->next_row(); + tb->integer("rank")->not_null()->next_row(); + tb->varchar("pre_salt", 100)->next_row(); + tb->varchar("post_salt", 100)->next_row(); + tb->varchar("password_hash", 100)->next_row(); + tb->integer("banned")->next_row(); + tb->varchar("password_reset_token", 100)->next_row(); + tb->integer("locked")->next_row(); + tb->primary_key("id"); + tb->ccreate_table(); + tb->run_query(); + //tb->print(); + + //todo sessions need to be separate + tb->result = ""; + + tb->create_table(_table_name + "_sessions"); + tb->integer("user_id")->not_null()->next_row(); + tb->varchar("session_id", 100)->next_row(); + tb->foreign_key("user_id"); + tb->references("user", "id"); + tb->ccreate_table(); + //tb->print(); + tb->run_query(); +} +void UserModel::drop_table() { + Ref tb = DatabaseManager::get_singleton()->ddb->get_table_builder(); + + tb->drop_table_if_exists(_table_name)->run_query(); + tb->drop_table_if_exists(_table_name + "_sessions")->run_query(); +} +void UserModel::migrate() { + drop_table(); + create_table(); +} UserModel *UserModel::get_singleton() { return _self; @@ -22,4 +319,7 @@ UserModel::~UserModel() { } } -UserModel *UserModel::_self = nullptr; \ No newline at end of file +UserModel *UserModel::_self = nullptr; + +std::string UserModel::_path = "./"; +std::string UserModel::_table_name = "users"; \ No newline at end of file diff --git a/modules/users/user_model.h b/modules/users/user_model.h index a20fb95..e4332f0 100644 --- a/modules/users/user_model.h +++ b/modules/users/user_model.h @@ -3,13 +3,28 @@ #include "core/object.h" +#include "user.h" #include - -class User; +#include class UserModel : public Object { public: - //Ref get_user(const int id); + Ref get_user(const int id); + Ref get_user(const std::string &user_name_input); + void save_user(Ref &user); + + std::vector > get_all(); + + bool is_username_taken(const std::string &user_name_input); + bool is_email_taken(const std::string &email_input); + + virtual bool check_password(const Ref &user, const std::string &p_password); + virtual void create_password(Ref &user, const std::string &p_password); + virtual std::string hash_password(const Ref &user, const std::string &p_password); + + virtual void create_table(); + virtual void drop_table(); + virtual void migrate(); static UserModel *get_singleton(); @@ -18,6 +33,11 @@ public: protected: static UserModel *_self; + + std::string _file_path; + + static std::string _path; + static std::string _table_name; }; #endif \ No newline at end of file