diff --git a/include/bank.h b/include/bank.h index 6bd490b..150424b 100644 --- a/include/bank.h +++ b/include/bank.h @@ -15,7 +15,6 @@ using BankResponse = std::pair; class Bank { -private: phmap::parallel_flat_hash_map< std::string, User, phmap::priv::hash_default_hash, @@ -25,6 +24,7 @@ private: std::mutex> users; +private: #if CONSERVATIVE_DISK_SAVE ChangeFlag save_flag; #endif @@ -53,16 +53,14 @@ public: void ChangePassword(const std::string &name, std::string &&new_pass) noexcept; BankResponse SetBal(const std::string &name, uint32_t amount) noexcept; - - int_fast8_t AddUser(const std::string &name, const std::string &init_pass) noexcept; - int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept; - - int_fast8_t DelUser(const std::string &name, const std::string &attempt) noexcept; - int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt) noexcept; - bool Contains(const std::string &name) const noexcept; bool AdminVerifyPass(const std::string &attempt) noexcept; + BankResponse AddUser(const std::string &name, std::string &&init_pass) noexcept; + BankResponse AdminAddUser(std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept; + + BankResponse DelUser(const std::string &name) noexcept; + void Save(); void Load(); }; \ No newline at end of file diff --git a/include/bank_api.h b/include/bank_api.h index e630316..fa7ed88 100644 --- a/include/bank_api.h +++ b/include/bank_api.h @@ -25,9 +25,9 @@ public: void Help(req_args) const; void Ping(req_args) const; void Close(req_args) const; - void AddUser(req_args, const std::string &name) const; - void AdminAddUser(req_args, std::string &&name, uint32_t init_bal) const; - void DelUser(req_args, const std::string &name) const; + void AddUser(req_args) const; + void AdminAddUser(req_args) const; + void DelUser(req_args) const; void AdminDelUser(req_args, const std::string &name) const; void Contains(req_args, const std::string &name) const; void AdminVerifyPass(req_args); @@ -37,7 +37,7 @@ public: //Usage METHOD_ADD(api::GetBal, "/v1/user/bal?name={name}", Get, Options); //done METHOD_ADD(api::GetLog, "/v1/user/log", Get, Options, "UserFilter"); //done (could be optimized further) - METHOD_ADD(api::SendFunds, "/v1/user/transfer", Post, Options, "UserFilter"); //responses incomplete + METHOD_ADD(api::SendFunds, "/v1/user/transfer", Post, Options, "UserFilter"); //done METHOD_ADD(api::VerifyPassword, "/v1/user/verify_password", Get, Options, "UserFilter"); //done //Meta Usage @@ -45,17 +45,17 @@ public: METHOD_ADD(api::SetBal, "/v1/admin/bal", Patch, Options, "AdminFilter"); //done //System Usage - METHOD_ADD(api::Help, "/help", Get, Options); - METHOD_ADD(api::Ping, "/ping", Get, Options); - METHOD_ADD(api::Close, "/admin/close", Post, Options); - METHOD_ADD(api::Contains, "/contains/{name}", Get, Options); - METHOD_ADD(api::AdminVerifyPass, "/admin/verify", Get, Options); + METHOD_ADD(api::Help, "/v1/help", Get, Options); //done + METHOD_ADD(api::Ping, "/v1/ping", Get, Options); //done + METHOD_ADD(api::Close, "/v1/admin/shutdown", Post, Options, "AdminFilter"); //done + METHOD_ADD(api::Contains, "/v1/user/exists?name={name}", Get, Options); //done + METHOD_ADD(api::AdminVerifyPass, "/v1/admin/verify_password", Get, Options, "AdminFilter"); //done //User Managment - METHOD_ADD(api::AddUser, "/user/{name}", Post, Options); - METHOD_ADD(api::AdminAddUser, "/admin/user/{name}?init_bal={init_bal}", Post, Options); - METHOD_ADD(api::DelUser, "/user/{name}", Delete, Options); - METHOD_ADD(api::AdminDelUser, "/admin/user/{name}", Delete, Options); + METHOD_ADD(api::AddUser, "/v1/user/register", Post, Options); //done + METHOD_ADD(api::AdminAddUser, "/v1/admin/user/register", Post, Options, "AdminFilter"); //done + METHOD_ADD(api::DelUser, "/v1/delete", Delete, Options, "UserFilter"); //done + METHOD_ADD(api::AdminDelUser, "/v1/admin/delete", Delete, Options, "AdminFilter"); //done METHOD_LIST_END #endif diff --git a/include/consts.hpp b/include/consts.hpp index 2a9c176..48b4255 100644 --- a/include/consts.hpp +++ b/include/consts.hpp @@ -4,7 +4,8 @@ #define MAX_LOG_SIZE 100 #define PRE_LOG_SIZE 10 -constexpr unsigned max_name_size = 50; +constexpr unsigned min_name_size = 3; +constexpr unsigned max_name_size = 16; constexpr const char *users_location = "../users.json"; constexpr const char *config_location = "../config.json"; diff --git a/include/error_responses.hpp b/include/error_responses.hpp index 4eefc19..e8e4ece 100644 --- a/include/error_responses.hpp +++ b/include/error_responses.hpp @@ -4,7 +4,7 @@ enum ErrorResponse UserNotFound = -1, //404 WrongPassword = -2, //401 InvalidRequest = -3, //400 - NameTooLong = -4, //400? + NameTooLong = -4, //400 UserAlreadyExists = -5, //409 - InsufficientFunds = -6, //402? + InsufficientFunds = -6, //400 }; \ No newline at end of file diff --git a/include/user.h b/include/user.h index 6d9dbfa..e03bc24 100644 --- a/include/user.h +++ b/include/user.h @@ -12,11 +12,11 @@ struct User Log log; #endif - User(const std::string &init_pass); - User(uint32_t init_bal, const std::string &init_pass); - User(uint32_t init_bal, uint64_t init_pass); + User(std::string &&init_pass) noexcept; + User(uint32_t init_bal, std::string &&init_pass) noexcept; + User(uint32_t init_bal, uint64_t init_pass) noexcept; #if MAX_LOG_SIZE > 0 - User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j); + User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j) noexcept; #endif Json::Value Serialize() const; //to be removed later diff --git a/src/bank.cpp b/src/bank.cpp index 024ccb9..3c97669 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -1,5 +1,21 @@ #include "bank.h" +__attribute__((always_inline)) inline bool ValidUsrname(const std::string &name) noexcept +{ + if (name.size() < min_name_size || name.size() > max_name_size) + { + return false; + } + for (const char &c : name) + { + if (!(std::isalpha(c) || std::isdigit(c) || c == '_')) + { + return false; + } + } + return true; +} + using namespace drogon; bool Bank::GetChangeState() const noexcept { return save_flag.GetChangeState(); } @@ -46,7 +62,7 @@ BankResponse Bank::SendFunds(const std::string &a_name, const std::string &b_nam } BankResponse state; - std::shared_lock lock{send_funds_l}; + std::shared_lock lock{send_funds_l}; //about 10% of this function's cost #if MAX_LOG_SIZE > 0 Transaction temp(a_name, b_name, amount); if (!users.modify_if(a_name, [&temp, &state, amount](User &a) { @@ -56,13 +72,13 @@ BankResponse Bank::SendFunds(const std::string &a_name, const std::string &b_nam //if A can afford it if (a.balance < amount) { - state = {k402PaymentRequired, "Sender has insufficient funds"}; + state = {k400BadRequest, "Sender has insufficient funds"}; } else { a.balance -= amount; #if MAX_LOG_SIZE > 0 - a.log.AddTrans(Transaction(temp)); + a.log.AddTrans(Transaction(temp)); //about 40% of this function's cost #endif state = {k200OK, "Transfer successful!"}; } @@ -75,13 +91,13 @@ BankResponse Bank::SendFunds(const std::string &a_name, const std::string &b_nam #if MAX_LOG_SIZE > 0 users.modify_if(b_name, [&temp, amount](User &b) { b.balance += amount; - b.log.AddTrans(std::move(temp)); }); + b.log.AddTrans(std::move(temp)); }); //about 40% of this function's cost #else users.modify_if(b_name, [amount](User &b) { b.balance += amount; }); #endif #if CONSERVATIVE_DISK_SAVE - save_flag.SetChangesOn(); + save_flag.SetChangesOn(); //about 5% of this function's cost #endif } return state; @@ -120,107 +136,6 @@ BankResponse Bank::SetBal(const std::string &name, uint32_t amount) noexcept return BankResponse(k404NotFound, "User not found"); } } - -int_fast8_t Bank::AddUser(const std::string &name, const std::string &init_pass) noexcept -{ - if (name.size() > max_name_size) - { - return ErrorResponse::NameTooLong; - } - if (name.find(' ') != std::string::npos) - { - return ErrorResponse::InvalidRequest; - } - - std::shared_lock lock{size_l}; - return (users.try_emplace_l( - name, [](User &) {}, init_pass)) - ? true - : ErrorResponse::UserAlreadyExists; -} -int_fast8_t Bank::AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept -{ - if (name.size() > max_name_size) - { - return ErrorResponse::NameTooLong; - } - if (admin_pass != attempt) - { - return ErrorResponse::WrongPassword; - } - - std::shared_lock lock{size_l}; - return (users.try_emplace_l( - name, [](User &) {}, init_bal, std::move(init_pass))) - ? true - : ErrorResponse::UserAlreadyExists; -} -int_fast8_t Bank::DelUser(const std::string &name, const std::string &attempt) noexcept -{ - std::shared_lock lock{size_l}; - bool state = false; -#if RETURN_ON_DEL - uint32_t bal; - if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) { - bal = u.balance; -#else - if (!users.erase_if(name, [this, &name, &state, &attempt](User &u) { -#endif - return state = (XXH3_64bits(attempt.data(), attempt.size()) == u.password); - })) - { - return ErrorResponse::UserNotFound; - } -#if RETURN_ON_DEL - if (state) //if the password matches - { - users.modify_if(return_account, [&bal](User &u) { - u.balance += bal; - }); - return true; - } - else - { - return ErrorResponse::WrongPassword; - } -#else - return (state) ? true : ErrorResponse::WrongPassword; -#endif -} -int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attempt) noexcept -{ - std::shared_lock lock{size_l}; - bool state = false; -#if RETURN_ON_DEL - uint32_t bal; - if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) { - bal = u.balance; -#else - if (users.erase_if(name, [this, &name, &state, &attempt](User &u) { -#endif - return state = (XXH3_64bits(attempt.data(), attempt.size()) == u.password); - })) - { - if (state) - { -#if RETURN_ON_DEL - users.modify_if(return_account, [&bal](User &u) { - u.balance += bal; - }); -#endif - return true; - } - else - { - return ErrorResponse::WrongPassword; - } - } - else - { - return ErrorResponse::UserNotFound; - } -} - bool Bank::Contains(const std::string &name) const noexcept { return users.contains(name); @@ -230,6 +145,54 @@ bool Bank::AdminVerifyPass(const std::string &attempt) noexcept return (admin_pass == attempt); } +BankResponse Bank::AddUser(const std::string &name, std::string &&init_pass) noexcept +{ + if (!ValidUsrname(name)) + { + return BankResponse(k400BadRequest, "Invalid Name, breaks size and/or character restrictions"); + } + + std::shared_lock lock{size_l}; + return (users.try_emplace_l( + name, [](User &) {}, std::move(init_pass))) + ? BankResponse(k200OK, "User added!") + : BankResponse(k409Conflict, "User already exists"); +} +BankResponse Bank::AdminAddUser(std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept +{ + if (!ValidUsrname(name)) + { + return BankResponse(k400BadRequest, "Invalid Name, breaks size and/or character restrictions"); + } + + std::shared_lock lock{size_l}; + return (users.try_emplace_l( + name, [](User &) {}, init_bal, std::move(init_pass))) + ? BankResponse(k200OK, "User added!") + : BankResponse(k409Conflict, "User already exists"); +} +BankResponse Bank::DelUser(const std::string &name) noexcept +{ + std::shared_lock lock{size_l}; +#if RETURN_ON_DEL + uint32_t bal; + if (users.erase_if(name, [this, &bal, &name](User &u) { + bal = u.balance; + return true; + })) +#else + if (!users.erase(name)) +#endif + { + return BankResponse(k404NotFound, "User not found"); + } +#if RETURN_ON_DEL + users.modify_if(return_account, [&bal](User &u) { + u.balance += bal; + }); +#endif + return BankResponse(k200OK, "User deleted!"); +} void Bank::Save() { #if CONSERVATIVE_DISK_SAVE diff --git a/src/bank_api.cpp b/src/bank_api.cpp index 66946fc..b4ed15d 100644 --- a/src/bank_api.cpp +++ b/src/bank_api.cpp @@ -48,6 +48,7 @@ api::api(Bank &b) : bank(b) #if API_VERSION == 1 +//Usage void api::GetBal(req_args, const std::string &name) const { RESPONSE_PARSE(bank.GetBal(name)); @@ -76,6 +77,7 @@ void api::VerifyPassword(req_args) const RESPOND_TRUE //as we know the user exists and is verified } +//Meta Usage void api::ChangePassword(req_args) const { GEN_BODY @@ -88,6 +90,7 @@ void api::SetBal(req_args) const RESPONSE_PARSE(bank.SetBal(NAME_PARAM, body["amount"].asUInt())); } +//System Usage void api::Help(req_args) const { auto resp = HttpResponse::newHttpResponse(); @@ -104,42 +107,38 @@ void api::Ping(req_args) const } void api::Close(req_args) const { - bool res; - if (PASS_HEADER == bank.admin_pass) - { - bank.Save(); - - res = true; - app().quit(); - } - else - { - res = false; - } - JSON(res); -} -void api::AddUser(req_args, const std::string &name) const -{ - JSON(bank.AddUser(std::move(name), PASS_HEADER)); -} -void api::AdminAddUser(req_args, std::string &&name, uint32_t init_bal) const -{ - JSON(bank.AdminAddUser(PASS_HEADER, std::move(name), init_bal, std::string(req->getBody()))); -} -void api::DelUser(req_args, const std::string &name) const -{ - JSON(bank.DelUser(name, PASS_HEADER)); -} -void api::AdminDelUser(req_args, const std::string &name) const -{ - JSON(bank.AdminDelUser(name, PASS_HEADER)); + bank.Save(); + app().quit(); + RESPOND_TRUE //filter handles admin creds } void api::Contains(req_args, const std::string &name) const { - JSON(bank.Contains(name)); + auto resp = HttpResponse::newHttpJsonResponse(JsonCast(bank.Contains(name))); + resp->setStatusCode(k200OK); + callback(resp); } void api::AdminVerifyPass(req_args) { - JSON(bank.AdminVerifyPass(PASS_HEADER)); + RESPOND_TRUE //filter handles admin creds +} + +void api::AddUser(req_args) const +{ + GEN_BODY + RESPONSE_PARSE(bank.AddUser(body["name"].asCString(), body["pass"].asCString())) +} +void api::AdminAddUser(req_args) const +{ + GEN_BODY + RESPONSE_PARSE(bank.AdminAddUser(body["name"].asCString(), body["balance"].asUInt(), body["pass"].asCString())) +} +void api::DelUser(req_args) const +{ + RESPONSE_PARSE(bank.DelUser(NAME_PARAM)) +} +void api::AdminDelUser(req_args) const +{ + GEN_BODY + RESPONSE_PARSE(bank.DelUser(body["name"].asCString())) } #endif \ No newline at end of file diff --git a/src/user.cpp b/src/user.cpp index dae4fb9..d697687 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -5,7 +5,7 @@ * * @param init_pass initial password */ -User::User(const std::string &init_pass) : password(XXH3_64bits(init_pass.data(), init_pass.size())) {} +User::User(std::string &&init_pass) noexcept : password(XXH3_64bits(init_pass.data(), init_pass.size())) {} /** * @brief User Constructor for admins @@ -13,7 +13,7 @@ User::User(const std::string &init_pass) : password(XXH3_64bits(init_pass.data() * @param init_bal initial balance * @param init_pass initial password */ -User::User(uint32_t init_bal, const std::string &init_pass) : balance(init_bal), password(XXH3_64bits(init_pass.data(), init_pass.size())) {} +User::User(uint32_t init_bal, std::string &&init_pass) noexcept : balance(init_bal), password(XXH3_64bits(init_pass.data(), init_pass.size())) {} /** * @brief User Constructor for loading @@ -21,9 +21,9 @@ User::User(uint32_t init_bal, const std::string &init_pass) : balance(init_bal), * @param init_bal * @param init_pass */ -User::User(uint32_t init_bal, uint64_t init_pass) : balance(init_bal), password(init_pass) {} +User::User(uint32_t init_bal, uint64_t init_pass) noexcept : balance(init_bal), password(init_pass) {} #if MAX_LOG_SIZE > 0 -User::User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j) : balance(init_bal), password(init_pass) +User::User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j) noexcept : balance(init_bal), password(init_pass) { if (log_j.size()) {