Added flexible ACL roles (#7)

Resolve #4
This commit is contained in:
Leifa♥ 2022-04-27 01:06:15 +02:00 committed by Rosemary Witchaven
parent b0555207d6
commit 657a47b029
20 changed files with 1020 additions and 405 deletions

View File

@ -25,6 +25,7 @@ DESTDIR = $$PWD/../bin
#DEFINES += NET_DEBUG
SOURCES += \
src/acl_roles_handler.cpp \
src/aoclient.cpp \
src/aopacket.cpp \
src/area_data.cpp \
@ -51,6 +52,7 @@ SOURCES += \
src/music_manager.cpp
HEADERS += include/aoclient.h \
include/acl_roles_handler.h \
include/aopacket.h \
include/area_data.h \
include/config_manager.h \

View File

@ -0,0 +1,210 @@
#ifndef ACL_ROLES_HANDLER_H
#define ACL_ROLES_HANDLER_H
#include <QFlags>
#include <QHash>
#include <QObject>
class ACLRole
{
public:
/**
* @brief This enum is used to specify permissions of a role.
*/
enum Permission : unsigned int
{
NONE = 0,
KICK = 1 << 0,
BAN = 1 << 1,
BGLOCK = 1 << 2,
MODIFY_USERS = 1 << 3,
CM = 1 << 4,
GLOBAL_TIMER = 1 << 5,
EVI_MOD = 1 << 6,
MOTD = 1 << 7,
ANNOUNCE = 1 << 8,
MODCHAT = 1 << 9,
MUTE = 1 << 10,
UNCM = 1 << 11,
SAVETEST = 1 << 12,
FORCE_CHARSELECT = 1 << 13,
BYPASS_LOCKS = 1 << 14,
IGNORE_BGLIST = 1 << 15,
SEND_NOTICE = 1 << 16,
JUKEBOX = 1 << 17,
SUPER = 0xffffffff,
};
Q_DECLARE_FLAGS(Permissions, Permission);
/**
* @brief Shared read-only captions for each permissions.
*
* @see ACLRoleHandler#loadFile and ACLRoleHandler#saveFile
*/
static const QHash<ACLRole::Permission, QString> permission_captions;
/**
* @brief Constructs a role without any permissions.
*/
ACLRole();
/**
* @brief Constructs a role of the given permissions.
*
* @param f_permissions The permissions to which
*/
ACLRole(ACLRole::Permissions f_permissions);
/**
* @brief Destroys the role.
*/
~ACLRole();
/**
* @brief Returns the permission flags for this role.
*
* @return Permission flags.
*/
ACLRole::Permissions getPermissions() const;
/**
* @brief Checks if a given permission is set.
*
* @param f_permission The permission flag to check.
*
* @return True if the permission is set, false otherwise.
*/
bool checkPermission(ACLRole::Permission f_permission) const;
/**
* @brief Sets the permission if f_mode is true or unsets if f_mode is false.
*
* @param f_permission The permission flag to set.
*
* @param f_mode If true, will set the flag, unsets otherwise.
*/
void setPermission(ACLRole::Permission f_permission, bool f_mode);
/**
* @brief Sets the permission flags to the given permission flags.
*
* @param f_permissions The permission flags to set to.
*/
void setPermissions(ACLRole::Permissions f_permissions);
private:
/**
* @brief The permission flags of the role.
*/
ACLRole::Permissions m_permissions;
};
class ACLRolesHandler : public QObject
{
Q_OBJECT
public:
/**
* @brief The identifier of the NONE role.
*/
static const QString NONE_ID;
/**
* @brief The identifier of the SUPER role.
*/
static const QString SUPER_ID;
/**
* @brief Constructs a role handler with parent object.
*
* @details The role handler starts off empty without any roles known with the exception of the shared read-only roles.
*
* The role handler do not load or save roles automatically.
*
* Multiple role handlers can exist simultaneously with different roles and these same roles may have entirely different permissions.
*
* @param parent Qt-based parent.
*/
ACLRolesHandler(QObject *parent = nullptr);
/**
* Destroys the role handler.
*/
~ACLRolesHandler();
/**
* @brief Checks if a role with the given identifier exists.
*
* @param f_id The identifier of the role. The identifier is not case-sensitive.
*
* @return True if the role exists, false otherwise.
*/
bool roleExists(QString f_id);
/**
* @brief Returns a role with the given identifier. If the role does not exist, a default constructed role will be returned.
*
* @param f_id The identifier of the role. The identifier is not case-sensitive.
*
* @return A role.
*/
ACLRole getRoleById(QString f_id);
/**
* @brief Inserts a role with the given identifier. If a role already exists, it will be overwritten. Read-only roles cannot be replaced and will return false.
*
* @param f_id The identifier of the role. The identifier is not case-sensitive.
*
* @param f_role The role to insert.
*
* @return True if the role was inserted, false otherwise.
*/
bool insertRole(QString f_id, ACLRole f_role);
/**
* @brief Removes a role of the given identifier. Read-only roles cannot be removed and will return false.
*
* @param f_id The identifier of the role. The identifier is not case-sensitive.
*
* @return True if the role was removed, false otherwise.
*/
bool removeRole(QString f_id);
/**
* @brief Removes all roles.
*/
void clearRoles();
/**
* @brief Clear the current roles and load the roles from the given file. The file must be INI format compatible.
*
* @param f_filename The filename may have no path, a relative path or an absolute path.
*
* @return True if successfull, false otherwise.
*/
bool loadFile(QString f_filename);
/**
* @brief Save the current roles to the given file. The file is saved to the INI format.
*
* @warning This will completely overwrite the given file.
*
* @param f_filename The filename may have no path, a relative path or an absolute path.
*
* @return True if successfull, false otherwise.
*/
bool saveFile(QString f_filename);
private:
/**
* @brief Shared read-only standard roles with the appropriate permissions.
*/
static const QHash<QString, ACLRole> readonly_roles;
/**
* @brief The roles of the handler.
*/
QHash<QString, ACLRole> m_roles;
};
#endif // ACL_ROLES_HANDLER_H

View File

@ -24,12 +24,13 @@
#include <QHostAddress>
#include <QRegularExpression>
#include <QTcpSocket>
#include <QtGlobal>
#include <QTimer>
#include <QtGlobal>
#if QT_VERSION > QT_VERSION_CHECK(5, 10, 0)
#include <QRandomGenerator>
#endif
#include "include/acl_roles_handler.h"
#include "include/aopacket.h"
class AreaData;
@ -87,6 +88,13 @@ class AOClient : public QObject
*/
bool hasJoined() const;
/**
* @brief Returns true if the client has logged-in as a role.
*
* @return True if loggged-in, false otherwise.
*/
bool isAuthenticated() const;
/**
* @brief Calculates the client's IPID based on a hashed version of its IP.
*/
@ -146,11 +154,6 @@ class AOClient : public QObject
*/
QString m_current_iniswap;
/**
* @brief If true, the client is a logged-in moderator.
*/
bool m_authenticated = false;
/**
* @brief If using advanced authentication, this is the moderator name that the client has logged in with.
*/
@ -221,33 +224,6 @@ class AOClient : public QObject
*/
ClientVersion m_version;
/**
* @brief The authorisation bitflag, representing what permissions a client can have.
*
* @showinitializer
*/
QMap<QString, unsigned long long> ACLFlags{
{"NONE", 0ULL},
{"KICK", 1ULL << 0},
{"BAN", 1ULL << 1},
{"BGLOCK", 1ULL << 2},
{"MODIFY_USERS", 1ULL << 3},
{"CM", 1ULL << 4},
{"GLOBAL_TIMER", 1ULL << 5},
{"EVI_MOD", 1ULL << 6},
{"MOTD", 1ULL << 7},
{"ANNOUNCE", 1ULL << 8},
{"MODCHAT", 1ULL << 9},
{"MUTE", 1ULL << 10},
{"UNCM", 1ULL << 11},
{"SAVETEST", 1ULL << 12},
{"FORCE_CHARSELECT", 1ULL << 13},
{"BYPASS_LOCKS", 1ULL << 14},
{"IGNORE_BGLIST", 1ULL << 15},
{"SEND_NOTICE", 1ULL << 16},
{"JUKEBOX", 1ULL << 17},
{"SUPER", ~0ULL}};
/**
* @brief A list of 5 casing preferences (def, pro, judge, jury, steno)
*/
@ -310,14 +286,13 @@ class AOClient : public QObject
bool m_is_logging_in = false;
/**
* @brief Checks if the client would be authorised to something based on its necessary permissions.
* @brief Checks if the client's ACL role has permission for the given permission.
*
* @param acl_mask The permissions bitflag that the client's own permissions should be checked against.
* @param f_permission The permission flags.
*
* @return True if the client's permissions are high enough for `acl_mask`, or higher than it.
* False if the client is missing some permissions.
* @return True if the client has permission, false otherwise.
*/
bool checkAuth(unsigned long long acl_mask);
bool checkPermission(ACLRole::Permission f_permission) const;
public slots:
/**
@ -611,6 +586,16 @@ class AOClient : public QObject
*/
///@{
/**
* @brief If true, the client is a logged-in moderator.
*/
bool m_authenticated = false;
/**
* @brief The ACL role identifier, used to determine what ACL role the client is linked to.
*/
QString m_acl_role_id;
/**
* @brief The client's character ID.
*
@ -660,7 +645,7 @@ class AOClient : public QObject
/// Describes a packet's interpretation details.
struct PacketInfo
{
unsigned long long acl_mask; //!< The permissions necessary for the packet.
ACLRole::Permission acl_permission; //!< The permissions necessary for the packet.
int minArgs; //!< The minimum arguments needed for the packet to be interpreted correctly / make sense.
void (AOClient::*action)(AreaData *, int, QStringList, AOPacket);
};
@ -686,27 +671,27 @@ class AOClient : public QObject
* See @ref PacketInfo "the type's documentation" for more details.
*/
const QMap<QString, PacketInfo> packets{
{"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"), 15, &AOClient::pktIcChat}},
{"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}},
{"ZZ", {ACLFlags.value("NONE"), 0, &AOClient::pktModCall}},
{"PE", {ACLFlags.value("NONE"), 3, &AOClient::pktAddEvidence}},
{"DE", {ACLFlags.value("NONE"), 1, &AOClient::pktRemoveEvidence}},
{"EE", {ACLFlags.value("NONE"), 4, &AOClient::pktEditEvidence}},
{"SETCASE", {ACLFlags.value("NONE"), 7, &AOClient::pktSetCase}},
{"CASEA", {ACLFlags.value("NONE"), 6, &AOClient::pktAnnounceCase}},
{"HI", {ACLRole::NONE, 1, &AOClient::pktHardwareId}},
{"ID", {ACLRole::NONE, 2, &AOClient::pktSoftwareId}},
{"askchaa", {ACLRole::NONE, 0, &AOClient::pktBeginLoad}},
{"RC", {ACLRole::NONE, 0, &AOClient::pktRequestChars}},
{"RM", {ACLRole::NONE, 0, &AOClient::pktRequestMusic}},
{"RD", {ACLRole::NONE, 0, &AOClient::pktLoadingDone}},
{"PW", {ACLRole::NONE, 1, &AOClient::pktCharPassword}},
{"CC", {ACLRole::NONE, 3, &AOClient::pktSelectChar}},
{"MS", {ACLRole::NONE, 15, &AOClient::pktIcChat}},
{"CT", {ACLRole::NONE, 2, &AOClient::pktOocChat}},
{"CH", {ACLRole::NONE, 1, &AOClient::pktPing}},
{"MC", {ACLRole::NONE, 2, &AOClient::pktChangeMusic}},
{"RT", {ACLRole::NONE, 1, &AOClient::pktWtCe}},
{"HP", {ACLRole::NONE, 2, &AOClient::pktHpBar}},
{"WSIP", {ACLRole::NONE, 1, &AOClient::pktWebSocketIp}},
{"ZZ", {ACLRole::NONE, 0, &AOClient::pktModCall}},
{"PE", {ACLRole::NONE, 3, &AOClient::pktAddEvidence}},
{"DE", {ACLRole::NONE, 1, &AOClient::pktRemoveEvidence}},
{"EE", {ACLRole::NONE, 4, &AOClient::pktEditEvidence}},
{"SETCASE", {ACLRole::NONE, 7, &AOClient::pktSetCase}},
{"CASEA", {ACLRole::NONE, 6, &AOClient::pktAnnounceCase}},
};
/**
@ -779,7 +764,7 @@ class AOClient : public QObject
*
* @iscommand
*/
void cmdAddPerms(int argc, QStringList argv);
void cmdSetPerms(int argc, QStringList argv);
/**
* @brief Removes permissions from a given user.
@ -2062,7 +2047,7 @@ class AOClient : public QObject
*/
struct CommandInfo
{
unsigned long long acl_mask; //!< The permissions necessary to be able to run the command. @see ACLFlags.
ACLRole::Permission acl_permission; //!< The permissions necessary to be able to run the command. @see ACLRole::Permission.
int minArgs; //!< The minimum mandatory arguments needed for the command to function.
void (AOClient::*action)(int, QStringList);
};
@ -2087,145 +2072,145 @@ class AOClient : public QObject
* See @ref CommandInfo "the type's documentation" for more details.
*/
const QMap<QString, CommandInfo> commands{
{"login", {ACLFlags.value("NONE"), 0, &AOClient::cmdLogin}},
{"getareas", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetAreas}},
{"gas", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetAreas}},
{"getarea", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetArea}},
{"ga", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetArea}},
{"ban", {ACLFlags.value("BAN"), 3, &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("NONE"), 0, &AOClient::cmdListPerms}},
{"addperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddPerms}},
{"removeperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdRemovePerms}},
{"listusers", {ACLFlags.value("MODIFY_USERS"), 0, &AOClient::cmdListUsers}},
{"logout", {ACLFlags.value("NONE"), 0, &AOClient::cmdLogout}},
{"pos", {ACLFlags.value("NONE"), 1, &AOClient::cmdPos}},
{"g", {ACLFlags.value("NONE"), 1, &AOClient::cmdG}},
{"need", {ACLFlags.value("NONE"), 1, &AOClient::cmdNeed}},
{"coinflip", {ACLFlags.value("NONE"), 0, &AOClient::cmdFlip}},
{"roll", {ACLFlags.value("NONE"), 0, &AOClient::cmdRoll}},
{"r", {ACLFlags.value("NONE"), 0, &AOClient::cmdRoll}},
{"rollp", {ACLFlags.value("NONE"), 0, &AOClient::cmdRollP}},
{"doc", {ACLFlags.value("NONE"), 0, &AOClient::cmdDoc}},
{"cleardoc", {ACLFlags.value("NONE"), 0, &AOClient::cmdClearDoc}},
{"cm", {ACLFlags.value("NONE"), 0, &AOClient::cmdCM}},
{"uncm", {ACLFlags.value("CM"), 0, &AOClient::cmdUnCM}},
{"invite", {ACLFlags.value("CM"), 1, &AOClient::cmdInvite}},
{"uninvite", {ACLFlags.value("CM"), 1, &AOClient::cmdUnInvite}},
{"lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}},
{"area_lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}},
{"spectatable", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}},
{"area_spectate", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}},
{"unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}},
{"area_unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}},
{"timer", {ACLFlags.value("CM"), 0, &AOClient::cmdTimer}},
{"area", {ACLFlags.value("NONE"), 1, &AOClient::cmdArea}},
{"play", {ACLFlags.value("CM"), 1, &AOClient::cmdPlay}},
{"areakick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}},
{"area_kick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}},
{"randomchar", {ACLFlags.value("NONE"), 0, &AOClient::cmdRandomChar}},
{"switch", {ACLFlags.value("NONE"), 1, &AOClient::cmdSwitch}},
{"toggleglobal", {ACLFlags.value("NONE"), 0, &AOClient::cmdToggleGlobal}},
{"mods", {ACLFlags.value("NONE"), 0, &AOClient::cmdMods}},
{"commands", {ACLFlags.value("NONE"), 0, &AOClient::cmdCommands}},
{"status", {ACLFlags.value("NONE"), 1, &AOClient::cmdStatus}},
{"forcepos", {ACLFlags.value("CM"), 2, &AOClient::cmdForcePos}},
{"currentmusic", {ACLFlags.value("NONE"), 0, &AOClient::cmdCurrentMusic}},
{"pm", {ACLFlags.value("NONE"), 2, &AOClient::cmdPM}},
{"evidence_mod", {ACLFlags.value("EVI_MOD"), 1, &AOClient::cmdEvidenceMod}},
{"motd", {ACLFlags.value("NONE"), 0, &AOClient::cmdMOTD}},
{"announce", {ACLFlags.value("ANNOUNCE"), 1, &AOClient::cmdAnnounce}},
{"m", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdM}},
{"gm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdGM}},
{"mute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdMute}},
{"unmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnMute}},
{"bans", {ACLFlags.value("BAN"), 0, &AOClient::cmdBans}},
{"unban", {ACLFlags.value("BAN"), 1, &AOClient::cmdUnBan}},
{"removeuser", {ACLFlags.value("MODIFY_USERS"), 1, &AOClient::cmdRemoveUser}},
{"subtheme", {ACLFlags.value("CM"), 1, &AOClient::cmdSubTheme}},
{"about", {ACLFlags.value("NONE"), 0, &AOClient::cmdAbout}},
{"evidence_swap", {ACLFlags.value("CM"), 2, &AOClient::cmdEvidence_Swap}},
{"notecard", {ACLFlags.value("NONE"), 1, &AOClient::cmdNoteCard}},
{"notecardreveal", {ACLFlags.value("CM"), 0, &AOClient::cmdNoteCardReveal}},
{"notecard_reveal", {ACLFlags.value("CM"), 0, &AOClient::cmdNoteCardReveal}},
{"notecardclear", {ACLFlags.value("NONE"), 0, &AOClient::cmdNoteCardClear}},
{"notecard_clear", {ACLFlags.value("NONE"), 0, &AOClient::cmdNoteCardClear}},
{"8ball", {ACLFlags.value("NONE"), 1, &AOClient::cmd8Ball}},
{"lm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdLM}},
{"judgelog", {ACLFlags.value("CM"), 0, &AOClient::cmdJudgeLog}},
{"allowblankposting", {ACLFlags.value("MODCHAT"), 0, &AOClient::cmdAllowBlankposting}},
{"allow_blankposting", {ACLFlags.value("MODCHAT"), 0, &AOClient::cmdAllowBlankposting}},
{"gimp", {ACLFlags.value("MUTE"), 1, &AOClient::cmdGimp}},
{"ungimp", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnGimp}},
{"baninfo", {ACLFlags.value("BAN"), 1, &AOClient::cmdBanInfo}},
{"testify", {ACLFlags.value("CM"), 0, &AOClient::cmdTestify}},
{"testimony", {ACLFlags.value("NONE"), 0, &AOClient::cmdTestimony}},
{"examine", {ACLFlags.value("CM"), 0, &AOClient::cmdExamine}},
{"pause", {ACLFlags.value("CM"), 0, &AOClient::cmdPauseTestimony}},
{"delete", {ACLFlags.value("CM"), 0, &AOClient::cmdDeleteStatement}},
{"update", {ACLFlags.value("CM"), 0, &AOClient::cmdUpdateStatement}},
{"add", {ACLFlags.value("CM"), 0, &AOClient::cmdAddStatement}},
{"reload", {ACLFlags.value("SUPER"), 0, &AOClient::cmdReload}},
{"disemvowel", {ACLFlags.value("MUTE"), 1, &AOClient::cmdDisemvowel}},
{"undisemvowel", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnDisemvowel}},
{"shake", {ACLFlags.value("MUTE"), 1, &AOClient::cmdShake}},
{"unshake", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnShake}},
{"forceimmediate", {ACLFlags.value("CM"), 0, &AOClient::cmdForceImmediate}},
{"force_noint_pres", {ACLFlags.value("CM"), 0, &AOClient::cmdForceImmediate}},
{"allowiniswap", {ACLFlags.value("CM"), 0, &AOClient::cmdAllowIniswap}},
{"allow_iniswap", {ACLFlags.value("CM"), 0, &AOClient::cmdAllowIniswap}},
{"afk", {ACLFlags.value("NONE"), 0, &AOClient::cmdAfk}},
{"savetestimony", {ACLFlags.value("NONE"), 1, &AOClient::cmdSaveTestimony}},
{"loadtestimony", {ACLFlags.value("CM"), 1, &AOClient::cmdLoadTestimony}},
{"permitsaving", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdPermitSaving}},
{"mutepm", {ACLFlags.value("NONE"), 0, &AOClient::cmdMutePM}},
{"toggleadverts", {ACLFlags.value("NONE"), 0, &AOClient::cmdToggleAdverts}},
{"oocmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdOocMute}},
{"ooc_mute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdOocMute}},
{"oocunmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdOocUnMute}},
{"ooc_unmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdOocUnMute}},
{"blockwtce", {ACLFlags.value("MUTE"), 1, &AOClient::cmdBlockWtce}},
{"block_wtce", {ACLFlags.value("MUTE"), 1, &AOClient::cmdBlockWtce}},
{"unblockwtce", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnBlockWtce}},
{"unblock_wtce", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnBlockWtce}},
{"blockdj", {ACLFlags.value("MUTE"), 1, &AOClient::cmdBlockDj}},
{"block_dj", {ACLFlags.value("MUTE"), 1, &AOClient::cmdBlockDj}},
{"unblockdj", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnBlockDj}},
{"unblock_dj", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnBlockDj}},
{"charcurse", {ACLFlags.value("MUTE"), 1, &AOClient::cmdCharCurse}},
{"uncharcurse", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnCharCurse}},
{"charselect", {ACLFlags.value("NONE"), 0, &AOClient::cmdCharSelect}},
{"togglemusic", {ACLFlags.value("CM"), 0, &AOClient::cmdToggleMusic}},
{"a", {ACLFlags.value("NONE"), 2, &AOClient::cmdA}},
{"s", {ACLFlags.value("NONE"), 0, &AOClient::cmdS}},
{"kickuid", {ACLFlags.value("KICK"), 2, &AOClient::cmdKickUid}},
{"kick_uid", {ACLFlags.value("KICK"), 2, &AOClient::cmdKickUid}},
{"firstperson", {ACLFlags.value("NONE"), 0, &AOClient::cmdFirstPerson}},
{"updateban", {ACLFlags.value("BAN"), 3, &AOClient::cmdUpdateBan}},
{"update_ban", {ACLFlags.value("BAN"), 3, &AOClient::cmdUpdateBan}},
{"changepass", {ACLFlags.value("NONE"), 1, &AOClient::cmdChangePassword}},
{"ignorebglist", {ACLFlags.value("IGNORE_BGLIST"), 0, &AOClient::cmdIgnoreBgList}},
{"ignore_bglist", {ACLFlags.value("IGNORE_BGLIST"), 0, &AOClient::cmdIgnoreBgList}},
{"notice", {ACLFlags.value("SEND_NOTICE"), 1, &AOClient::cmdNotice}},
{"noticeg", {ACLFlags.value("SEND_NOTICE"), 1, &AOClient::cmdNoticeGlobal}},
{"togglejukebox", {ACLFlags.value("None"), 0, &AOClient::cmdToggleJukebox}},
{"help", {ACLFlags.value("NONE"), 1, &AOClient::cmdHelp}},
{"clearcm", {ACLFlags.value("KICK"), 0, &AOClient::cmdClearCM}},
{"togglemessage", {ACLFlags.value("CM"), 0, &AOClient::cmdToggleAreaMessageOnJoin}},
{"clearmessage", {ACLFlags.value("CM"), 0, &AOClient::cmdClearAreaMessage}},
{"areamessage", {ACLFlags.value("CM"), 0, &AOClient::cmdAreaMessage}},
{"addsong", {ACLFlags.value("CM"), 1, &AOClient::cmdAddSong}},
{"addcategory", {ACLFlags.value("CM"), 1, &AOClient::cmdAddCategory}},
{"removeentry", {ACLFlags.value("CM"), 1, &AOClient::cmdRemoveCategorySong}},
{"toggleroot", {ACLFlags.value("CM"), 0, &AOClient::cmdToggleRootlist}},
{"clearcustom", {ACLFlags.value("CM"), 0, &AOClient::cmdClearCustom}}};
{"login", {ACLRole::NONE, 0, &AOClient::cmdLogin}},
{"getareas", {ACLRole::NONE, 0, &AOClient::cmdGetAreas}},
{"gas", {ACLRole::NONE, 0, &AOClient::cmdGetAreas}},
{"getarea", {ACLRole::NONE, 0, &AOClient::cmdGetArea}},
{"ga", {ACLRole::NONE, 0, &AOClient::cmdGetArea}},
{"ban", {ACLRole::BAN, 3, &AOClient::cmdBan}},
{"kick", {ACLRole::KICK, 2, &AOClient::cmdKick}},
{"changeauth", {ACLRole::SUPER, 0, &AOClient::cmdChangeAuth}},
{"rootpass", {ACLRole::SUPER, 1, &AOClient::cmdSetRootPass}},
{"background", {ACLRole::NONE, 1, &AOClient::cmdSetBackground}},
{"bg", {ACLRole::NONE, 1, &AOClient::cmdSetBackground}},
{"bglock", {ACLRole::BGLOCK, 0, &AOClient::cmdBgLock}},
{"bgunlock", {ACLRole::BGLOCK, 0, &AOClient::cmdBgUnlock}},
{"adduser", {ACLRole::MODIFY_USERS, 2, &AOClient::cmdAddUser}},
{"removeuser", {ACLRole::MODIFY_USERS, 1, &AOClient::cmdRemoveUser}},
{"listusers", {ACLRole::MODIFY_USERS, 0, &AOClient::cmdListUsers}},
{"setperms", {ACLRole::MODIFY_USERS, 2, &AOClient::cmdSetPerms}},
{"removeperms", {ACLRole::MODIFY_USERS, 1, &AOClient::cmdRemovePerms}},
{"listperms", {ACLRole::NONE, 0, &AOClient::cmdListPerms}},
{"logout", {ACLRole::NONE, 0, &AOClient::cmdLogout}},
{"pos", {ACLRole::NONE, 1, &AOClient::cmdPos}},
{"g", {ACLRole::NONE, 1, &AOClient::cmdG}},
{"need", {ACLRole::NONE, 1, &AOClient::cmdNeed}},
{"coinflip", {ACLRole::NONE, 0, &AOClient::cmdFlip}},
{"roll", {ACLRole::NONE, 0, &AOClient::cmdRoll}},
{"r", {ACLRole::NONE, 0, &AOClient::cmdRoll}},
{"rollp", {ACLRole::NONE, 0, &AOClient::cmdRollP}},
{"doc", {ACLRole::NONE, 0, &AOClient::cmdDoc}},
{"cleardoc", {ACLRole::NONE, 0, &AOClient::cmdClearDoc}},
{"cm", {ACLRole::NONE, 0, &AOClient::cmdCM}},
{"uncm", {ACLRole::CM, 0, &AOClient::cmdUnCM}},
{"invite", {ACLRole::CM, 1, &AOClient::cmdInvite}},
{"uninvite", {ACLRole::CM, 1, &AOClient::cmdUnInvite}},
{"lock", {ACLRole::CM, 0, &AOClient::cmdLock}},
{"area_lock", {ACLRole::CM, 0, &AOClient::cmdLock}},
{"spectatable", {ACLRole::CM, 0, &AOClient::cmdSpectatable}},
{"area_spectate", {ACLRole::CM, 0, &AOClient::cmdSpectatable}},
{"unlock", {ACLRole::CM, 0, &AOClient::cmdUnLock}},
{"area_unlock", {ACLRole::CM, 0, &AOClient::cmdUnLock}},
{"timer", {ACLRole::CM, 0, &AOClient::cmdTimer}},
{"area", {ACLRole::NONE, 1, &AOClient::cmdArea}},
{"play", {ACLRole::CM, 1, &AOClient::cmdPlay}},
{"areakick", {ACLRole::CM, 1, &AOClient::cmdAreaKick}},
{"area_kick", {ACLRole::CM, 1, &AOClient::cmdAreaKick}},
{"randomchar", {ACLRole::NONE, 0, &AOClient::cmdRandomChar}},
{"switch", {ACLRole::NONE, 1, &AOClient::cmdSwitch}},
{"toggleglobal", {ACLRole::NONE, 0, &AOClient::cmdToggleGlobal}},
{"mods", {ACLRole::NONE, 0, &AOClient::cmdMods}},
{"commands", {ACLRole::NONE, 0, &AOClient::cmdCommands}},
{"status", {ACLRole::NONE, 1, &AOClient::cmdStatus}},
{"forcepos", {ACLRole::CM, 2, &AOClient::cmdForcePos}},
{"currentmusic", {ACLRole::NONE, 0, &AOClient::cmdCurrentMusic}},
{"pm", {ACLRole::NONE, 2, &AOClient::cmdPM}},
{"evidence_mod", {ACLRole::EVI_MOD, 1, &AOClient::cmdEvidenceMod}},
{"motd", {ACLRole::NONE, 0, &AOClient::cmdMOTD}},
{"announce", {ACLRole::ANNOUNCE, 1, &AOClient::cmdAnnounce}},
{"m", {ACLRole::MODCHAT, 1, &AOClient::cmdM}},
{"gm", {ACLRole::MODCHAT, 1, &AOClient::cmdGM}},
{"mute", {ACLRole::MUTE, 1, &AOClient::cmdMute}},
{"unmute", {ACLRole::MUTE, 1, &AOClient::cmdUnMute}},
{"bans", {ACLRole::BAN, 0, &AOClient::cmdBans}},
{"unban", {ACLRole::BAN, 1, &AOClient::cmdUnBan}},
{"subtheme", {ACLRole::CM, 1, &AOClient::cmdSubTheme}},
{"about", {ACLRole::NONE, 0, &AOClient::cmdAbout}},
{"evidence_swap", {ACLRole::CM, 2, &AOClient::cmdEvidence_Swap}},
{"notecard", {ACLRole::NONE, 1, &AOClient::cmdNoteCard}},
{"notecardreveal", {ACLRole::CM, 0, &AOClient::cmdNoteCardReveal}},
{"notecard_reveal", {ACLRole::CM, 0, &AOClient::cmdNoteCardReveal}},
{"notecardclear", {ACLRole::NONE, 0, &AOClient::cmdNoteCardClear}},
{"notecard_clear", {ACLRole::NONE, 0, &AOClient::cmdNoteCardClear}},
{"8ball", {ACLRole::NONE, 1, &AOClient::cmd8Ball}},
{"lm", {ACLRole::MODCHAT, 1, &AOClient::cmdLM}},
{"judgelog", {ACLRole::CM, 0, &AOClient::cmdJudgeLog}},
{"allowblankposting", {ACLRole::MODCHAT, 0, &AOClient::cmdAllowBlankposting}},
{"allow_blankposting", {ACLRole::MODCHAT, 0, &AOClient::cmdAllowBlankposting}},
{"gimp", {ACLRole::MUTE, 1, &AOClient::cmdGimp}},
{"ungimp", {ACLRole::MUTE, 1, &AOClient::cmdUnGimp}},
{"baninfo", {ACLRole::BAN, 1, &AOClient::cmdBanInfo}},
{"testify", {ACLRole::CM, 0, &AOClient::cmdTestify}},
{"testimony", {ACLRole::NONE, 0, &AOClient::cmdTestimony}},
{"examine", {ACLRole::CM, 0, &AOClient::cmdExamine}},
{"pause", {ACLRole::CM, 0, &AOClient::cmdPauseTestimony}},
{"delete", {ACLRole::CM, 0, &AOClient::cmdDeleteStatement}},
{"update", {ACLRole::CM, 0, &AOClient::cmdUpdateStatement}},
{"add", {ACLRole::CM, 0, &AOClient::cmdAddStatement}},
{"reload", {ACLRole::SUPER, 0, &AOClient::cmdReload}},
{"disemvowel", {ACLRole::MUTE, 1, &AOClient::cmdDisemvowel}},
{"undisemvowel", {ACLRole::MUTE, 1, &AOClient::cmdUnDisemvowel}},
{"shake", {ACLRole::MUTE, 1, &AOClient::cmdShake}},
{"unshake", {ACLRole::MUTE, 1, &AOClient::cmdUnShake}},
{"forceimmediate", {ACLRole::CM, 0, &AOClient::cmdForceImmediate}},
{"force_noint_pres", {ACLRole::CM, 0, &AOClient::cmdForceImmediate}},
{"allowiniswap", {ACLRole::CM, 0, &AOClient::cmdAllowIniswap}},
{"allow_iniswap", {ACLRole::CM, 0, &AOClient::cmdAllowIniswap}},
{"afk", {ACLRole::NONE, 0, &AOClient::cmdAfk}},
{"savetestimony", {ACLRole::NONE, 1, &AOClient::cmdSaveTestimony}},
{"loadtestimony", {ACLRole::CM, 1, &AOClient::cmdLoadTestimony}},
{"permitsaving", {ACLRole::MODCHAT, 1, &AOClient::cmdPermitSaving}},
{"mutepm", {ACLRole::NONE, 0, &AOClient::cmdMutePM}},
{"toggleadverts", {ACLRole::NONE, 0, &AOClient::cmdToggleAdverts}},
{"oocmute", {ACLRole::MUTE, 1, &AOClient::cmdOocMute}},
{"ooc_mute", {ACLRole::MUTE, 1, &AOClient::cmdOocMute}},
{"oocunmute", {ACLRole::MUTE, 1, &AOClient::cmdOocUnMute}},
{"ooc_unmute", {ACLRole::MUTE, 1, &AOClient::cmdOocUnMute}},
{"blockwtce", {ACLRole::MUTE, 1, &AOClient::cmdBlockWtce}},
{"block_wtce", {ACLRole::MUTE, 1, &AOClient::cmdBlockWtce}},
{"unblockwtce", {ACLRole::MUTE, 1, &AOClient::cmdUnBlockWtce}},
{"unblock_wtce", {ACLRole::MUTE, 1, &AOClient::cmdUnBlockWtce}},
{"blockdj", {ACLRole::MUTE, 1, &AOClient::cmdBlockDj}},
{"block_dj", {ACLRole::MUTE, 1, &AOClient::cmdBlockDj}},
{"unblockdj", {ACLRole::MUTE, 1, &AOClient::cmdUnBlockDj}},
{"unblock_dj", {ACLRole::MUTE, 1, &AOClient::cmdUnBlockDj}},
{"charcurse", {ACLRole::MUTE, 1, &AOClient::cmdCharCurse}},
{"uncharcurse", {ACLRole::MUTE, 1, &AOClient::cmdUnCharCurse}},
{"charselect", {ACLRole::NONE, 0, &AOClient::cmdCharSelect}},
{"togglemusic", {ACLRole::CM, 0, &AOClient::cmdToggleMusic}},
{"a", {ACLRole::NONE, 2, &AOClient::cmdA}},
{"s", {ACLRole::NONE, 0, &AOClient::cmdS}},
{"kickuid", {ACLRole::KICK, 2, &AOClient::cmdKickUid}},
{"kick_uid", {ACLRole::KICK, 2, &AOClient::cmdKickUid}},
{"firstperson", {ACLRole::NONE, 0, &AOClient::cmdFirstPerson}},
{"updateban", {ACLRole::BAN, 3, &AOClient::cmdUpdateBan}},
{"update_ban", {ACLRole::BAN, 3, &AOClient::cmdUpdateBan}},
{"changepass", {ACLRole::NONE, 1, &AOClient::cmdChangePassword}},
{"ignorebglist", {ACLRole::IGNORE_BGLIST, 0, &AOClient::cmdIgnoreBgList}},
{"ignore_bglist", {ACLRole::IGNORE_BGLIST, 0, &AOClient::cmdIgnoreBgList}},
{"notice", {ACLRole::SEND_NOTICE, 1, &AOClient::cmdNotice}},
{"noticeg", {ACLRole::SEND_NOTICE, 1, &AOClient::cmdNoticeGlobal}},
{"togglejukebox", {ACLRole::NONE, 0, &AOClient::cmdToggleJukebox}},
{"help", {ACLRole::NONE, 1, &AOClient::cmdHelp}},
{"clearcm", {ACLRole::KICK, 0, &AOClient::cmdClearCM}},
{"togglemessage", {ACLRole::CM, 0, &AOClient::cmdToggleAreaMessageOnJoin}},
{"clearmessage", {ACLRole::CM, 0, &AOClient::cmdClearAreaMessage}},
{"areamessage", {ACLRole::CM, 0, &AOClient::cmdAreaMessage}},
{"addsong", {ACLRole::CM, 1, &AOClient::cmdAddSong}},
{"addcategory", {ACLRole::CM, 1, &AOClient::cmdAddCategory}},
{"removeentry", {ACLRole::CM, 1, &AOClient::cmdRemoveCategorySong}},
{"toggleroot", {ACLRole::CM, 0, &AOClient::cmdToggleRootlist}},
{"clearcustom", {ACLRole::CM, 0, &AOClient::cmdClearCustom}}};
/**
* @brief Filled with part of a packet if said packet could not be read fully from the client's socket.

View File

@ -29,6 +29,8 @@
#include <QSqlError>
#include <QSqlQuery>
#include "include/acl_roles_handler.h"
/**
* @brief A class used to handle database interaction.
*
@ -138,14 +140,14 @@ class DBManager : public QObject
* @param username The username clients can use to log in with.
* @param salt The salt to obfuscate the password with.
* @param password The user's password.
* @param acl The user's authority bitflag -- what special permissions does the user have.
* @param acl The ACL role identifier.
*
* @return False if the user already exists, true if the user was successfully created.
*
* @see AOClient::cmdLogin() and AOClient::cmdLogout() for the username and password's contexts.
* @see AOClient::ACLFlags for the potential special permissions a user may have.
* @see AOClient#cmdLogin and AOClient#cmdLogout for the username and password's contexts.
* @see ACLRolesHandler for details regarding ACL roles and ACL role identifiers.
*/
bool createUser(QString username, QString salt, QString password, unsigned long long acl);
bool createUser(QString username, QString salt, QString password, QString acl);
/**
* @brief Deletes an authorised user from the database.
@ -157,16 +159,15 @@ class DBManager : public QObject
bool deleteUser(QString username);
/**
* @brief Gets the permissions of a given user.
* @brief Gets the ACL role of a given user.
*
* @param moderator_name The authorised user's name.
* @param username The authorised user's name.
*
* @return `0` if `moderator_name` is empty, `0` if such user does not exist in the Users table,
* or the primitive representation of an AOClient::ACLFlags permission matrix if neither of the above are true.
* @return The name identifier of a ACL role.
*
* @see AOClient::ACLFlags for the potential permissions a user may have.
* @see ACLRolesHandler for details about ACL roles.
*/
unsigned long long getACL(QString moderator_name);
QString getACL(QString f_username);
/**
* @brief Authenticates a given user.
@ -180,38 +181,17 @@ class DBManager : public QObject
bool authenticate(QString username, QString password);
/**
* @brief Updates the permissions of a given user.
* @brief Updates the ACL role identifier of a given user.
*
* @details This function can add or remove permissions as needed.
* `acl` determines what permissions are modified, while `mode` determines whether said permissions are
* added or removed.
* @details This function **DOES NOT** modify the ACL role itself. It is simply an identifier that determines which ACL role the user is linked to.
*
* `acl` **is not** the user's current permissions *or* the sum permissions you want for the user at the end
* -- it is the 'difference' between the user's current and desired permissions.
* @param username The username of the user to be updated.
*
* If `acl` is `"NONE"`, then no matter the mode, the user's permissions are cleared.
*
* For some practical examples, consult this example table:
*
* | Starting permissions | `acl` | `mode` | Resulting permissions |
* | -------------------: | :---------: | :-----: | :-------------------- |
* | KICK | BAN | `TRUE` | KICK, BAN |
* | BAN, KICK | BAN | `TRUE` | KICK, BAN |
* | KICK | BAN, BGLOCK | `TRUE` | KICK, BAN, BGLOCK |
* | BGLOCK, BAN, KICK | NONE | `TRUE` | NONE |
* | KICK | BAN | `FALSE` | KICK |
* | BAN, KICK | BAN | `FALSE` | KICK |
* | BGLOCK, BAN, KICK | BAN, BGLOCK | `FALSE` | KICK |
* | BGLOCK, BAN, KICK | NONE | `FALSE` | NONE |
*
* @param username The username of the user whose permissions should be updated.
* @param acl The primitive representation of the permission matrix being modified.
* @param mode If true, the permissions described in `acl` are *added* to the user;
* if false, they are removed instead.
* @param acl The ACL role identifier.
*
* @return True if the modification was successful, false if the user does not exist in the records.
*/
bool updateACL(QString username, unsigned long long acl, bool mode);
bool updateACL(QString username, QString acl);
/**
* @brief Returns a list of the recorded users' usernames, ordered by ID.

View File

@ -31,6 +31,7 @@
#include "include/aopacket.h"
class ACLRolesHandler;
class Advertiser;
class AOClient;
class AreaData;
@ -285,6 +286,8 @@ class Server : public QObject
*/
DBManager *getDatabaseManager();
ACLRolesHandler *getACLRolesHandler();
/**
* @brief The server-wide global timer.
*/
@ -508,6 +511,8 @@ class Server : public QObject
*/
DBManager *db_manager;
ACLRolesHandler *acl_roles_handler;
/**
* @brief Connects new AOClient to logger and disconnect handling.
**/

View File

@ -0,0 +1,283 @@
#include "include/acl_roles_handler.h"
#include <QDebug>
#include <QSettings>
const QString ACLRolesHandler::NONE_ID = "NONE";
const QString ACLRolesHandler::SUPER_ID = "SUPER";
const QHash<QString, ACLRole> ACLRolesHandler::readonly_roles{
{ACLRolesHandler::NONE_ID, ACLRole(ACLRole::NONE)},
{ACLRolesHandler::SUPER_ID, ACLRole(ACLRole::SUPER)},
};
const QHash<ACLRole::Permission, QString> ACLRole::permission_captions{
{
ACLRole::Permission::KICK,
"kick",
},
{
ACLRole::Permission::BAN,
"ban",
},
{
ACLRole::Permission::BGLOCK,
"lock_background",
},
{
ACLRole::Permission::MODIFY_USERS,
"modify_users",
},
{
ACLRole::Permission::CM,
"set_gamemaster",
},
{
ACLRole::Permission::GLOBAL_TIMER,
"use_global_timer",
},
{
ACLRole::Permission::EVI_MOD,
"modify_evidence",
},
{
ACLRole::Permission::MOTD,
"set_motd",
},
{
ACLRole::Permission::ANNOUNCE,
"announcer",
},
{
ACLRole::Permission::MODCHAT,
"chat_moderator",
},
{
ACLRole::Permission::MUTE,
"mute",
},
{
ACLRole::Permission::UNCM,
"remove_gamemaster",
},
{
ACLRole::Permission::SAVETEST,
"save_testimony",
},
{
ACLRole::Permission::FORCE_CHARSELECT,
"force_charselect",
},
{
ACLRole::Permission::BYPASS_LOCKS,
"bypass_locks",
},
{
ACLRole::Permission::IGNORE_BGLIST,
"ignore_bg_list",
},
{
ACLRole::Permission::SEND_NOTICE,
"send_notice",
},
{
ACLRole::Permission::JUKEBOX,
"jukebox",
},
{
ACLRole::Permission::SUPER,
"super",
},
};
ACLRole::ACLRole() {}
ACLRole::ACLRole(ACLRole::Permissions f_permissions) :
m_permissions(f_permissions)
{
}
ACLRole::~ACLRole() {}
ACLRole::Permissions ACLRole::getPermissions() const
{
return m_permissions;
}
bool ACLRole::checkPermission(Permission f_permission) const
{
if (f_permission == ACLRole::NONE) {
return true;
}
return m_permissions.testFlag(f_permission);
}
void ACLRole::setPermissions(ACLRole::Permissions f_permissions)
{
m_permissions = f_permissions;
}
void ACLRole::setPermission(Permission f_permission, bool f_mode)
{
m_permissions.setFlag(f_permission, f_mode);
}
ACLRolesHandler::ACLRolesHandler(QObject *parent) :
QObject(parent)
{}
ACLRolesHandler::~ACLRolesHandler() {}
bool ACLRolesHandler::roleExists(QString f_id)
{
f_id = f_id.toUpper();
return readonly_roles.contains(f_id) || m_roles.contains(f_id);
}
ACLRole ACLRolesHandler::getRoleById(QString f_id)
{
f_id = f_id.toUpper();
return readonly_roles.contains(f_id) ? readonly_roles.value(f_id) : m_roles.value(f_id);
}
bool ACLRolesHandler::insertRole(QString f_id, ACLRole f_role)
{
f_id = f_id.toUpper();
if (readonly_roles.contains(f_id)) {
return false;
}
m_roles.insert(f_id, f_role);
return true;
}
bool ACLRolesHandler::removeRole(QString f_id)
{
f_id = f_id.toUpper();
if (readonly_roles.contains(f_id)) {
return false;
}
else if (!m_roles.contains(f_id)) {
return false;
}
m_roles.remove(f_id);
return true;
}
void ACLRolesHandler::clearRoles()
{
m_roles.clear();
}
bool ACLRolesHandler::loadFile(QString f_file_name)
{
QSettings l_settings(f_file_name, QSettings::IniFormat);
l_settings.setIniCodec("UTF-8");
if (l_settings.status() != QSettings::NoError) {
switch (l_settings.status()) {
case QSettings::AccessError:
qWarning() << "ACLRolesHandler"
<< "error: failed to open file; aborting (" << f_file_name << ")";
break;
case QSettings::FormatError:
qWarning() << "ACLRolesHandler"
<< "error: file is malformed; aborting (" << f_file_name << ")";
break;
default:
qWarning() << "ACLRolesHandler"
<< "error: unknown error; aborting; aborting (" << f_file_name << ")";
break;
}
return false;
}
m_roles.clear();
QStringList l_role_records;
const QStringList l_group_list = l_settings.childGroups();
for (const QString &i_group : l_group_list) {
const QString l_upper_group = i_group.toUpper();
if (readonly_roles.contains(l_upper_group)) {
qWarning() << "ACLRolesHandler warning: cannot modify role;" << i_group << "is read-only";
continue;
}
l_settings.beginGroup(i_group);
if (l_role_records.contains(l_upper_group)) {
qWarning() << "ACLRolesHandler warning: role" << l_upper_group << "already exist! Overwriting.";
}
l_role_records.append(l_upper_group);
ACLRole l_role;
const QList<ACLRole::Permission> l_permissions = ACLRole::permission_captions.keys();
for (const ACLRole::Permission &i_permission : l_permissions) {
l_role.setPermission(i_permission, l_settings.value(ACLRole::permission_captions.value(i_permission), false).toBool());
}
m_roles.insert(l_upper_group, std::move(l_role));
l_settings.endGroup();
}
return true;
}
bool ACLRolesHandler::saveFile(QString f_file_name)
{
QSettings l_settings(f_file_name, QSettings::IniFormat);
l_settings.setIniCodec("UTF-8");
if (l_settings.status() != QSettings::NoError) {
switch (l_settings.status()) {
case QSettings::AccessError:
qWarning() << "ACLRolesHandler"
<< "error: failed to open file; aborting (" << f_file_name << ")";
break;
case QSettings::FormatError:
qWarning() << "ACLRolesHandler"
<< "error: file is malformed; aborting (" << f_file_name << ")";
break;
default:
qWarning() << "ACLRolesHandler"
<< "error: unknown error; aborting; aborting (" << f_file_name << ")";
break;
}
return false;
}
l_settings.clear();
const QStringList l_role_id_list = m_roles.keys();
for (const QString &l_role_id : l_role_id_list) {
const QString l_upper_role_id = l_role_id.toUpper();
if (readonly_roles.contains(l_upper_role_id)) {
continue;
}
const ACLRole i_role = m_roles.value(l_upper_role_id);
l_settings.beginGroup(l_upper_role_id);
if (i_role.checkPermission(ACLRole::SUPER)) {
l_settings.setValue(ACLRole::permission_captions.value(ACLRole::SUPER), true);
}
else {
const QList<ACLRole::Permission> l_permissions = ACLRole::permission_captions.keys();
for (const ACLRole::Permission i_permission : l_permissions) {
if (!i_role.checkPermission(i_permission)) {
continue;
}
l_settings.setValue(ACLRole::permission_captions.value(i_permission), true);
}
}
l_settings.endGroup();
}
l_settings.sync();
if (l_settings.status() != QSettings::NoError) {
qWarning() << "ACLRolesHandler"
<< "error: failed to write file; aborting (" << f_file_name << ")";
return false;
}
return true;
}

View File

@ -87,13 +87,13 @@ void AOClient::handlePacket(AOPacket packet)
qDebug() << "Received packet:" << packet.header << ":" << packet.contents << "args length:" << packet.contents.length();
#endif
AreaData *l_area = server->getAreaById(m_current_area);
PacketInfo l_info = packets.value(packet.header, {false, 0, &AOClient::pktDefault});
PacketInfo l_info = packets.value(packet.header, {ACLRole::NONE, 0, &AOClient::pktDefault});
if (packet.contents.join("").size() > 16384) {
return;
}
if (!checkAuth(l_info.acl_mask)) {
if (!checkPermission(l_info.acl_permission)) {
return;
}
@ -120,7 +120,7 @@ void AOClient::changeArea(int new_area)
sendServerMessage("You are already in area " + server->getAreaName(m_current_area));
return;
}
if (server->getAreaById(new_area)->lockStatus() == AreaData::LockStatus::LOCKED && !server->getAreaById(new_area)->invited().contains(m_id) && !checkAuth(ACLFlags.value("BYPASS_LOCKS"))) {
if (server->getAreaById(new_area)->lockStatus() == AreaData::LockStatus::LOCKED && !server->getAreaById(new_area)->invited().contains(m_id) && !checkPermission(ACLRole::BYPASS_LOCKS)) {
sendServerMessage("Area " + server->getAreaName(new_area) + " is locked.");
return;
}
@ -202,9 +202,9 @@ void AOClient::changePosition(QString new_pos)
void AOClient::handleCommand(QString command, int argc, QStringList argv)
{
CommandInfo l_info = commands.value(command, {false, -1, &AOClient::cmdDefault});
CommandInfo l_info = commands.value(command, {ACLRole::NONE, -1, &AOClient::cmdDefault});
if (!checkAuth(l_info.acl_mask)) {
if (!checkPermission(l_info.acl_permission)) {
sendServerMessage("You do not have permission to use that command.");
return;
}
@ -331,33 +331,24 @@ void AOClient::sendServerBroadcast(QString message)
server->broadcast(AOPacket("CT", {ConfigManager::serverName(), message, "1"}));
}
bool AOClient::checkAuth(unsigned long long acl_mask)
bool AOClient::checkPermission(ACLRole::Permission f_permission) const
{
#ifdef SKIP_AUTH
return true;
#endif
if (acl_mask != ACLFlags.value("NONE")) {
if (acl_mask == ACLFlags.value("CM")) {
AreaData *l_area = server->getAreaById(m_current_area);
if (l_area->owners().contains(m_id))
if (f_permission == ACLRole::NONE) {
return true;
}
else if (!m_authenticated) {
if (!isAuthenticated()) {
return false;
}
switch (ConfigManager::authType()) {
case DataTypes::AuthType::SIMPLE:
return m_authenticated;
break;
case DataTypes::AuthType::ADVANCED:
unsigned long long l_user_acl = server->getDatabaseManager()->getACL(m_moderator_name);
return (l_user_acl & acl_mask) != 0;
break;
}
}
if (ConfigManager::authType() == DataTypes::AuthType::SIMPLE) {
return true;
}
const ACLRole l_role = server->getACLRolesHandler()->getRoleById(m_acl_role_id);
return l_role.checkPermission(f_permission);
}
QString AOClient::getIpid() const
{
return m_ipid;
@ -373,6 +364,11 @@ bool AOClient::hasJoined() const
return m_joined;
}
bool AOClient::isAuthenticated() const
{
return m_authenticated;
}
Server *AOClient::getServer() { return server; }
void AOClient::onAfkTimeout()

View File

@ -78,7 +78,7 @@ void AOClient::cmdUnCM(int argc, QStringList argv)
l_uid = m_id;
sendServerMessage("You are no longer CM in this area.");
}
else if (checkAuth(ACLFlags.value("UNCM")) && argc == 1) {
else if (checkPermission(ACLRole::UNCM) && argc == 1) {
bool conv_ok = false;
l_uid = argv[0].toInt(&conv_ok);
if (!conv_ok) {
@ -366,7 +366,7 @@ void AOClient::cmdJudgeLog(int argc, QStringList argv)
}
QString l_message = l_area->judgelog().join("\n");
// Judgelog contains an IPID, so we shouldn't send that unless the caller has appropriate permissions
if (checkAuth(ACLFlags.value("KICK")) == 1 || checkAuth(ACLFlags.value("BAN")) == 1) {
if (checkPermission(ACLRole::KICK) || checkPermission(ACLRole::BAN)) {
sendServerMessage(l_message);
}
else {

View File

@ -90,7 +90,7 @@ void AOClient::cmdSetRootPass(int argc, QStringList argv)
#endif
QString l_salt = QStringLiteral("%1").arg(l_salt_number, 16, 16, QLatin1Char('0'));
server->getDatabaseManager()->createUser("root", l_salt, argv[0], ACLFlags.value("SUPER"));
server->getDatabaseManager()->createUser("root", l_salt, argv[0], ACLRolesHandler::SUPER_ID);
}
void AOClient::cmdAddUser(int argc, QStringList argv)
@ -111,8 +111,8 @@ void AOClient::cmdAddUser(int argc, QStringList argv)
#endif
QString l_salt = QStringLiteral("%1").arg(l_salt_number, 16, 16, QLatin1Char('0'));
if (server->getDatabaseManager()->createUser(argv[0], l_salt, argv[1], ACLFlags.value("NONE")))
sendServerMessage("Created user " + argv[0] + ".\nUse /addperm to modify their permissions.");
if (server->getDatabaseManager()->createUser(argv[0], l_salt, argv[1], ACLRolesHandler::NONE_ID))
sendServerMessage("Created user " + argv[0] + ".\nUse /setperms to modify their permissions.");
else
sendServerMessage("Unable to create user " + argv[0] + ".\nDoes a user with that name already exist?");
}
@ -129,125 +129,73 @@ void AOClient::cmdRemoveUser(int argc, QStringList argv)
void AOClient::cmdListPerms(int argc, QStringList argv)
{
unsigned long long l_user_acl = server->getDatabaseManager()->getACL(m_moderator_name);
const ACLRole l_role = server->getACLRolesHandler()->getRoleById(m_acl_role_id);
ACLRole l_target_role = l_role;
QStringList l_message;
const QStringList l_keys = ACLFlags.keys();
if (argc == 0) {
// Just print out all permissions available to the user.
l_message.append("You have been given the following permissions:");
for (const QString &l_perm : l_keys) {
if (l_perm == "NONE")
; // don't need to list this one
else if (l_perm == "SUPER") {
if (l_user_acl == ACLFlags.value("SUPER")) // This has to be checked separately, because SUPER & anything will always be truthy
l_message.append("SUPER (Be careful! This grants the user all permissions.)");
}
else if ((ACLFlags.value(l_perm) & l_user_acl) == 0)
; // user doesn't have this permission, don't print it
else
l_message.append(l_perm);
}
}
else {
if ((l_user_acl & ACLFlags.value("MODIFY_USERS")) == 0) {
if (!l_role.checkPermission(ACLRole::MODIFY_USERS)) {
sendServerMessage("You do not have permission to view other users' permissions.");
return;
}
l_message.append("User " + argv[0] + " has the following permissions:");
unsigned long long l_acl = server->getDatabaseManager()->getACL(argv[0]);
if (l_acl == 0) {
sendServerMessage("This user either doesn't exist, or has no permissions set.");
return;
l_target_role = server->getACLRolesHandler()->getRoleById(argv[0]);
}
for (const QString &l_perm : l_keys) {
if ((ACLFlags.value(l_perm) & l_acl) != 0 && l_perm != "SUPER") {
l_message.append(l_perm);
if (l_target_role.getPermissions() == ACLRole::NONE) {
l_message.append("NONE");
}
else if (l_target_role.checkPermission(ACLRole::SUPER)) {
l_message.append("SUPER (Be careful! This grants the user all permissions.)");
}
else {
const QList<ACLRole::Permission> l_permissions = ACLRole::permission_captions.keys();
for (const ACLRole::Permission i_permission : l_permissions) {
if (l_target_role.checkPermission(i_permission)) {
l_message.append(ACLRole::permission_captions.value(i_permission));
}
}
}
sendServerMessage(l_message.join("\n"));
}
void AOClient::cmdAddPerms(int argc, QStringList argv)
void AOClient::cmdSetPerms(int argc, QStringList argv)
{
Q_UNUSED(argc);
unsigned long long l_user_acl = server->getDatabaseManager()->getACL(m_moderator_name);
argv[1] = argv[1].toUpper();
const QStringList l_keys = ACLFlags.keys();
if (!l_keys.contains(argv[1])) {
sendServerMessage("That permission doesn't exist!");
const QString l_target_acl = argv[1];
if (!server->getACLRolesHandler()->roleExists(l_target_acl)) {
sendServerMessage("That role doesn't exist!");
return;
}
if (argv[1] == "SUPER") {
if (l_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!");
if (l_target_acl == ACLRolesHandler::SUPER_ID && !checkPermission(ACLRole::SUPER)) {
sendServerMessage("You aren't allowed to set that role!");
return;
}
unsigned long long l_newperm = ACLFlags.value(argv[1]);
if ((l_newperm & l_user_acl) != 0) {
if (server->getDatabaseManager()->updateACL(argv[0], l_newperm, true))
sendServerMessage("Successfully added permission " + argv[1] + " to user " + argv[0]);
else
sendServerMessage(argv[0] + " wasn't found!");
const QString l_target_username = argv[0];
if (l_target_username == "root") {
sendServerMessage("You can't change root's role!");
return;
}
sendServerMessage("You aren't allowed to add that permission!");
if (server->getDatabaseManager()->updateACL(l_target_username, l_target_acl)) {
sendServerMessage("Successfully changed role " + l_target_acl + " to user " + l_target_username);
}
else {
sendServerMessage(l_target_username + " wasn't found!");
}
}
void AOClient::cmdRemovePerms(int argc, QStringList argv)
{
Q_UNUSED(argc);
unsigned long long l_user_acl = server->getDatabaseManager()->getACL(m_moderator_name);
argv[1] = argv[1].toUpper();
const QStringList l_keys = ACLFlags.keys();
if (!l_keys.contains(argv[1])) {
sendServerMessage("That permission doesn't exist!");
return;
}
if (argv[0] == "root") {
sendServerMessage("You cannot change the permissions of the root account!");
return;
}
if (argv[1] == "SUPER") {
if (l_user_acl != ACLFlags.value("SUPER")) {
// This has to be checked separately, because SUPER & anything will always be truthy
sendServerMessage("You aren't allowed to remove that permission!");
return;
}
}
if (argv[1] == "NONE") {
sendServerMessage("Removed no permissions!");
return;
}
unsigned long long l_newperm = ACLFlags.value(argv[1]);
if ((l_newperm & l_user_acl) != 0) {
if (server->getDatabaseManager()->updateACL(argv[0], l_newperm, false))
sendServerMessage("Successfully removed permission " + argv[1] + " from user " + argv[0]);
else
sendServerMessage(argv[0] + " wasn't found!");
return;
}
sendServerMessage("You aren't allowed to remove that permission!");
argv.append(ACLRolesHandler::NONE_ID);
cmdSetPerms(argc, argv);
}
void AOClient::cmdListUsers(int argc, QStringList argv)
@ -269,6 +217,7 @@ void AOClient::cmdLogout(int argc, QStringList argv)
return;
}
m_authenticated = false;
m_acl_role_id = "";
m_moderator_name = "";
sendPacket("AUTH", {"-1"}); // Client: "You were logged out."
}
@ -284,8 +233,9 @@ void AOClient::cmdChangePassword(int argc, QStringList argv)
}
l_username = m_moderator_name;
}
else if (argc == 2 && checkAuth(ACLFlags.value("SUPER"))) {
l_username = argv[1];
else if (argc == 2 && checkPermission(ACLRole::SUPER)) {
l_username = argv[0];
l_password = argv[1];
}
else {
sendServerMessage("Invalid command syntax.");

View File

@ -214,7 +214,7 @@ void AOClient::cmdSaveTestimony(int argc, QStringList argv)
Q_UNUSED(argc);
bool l_permission_found = false;
if (checkAuth(ACLFlags.value("SAVETEST")))
if (checkPermission(ACLRole::SAVETEST))
l_permission_found = true;
if (m_testimony_saving == true)

View File

@ -479,7 +479,7 @@ void AOClient::cmdCharSelect(int argc, QStringList argv)
sendPacket("DONE");
}
else {
if (!checkAuth(ACLFlags.value("FORCE_CHARSELECT"))) {
if (!checkPermission(ACLRole::FORCE_CHARSELECT)) {
sendServerMessage("You do not have permission to force another player to character select!");
return;
}

View File

@ -141,8 +141,10 @@ void AOClient::cmdMods(int argc, QStringList argv)
for (AOClient *l_client : l_clients) {
if (l_client->m_authenticated) {
l_entries << "---";
if (ConfigManager::authType() != DataTypes::AuthType::SIMPLE)
if (ConfigManager::authType() != DataTypes::AuthType::SIMPLE) {
l_entries << "Moderator: " + l_client->m_moderator_name;
l_entries << "Role:" << l_client->m_acl_role_id;
}
l_entries << "OOC name: " + l_client->m_ooc_name;
l_entries << "ID: " + QString::number(l_client->m_id);
l_entries << "Area: " + QString::number(l_client->m_current_area);
@ -165,7 +167,7 @@ void AOClient::cmdCommands(int argc, QStringList argv)
QMap<QString, CommandInfo>::const_iterator i;
for (i = commands.constBegin(); i != commands.constEnd(); ++i) {
CommandInfo info = i.value();
if (checkAuth(info.acl_mask)) { // if we are allowed to use this command
if (checkPermission(info.acl_permission)) { // if we are allowed to use this command
l_entries << "/" + i.key();
}
}
@ -193,7 +195,7 @@ void AOClient::cmdMOTD(int argc, QStringList argv)
sendServerMessage("=== MOTD ===\r\n" + ConfigManager::motd() + "\r\n=============");
}
else if (argc > 0) {
if (checkAuth(ACLFlags.value("MOTD"))) {
if (checkPermission(ACLRole::MOTD)) {
QString l_MOTD = argv.join(" ");
ConfigManager::setMotd(l_MOTD);
sendServerMessage("MOTD has been changed.");

View File

@ -127,7 +127,7 @@ void AOClient::cmdToggleJukebox(int argc, QStringList argv)
Q_UNUSED(argc);
Q_UNUSED(argv);
if (checkAuth(ACLFlags.value("CM")) | checkAuth(ACLFlags.value("Jukebox"))) {
if (checkPermission(ACLRole::CM) | checkPermission(ACLRole::JUKEBOX)) {
AreaData *l_area = server->getAreaById(m_current_area);
l_area->toggleJukebox();
QString l_state = l_area->isjukeboxEnabled() ? "enabled." : "disabled.";

View File

@ -84,7 +84,7 @@ void AOClient::cmdTimer(int argc, QStringList argv)
// Check against permissions if global timer is selected
QTimer *l_requested_timer;
if (l_timer_id == 0) {
if (!checkAuth(ACLFlags.value("GLOBAL_TIMER"))) {
if (!checkPermission(ACLRole::GLOBAL_TIMER)) {
sendServerMessage("You are not authorized to alter the global timer.");
return;
}

View File

@ -171,11 +171,11 @@ bool DBManager::invalidateBan(int id)
return true;
}
bool DBManager::createUser(QString username, QString salt, QString password, unsigned long long acl)
bool DBManager::createUser(QString f_username, QString f_salt, QString f_password, QString f_acl)
{
QSqlQuery username_exists;
username_exists.prepare("SELECT ACL FROM users WHERE USERNAME = ?");
username_exists.addBindValue(username);
username_exists.addBindValue(f_username);
username_exists.exec();
if (username_exists.first())
@ -185,15 +185,15 @@ bool DBManager::createUser(QString username, QString salt, QString password, uns
QString salted_password;
QMessageAuthenticationCode hmac(QCryptographicHash::Sha256);
hmac.setKey(salt.toUtf8());
hmac.addData(password.toUtf8());
hmac.setKey(f_salt.toUtf8());
hmac.addData(f_password.toUtf8());
salted_password = hmac.result().toHex();
query.prepare("INSERT INTO users(USERNAME, SALT, PASSWORD, ACL) VALUES(?, ?, ?, ?)");
query.addBindValue(username);
query.addBindValue(salt);
query.addBindValue(f_username);
query.addBindValue(f_salt);
query.addBindValue(salted_password);
query.addBindValue(acl);
query.addBindValue(f_acl);
query.exec();
return true;
@ -226,7 +226,7 @@ bool DBManager::deleteUser(QString username)
}
}
unsigned long long DBManager::getACL(QString moderator_name)
QString DBManager::getACL(QString moderator_name)
{
if (moderator_name == "")
return 0;
@ -235,7 +235,7 @@ unsigned long long DBManager::getACL(QString moderator_name)
query.exec();
if (!query.first())
return 0;
return query.value(0).toULongLong();
return query.value(0).toString();
}
bool DBManager::authenticate(QString username, QString password)
@ -263,30 +263,21 @@ bool DBManager::authenticate(QString username, QString password)
return salted_password == stored_pass;
}
bool DBManager::updateACL(QString username, unsigned long long acl, bool mode)
bool DBManager::updateACL(QString f_username, QString f_acl)
{
QSqlQuery username_exists;
username_exists.prepare("SELECT ACL FROM users WHERE USERNAME = ?");
username_exists.addBindValue(username);
username_exists.exec();
QSqlQuery l_username_exists;
l_username_exists.prepare("SELECT ACL FROM users WHERE USERNAME = ?");
l_username_exists.addBindValue(f_username);
l_username_exists.exec();
if (!username_exists.first())
if (!l_username_exists.first())
return false;
unsigned long long old_acl = username_exists.value(0).toULongLong();
unsigned long long new_acl;
if (mode) // adding perm
new_acl = old_acl | acl;
else // removing perm
new_acl = old_acl & ~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();
QSqlQuery l_update_acl;
l_update_acl.prepare("UPDATE users SET ACL = ? WHERE USERNAME = ?");
l_update_acl.addBindValue(f_acl);
l_update_acl.addBindValue(f_username);
l_update_acl.exec();
return true;
}

View File

@ -308,7 +308,7 @@ void AOClient::pktChangeMusic(AreaData *area, int argc, QStringList argv, AOPack
sendServerMessage("You are blocked from changing the music.");
return;
}
if (!area->isMusicAllowed() && !checkAuth(ACLFlags.value("CM"))) {
if (!area->isMusicAllowed() && !checkPermission(ACLRole::CM)) {
sendServerMessage("Music is disabled in this area.");
return;
}
@ -613,7 +613,7 @@ void AOClient::updateEvidenceList(AreaData *area)
const QList<AreaData::Evidence> l_area_evidence = area->evidence();
for (const AreaData::Evidence &evidence : l_area_evidence) {
if (!checkAuth(ACLFlags.value("CM")) && area->eviMod() == AreaData::EvidenceMod::HIDDEN_CM) {
if (!checkPermission(ACLRole::CM) && area->eviMod() == AreaData::EvidenceMod::HIDDEN_CM) {
QRegularExpression l_regex("<owner=(.*?)>");
QRegularExpressionMatch l_match = l_regex.match(evidence.description);
if (l_match.hasMatch()) {
@ -648,7 +648,7 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// Spectators cannot use IC
return l_invalid;
AreaData *area = server->getAreaById(m_current_area);
if (area->lockStatus() == AreaData::LockStatus::SPECTATABLE && !area->invited().contains(m_id) && !checkAuth(ACLFlags.value("BYPASS_LOCKS")))
if (area->lockStatus() == AreaData::LockStatus::SPECTATABLE && !area->invited().contains(m_id) && !checkPermission(ACLRole::BYPASS_LOCKS))
// Non-invited players cannot speak in spectatable areas
return l_invalid;
@ -1007,7 +1007,7 @@ bool AOClient::checkEvidenceAccess(AreaData *area)
return true;
case AreaData::EvidenceMod::CM:
case AreaData::EvidenceMod::HIDDEN_CM:
return checkAuth(ACLFlags.value("CM"));
return checkPermission(ACLRole::CM);
case AreaData::EvidenceMod::MOD:
return m_authenticated;
default:
@ -1043,6 +1043,7 @@ void AOClient::loginAttempt(QString message)
sendPacket("AUTH", {"1"}); // Client: "You were granted the Disable Modcalls button."
sendServerMessage("Logged in as a moderator."); // pre-2.9.1 clients are hardcoded to display the mod UI when this string is sent in OOC
m_authenticated = true;
m_acl_role_id = ACLRolesHandler::SUPER_ID;
}
else {
sendPacket("AUTH", {"0"}); // Client: "Login unsuccessful."
@ -1062,8 +1063,9 @@ void AOClient::loginAttempt(QString message)
QString username = l_login[0];
QString password = l_login[1];
if (server->getDatabaseManager()->authenticate(username, password)) {
m_moderator_name = username;
m_authenticated = true;
m_acl_role_id = server->getDatabaseManager()->getACL(username);
m_moderator_name = username;
sendPacket("AUTH", {"1"}); // Client: "You were granted the Disable Modcalls button."
if (m_version.release <= 2 && m_version.major <= 9 && m_version.minor <= 0)
sendServerMessage("Logged in as a moderator."); // pre-2.9.1 clients are hardcoded to display the mod UI when this string is sent in OOC

View File

@ -17,6 +17,7 @@
//////////////////////////////////////////////////////////////////////////////////////
#include "include/server.h"
#include "include/acl_roles_handler.h"
#include "include/advertiser.h"
#include "include/aoclient.h"
#include "include/aopacket.h"
@ -42,7 +43,10 @@ Server::Server(int p_port, int p_ws_port, QObject *parent) :
proxy->start();
timer = new QTimer(this);
db_manager = new DBManager();
db_manager = new DBManager;
acl_roles_handler = new ACLRolesHandler;
acl_roles_handler->loadFile("config/acl_roles.ini");
// We create it, even if its not used later on.
discord = new Discord(this);
@ -304,7 +308,7 @@ void Server::broadcast(AOPacket packet, TARGET_TYPE target)
for (AOClient *l_client : qAsConst(m_clients)) {
switch (target) {
case TARGET_TYPE::MODCHAT:
if (l_client->checkAuth(l_client->ACLFlags.value("MODCHAT"))) {
if (l_client->checkPermission(ACLRole::MODCHAT)) {
l_client->sendPacket(packet);
}
break;
@ -324,7 +328,7 @@ void Server::broadcast(AOPacket packet, AOPacket other_packet, TARGET_TYPE targe
switch (target) {
case TARGET_TYPE::AUTHENTICATED:
for (AOClient *l_client : qAsConst(m_clients)) {
if (l_client->m_authenticated) {
if (l_client->isAuthenticated()) {
l_client->sendPacket(other_packet);
}
else {
@ -454,6 +458,11 @@ DBManager *Server::getDatabaseManager()
return db_manager;
}
ACLRolesHandler *Server::getACLRolesHandler()
{
return acl_roles_handler;
}
void Server::allowMessage()
{
m_can_send_ic_messages = true;
@ -529,6 +538,7 @@ Server::~Server()
server->deleteLater();
proxy->deleteLater();
discord->deleteLater();
acl_roles_handler->deleteLater();
delete db_manager;
}

View File

@ -2,4 +2,5 @@ TEMPLATE = subdirs
SUBDIRS += \
unittest_area \
unittest_music_manager
unittest_music_manager \
unittest_acl_roles_handler

View File

@ -0,0 +1,193 @@
#include <QDebug>
#include <QFile>
#include <QTest>
#include <include/acl_roles_handler.h>
namespace tests {
namespace unittests {
/**
* @brief Unit Tester class for ACL roles-related functions.
*/
class tst_ACLRolesHandler : public QObject
{
Q_OBJECT
public:
ACLRolesHandler *m_handler;
private slots:
/**
* @brief Initialises every tests with creating a new ACLRolesHandler.
*/
void init();
/**
* @brief Tests the general state of read-only roles.
*/
void checkReadOnlyRoles();
/**
* @brief Tests removal of read-only roles.
*/
void removeReadOnlyRoles();
/**
* @brief Tests general modifications of read-only roles.
*/
void replaceReadOnlyRoles();
/**
* @brief Tests general modifications of roles.
*/
void modifyRoles();
/**
* @brief Tests clearance of roles.
*/
void clearAllRoles();
};
void tst_ACLRolesHandler::init()
{
m_handler = new ACLRolesHandler;
}
void tst_ACLRolesHandler::checkReadOnlyRoles()
{
{
const QString l_role_name = ACLRolesHandler::NONE_ID;
// Checks if the role exists
QCOMPARE(m_handler->roleExists(ACLRolesHandler::NONE_ID), true);
ACLRole l_role = m_handler->getRoleById(ACLRolesHandler::NONE_ID);
// Checks every permissions
QCOMPARE(l_role.checkPermission(ACLRole::NONE), true);
QCOMPARE(l_role.checkPermission(ACLRole::KICK), false);
QCOMPARE(l_role.checkPermission(ACLRole::BAN), false);
QCOMPARE(l_role.checkPermission(ACLRole::BGLOCK), false);
QCOMPARE(l_role.checkPermission(ACLRole::MODIFY_USERS), false);
QCOMPARE(l_role.checkPermission(ACLRole::CM), false);
QCOMPARE(l_role.checkPermission(ACLRole::GLOBAL_TIMER), false);
QCOMPARE(l_role.checkPermission(ACLRole::EVI_MOD), false);
QCOMPARE(l_role.checkPermission(ACLRole::MOTD), false);
QCOMPARE(l_role.checkPermission(ACLRole::ANNOUNCE), false);
QCOMPARE(l_role.checkPermission(ACLRole::MODCHAT), false);
QCOMPARE(l_role.checkPermission(ACLRole::MUTE), false);
QCOMPARE(l_role.checkPermission(ACLRole::UNCM), false);
QCOMPARE(l_role.checkPermission(ACLRole::SAVETEST), false);
QCOMPARE(l_role.checkPermission(ACLRole::FORCE_CHARSELECT), false);
QCOMPARE(l_role.checkPermission(ACLRole::BYPASS_LOCKS), false);
QCOMPARE(l_role.checkPermission(ACLRole::IGNORE_BGLIST), false);
QCOMPARE(l_role.checkPermission(ACLRole::SEND_NOTICE), false);
QCOMPARE(l_role.checkPermission(ACLRole::JUKEBOX), false);
QCOMPARE(l_role.checkPermission(ACLRole::SUPER), false);
}
{
const QString l_role_name = ACLRolesHandler::SUPER_ID;
// Checks if the role exists
QCOMPARE(m_handler->roleExists(l_role_name), true);
ACLRole l_role = m_handler->getRoleById(l_role_name);
// Checks every permissions
QCOMPARE(l_role.checkPermission(ACLRole::NONE), true);
QCOMPARE(l_role.checkPermission(ACLRole::KICK), true);
QCOMPARE(l_role.checkPermission(ACLRole::BAN), true);
QCOMPARE(l_role.checkPermission(ACLRole::BGLOCK), true);
QCOMPARE(l_role.checkPermission(ACLRole::MODIFY_USERS), true);
QCOMPARE(l_role.checkPermission(ACLRole::CM), true);
QCOMPARE(l_role.checkPermission(ACLRole::GLOBAL_TIMER), true);
QCOMPARE(l_role.checkPermission(ACLRole::EVI_MOD), true);
QCOMPARE(l_role.checkPermission(ACLRole::MOTD), true);
QCOMPARE(l_role.checkPermission(ACLRole::ANNOUNCE), true);
QCOMPARE(l_role.checkPermission(ACLRole::MODCHAT), true);
QCOMPARE(l_role.checkPermission(ACLRole::MUTE), true);
QCOMPARE(l_role.checkPermission(ACLRole::UNCM), true);
QCOMPARE(l_role.checkPermission(ACLRole::SAVETEST), true);
QCOMPARE(l_role.checkPermission(ACLRole::FORCE_CHARSELECT), true);
QCOMPARE(l_role.checkPermission(ACLRole::BYPASS_LOCKS), true);
QCOMPARE(l_role.checkPermission(ACLRole::IGNORE_BGLIST), true);
QCOMPARE(l_role.checkPermission(ACLRole::SEND_NOTICE), true);
QCOMPARE(l_role.checkPermission(ACLRole::JUKEBOX), true);
QCOMPARE(l_role.checkPermission(ACLRole::SUPER), true);
}
}
void tst_ACLRolesHandler::removeReadOnlyRoles()
{
{ // SUPER role
// Removes the role. This should fail.
QCOMPARE(m_handler->removeRole(ACLRolesHandler::SUPER_ID), false);
// Checks if the role exists.
QCOMPARE(m_handler->roleExists(ACLRolesHandler::SUPER_ID), true);
}
}
void tst_ACLRolesHandler::replaceReadOnlyRoles()
{
{
// Attempts to overwrite a read-only role. This should fail.
QCOMPARE(m_handler->insertRole(ACLRolesHandler::NONE_ID, ACLRole(ACLRole::NONE)), false);
}
}
void tst_ACLRolesHandler::modifyRoles()
{
{
const QString l_role_id = "new_role";
// Checks if a the role exists. This should fail.
QCOMPARE(m_handler->roleExists(l_role_id), false);
// Inserts a role.
QCOMPARE(m_handler->insertRole(l_role_id, ACLRole(ACLRole::KICK)), true);
// Inserts a role again.
QCOMPARE(m_handler->insertRole(l_role_id, ACLRole(ACLRole::MODIFY_USERS)), true);
// Checks if the role exists.
QCOMPARE(m_handler->roleExists(l_role_id), true);
const ACLRole l_role = m_handler->getRoleById(l_role_id);
// Checks every permissions
QCOMPARE(l_role.checkPermission(ACLRole::NONE), true);
QCOMPARE(l_role.checkPermission(ACLRole::KICK), false);
QCOMPARE(l_role.checkPermission(ACLRole::MODIFY_USERS), true);
QCOMPARE(l_role.checkPermission(ACLRole::SUPER), false);
// Removes the role.
QCOMPARE(m_handler->removeRole(l_role_id), true);
// Removes the role again. This should fail.
QCOMPARE(m_handler->removeRole(l_role_id), false);
// Checks if the role exists. This should fail.
QCOMPARE(m_handler->roleExists(l_role_id), false);
}
}
void tst_ACLRolesHandler::clearAllRoles()
{
{
const QString l_role_id = "new_role";
// Inserts a role.
QCOMPARE(m_handler->insertRole(l_role_id, ACLRole(ACLRole::KICK)), true);
m_handler->clearRoles();
// Checks if a the role exists. This should fail.
QCOMPARE(m_handler->roleExists(l_role_id), false);
}
}
}
}
QTEST_APPLESS_MAIN(tests::unittests::tst_ACLRolesHandler)
#include "tst_unittest_acl_roles_handler.moc"

View File

@ -0,0 +1,5 @@
QT -= gui
include(../tests_common.pri)
SOURCES += tst_unittest_acl_roles_handler.cpp