1
0
Fork 0
mirror of https://gitlab.com/niansa/asbots.git synced 2025-03-06 20:48:25 +01:00

First basic NickServ implementation has arrived!!!

This commit is contained in:
Nils 2021-06-23 15:35:11 +02:00
parent def0dc6715
commit 773369dab7
8 changed files with 108 additions and 37 deletions

View file

@ -20,7 +20,8 @@ find_package(PkgConfig REQUIRED)
pkg_check_modules(libasync-uv REQUIRED IMPORTED_TARGET libasync-uv)
pkg_check_modules(fmt REQUIRED IMPORTED_TARGET fmt)
pkg_check_modules(sqlite3 REQUIRED IMPORTED_TARGET sqlite3)
pkg_check_modules(cryptopp REQUIRED IMPORTED_TARGET cryptopp)
target_link_libraries(asbots PRIVATE PkgConfig::libasync-uv PkgConfig::fmt PkgConfig::sqlite3)
target_link_libraries(asbots PRIVATE PkgConfig::libasync-uv PkgConfig::fmt PkgConfig::sqlite3 PkgConfig::cryptopp)
configure_file(config.inil config.inil COPYONLY)

View file

@ -14,6 +14,6 @@ description: A test service!!
uid: 23X
end
[testservice]
channel: #testservice
[NickServ]
salt: changeme!!!
end

View file

@ -425,7 +425,7 @@ async::result<void> Instance::process(Event event) {
// Update login name
if (event.args.size() > 3) {
auto loginName = event.args[3];
if (loginName == "*" || loginName == "0") {
if (loginName.empty() || loginName == "*" || loginName == "0") {
res->get()->loginName.reset();
} else {
res->get()->loginName = event.args[3];

View file

@ -156,10 +156,10 @@ struct User {
};
}
Event get_encap_su(std::string_view login_name) {
Event get_encap_su(std::string_view login_name, const SUID& my_sid) {
//:<my_sid> ENCAP * SU <user_uid> <new_account_name>
return {
.sender = server,
.sender = my_sid,
.name = "ENCAP",
.raw_args = fmt::format("* SU {} {}", uid.str(), login_name)
};

View file

@ -59,11 +59,17 @@ async::result<void> ServiceBase::on_direct_privmsg(std::string_view msg, u_User&
co_await i->send_event(user.get_notice("Error: Command not found", author->uid));
} else {
split.erase(split.begin());
auto& [fnc, usage, minArgs] = res->second;
if (split.size() < minArgs) {
co_await i->send_event(user.get_notice("Error: Usage: {}"_format(usage), author->uid));
auto& [fnc, usage, minArgs, requiresAuth] = res->second;
if (requiresAuth && !author->loginName.has_value()) {
co_await i->send_event(user.get_notice("Error: You need to identify first", author->uid));
} else if (split.size() < minArgs) {
co_await i->send_event(user.get_notice("Usage: {}"_format(usage), author->uid));
} else {
co_await fnc(author, split);
}
}
}
async::result<void> ServiceBase::on_event(const Event& event) {
co_return;
}

View file

@ -38,10 +38,10 @@ public:
UUID uuid;
Instance *i;
bool ready = false;
std::unordered_map<std::string_view, std::tuple<std::function<async::result<void> (u_User& sender, const std::vector<std::string_view>& args)>, std::string_view, size_t>> commands;
std::unordered_map<std::string_view, std::tuple<std::function<async::result<void> (u_User& sender, const std::vector<std::string_view>& args)>, std::string_view, size_t, bool>> commands;
virtual async::result<void> intitialize() = 0;
virtual async::result<void> on_event(const Event& event) = 0;
virtual async::result<void> on_event(const Event& event);
virtual async::result<void> on_direct_privmsg(std::string_view msg, u_User& author);
async::result<void> mark_ready(const User& user);

View file

@ -2,6 +2,9 @@
#include "../instance.hpp"
#include <async/result.hpp>
#include <cryptopp/hex.h>
#include <cryptopp/sha.h>
#include <cryptopp/pwdbased.h>
#include <string>
#include <string_view>
#include <vector>
@ -26,35 +29,95 @@ async::result<void> NickServ::intitialize() {
make_table("users",
make_column("id", &Account::id, autoincrement(), primary_key()),
make_column("name", &Account::name),
make_column("email", &Account::email),
make_column("password_hash", &Account::password_hash))
));
storage->sync_schema();
commands = {
{"test", {[this] (u_User& sender, std::vector<std::string_view> args) -> async::result<void> {
co_await i->send_event(user.get_notice("Hello world! Works", sender->uid));
}, "", 0}
},
{"identify", {[this] (u_User& sender, const std::vector<std::string_view>& args) -> async::result<void> {
// Get user from storage
auto accounts_found = storage->get_all<Account>(where(c(&Account::name) == sender->nick));
if (accounts_found.empty()) {
co_await i->send_event(user.get_notice("Error: Your nick is not registered!", sender->uid));
co_return;
}
auto& account = accounts_found[0];
// Check password
if (account.password_hash != args[0]) {
co_await i->send_event(user.get_notice("Error: Invalid password!", sender->uid));
co_return;
}
// Login
co_await i->send_event(sender->get_encap_su(account.name));
}, "<password>", 1}
}
// Check that user is not already identified
if (sender->loginName.has_value()) {
co_await i->send_event(user.get_notice("Error: You've already been identified!", sender->uid));
co_return;
}
// Get account name and password to use
std::string_view accountName, accountPwd;
if (args.size() > 1) {
accountName = args[0];
accountPwd = args[1];
} else {
accountName = sender->nick;
accountPwd = args[0];
}
// Get account from storage
auto accounts_found = storage->get_all<Account>(where(c(&Account::name) == std::string(accountName)));
if (accounts_found.empty()) {
co_await i->send_event(user.get_notice("Error: '"+std::string(accountName)+"' is not registered!", sender->uid));
co_return;
}
auto& account = accounts_found[0];
// Check password
if (account.password_hash != pwdHash(accountPwd)) {
co_await i->send_event(user.get_notice("Error: Invalid password!", sender->uid));
co_return;
}
// Login
co_await i->send_event(sender->get_encap_su(account.name, i->config.server.uid));
// Report success
co_await i->send_event(user.get_notice("You have been identified!", sender->uid));
}, "[username] <password>", 1, false}
},
{"deidentify", {[this] (u_User& sender, const std::vector<std::string_view>& args) -> async::result<void> {
co_await i->send_event(sender->get_encap_su("", i->config.server.uid));
co_await i->send_event(user.get_notice("You have been deidentified!", sender->uid));
}, "", 0, true}
},
{"register", {[this] (u_User& sender, std::vector<std::string_view> args) -> async::result<void> {
// Check args
if (args.size() != 2) {
co_await i->send_event(user.get_notice("Error: Passwords may not contain spaces!", sender->uid));
co_return;
}
// Check that user does not exist already
{
auto accounts_found = storage->get_all<Account>(where(c(&Account::name) == sender->nick));
if (!accounts_found.empty()) {
co_await i->send_event(user.get_notice("Error: This nick has already been registered!", sender->uid));
co_return;
}
}
// Insert new account into database
storage->insert(Account{
.name = sender->nick,
.email = std::string(args[1]),
.password_hash = pwdHash(args[0])
});
// Report success
co_await i->send_event(user.get_notice("Your nickname has been registered!", sender->uid));
}, "<password> <email>", 2, false}
},
};
}
async::result<void> NickServ::on_event(const Event& event) {
co_return;
std::string NickServ::pwdHash(std::string_view pwd) {
using namespace CryptoPP;
auto salt = getConfig("NickServ", "salt").value_or("defaultsalt");
static_assert(sizeof(pwd[0]) == 1, "std::string_view character width must be 1 for this to work... You might have a bad STL implementation.");
int c = 1;
byte derived[20];
PKCS5_PBKDF2_HMAC<SHA1> pbkdf2;
pbkdf2.DeriveKey(derived, sizeof(derived), 0, reinterpret_cast<const byte*>(pwd.data()), pwd.size(), reinterpret_cast<const byte*>(salt.data()), salt.size(), c);
std::string fres;
HexEncoder encoder(new StringSink(fres));
encoder.Put(derived, sizeof(derived));
encoder.MessageEnd();
return fres;
}

View file

@ -29,11 +29,11 @@
struct Account {
size_t id;
std::string name, password_hash;
int id = -1;
std::string name, email, password_hash;
};
using Storage = sqlite_orm::internal::storage_t<sqlite_orm::internal::table_t<Account, sqlite_orm::internal::column_t<Account, long unsigned int, const long unsigned int& (Account::*)() const, void (Account::*)(long unsigned int), sqlite_orm::constraints::autoincrement_t, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >;
using Storage = sqlite_orm::internal::storage_t<sqlite_orm::internal::table_t<Account, sqlite_orm::internal::column_t<Account, int, const int& (Account::*)() const, void (Account::*)(int), sqlite_orm::constraints::autoincrement_t, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >;
class NickServ : public ServiceBase {
std::unique_ptr<Storage> storage;
@ -41,6 +41,7 @@ class NickServ : public ServiceBase {
User user;
virtual async::result<void> intitialize() override;
virtual async::result<void> on_event(const Event& event) override;
std::string pwdHash(std::string_view pwd);
};
#endif