Commit 1c282eeb authored by louiz’'s avatar louiz’

Basic user login on the master server

parent 9fb7610c
......@@ -41,7 +41,7 @@ option(BUILD_CLIENT "build the server" ${BUILD_CLIENT})
##########################
find_package(Boost 1.48.0 COMPONENTS filesystem system iostreams locale serialization chrono REQUIRED)
find_package(Cryptopp REQUIRED)
find_package(BOTAN REQUIRED)
find_package(Protobuf REQUIRED)
find_package(LITESQL REQUIRED)
......@@ -58,14 +58,13 @@ find_package(GTest)
include_directories("src/")
include_directories(${Boost_INCLUDE_DIR})
include_directories(${CRYPTO++_INCLUDE_DIR})
include_directories(${BOTAN_INCLUDE_DIRS})
include_directories(${PROTOBUF_INCLUDE_DIRS})
if(GTEST_FOUND)
include_directories(${GTEST_INCLUDE_DIRS})
endif()
link_directories(${Boost_LIBRARY_DIRS})
link_directories(${CRYPTO++_LIBRARIES})
##########################
# Targets
......@@ -152,7 +151,7 @@ LITESQL_GENERATE_CPP("src/database/database.xml"
add_library(database STATIC src/database/database.cpp
${LITESQL_GENERATED_SOURCES})
target_link_libraries(database ${LITESQL_LIBRARIES})
target_link_libraries(database ${LITESQL_LIBRARIES} ${BOTAN_LIBRARIES})
#
## Gui
......
#include <database/database.hpp>
#include <config/config.hpp>
#include <logging/logging.hpp>
#include <botan/hex.h>
#include <tuple>
Database::Database()
using namespace std::string_literals;
Database::Database():
rng(),
db(Config::get("db_type", "postgresql"),
Config::get("db_login", ""))
{
this->db.verbose = true;
if (this->db.needsUpgrade())
{
log_debug("The database scheme changed, updating it...");
this->db.upgrade();
log_debug("Done.");
}
}
boost::optional<db::User> Database::get_user_by_login(const std::string& login)
{
boost::optional<db::User> user;
auto users = litesql::select<db::User>(this->db, db::User::Login == login).all();
if (!users.empty())
user = users[0];
return user;
}
std::tuple<bool, std::string> Database::create_new_user(const std::string& login, const std::string& password)
{
auto nb = litesql::select<db::User>(this->db, db::User::Login == login).count();
if (nb != 0)
return std::make_tuple(false, "User with login "s + login + " already exists.");
db::User user(this->db);
user.login = login;
set_user_password(user, password);
user.update();
user.update();
return std::make_tuple(true, "");
}
void Database::change_user_password(db::User& user, const std::string& password)
{
set_user_password(user, password);
user.update();
}
void Database::set_user_password(db::User& user, const std::string& password)
{
// A salt is generated by the bcrypt function and stored directly as part
// of the resulting hash. We do not need to generate one ourself to store
// it in the database.
user.hash = Botan::generate_bcrypt(password, this->rng, 8);
}
std::tuple<bool, std::string> Database::check_user_password(const db::User& user, const std::string& password) const
{
std::string hash = user.hash;
if (Botan::check_bcrypt(password, hash) == true)
return std::make_tuple(true, "");
return std::make_tuple(false, "Password mismatch.");
}
#ifndef DATABASE_HPP_INCLUDED
#define DATABASE_HPP_INCLUDED
#include <litesql.hpp>
#include <boost/optional.hpp>
#include <botan/botan.h>
#include <botan/bcrypt.h>
#include "batadb.hpp"
#include <tuple>
#include <string>
/**
* Unique class that handles all database access.
* It internally uses the litesql.
......@@ -13,8 +20,17 @@ class Database
public:
Database();
~Database() = default;
boost::optional<db::User> get_user_by_login(const std::string& login);
std::tuple<bool, std::string> create_new_user(const std::string& login,
const std::string& password);
std::tuple<bool, std::string> check_user_password(const db::User& user,
const std::string& password) const;
void change_user_password(db::User& user, const std::string& password);
void set_user_password(db::User& user, const std::string& password);
private:
Botan::AutoSeeded_RNG rng;
db::BataDB db;
Database(const Database&) = delete;
Database(Database&&) = delete;
Database& operator=(const Database&) = delete;
......
......@@ -3,7 +3,7 @@
<database name="BataDB" namespace="db">
<object name="User">
<field name="login" type="string" length="256"/>
<field name="password" type="string" lenght="256"/>
<field name="hash" type="string" length="256"/>
<index unique="true">
<indexfield name="login"/>
</index>
......
#include <master_server/master_server.hpp>
#include <config/config.hpp>
#include <logging/logging.hpp>
#include <utils/time.hpp>
#include <iostream>
int main()
{
std::ios_base::sync_with_stdio(false);
if (!Config::read_conf("./batajelo.conf"))
return 1;
log_debug("Starting master server");
log_debug(Config::get("db_login", "non"));
srand(getpid());
MasterServer s(7878);
s.start();
while (s.is_started())
s.poll(0);
log_debug("Exiting...");
return 0;
}
#include <menu_client/menu_client.hpp>
#include <config/config.hpp>
#include <logging/logging.hpp>
#include <iostream>
int main()
{
std::ios_base::sync_with_stdio(false);
MenuClient client;
client.run();
return 0;
}
#include <master_server/master_server.hpp>
MasterServer::MasterServer(const short port):
Server<RemoteClient>(port)
{
}
const RemoteClient* MasterServer::find_client_by_login(const std::string& login) const
{
for (const RemoteClient* client: this->clients)
{
const auto user = client->get_user();
if (user && user->login == login)
return client;
}
return nullptr;
}
Database* MasterServer::get_database()
{
return &this->database;
}
void MasterServer::on_new_client(RemoteClient* client)
{
client->set_server(this);
}
void MasterServer::on_client_left(RemoteClient* client)
{
log_debug("Client left");
}
#ifndef MASTER_SERVER_HPP_INCLUDED
#define MASTER_SERVER_HPP_INCLUDED
#include <network/server.hpp>
#include <master_server/remote_client.hpp>
#include <database/database.hpp>
class MasterServer: public Server<RemoteClient>
{
public:
MasterServer(const short port);
~MasterServer() = default;
/**
* Search for a connected client with this login
* @return RemoteClient* can be nullptr if nothing is found
*/
const RemoteClient* find_client_by_login(const std::string& login) const;
void on_new_client(RemoteClient* client) override final;
void on_client_left(RemoteClient* client) override final;
Database* get_database();
private:
Database database;
MasterServer(const MasterServer&) = delete;
MasterServer(MasterServer&&) = delete;
MasterServer& operator=(const MasterServer&) = delete;
MasterServer& operator=(MasterServer&&) = delete;
};
#endif /* MASTER_SERVER_HPP_INCLUDED */
#include <network/remote_client.hpp>
#include <master_server/remote_client.hpp>
#include <master_server/master_server.hpp>
#include "master.pb.h"
RemoteClient::RemoteClient(boost::asio::io_service& io_service,
Server<RemoteClient>* server):
RemoteClient::RemoteClient(boost::asio::io_service& io_service):
RemoteClientBase(io_service),
server(server)
user(nullptr),
server(nullptr),
senders{}
{
}
......@@ -12,14 +15,51 @@ RemoteClient::~RemoteClient()
log_info("Deleting remote client " << this->id);
}
void RemoteClient::set_server(MasterServer* server)
{
this->server = server;
}
const db::User* RemoteClient::get_user() const
{
return this->user.get();
}
void RemoteClient::install_callbacks()
{
this->install_callback("AUTH", std::bind(&RemoteClient::auth_callback, this, std::placeholders::_1));
this->install_callback("TRANSFER", std::bind(&RemoteClient::transfer_callback, this, std::placeholders::_1));
}
void RemoteClient::auth_callback(Message*)
void RemoteClient::auth_callback(Message* message)
{
auto srl = message->parse_body_to_protobuf_object<ser::master::Authenticate>();
ser::master::AuthResponse response;
auto db = this->server->get_database();
auto maybe_user = db->get_user_by_login(srl.login());
if (!maybe_user)
response.set_result(ser::master::AuthResponse::ERROR_INVALID_LOGIN);
else
{
auto user = *maybe_user;
auto res = db->check_user_password(user, srl.password());
if (std::get<0>(res) == true)
{
this->user = std::make_unique<db::User>(user);
response.set_result(ser::master::AuthResponse::SUCCESS);
}
else
response.set_result(ser::master::AuthResponse::ERROR_INVALID_PASSWORD);
}
this->send_message("AUTH_RESPONSE", response);
if (this->user)
this->on_user_logged_in();
}
void RemoteClient::on_user_logged_in()
{
// TODO send various information, the friend list, the news, etc
}
void RemoteClient::transfer_callback(Message* received_message)
......
......@@ -13,17 +13,21 @@
#include <boost/asio.hpp>
#include <functional>
#include "batadb.hpp"
#include <network/remote_client_base.hpp>
#include <network/transfer_sender.hpp>
#include <network/server.hpp>
class MasterServer;
class Message;
class TransferSender;
class RemoteClient: public RemoteClientBase
{
public:
explicit RemoteClient(boost::asio::io_service&, Server<RemoteClient>*);
explicit RemoteClient(boost::asio::io_service&);
~RemoteClient();
virtual void on_connection_closed();
void on_connection_closed() override final;
void set_server(MasterServer* server);
/**
* Sends a file to the remote client.
......@@ -49,8 +53,16 @@ public:
* For example, checks if there are news to send, or offline messages, etc
*/
void on_auth_success();
const db::User* get_user() const;
private:
/**
* Keep track of which user is associated with this client. Used to get
* various other information from the database (the contact list, account
* informations, etc).
* If it's nullptr, the user did not successfully log in (yet)
*/
std::unique_ptr<db::User> user;
/**
* Creates the default callbacks associated with a network message.
* It is executed whenever that message is received.
......@@ -59,11 +71,11 @@ private:
virtual void install_callbacks();
void auth_callback(Message*);
void transfer_callback(Message*);
void on_user_logged_in();
/**
* A pointer to the server, to call its method when the RemoteClient
* has to be deleted.
* A pointer to the server owning use
*/
Server<RemoteClient>* server;
MasterServer* server;
/**
* A list of all the current file transfers with the client.
*/
......
#include <menu_client/menu_client.hpp>
#include <logging/logging.hpp>
#include "master.pb.h"
MenuClient::MenuClient():
ClientBase(),
connected(false)
{
}
void MenuClient::run()
{
log_debug("run");
this->connect();
while (true)
this->poll();
}
void MenuClient::connect()
{
ClientBase::connect("127.0.0.1", 7878,
[this]()
{
this->on_connection_success();
},
[this](const boost::system::error_code& error)
{
this->on_connection_failed(error);
});
}
void MenuClient::on_connection_success()
{
log_debug("connection success. TODO send AUTH");
this->connected = true;
ser::master::Authenticate auth_message;
auth_message.set_login("louiz");
auth_message.set_password("new_password");
this->send_message("AUTH", auth_message);
this->install_callback_once("AUTH_RESPONSE",
[this](Message* message)
{
this->on_auth_response(message);
});
}
void MenuClient::on_connection_failed(const boost::system::error_code& error)
{
log_debug("Connection failed: " << error);
}
void MenuClient::install_callbacks()
{
log_debug("install_callbacks");
}
void MenuClient::on_connection_closed()
{
log_debug("connection closed");
this->connected = false;
}
void MenuClient::on_auth_response(Message* message)
{
auto srl = message->parse_body_to_protobuf_object<ser::master::AuthResponse>();
if (srl.result() == ser::master::AuthResponse::SUCCESS)
{
log_debug("Logged in!");
}
else
{
log_debug("failure.");
}
}
#ifndef MENU_CLIENT_HPP_INCLUDED
#define MENU_CLIENT_HPP_INCLUDED
#include <network/client_base.hpp>
class MenuClient: public ClientBase
{
public:
MenuClient();
~MenuClient() = default;
void run();
void connect();
void on_connection_success();
void on_connection_failed(const boost::system::error_code& error);
void install_callbacks() override final;
void on_connection_closed() override final;
void on_auth_response(Message* message);
private:
bool connected;
MenuClient(const MenuClient&) = delete;
MenuClient(MenuClient&&) = delete;
MenuClient& operator=(const MenuClient&) = delete;
MenuClient& operator=(MenuClient&&) = delete;
};
#endif /* MENU_CLIENT_HPP_INCLUDED */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment