generated from Sithas/conan_template
Compare commits
10 Commits
cd1a75eb88
...
b2695ee582
| Author | SHA1 | Date | |
|---|---|---|---|
| b2695ee582 | |||
| 6d1a567369 | |||
| 2bcc250a24 | |||
| 1ca4120164 | |||
| eb187025eb | |||
| e0bb9025b1 | |||
| 87f1e4a735 | |||
| 7d19dcb69a | |||
| c384fdab7c | |||
| fb4ee8fedb |
@@ -48,6 +48,8 @@ add_executable(App ./src/main.cpp
|
||||
./src/DAO/MemoryAuthDAO.cpp
|
||||
./src/DAO/MemoryAuthDAO.h
|
||||
./src/endpoints_handlers/AuthLogoutExecutor.h
|
||||
./src/exceptions/session_exception.cpp
|
||||
./src/exceptions/session_exception.h
|
||||
)
|
||||
|
||||
target_link_libraries(App PRIVATE Boost::boost Boost::json Threads::Threads mysql::concpp)
|
||||
|
||||
@@ -10,10 +10,13 @@
|
||||
- ~~Перевести GetByUUID GetByLogin на const ref/string_view в IUserDAO - также не vector, а span(погуглить)~~ - span не применим
|
||||
- ~~Привести к единому виду функции IUserDAO~~
|
||||
- ~~Пройтись по коду и максимально наставить const~~
|
||||
- Указать возможные исключения в интерфейсах DAO
|
||||
- ~~Указать возможные исключения в интерфейсах DAO - почему может выбросить исключение~~
|
||||
- ~~Вынести User в структуру. Hashed Password структура должна изначально состоять в другой структуре~~
|
||||
- ~~SharedPtr - передавать по константной ссылке.~~
|
||||
- Вынести обработку исключений в RootExecutor
|
||||
- ~~Вынести обработку исключений в RootExecutor~~
|
||||
- ~~Уменьшить дублирование кода в исключениях~~
|
||||
- Покрыть логами
|
||||
- ~~Сделать один класс исключений, имеющих метод HTTP code - код и сообщение записывать уже в ловушке~~
|
||||
- Сделать интеграционный тест по ручкам
|
||||
|
||||
# UseCase'ы приложения:
|
||||
@@ -141,7 +144,7 @@ null
|
||||
```
|
||||
|
||||
##### Errors
|
||||
* `401 BAD_CREDENTIALS` — Такого токена не существует(B3)
|
||||
* `400 BAD_REQUEST` — Такого токена не существует(B3)
|
||||
|
||||
### 10. Используемые сущности ДБ
|
||||
* users(uuid(PK), login(unique), hashed_password)
|
||||
|
||||
@@ -27,10 +27,10 @@ bool MemoryAuthDAO::HasAuthorized(const std::string& auth_token)
|
||||
|
||||
bool MemoryAuthDAO::Logout(const std::string& auth_token)
|
||||
{
|
||||
string user_uuid = auth_tokens_to_users_uuids_[auth_token];
|
||||
|
||||
if (!HasAuthorized(auth_token)) return false;
|
||||
|
||||
string user_uuid = auth_tokens_to_users_uuids_[auth_token];
|
||||
|
||||
users_uuids_to_auth_tokens_.erase(user_uuid);
|
||||
auth_tokens_to_users_uuids_.erase(auth_token);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ string MySQLUserDAO::Create(const user& created_user)
|
||||
const std::string uuid_str = boost::uuids::to_string(uuid);
|
||||
|
||||
|
||||
const string sql_script =
|
||||
static const string sql_script =
|
||||
"INSERT INTO `up_and_down`.`users` (`uuid`, `login`, `hashed_password`) VALUES (?, ?, ?);"s;
|
||||
|
||||
session_.
|
||||
@@ -33,7 +33,7 @@ string MySQLUserDAO::Create(const user& created_user)
|
||||
|
||||
optional<user> MySQLUserDAO::GetByUUID(const string& uuid)
|
||||
{
|
||||
const string sql_script = "SELECT * FROM `up_and_down`.`users` WHERE (uuid = ?) LIMIT 1;"s;
|
||||
static const string sql_script = "SELECT * FROM `up_and_down`.`users` WHERE (uuid = ?) LIMIT 1;"s;
|
||||
mysqlx::SqlResult sql_result = session_.
|
||||
sql(sql_script)
|
||||
.bind(uuid)
|
||||
@@ -44,7 +44,7 @@ optional<user> MySQLUserDAO::GetByUUID(const string& uuid)
|
||||
|
||||
optional<user> MySQLUserDAO::GetByLogin(const string& login)
|
||||
{
|
||||
const std::string sql_script = "SELECT * FROM `up_and_down`.`users` WHERE (login = ?) LIMIT 1;"s;
|
||||
static const std::string sql_script = "SELECT * FROM `up_and_down`.`users` WHERE (login = ?) LIMIT 1;"s;
|
||||
mysqlx::SqlResult sql_result = session_.
|
||||
sql(sql_script)
|
||||
.bind(login)
|
||||
@@ -55,8 +55,10 @@ optional<user> MySQLUserDAO::GetByLogin(const string& login)
|
||||
|
||||
pair<bool, vector<user>> MySQLUserDAO::GetAll(size_t limit, size_t offset)
|
||||
{
|
||||
static const string sql_script = "SELECT * FROM `up_and_down`.`users` LIMIT ? OFFSET ?;"s;
|
||||
|
||||
mysqlx::SqlResult sql_result = session_
|
||||
.sql("SELECT * FROM `up_and_down`.`users` LIMIT ? OFFSET ?;"s)
|
||||
.sql(sql_script)
|
||||
.bind(limit, offset)
|
||||
.execute();
|
||||
list<mysqlx::Row> rows = sql_result.fetchAll();
|
||||
@@ -96,7 +98,7 @@ pair<bool, vector<user>> MySQLUserDAO::GetAll(size_t limit, size_t offset)
|
||||
|
||||
bool MySQLUserDAO::Update(const user& u)
|
||||
{
|
||||
const string sql_script = "UPDATE `up_and_down`.`users` SET `login` = ? WHERE `uuid` = ?;"s;
|
||||
static const string sql_script = "UPDATE `up_and_down`.`users` SET `login` = ? WHERE `uuid` = ?;"s;
|
||||
|
||||
auto schema = session_.sql(sql_script)
|
||||
.bind(u.login, u.uuid)
|
||||
@@ -107,7 +109,7 @@ bool MySQLUserDAO::Update(const user& u)
|
||||
|
||||
bool MySQLUserDAO::Delete(const string& uuid)
|
||||
{
|
||||
const string sql_script = "DELETE FROM `up_and_down`.`users` WHERE `uuid` = ?;";
|
||||
static const string sql_script = "DELETE FROM `up_and_down`.`users` WHERE `uuid` = ?;";
|
||||
|
||||
auto schema = session_.sql(sql_script)
|
||||
.bind(uuid)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "../DAO/IUserDAO.h"
|
||||
#include "../DAO/IAuthDAO.h"
|
||||
#include "../helpers/helpers.h"
|
||||
#include "../exceptions/session_exception.h"
|
||||
|
||||
namespace uad
|
||||
{
|
||||
@@ -39,65 +40,45 @@ public:
|
||||
|
||||
const auto body = req.body();
|
||||
value req_json;
|
||||
value response_body;
|
||||
|
||||
response_body.emplace_object();
|
||||
|
||||
try
|
||||
{
|
||||
req_json = json::parse(body);
|
||||
|
||||
const std::string login = req_json.as_object().at("login").as_string().c_str();
|
||||
const std::string password = req_json.as_object().at("password").as_string().c_str();
|
||||
|
||||
if (login.empty() || password.empty())
|
||||
{
|
||||
http::response<ResponseType> res{http::status::unprocessable_entity, req.version()};
|
||||
response_body.as_object().emplace("Result", "Login or password are empty");
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const std::optional<user> maybe_user = user_dao_->GetByLogin(login);
|
||||
|
||||
if (!maybe_user.has_value() || maybe_user.value().hashed_password != HashPassword(password))
|
||||
{
|
||||
http::response<ResponseType> res{http::status::unprocessable_entity, req.version()};
|
||||
response_body.as_object().emplace("Result", "Incorrect login or password");
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
const std::string token = GenerateUUID();
|
||||
auth_dao_->Login(maybe_user.value().uuid, token);
|
||||
|
||||
http::response<ResponseType> res{http::status::ok, req.version()};
|
||||
response_body.as_object().emplace("token", token);
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
catch (const system::system_error& err)
|
||||
{
|
||||
http::response<ResponseType> res{http::status::bad_request, req.version()};
|
||||
response_body.as_object().emplace("Result", "cannot deserialize json");
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
throw session_exception(http::status::bad_request, "cannot deserialize json");
|
||||
}
|
||||
|
||||
|
||||
const std::string login = req_json.as_object().at("login").as_string().c_str();
|
||||
const std::string password = req_json.as_object().at("password").as_string().c_str();
|
||||
|
||||
if (login.empty() || password.empty())
|
||||
{
|
||||
throw session_exception(http::status::unprocessable_entity, "Login or password are empty"s);
|
||||
}
|
||||
|
||||
const std::optional<user> maybe_user = user_dao_->GetByLogin(login);
|
||||
|
||||
if (!maybe_user.has_value() && maybe_user.value().hashed_password != HashPassword(password))
|
||||
{
|
||||
throw session_exception(http::status::forbidden,"Incorrect login or password");
|
||||
}
|
||||
const std::string token = GenerateUUID();
|
||||
auth_dao_->Login(maybe_user.value().uuid, token);
|
||||
|
||||
http::response<ResponseType> res{http::status::ok, req.version()};
|
||||
value response_body;
|
||||
|
||||
response_body.emplace_object();
|
||||
response_body.as_object().emplace("token", token);
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ class AuthLogoutExecutor : public IExecutor<Body, Allocator, ResponseType>
|
||||
|
||||
public:
|
||||
AuthLogoutExecutor(mysqlx::Session& session,
|
||||
const std::shared_ptr<IAuthDAO>& auth_dao)
|
||||
: session_(session), auth_dao_(auth_dao)
|
||||
const std::shared_ptr<IAuthDAO>& auth_dao) :
|
||||
session_(session), auth_dao_(auth_dao)
|
||||
{
|
||||
}
|
||||
|
||||
boost::beast::http::response<ResponseType> operator ()(
|
||||
boost::beast::http::request<Body, boost::beast::http::basic_fields<Allocator>>&& req
|
||||
) override
|
||||
) override
|
||||
{
|
||||
using namespace boost;
|
||||
using namespace boost::json;
|
||||
@@ -38,47 +38,29 @@ public:
|
||||
const auto body = req.body();
|
||||
value req_json;
|
||||
|
||||
value response_body;
|
||||
response_body.emplace_object();
|
||||
|
||||
try
|
||||
{
|
||||
req_json = json::parse(body);
|
||||
|
||||
const std::string token = req_json.as_object().at("token").as_string().c_str();
|
||||
|
||||
if (!auth_dao_->Logout(token))
|
||||
{
|
||||
http::response<ResponseType> res{http::status::bad_request, req.version()};
|
||||
|
||||
response_body.as_object().emplace("Result", "token is not authorized");
|
||||
|
||||
res.body() = json::serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
http::response<ResponseType> res{http::status::ok, req.version()};
|
||||
|
||||
res.body() = "true"s;
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
catch (const system::system_error& err)
|
||||
{
|
||||
http::response<ResponseType> res{http::status::bad_request, req.version()};
|
||||
response_body.as_object().emplace("Result", "cannot deserialize json");
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
throw session_exception(http::status::internal_server_error, "cannot deserialize json"s);
|
||||
}
|
||||
|
||||
const std::string token = req_json.as_object().at("token").as_string().c_str();
|
||||
|
||||
if (!auth_dao_->Logout(token))
|
||||
{
|
||||
throw session_exception(http::status::bad_request, "token is not authorized"s);
|
||||
}
|
||||
|
||||
http::response<ResponseType> res{http::status::ok, req.version()};
|
||||
|
||||
res.body() = "null"s;
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#include <regex>
|
||||
#include <boost/json.hpp>
|
||||
#include <mysqlx/xdevapi.h>
|
||||
#include <mysqlx/common/api.h>
|
||||
#include <boost/uuid.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "IExecutor.h"
|
||||
#include "../DAO/IUserDAO.h"
|
||||
#include "../exceptions/session_exception.h"
|
||||
|
||||
namespace uad
|
||||
{
|
||||
@@ -35,9 +35,6 @@ public:
|
||||
|
||||
const auto& body = req.body();
|
||||
value req_json;
|
||||
value response_body;
|
||||
|
||||
response_body.emplace_object();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -45,14 +42,7 @@ public:
|
||||
}
|
||||
catch (const system::system_error& err)
|
||||
{
|
||||
http::response<ResponseType> res{http::status::bad_request, req.version()};
|
||||
response_body.as_object().emplace("Result", "cannot deserialize json");
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
throw session_exception(http::status::bad_request, "cannot deserialize json");
|
||||
}
|
||||
|
||||
const std::string login = req_json.as_object().at("login").as_string().c_str();
|
||||
@@ -60,34 +50,15 @@ public:
|
||||
|
||||
if (!ValidateLogin(login) || !ValidatePassword(password))
|
||||
{
|
||||
http::response<ResponseType> res{http::status::unprocessable_entity, req.version()};
|
||||
|
||||
response_body.as_object().emplace(
|
||||
"Result",
|
||||
"Validations failed. Login should have length from 3 to 50. Password from 5 characters length."
|
||||
throw session_exception(
|
||||
http::status::unprocessable_entity,
|
||||
"Validations failed. Login should have length from 3 to 50. Password from 5 characters length."s
|
||||
);
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (user_dao_->GetByLogin(login).has_value())
|
||||
{
|
||||
http::response<ResponseType> res{http::status::conflict, req.version()};
|
||||
|
||||
response_body.as_object().emplace(
|
||||
"Result",
|
||||
"user with login "s + login + " exists"s
|
||||
);
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return res;
|
||||
throw session_exception(http::status::conflict, "user with login "s + login + " exists"s);
|
||||
}
|
||||
|
||||
user user;
|
||||
@@ -100,6 +71,9 @@ public:
|
||||
http::response<ResponseType> res{
|
||||
http::status::created, req.version()
|
||||
};
|
||||
value response_body;
|
||||
|
||||
response_body.emplace_object();
|
||||
|
||||
response_body.as_object().emplace(
|
||||
"uuid",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#include <mysqlx/xdevapi.h>
|
||||
#include <mysqlx/common/api.h>
|
||||
|
||||
#include "IExecutor.h"
|
||||
#include "IController.h"
|
||||
#include "Controller.h"
|
||||
@@ -7,6 +10,7 @@
|
||||
#include "../DAO/IUserDAO.h"
|
||||
#include "../DAO/IAuthDAO.h"
|
||||
#include "./../helpers/helpers.h"
|
||||
#include "./../exceptions/session_exception.h"
|
||||
|
||||
namespace uad
|
||||
{
|
||||
@@ -84,7 +88,26 @@ public:
|
||||
{
|
||||
IRouteExecutor& executor = *maybe_executor_ptr.value();
|
||||
|
||||
return send(executor(std::move(req)));
|
||||
try
|
||||
{
|
||||
boost::beast::http::response<ResponseType> res = executor(std::move(req));
|
||||
|
||||
return send(std::move(res));
|
||||
}
|
||||
catch (const session_exception& e)
|
||||
{
|
||||
boost::beast::http::response<ResponseType> res{e.code, req.version()};
|
||||
boost::json::value response_body;
|
||||
|
||||
response_body.emplace_object();
|
||||
response_body.as_object().emplace("Result", e.what());
|
||||
|
||||
res.body() = serialize(response_body);
|
||||
res.set(boost::beast::http::field::content_type, "application/json");
|
||||
res.content_length(res.body().size());
|
||||
|
||||
return send(std::move(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "session_exception.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace uad
|
||||
{
|
||||
char const* session_exception::what() const
|
||||
{
|
||||
return message.c_str();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <string>
|
||||
#include <boost/exception/to_string.hpp>
|
||||
|
||||
namespace uad
|
||||
{
|
||||
struct session_exception : std::exception
|
||||
{
|
||||
const boost::beast::http::status code;
|
||||
const std::string comment;
|
||||
const std::string message;
|
||||
|
||||
session_exception(const boost::beast::http::status ec, const std::string info)
|
||||
: code(ec), comment(info), message(std::to_string(static_cast<uint64_t>(ec)) + " - " + comment)
|
||||
{}
|
||||
char const* what() const override;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user