diff --git a/include/aoclient.h b/include/aoclient.h index 9596fd3..b80f5f4 100644 --- a/include/aoclient.h +++ b/include/aoclient.h @@ -53,12 +53,13 @@ class AOClient : public QObject { QString moderator_name = ""; QString ooc_name = ""; - enum ACLFlags { - NONE = 0ULL, - KICK = 1ULL << 0, - BAN = 1ULL << 1, - BGLOCK = 1ULL << 2, - SUPER = ~0ULL + QMap ACLFlags { + {"NONE", 0ULL}, + {"KICK", 1ULL << 0}, + {"BAN", 1ULL << 1}, + {"BGLOCK", 1ULL << 2}, + {"MODIFY_USERS", 1ULL << 3}, + {"SUPER", ~0ULL} }; public slots: @@ -112,21 +113,21 @@ class AOClient : public QObject { }; const QMap packets { - {"HI", {ACLFlags::NONE, 1, &AOClient::pktHardwareId}}, - {"ID", {ACLFlags::NONE, 2, &AOClient::pktSoftwareId}}, - {"askchaa", {ACLFlags::NONE, 0, &AOClient::pktBeginLoad}}, - {"RC", {ACLFlags::NONE, 0, &AOClient::pktRequestChars}}, - {"RM", {ACLFlags::NONE, 0, &AOClient::pktRequestMusic}}, - {"RD", {ACLFlags::NONE, 0, &AOClient::pktLoadingDone}}, - {"PW", {ACLFlags::NONE, 1, &AOClient::pktCharPassword}}, - {"CC", {ACLFlags::NONE, 3, &AOClient::pktSelectChar}}, - {"MS", {ACLFlags::NONE, 1, &AOClient::pktIcChat}}, // TODO: doublecheck - {"CT", {ACLFlags::NONE, 2, &AOClient::pktOocChat}}, - {"CH", {ACLFlags::NONE, 1, &AOClient::pktPing}}, - {"MC", {ACLFlags::NONE, 2, &AOClient::pktChangeMusic}}, - {"RT", {ACLFlags::NONE, 1, &AOClient::pktWtCe}}, - {"HP", {ACLFlags::NONE, 2, &AOClient::pktHpBar}}, - {"WSIP", {ACLFlags::NONE, 1, &AOClient::pktWebSocketIp}} + {"HI", {ACLFlags.value("NONE"), 1, &AOClient::pktHardwareId}}, + {"ID", {ACLFlags.value("NONE"), 2, &AOClient::pktSoftwareId}}, + {"askchaa", {ACLFlags.value("NONE"), 0, &AOClient::pktBeginLoad}}, + {"RC", {ACLFlags.value("NONE"), 0, &AOClient::pktRequestChars}}, + {"RM", {ACLFlags.value("NONE"), 0, &AOClient::pktRequestMusic}}, + {"RD", {ACLFlags.value("NONE"), 0, &AOClient::pktLoadingDone}}, + {"PW", {ACLFlags.value("NONE"), 1, &AOClient::pktCharPassword}}, + {"CC", {ACLFlags.value("NONE"), 3, &AOClient::pktSelectChar}}, + {"MS", {ACLFlags.value("NONE"), 1, &AOClient::pktIcChat}}, // TODO: doublecheck + {"CT", {ACLFlags.value("NONE"), 2, &AOClient::pktOocChat}}, + {"CH", {ACLFlags.value("NONE"), 1, &AOClient::pktPing}}, + {"MC", {ACLFlags.value("NONE"), 2, &AOClient::pktChangeMusic}}, + {"RT", {ACLFlags.value("NONE"), 1, &AOClient::pktWtCe}}, + {"HP", {ACLFlags.value("NONE"), 2, &AOClient::pktHpBar}}, + {"WSIP", {ACLFlags.value("NONE"), 1, &AOClient::pktWebSocketIp}} }; // Commands @@ -141,6 +142,9 @@ class AOClient : public QObject { void cmdSetBackground(int argc, QStringList argv); void cmdBgLock(int argc, QStringList argv); void cmdBgUnlock(int argc, QStringList argv); + void cmdAddUser(int argc, QStringList argv); + void cmdListPerms(int argc, QStringList argv); + void cmdAddPerms(int argc, QStringList argv); // Command helper functions QStringList buildAreaList(int area_idx); @@ -155,17 +159,20 @@ class AOClient : public QObject { }; const QMap commands { - {"login", {ACLFlags::NONE, 1, &AOClient::cmdLogin}}, - {"getareas", {ACLFlags::NONE, 0 , &AOClient::cmdGetAreas}}, - {"getarea", {ACLFlags::NONE, 0, &AOClient::cmdGetArea}}, - {"ban", {ACLFlags::BAN, 2, &AOClient::cmdBan}}, - {"kick", {ACLFlags::KICK, 2, &AOClient::cmdKick}}, - {"changeauth", {ACLFlags::SUPER, 0, &AOClient::cmdChangeAuth}}, - {"rootpass", {ACLFlags::SUPER, 1, &AOClient::cmdSetRootPass}}, - {"background", {ACLFlags::NONE, 1, &AOClient::cmdSetBackground}}, - {"bg", {ACLFlags::NONE, 1, &AOClient::cmdSetBackground}}, - {"bglock", {ACLFlags::BGLOCK, 0, &AOClient::cmdBgLock}}, - {"bgunlock", {ACLFlags::BGLOCK, 0, &AOClient::cmdBgUnlock}} + {"login", {ACLFlags.value("NONE"), 1, &AOClient::cmdLogin}}, + {"getareas", {ACLFlags.value("NONE"), 0 , &AOClient::cmdGetAreas}}, + {"getarea", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetArea}}, + {"ban", {ACLFlags.value("BAN"), 2, &AOClient::cmdBan}}, + {"kick", {ACLFlags.value("KICK"), 2, &AOClient::cmdKick}}, + {"changeauth", {ACLFlags.value("SUPER"), 0, &AOClient::cmdChangeAuth}}, + {"rootpass", {ACLFlags.value("SUPER"), 1, &AOClient::cmdSetRootPass}}, + {"background", {ACLFlags.value("NONE"), 1, &AOClient::cmdSetBackground}}, + {"bg", {ACLFlags.value("NONE"), 1, &AOClient::cmdSetBackground}}, + {"bglock", {ACLFlags.value("BGLOCK"), 0, &AOClient::cmdBgLock}}, + {"bgunlock", {ACLFlags.value("BGLOCK"), 0, &AOClient::cmdBgUnlock}}, + {"adduser", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddUser}}, + {"listperms", {ACLFlags.value("MODIFY_USERS"), 0, &AOClient::cmdListPerms}}, + {"addperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddPerms}} }; QString partial_packet; diff --git a/include/db_manager.h b/include/db_manager.h index d1a126e..51e0a4c 100644 --- a/include/db_manager.h +++ b/include/db_manager.h @@ -18,8 +18,6 @@ #ifndef BAN_MANAGER_H #define BAN_MANAGER_H -#include "include/aoclient.h" - #include #include #include @@ -45,6 +43,7 @@ public: void createUser(QString username, QString salt, QString password, unsigned long long acl); unsigned long long getACL(QString moderator_name); bool authenticate(QString username, QString password); + bool updateACL(QString username, unsigned long long acl); private: const QString DRIVER; diff --git a/src/aoclient.cpp b/src/aoclient.cpp index ccfd0ec..a3eccd5 100644 --- a/src/aoclient.cpp +++ b/src/aoclient.cpp @@ -218,7 +218,7 @@ void AOClient::sendServerMessage(QString message) bool AOClient::checkAuth(unsigned long long acl_mask) { - if (acl_mask != ACLFlags::NONE) { + if (acl_mask != ACLFlags.value("NONE")) { if (!authenticated) { return false; } diff --git a/src/commands.cpp b/src/commands.cpp index c3e576b..819e09b 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -32,6 +32,11 @@ void AOClient::cmdLogin(int argc, QStringList argv) QString modpass = config.value("modpass", "default").toString(); QString auth_type = config.value("auth", "simple").toString(); + if (authenticated) { + sendServerMessage("You are already logged in!"); + return; + } + // TODO: tell the user if no modpass is set if (auth_type == "simple") { if(argv[0] == modpass) { @@ -162,7 +167,7 @@ void AOClient::cmdSetRootPass(int argc, QStringList argv) quint64 salt_number = QRandomGenerator::system()->generate64(); QString salt = QStringLiteral("%1").arg(salt_number, 16, 16, QLatin1Char('0')); - server->db_manager->createUser("root", salt, argv[0], ACLFlags::SUPER); + server->db_manager->createUser("root", salt, argv[0], ACLFlags.value("SUPER")); } void AOClient::cmdSetBackground(int argc, QStringList argv) @@ -199,6 +204,84 @@ void AOClient::cmdBgUnlock(int argc, QStringList argv) server->broadcast(AOPacket("CT", {"Server", current_char + " unlocked the background.", "1"}), current_area); } +void AOClient::cmdAddUser(int argc, QStringList argv) +{ + quint64 salt_number = QRandomGenerator::system()->generate64(); + QString salt = QStringLiteral("%1").arg(salt_number, 16, 16, QLatin1Char('0')); + + server->db_manager->createUser(argv[0], salt, argv[1], ACLFlags.value("NONE")); + sendServerMessage("Created user " + argv[0] + ".\nUse /addperm to modify their permissions."); +} + +void AOClient::cmdListPerms(int argc, QStringList argv) +{ + unsigned long long user_acl = server->db_manager->getACL(moderator_name); + QStringList message; + if (argc == 0) { + // Just print out all permissions available to the user. + message.append("You can add the following permissions to users:"); + for (QString perm : ACLFlags.keys()) { + if (perm == "NONE"); // don't need to list this one + else if (perm == "SUPER") { + if (user_acl == ACLFlags.value("SUPER")) // This has to be checked separately, because SUPER & anything will always be truthy + message.append("SUPER (Be careful! This grants the user all permissions.)"); + } + else if ((ACLFlags.value(perm) & user_acl) == 0); // user doesn't have this permission, don't print it + else + message.append(perm); + } + } + else { + message.append("User " + argv[0] + " has the following permissions:"); + unsigned long long acl = server->db_manager->getACL(argv[0]); + if (acl == 0) { + sendServerMessage("This user either doesn't exist, or has no permissions set."); + return; + } + + for (QString perm : ACLFlags.keys()) { + if ((ACLFlags.value(perm) & acl) != 0 && perm != "SUPER") { + message.append(perm); + } + } + } + sendServerMessage(message.join("\n")); +} + +void AOClient::cmdAddPerms(int argc, QStringList argv) +{ + unsigned long long user_acl = server->db_manager->getACL(moderator_name); + argv[1] = argv[1].toUpper(); + + if (!ACLFlags.keys().contains(argv[1])) { + sendServerMessage("That permission doesn't exist!"); + return; + } + + if (argv[1] == "SUPER") { + if (user_acl != ACLFlags.value("SUPER")) { + // This has to be checked separately, because SUPER & anything will always be truthy + sendServerMessage("You aren't allowed to add that permission!"); + return; + } + } + if (argv[1] == "NONE") { + sendServerMessage("Added no permissions!"); + return; + } + + unsigned long long newperm = ACLFlags.value(argv[1]); + if ((newperm & user_acl) != 0) { + if (server->db_manager->updateACL(argv[0], newperm)) + sendServerMessage("Successfully added permission " + argv[1] + " to user " + argv[0]); + else + sendServerMessage(argv[0] + " wasn't found!"); + return; + } + + sendServerMessage("You aren't allowed to add that permission!"); +} + QStringList AOClient::buildAreaList(int area_idx) { QStringList entries; diff --git a/src/db_manager.cpp b/src/db_manager.cpp index 9a083f6..858f984 100644 --- a/src/db_manager.cpp +++ b/src/db_manager.cpp @@ -110,12 +110,12 @@ void DBManager::createUser(QString username, QString salt, QString password, uns unsigned long long DBManager::getACL(QString moderator_name) { if (moderator_name == "") - return AOClient::ACLFlags::NONE; + return 0; QSqlQuery query("SELECT ACL FROM users WHERE USERNAME = ?"); query.addBindValue(moderator_name); query.exec(); if (!query.first()) - return AOClient::ACLFlags::NONE; + return 0; return query.value(0).toULongLong(); } @@ -146,6 +146,29 @@ bool DBManager::authenticate(QString username, QString password) return salted_password == stored_pass; } +bool DBManager::updateACL(QString username, unsigned long long acl) +{ + QSqlQuery username_exists; + username_exists.prepare("SELECT ACL FROM users WHERE USERNAME = ?"); + username_exists.addBindValue(username); + username_exists.exec(); + + if (!username_exists.first()) + return false; + + unsigned long long old_acl = username_exists.value(0).toULongLong(); + unsigned long long new_acl = acl | old_acl; + if (acl == 0) // Allow clearing all perms via adding perm "NONE" + new_acl = 0; + + QSqlQuery update_acl; + update_acl.prepare("UPDATE users SET ACL = ? WHERE USERNAME = ?"); + update_acl.addBindValue(new_acl); + update_acl.addBindValue(username); + update_acl.exec(); + return true; +} + DBManager::~DBManager() { db.close();