mirror of
https://gitlab.com/niansa/asbots.git
synced 2025-03-06 20:48:25 +01:00
143 lines
6.7 KiB
C++
143 lines
6.7 KiB
C++
/*
|
|
* asbots
|
|
* Copyright (C) 2021 niansa
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "nickserv.hpp"
|
|
#include "../instance.hpp"
|
|
|
|
#include <async/result.hpp>
|
|
#include <fmt/format.h>
|
|
#include <cryptopp/hex.h>
|
|
#include <cryptopp/sha.h>
|
|
#include <cryptopp/pwdbased.h>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
using fmt::operator""_format;
|
|
|
|
|
|
|
|
async::result<void> NickServ::intitialize() {
|
|
co_await i->netInfo.wait_ready();
|
|
user = {
|
|
.server = i->config.server.uid,
|
|
.nick = std::string(getConfig("NickServ", "nickname").value_or("NickServ")),
|
|
.realhost = i->config.server.name,
|
|
.uid = uuid,
|
|
.realname = i->netInfo.name
|
|
};
|
|
co_await mark_ready(user);
|
|
|
|
using namespace sqlite_orm;
|
|
storage = std::make_unique<Storage>(make_storage(
|
|
std::string(getConfig("NickServ", "database").value_or("nickserv.sqlite")),
|
|
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 = {
|
|
{"identify", {[this] (u_User& sender, const std::vector<std::string_view>& args) -> async::result<void> {
|
|
// 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: {} is not registered!"_format(accountName), 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 account 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: {} has already been registered!"_format(sender->nick), 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("{} has been registered!"_format(sender->nick), sender->uid));
|
|
}, "<password> <email>", 2, false}
|
|
},
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|