finished Endpoint/Internal rewrite

This commit is contained in:
EntireTwix 2021-06-28 19:47:17 -07:00
parent 0c34bc3689
commit 0e62dbe9ef
8 changed files with 130 additions and 169 deletions

View file

@ -15,7 +15,6 @@ using BankResponse = std::pair<drogon::HttpStatusCode, Json::Value>;
class Bank
{
private:
phmap::parallel_flat_hash_map<
std::string, User,
phmap::priv::hash_default_hash<std::string>,
@ -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();
};

View file

@ -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

View file

@ -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";

View file

@ -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
};

View file

@ -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

View file

@ -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<std::shared_mutex> lock{send_funds_l};
std::shared_lock<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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

View file

@ -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));
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

View file

@ -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())
{