From 6f2d8ab6e40c382df0de28087955de662fcc229e Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 26 Feb 2021 21:32:57 +0100 Subject: [PATCH] Introduced the new group manager --- server/CMakeLists.txt | 4 + server/src/DatabaseHelper.cpp | 34 +- server/src/DatabaseHelper.h | 4 +- server/src/Group.cpp | 8 +- server/src/InstanceHandler.cpp | 28 +- server/src/InstanceHandler.h | 4 +- server/src/InstanceHandlerSetup.cpp | 2 +- server/src/TS3ServerHeartbeat.cpp | 2 +- server/src/VirtualServer.cpp | 26 +- server/src/channel/ServerChannel.cpp | 2 +- .../client/ConnectedClientNotifyHandler.cpp | 4 +- server/src/client/DataClient.cpp | 2 +- server/src/client/command_handler/channel.cpp | 14 +- server/src/client/command_handler/server.cpp | 26 +- server/src/client/query/QueryClient.cpp | 2 +- .../src/client/query/QueryClientCommands.cpp | 10 +- server/src/groups/Group.cpp | 30 ++ server/src/groups/Group.h | 86 +++ server/src/groups/GroupAssignmentManager.cpp | 497 ++++++++++++++++++ server/src/groups/GroupAssignmentManager.h | 106 ++++ server/src/groups/GroupManager.cpp | 346 ++++++++++++ server/src/groups/GroupManager.h | 247 +++++++++ server/src/server/QueryServer.cpp | 2 +- 23 files changed, 1395 insertions(+), 91 deletions(-) create mode 100644 server/src/groups/Group.cpp create mode 100644 server/src/groups/Group.h create mode 100644 server/src/groups/GroupAssignmentManager.cpp create mode 100644 server/src/groups/GroupAssignmentManager.h create mode 100644 server/src/groups/GroupManager.cpp create mode 100644 server/src/groups/GroupManager.h diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index d47cb89..40102cb 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -134,6 +134,10 @@ set(SERVER_SOURCE_FILES src/snapshots/music.cpp src/snapshots/parser.cpp + src/groups/Group.cpp + src/groups/GroupManager.cpp + src/groups/GroupAssignmentManager.cpp + src/manager/ActionLogger.cpp src/manager/ActionLoggerImpl.cpp diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index 347b912..81a3fc0 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -167,14 +167,13 @@ void DatabaseHelper::deleteClient(const std::shared_ptr& server, } inline sql::result load_permissions_v2( - const std::shared_ptr& server, + const ServerId& server_id, v2::PermissionManager* manager, sql::command& command, bool test_channel, /* only used for client permissions (client channel permissions) */ bool is_channel) { auto start = system_clock::now(); - auto server_id = server ? server->getServerId() : 0; return command.query([&](int length, char** values, char** names){ permission::PermissionType key = permission::PermissionType::undefined; permission::PermissionValue value = permNotGranted, granted = permNotGranted; @@ -209,16 +208,6 @@ inline sql::result load_permissions_v2( logError(server_id, "[SQL] Cant load permissions! Failed to parse value! Command: {} Message: {} Key: {} Value: {}", command.sqlCommand(), ex.what(),names[index], values[index]); return 0; } - if(channel_id > 0 && test_channel) { - if(!server) - logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); - else { - auto channel = server->getChannelTree()->findChannel(channel_id); - if(!channel) - logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); - } - } - if(key == permission::undefined) { debugMessage(server_id, "[SQL] Permission entry misses permission type! Command: {}", command.sqlCommand()); @@ -306,7 +295,7 @@ std::shared_ptr DatabaseHelper::loadClientPermissionManag variable{":serverId", server ? server->getServerId() : 0}, variable{":type", permission::SQL_PERM_USER}, variable{":id", cldbid}); - LOG_SQL_CMD(load_permissions_v2(server, permission_manager.get(), command, true, false)); + LOG_SQL_CMD(load_permissions_v2(server_id, permission_manager.get(), command, true, false)); } @@ -368,14 +357,14 @@ void DatabaseHelper::saveClientPermissions(const std::shared_ptr DatabaseHelper::loadGroupPermissions(const std::shared_ptr& server, ts::GroupId group_id) { +std::shared_ptr DatabaseHelper::loadGroupPermissions(const ServerId& server_id, ts::GroupId group_id, uint8_t /* target */) { auto result = std::make_shared(); - if(this->use_startup_cache && server) { + if(this->use_startup_cache && server_id > 0) { shared_ptr entry; { threads::MutexLock lock(this->startup_lock); for(const auto& entries : this->startup_entries) { - if(entries->sid == server->getServerId()) { + if(entries->sid == server_id) { entry = entries; break; } @@ -393,19 +382,18 @@ std::shared_ptr DatabaseHelper::loadGroupPerm //7931 auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, + variable{":serverId", server_id}, variable{":type", permission::SQL_PERM_GROUP}, variable{":id", group_id}); - LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false, false)); + LOG_SQL_CMD(load_permissions_v2(server_id, result.get(), command, false, false)); return result; } -void DatabaseHelper::saveGroupPermissions(const std::shared_ptr &server, ts::GroupId group_id, const std::shared_ptr &permissions) { +void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId group_id, uint8_t /* target */, const std::shared_ptr &permissions) { const auto updates = permissions->flush_db_updates(); if(updates.empty()) return; - auto server_id = server ? server->getServerId() : 0; for(auto& update : updates) { std::string query{update.flag_delete ? kPermissionDeleteCommand : (update.flag_db ? kPermissionUpdateCommand : kPermissionInsertCommand)}; @@ -420,7 +408,7 @@ void DatabaseHelper::saveGroupPermissions(const std::shared_ptrsql, query, - variable{":serverId", server ? server->getServerId() : 0}, + variable{":serverId", server_id}, variable{":id", group_id}, variable{":chId", 0}, variable{":type", permission::SQL_PERM_GROUP}, @@ -468,7 +456,7 @@ std::shared_ptr DatabaseHelper::loadPlaylistP variable{":serverId", server ? server->getServerId() : 0}, variable{":type", permission::SQL_PERM_PLAYLIST}, variable{":id", playlist_id}); - LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false, false)); + LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false, false)); return result; } @@ -534,7 +522,7 @@ std::shared_ptr DatabaseHelper::loadChannelPe variable{":chid", channel}, variable{":id", 0}, variable{":type", permission::SQL_PERM_CHANNEL}); - LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false, true)); + LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false, true)); return result; } diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index a84f1f3..7c2d321 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -98,8 +98,8 @@ namespace ts::server { std::shared_ptr loadChannelPermissions(const std::shared_ptr&, ChannelId); void saveChannelPermissions(const std::shared_ptr&, ChannelId, const std::shared_ptr& /* permission manager */); - std::shared_ptr loadGroupPermissions(const std::shared_ptr&, GroupId); - void saveGroupPermissions(const std::shared_ptr&, GroupId, const std::shared_ptr& /* permission manager */); + std::shared_ptr loadGroupPermissions(const ServerId& server_id, GroupId, uint8_t /* target */); + void saveGroupPermissions(const ServerId&, GroupId, uint8_t /* target */, const std::shared_ptr& /* permission manager */); std::shared_ptr loadPlaylistPermissions(const std::shared_ptr&, PlaylistId /* playlist id */); void savePlaylistPermissions(const std::shared_ptr&, PlaylistId, const std::shared_ptr& /* permission manager */); diff --git a/server/src/Group.cpp b/server/src/Group.cpp index 82f5966..ab8507d 100644 --- a/server/src/Group.cpp +++ b/server/src/Group.cpp @@ -224,7 +224,7 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) { group->properties()[property::GROUP_NAME] = targetName; - group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->server.lock(), group->groupId())); + group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0)); debugMessage(this->getServerId(), "Push back group -> " + to_string(group->groupId()) + " - " + group->name()); this->groups.push_back(group); @@ -326,7 +326,7 @@ std::shared_ptr GroupManager::createGroup(GroupTarget target, GroupType t std::shared_ptr group = std::make_shared(this, target, type, groupId); group->properties()[property::GROUP_NAME] = name; - group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->server.lock(), group->groupId())); + group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0)); std::lock_guard glock{this->group_lock}; this->groups.push_back(group); @@ -368,7 +368,7 @@ bool GroupManager::copyGroupPermissions(const shared_ptr &source, const s res = sql::command(this->sql, "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source", variable{":ssid", sourceServer}, variable{":tsid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":source", source->groupId()}, variable{":target", target->groupId()}).execute(); - target->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(target->handle->server.lock(), target->groupId())); + target->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(target->handle->getServerId(), target->groupId(), 0)); LOG_SQL_CMD(res); return true; } @@ -379,7 +379,7 @@ bool GroupManager::reloadGroupPermissions(std::shared_ptr group) { return false; } - group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->server.lock(), group->groupId())); + group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0)); return true; } diff --git a/server/src/InstanceHandler.cpp b/server/src/InstanceHandler.cpp index 4c5baef..5d24268 100644 --- a/server/src/InstanceHandler.cpp +++ b/server/src/InstanceHandler.cpp @@ -128,10 +128,10 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { dynamic_pointer_cast(this->_musicRoot)->initialize_weak_reference(this->_musicRoot); { - this->groupManager = std::make_shared(nullptr, this->getSql()); - this->groupManager->loadGroupFormDatabase(); + this->__old__groupManager = std::make_shared(nullptr, this->getSql()); + this->__old__groupManager->loadGroupFormDatabase(); - if (this->groupManager->availableServerGroups(false).empty()) { + if (this->__old__groupManager->availableServerGroups(false).empty()) { if(!this->setupDefaultGroups()){ logCritical(LOG_INSTANCE, "Could not setup server instance! Stopping..."); mainThreadActive = false; @@ -140,24 +140,24 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { } debugMessage(LOG_INSTANCE, "Instance admin group id " + to_string(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as())); - auto instance_server_admin = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as()); + auto instance_server_admin = this->__old__groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as()); if (!instance_server_admin) { - instance_server_admin = this->groupManager->availableServerGroups(false).front(); + instance_server_admin = this->__old__groupManager->availableServerGroups(false).front(); logCritical(LOG_INSTANCE, "Missing instance server admin group! Using first available (" + (instance_server_admin ? instance_server_admin->name() : "nil") + ")"); } - if(this->groupManager->listGroupAssignments(this->globalServerAdmin->getClientDatabaseId()).empty()) - this->groupManager->addServerGroup(this->globalServerAdmin->getClientDatabaseId(), instance_server_admin); + if(this->__old__groupManager->listGroupAssignments(this->globalServerAdmin->getClientDatabaseId()).empty()) + this->__old__groupManager->addServerGroup(this->globalServerAdmin->getClientDatabaseId(), instance_server_admin); debugMessage(LOG_INSTANCE, "Server guest group id " + to_string(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as())); - auto instance_server_guest = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save()); + auto instance_server_guest = this->__old__groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save()); if (!instance_server_guest) { - instance_server_guest = this->groupManager->availableServerGroups(false).front(); + instance_server_guest = this->__old__groupManager->availableServerGroups(false).front(); logCritical(LOG_INSTANCE, "Missing instance server guest group! Using first available (" + (instance_server_guest ? instance_server_guest->name() : "nil") + ")"); } this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] = instance_server_guest->groupId(); debugMessage(LOG_INSTANCE, "Server music group id " + to_string(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as())); - auto instance_server_music = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save()); + auto instance_server_music = this->__old__groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save()); if (!instance_server_music) { instance_server_music = instance_server_guest; logCritical(LOG_INSTANCE, "Missing instance server music group! Using serverguest (" + (instance_server_music ? instance_server_music->name() : "nil") + ")"); @@ -214,7 +214,7 @@ InstanceHandler::~InstanceHandler() { delete this->banMgr; delete this->dbHelper; - groupManager = nullptr; + __old__groupManager = nullptr; globalServerAdmin = nullptr; _musicRoot = nullptr; @@ -554,12 +554,12 @@ void InstanceHandler::tickInstance() { } void InstanceHandler::save_group_permissions() { - auto groups = this->getGroupManager()->availableGroups(false); + auto groups = this->getOldGroupManager()->availableGroups(false); for(auto& group : groups) { auto permissions = group->permissions(); if(permissions->require_db_updates()) { auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveGroupPermissions(nullptr, group->groupId(), permissions); + serverInstance->databaseHelper()->saveGroupPermissions(0, group->groupId(), 0, permissions); auto end = system_clock::now(); debugMessage(0, "Saved instance group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast(end - begin).count()); } @@ -899,7 +899,7 @@ std::vectorgetGroupManager()->getServerGroups(cldbid, type); + auto server_groups = this->getOldGroupManager()->getServerGroups(cldbid, type); for(const auto& permission : permissions) { permission::v2::PermissionFlaggedValue value{0, false}; diff --git a/server/src/InstanceHandler.h b/server/src/InstanceHandler.h index 96417a5..69f530a 100644 --- a/server/src/InstanceHandler.h +++ b/server/src/InstanceHandler.h @@ -47,7 +47,7 @@ namespace ts { } std::shared_ptr getInitialServerAdmin(){ return globalServerAdmin; } - std::shared_ptr getGroupManager(){ return groupManager; } + std::shared_ptr getOldGroupManager(){ return __old__groupManager; } std::shared_ptr getChannelTree() { return this->default_tree; } std::shared_mutex& getChannelTreeLock() { return this->default_tree_lock; } @@ -143,7 +143,7 @@ namespace ts { std::shared_ptr default_server_properties = nullptr; std::shared_ptr default_tree = nullptr; std::shared_mutex default_tree_lock; - std::shared_ptr groupManager = nullptr; + std::shared_ptr __old__groupManager = nullptr; std::shared_ptr globalServerAdmin = nullptr; std::shared_ptr _musicRoot = nullptr; diff --git a/server/src/InstanceHandlerSetup.cpp b/server/src/InstanceHandlerSetup.cpp index 95bf060..52b7af8 100644 --- a/server/src/InstanceHandlerSetup.cpp +++ b/server/src/InstanceHandlerSetup.cpp @@ -123,7 +123,7 @@ bool InstanceHandler::setupDefaultGroups() { for(const auto& info : groups) { debugMessage(LOG_INSTANCE, "Creating default group {} with type {}", info->name, to_string(info->target)); //Query groups - auto group = this->groupManager->createGroup( + auto group = this->__old__groupManager->createGroup( info->target != 2 ? GroupTarget::GROUPTARGET_SERVER : GroupTarget::GROUPTARGET_CHANNEL, info->target == 0 ? GroupType::GROUP_TYPE_QUERY : GroupType::GROUP_TYPE_TEMPLATE, info->name diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index baa3834..665bb23 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -241,7 +241,7 @@ void VirtualServer::executeServerTick() { auto permissions = group->permissions(); if(permissions->require_db_updates()) { auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveGroupPermissions(this->ref(), group->groupId(), permissions); + serverInstance->databaseHelper()->saveGroupPermissions(this->getServerId(), group->groupId(), 0, permissions); auto end = system_clock::now(); debugMessage(this->serverId, "Saved group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast(end - begin).count()); } diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index 3f7ddc3..fe89d6a 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -187,7 +187,7 @@ bool VirtualServer::initialize(bool test_properties) { channelTree = new ServerChannelTree(self.lock(), this->sql); channelTree->loadChannelsFromDatabase(); - this->groups = new GroupManager(self.lock(), this->sql, serverInstance->getGroupManager()); + this->groups = new GroupManager(self.lock(), this->sql, serverInstance->getOldGroupManager()); if(!this->groups->loadGroupFormDatabase()){ //TODO exception etc logCritical(this->serverId, "Cant setup group manager!"); return false; @@ -1137,7 +1137,7 @@ bool VirtualServer::resetPermissions(std::string& new_permission_token) { threads::MutexLock lock(this->getGroupManager()->cacheLock); this->getGroupManager()->deleteAllGroups(); deque> saved_groups; - for(const auto& group : serverInstance->getGroupManager()->availableGroups(false)){ + for(const auto& group : serverInstance->getOldGroupManager()->availableGroups(false)){ if(group->type() != GroupType::GROUP_TYPE_TEMPLATE) continue; debugMessage(this->serverId, "Copy default group {{Id: {}, Type: {}, Target: {}, Name: {}}} to server", group->groupId(), group->type(), group->target(), group->name()); @@ -1146,32 +1146,32 @@ bool VirtualServer::resetPermissions(std::string& new_permission_token) { } //Server admin - auto default_server_admin = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP].as()); - auto default_server_music = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as()); - auto default_server_guest = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as()); + auto default_server_admin = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP].as()); + auto default_server_music = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as()); + auto default_server_guest = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as()); - auto default_channel_admin = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as()); - auto default_channel_guest = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as()); + auto default_channel_admin = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as()); + auto default_channel_guest = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as()); if(!default_server_guest) { logCritical(0, "Missing default server guest template group!"); - assert(!serverInstance->getGroupManager()->availableChannelGroups().empty()); + assert(!serverInstance->getOldGroupManager()->availableChannelGroups().empty()); - default_server_guest = serverInstance->getGroupManager()->availableServerGroups().front(); + default_server_guest = serverInstance->getOldGroupManager()->availableServerGroups().front(); logCritical(0, "Using group {} as default server guest group for server {}.", default_server_guest->name(), this->serverId); } if(!default_channel_admin) { logCritical(0, "Missing default channel guest template group!"); - assert(!serverInstance->getGroupManager()->availableChannelGroups().empty()); + assert(!serverInstance->getOldGroupManager()->availableChannelGroups().empty()); - default_channel_admin = serverInstance->getGroupManager()->availableChannelGroups().front(); + default_channel_admin = serverInstance->getOldGroupManager()->availableChannelGroups().front(); logCritical(0, "Using group {} as channel server guest group for server {}.", default_channel_admin->name(), this->serverId); } if(!default_server_music) { logCritical(0, "Missing default channel guest template group!"); - assert(!serverInstance->getGroupManager()->availableChannelGroups().empty()); + assert(!serverInstance->getOldGroupManager()->availableChannelGroups().empty()); - default_server_music = serverInstance->getGroupManager()->availableChannelGroups().front(); + default_server_music = serverInstance->getOldGroupManager()->availableChannelGroups().front(); logCritical(0, "Using group {} as channel server guest group for server {}.", default_server_music->name(), this->serverId); } diff --git a/server/src/channel/ServerChannel.cpp b/server/src/channel/ServerChannel.cpp index ae42645..08c54f9 100644 --- a/server/src/channel/ServerChannel.cpp +++ b/server/src/channel/ServerChannel.cpp @@ -578,7 +578,7 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr server->conversation_manager()->delete_conversation(channel->channelId()); server->rtc_server().destroy_channel(server_channel->rtc_channel_id); } else { - serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId()); + serverInstance->getOldGroupManager()->handleChannelDeleted(channel->channelId()); } diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 2ea70d1..e64b215 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -45,7 +45,7 @@ bool ConnectedClient::notifyServerGroupList(bool as_notify) { Command cmd(as_notify ? "notifyservergrouplist" : ""); int index = 0; - for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) { + for (const auto& group : (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->availableServerGroups(true)) { if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { cmd[index]["cgid"] = group->groupId(); } else { @@ -171,7 +171,7 @@ bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_ bool ConnectedClient::notifyChannelGroupList(bool as_notify) { Command cmd(as_notify ? "notifychannelgrouplist" : ""); int index = 0; - for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) { + for (const auto &group : (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->availableChannelGroups(true)) { if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { cmd[index]["cgid"] = group->groupId(); } else { diff --git a/server/src/client/DataClient.cpp b/server/src/client/DataClient.cpp index eb4333d..0373ff3 100644 --- a/server/src/client/DataClient.cpp +++ b/server/src/client/DataClient.cpp @@ -177,7 +177,7 @@ permission::v2::PermissionFlaggedValue DataClient::calculate_permission( } std::vector> DataClient::assignedServerGroups() { - if(!this->server) return serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType()); + if(!this->server) return serverInstance->getOldGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType()); return this->server->groups->getServerGroups(this->getClientDatabaseId(), this->getType()); } diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index 4925a0c..7cd5735 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -136,7 +136,7 @@ command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); log::GroupType log_group_type; if (cmd["type"].as() == GroupType::GROUP_TYPE_QUERY) { @@ -203,7 +203,7 @@ command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) { auto ref_server = this->server; - auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->groups : serverInstance->getOldGroupManager().get(); auto source_group_id = cmd["scgid"].as(); auto source_group = group_manager->findGroup(source_group_id); @@ -338,7 +338,7 @@ command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); auto channel_group = group_manager->findGroup(cmd["cgid"].as()); if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; @@ -373,7 +373,7 @@ command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); auto channel_group = group_manager->findGroup(cmd["cgid"].as()); if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; @@ -508,7 +508,7 @@ command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd) CMD_CHK_AND_INC_FLOOD_POINTS(5); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_permission_list, 1); - auto channelGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["cgid"].as()); + auto channelGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["cgid"].as()); if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; if (!this->notifyGroupPermList(channelGroup, cmd.hasParm("permsid"))) return command_result{error::database_empty_result}; @@ -522,7 +522,7 @@ command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd) command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); auto channelGroup = group_manager->findGroup(cmd["cgid"].as()); if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true); @@ -574,7 +574,7 @@ command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) { command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); auto channelGroup = group_manager->findGroup(cmd["cgid"].as()); if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true); diff --git a/server/src/client/command_handler/server.cpp b/server/src/client/command_handler/server.cpp index 1c014f7..0128a5c 100644 --- a/server/src/client/command_handler/server.cpp +++ b/server/src/client/command_handler/server.cpp @@ -314,7 +314,7 @@ command_result ConnectedClient::handleCommandServerGroupAdd(Command &cmd) { log_group_type = log::GroupType::NORMAL; } - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); for(const auto& gr : group_manager->availableServerGroups(true)) { if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_SERVER) { return command_result{error::parameter_invalid, "Group already exists"}; @@ -360,7 +360,7 @@ command_result ConnectedClient::handleCommandServerGroupCopy(Command &cmd) { auto ref_server = this->server; ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_create, 1); - auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->groups : serverInstance->getOldGroupManager().get(); auto source_group_id = cmd["ssgid"].as(); auto source_group = group_manager->findGroup(source_group_id); @@ -494,7 +494,7 @@ command_result ConnectedClient::handleCommandServerGroupRename(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); auto serverGroup = group_manager->findGroup(cmd["sgid"].as()); if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id}; @@ -529,7 +529,7 @@ command_result ConnectedClient::handleCommandServerGroupDel(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_delete, 1); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getOldGroupManager().get(); auto serverGroup = group_manager->findGroup(cmd["sgid"].as()); if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id}; ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true); @@ -589,7 +589,7 @@ command_result ConnectedClient::handleCommandServerGroupClientList(Command &cmd) ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_client_list, 1); auto server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto groupManager = server ? this->server->groups : serverInstance->getGroupManager().get(); + auto groupManager = server ? this->server->groups : serverInstance->getOldGroupManager().get(); auto serverGroup = groupManager->findGroup(cmd["sgid"].as()); if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id}; @@ -617,7 +617,7 @@ command_result ConnectedClient::handleCommandServerGroupAddClient(Command &cmd) CMD_CHK_AND_INC_FLOOD_POINTS(25); auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); + auto group_manager = target_server ? this->server->groups : serverInstance->getOldGroupManager().get(); auto target_cldbid = cmd["cldbid"].as(); if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return command_result{error::client_invalid_id, "invalid cldbid"}; @@ -741,7 +741,7 @@ command_result ConnectedClient::handleCommandServerGroupDelClient(Command &cmd) CMD_CHK_AND_INC_FLOOD_POINTS(25); auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); + auto group_manager = target_server ? this->server->groups : serverInstance->getOldGroupManager().get(); auto target_cldbid = cmd["cldbid"].as(); if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return command_result{error::client_invalid_id, "invalid cldbid"}; @@ -879,7 +879,7 @@ command_result ConnectedClient::handleCommandServerGroupPermList(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_permission_list, 1); - auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); + auto serverGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["sgid"].as()); if (!serverGroup) return command_result{error::group_invalid_id}; if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { @@ -894,7 +894,7 @@ command_result ConnectedClient::handleCommandServerGroupAddPerm(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); + auto serverGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["sgid"].as()); if (!serverGroup) return command_result{error::group_invalid_id}; if (serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid}; ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, 1); @@ -959,7 +959,7 @@ command_result ConnectedClient::handleCommandServerGroupDelPerm(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); + auto serverGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["sgid"].as()); if (!serverGroup) return command_result{error::group_invalid_id}; if (serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid}; ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, 1); @@ -1024,7 +1024,7 @@ command_result ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command& CMD_CHK_AND_INC_FLOOD_POINTS(25); auto ref_server = this->server; - auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager(); + auto group_manager = ref_server ? this->server->groups : &*serverInstance->getOldGroupManager(); deque> groups; for(const auto& group : group_manager->availableGroups(false)) { @@ -1102,7 +1102,7 @@ command_result ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command& CMD_CHK_AND_INC_FLOOD_POINTS(25); auto ref_server = this->server; - auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager(); + auto group_manager = ref_server ? this->server->groups : &*serverInstance->getOldGroupManager(); deque> groups; for(const auto& group : group_manager->availableGroups(false)) { @@ -1191,7 +1191,7 @@ command_result ConnectedClient::handleCommandServerGroupsByClientId(Command &cmd index++; } } else { - for (const auto &group : serverInstance->getGroupManager()->getAssignedServerGroups(cldbid)) { + for (const auto &group : serverInstance->getOldGroupManager()->getAssignedServerGroups(cldbid)) { result[index]["name"] = group->group->name(); result[index]["sgid"] = group->group->groupId(); result[index]["cldbid"] = cldbid; diff --git a/server/src/client/query/QueryClient.cpp b/server/src/client/query/QueryClient.cpp index aa52eb8..8c50ac3 100644 --- a/server/src/client/query/QueryClient.cpp +++ b/server/src/client/query/QueryClient.cpp @@ -635,5 +635,5 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) { this->loadDataForCurrentServer(); } - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + serverInstance->getOldGroupManager()->enableCache(this->getClientDatabaseId()); } \ No newline at end of file diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index bfaaf34..136071b 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -248,7 +248,7 @@ command_result QueryClient::handleCommandLogin(Command& cmd) { this->server->unregisterClient(this->ref(), "login", tree_lock); } this->server->groups->disableCache(this->getClientDatabaseId()); - } else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); + } else serverInstance->getOldGroupManager()->disableCache(this->getClientDatabaseId()); logMessage(LOG_QUERY, "Got new authenticated client. Username: {}, Unique-ID: {}, Bounded Server: {}", account->username, account->unique_id, account->bound_server); @@ -292,7 +292,7 @@ command_result QueryClient::handleCommandLogin(Command& cmd) { this->task_update_needed_permissions.enqueue(); } } else { - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + serverInstance->getOldGroupManager()->enableCache(this->getClientDatabaseId()); this->task_update_needed_permissions.enqueue(); } @@ -318,7 +318,7 @@ command_result QueryClient::handleCommandLogout(Command &) { this->server->unregisterClient(this->ref(), "logout", tree_lock); } this->server->groups->disableCache(this->getClientDatabaseId()); - } else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); + } else serverInstance->getOldGroupManager()->disableCache(this->getClientDatabaseId()); this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery"; //TODO load from table this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(ntohs(this->getPeerPort())); @@ -346,7 +346,7 @@ command_result QueryClient::handleCommandLogout(Command &) { this->task_update_needed_permissions.enqueue(); } } else { - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + serverInstance->getOldGroupManager()->enableCache(this->getClientDatabaseId()); this->task_update_needed_permissions.enqueue(); } @@ -419,7 +419,7 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) { this->task_update_needed_permissions.enqueue(); } } else { - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + serverInstance->getOldGroupManager()->enableCache(this->getClientDatabaseId()); this->task_update_needed_permissions.enqueue(); } diff --git a/server/src/groups/Group.cpp b/server/src/groups/Group.cpp new file mode 100644 index 0000000..7cf0876 --- /dev/null +++ b/server/src/groups/Group.cpp @@ -0,0 +1,30 @@ +// +// Created by WolverinDEV on 03/03/2020. +// + +#include "Group.h" +#include "./GroupManager.h" + +using namespace ts::server::groups; + +Group::Group(ServerId sid, ts::GroupId id, ts::server::groups::GroupType type, std::string name, + std::shared_ptr permissions) : virtual_server_id_{sid}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { } + +void Group::set_permissions(const std::shared_ptr &permissions) { + assert(permissions); + this->permissions_ = permissions; +} + +ServerGroup::ServerGroup(ServerId sid, GroupId id, GroupType type, std::string name, + std::shared_ptr permissions) + : Group{sid, id, type, std::move(name), std::move(permissions)} +{ + +} + +ChannelGroup::ChannelGroup(ServerId sid, GroupId id, GroupType type, std::string name, + std::shared_ptr permissions) + : Group{sid, id, type, std::move(name), std::move(permissions)} +{ + +} \ No newline at end of file diff --git a/server/src/groups/Group.h b/server/src/groups/Group.h new file mode 100644 index 0000000..f43f13c --- /dev/null +++ b/server/src/groups/Group.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +namespace ts::server::groups { + enum GroupType { + GROUP_TYPE_QUERY, + GROUP_TYPE_TEMPLATE, + GROUP_TYPE_NORMAL, + + GROUP_TYPE_UNKNOWN = 0xFF + }; + + enum GroupNameMode { + GROUP_NAME_MODE_HIDDEN, + GROUP_NAME_MODE_BEFORE, + GROUP_NAME_MODE_BEHIND + }; + + typedef uint32_t GroupSortId; + typedef uint32_t GroupIconId; + + class AbstractGroupManager; + class Group { + friend class AbstractGroupManager; + public: + Group(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr /* permissions */); + virtual ~Group() = default; + + /* information getters */ + [[nodiscard]] inline ServerId virtual_server_id() const { return this->virtual_server_id_; } + [[nodiscard]] inline GroupId group_id() const { return this->group_id_; } + [[nodiscard]] inline GroupType group_type() const { return this->type_; } + [[nodiscard]] inline const std::string& display_name() const { return this->name_; } + + /* we're not returning a cr here because the permissions might get reloaded */ + [[nodiscard]] inline std::shared_ptr permissions() { return this->permissions_; } + + [[nodiscard]] inline bool save_assignments() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::b_group_is_permanent); + return value.has_value ? permission::v2::permission_granted(1, value) : true; + } + + [[nodiscard]] inline GroupNameMode name_mode() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::i_group_show_name_in_tree); + return value.has_value ? (GroupNameMode) value.value : GroupNameMode::GROUP_NAME_MODE_HIDDEN; + } + + [[nodiscard]] inline GroupSortId sort_id() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::i_group_sort_id); + return value.has_value ? value.value : 0; + } + + [[nodiscard]] inline GroupIconId icon_id() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::i_icon_id); + return value.has_value ? value.value : 0; + } + + private: + const ServerId virtual_server_id_; + const GroupId group_id_; + const GroupType type_; + std::string name_; + + std::shared_ptr permissions_; + + void set_permissions(const std::shared_ptr& /* permissions */); + }; + + class ServerGroup : public Group { + public: + ServerGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr /* permissions */); + }; + + class ChannelGroup : public Group { + public: + ChannelGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr /* permissions */); + }; +} +DEFINE_TRANSFORMS(ts::server::groups::GroupType, uint8_t); +DEFINE_TRANSFORMS(ts::server::groups::GroupNameMode, uint8_t); \ No newline at end of file diff --git a/server/src/groups/GroupAssignmentManager.cpp b/server/src/groups/GroupAssignmentManager.cpp new file mode 100644 index 0000000..ea2a9f8 --- /dev/null +++ b/server/src/groups/GroupAssignmentManager.cpp @@ -0,0 +1,497 @@ +// +// Created by WolverinDEV on 03/03/2020. +// +#include +#include "./GroupAssignmentManager.h" +#include "./GroupManager.h" + +using namespace ts::server::groups; + +GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle} { } +GroupAssignmentManager::~GroupAssignmentManager() = default; + +bool GroupAssignmentManager::initialize(std::string &error) { + return true; +} + +sql::SqlManager* GroupAssignmentManager::sql_manager() { + return this->manager_->sql_manager(); +} + +ts::ServerId GroupAssignmentManager::server_id() { + return this->manager_->server_id(); +} + +bool GroupAssignmentManager::load_data(std::string &error) { + if constexpr(kCacheAllClients) { + std::lock_guard cache_lock{this->client_cache_lock}; + std::unique_ptr current_entry{nullptr}; + + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `cldbid`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid ORDER BY `cldbid`", variable{":sid", this->server_id()}) + .query([&](int length, std::string* value, std::string* column) { + ChannelId channel_id{0}; + GroupId group_id{0}; + ClientDbId client_dbid{0}; + + for(int index = 0; index < length; index++){ + try { + if(column[index] == "groupId"){ + group_id = stoll(value[index]); + } else if(column[index] == "until"){ + } else if(column[index] == "channelId"){ + channel_id = stoll(value[index]); + } else if(column[index] == "cldbid"){ + client_dbid = stoll(value[index]); + } + } catch(std::exception& ex) { + logError(this->server_id(), "Failed to load group assignment from database. Column {} contains an invalid value: {}", column[index], value[index]); + return 0; + } + } + + if(current_entry) + if(current_entry->client_database_id != client_dbid) + this->client_cache.push_back(std::move(current_entry)); + + if(!current_entry) { + current_entry = std::make_unique(); + current_entry->client_database_id = client_dbid; + } + + if(channel_id) + current_entry->channel_group_assignments.emplace_back(channel_id, group_id, false); + else + current_entry->server_group_assignments.emplace_back(group_id); + return 0; + }); + + if(!res) { + error = "failed to query database (" + res.fmtStr() + ")"; + return false; + } + + if(current_entry) { + this->client_cache.push_back(std::move(current_entry)); + } + } + return true; +} + +void GroupAssignmentManager::unload_data() { + std::lock_guard cache_lock{this->client_cache_lock}; + this->client_cache.clear(); +} + +void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { + if constexpr(!kCacheAllClients) { + bool cache_exists{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) + if(client->client_database_id == cldbid) { + client->use_count++; + cache_exists = true; + break; + } + } + + if(!cache_exists) { + auto cache = std::make_unique(); + cache->client_database_id = cldbid; + cache->use_count++; + + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) + .query([&](int length, std::string* value, std::string* column) { + ChannelId channel_id{0}; + GroupId group_id{0}; + + for(int index = 0; index < length; index++){ + try { + if(column[index] == "groupId"){ + group_id = stoll(value[index]); + } else if(column[index] == "until"){ + } else if(column[index] == "channelId"){ + channel_id = stoll(value[index]); + } + } catch(std::exception& ex) { + logError(this->server_id(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", cldbid, column[index], value[index]); + return 0; + } + } + if(!group_id) + return 0; + + if(channel_id) + cache->channel_group_assignments.emplace_back(channel_id, group_id, false); + else + cache->server_group_assignments.emplace_back(group_id); + return 0; + }); + + std::lock_guard cache_lock{this->client_cache_lock}; +#if 0 /* lets have some performance over double entries :D */ + for(auto& client : this->client_cache) + if(client->client_database_id == cldbid) { + /* somebody already inserted that client while we've loaded him */ + cache_exists = true; + break; + } + if(!cache_exists) +#endif + this->client_cache.push_back(std::move(cache)); + } + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) { + if(auto parent = this->manager_->parent_manager(); parent) { + parent->assignments().enable_cache_for_client(mode, cldbid); + } + } +} + +void GroupAssignmentManager::disable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { + if constexpr(!kCacheAllClients) { + std::lock_guard cache_lock{this->client_cache_lock}; + this->client_cache.erase(std::remove_if(this->client_cache.begin(), this->client_cache.end(), [cldbid](const std::unique_ptr& client) { + return client->client_database_id == cldbid; + }), this->client_cache.end()); + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) + if(auto parent = this->manager_->parent_manager(); parent) + parent->assignments().disable_cache_for_client(mode, cldbid); +} + + +std::vector GroupAssignmentManager::server_groups_of_client(ts::server::groups::GroupAssignmentCalculateMode mode, + ts::ClientDbId cldbid) { + std::vector result{}; + bool cache_found{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != cldbid) continue; + + result.reserve(entry->server_group_assignments.size()); + for(auto& assignment : entry->server_group_assignments) + result.push_back(assignment.group_id); + + cache_found = true; + break; + } + } + + if(!cache_found && !kCacheAllClients) { + debugMessage(this->server_id(), "Query client groups for client {} on server {}.", cldbid, this->server_id()); + + result.reserve(64); + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) + .query([&](int length, std::string* value, std::string* column) { + GroupId group_id{0}; + try { + for(int index = 0; index < length; index++) { + if(column[index] == "groupId") { + group_id = std::stoull(value[index]); + break; + } + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse group id."); + return 0; + } + if(!group_id) return 0; + + result.push_back(group_id); + return 0; + }); + LOG_SQL_CMD(res); + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) + if(auto parent = this->manager_->parent_manager(); parent) { + auto parent_groups = parent->assignments().server_groups_of_client(mode, cldbid); + result.reserve(result.size() + parent_groups.size()); + result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); + } + + return result; +} + +std::vector GroupAssignmentManager::channel_group_of_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { + std::vector result{}; + bool cache_found{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != cldbid) continue; + + result.reserve(entry->channel_group_assignments.size()); + result.insert(result.begin(), entry->channel_group_assignments.begin(), entry->channel_group_assignments.end()); + cache_found = true; + break; + } + } + + if(!cache_found && !kCacheAllClients) { + debugMessage(this->server_id(), "Query client groups for client {} on server {}.", cldbid, this->server_id()); + + result.reserve(64); + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` != 0", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) + .query([&](int length, std::string* value, std::string* column) { + GroupId group_id{0}; + ChannelId channel_id{0}; + try { + for(int index = 0; index < length; index++) { + if(column[index] == "groupId") { + group_id = std::stoull(value[index]); + } else if(column[index] == "channelId") { + channel_id = std::stoull(value[index]); + } + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse group or channel id."); + return 0; + } + if(!group_id || !channel_id) return 0; + + result.emplace_back(channel_id, group_id, false); + return 0; + }); + LOG_SQL_CMD(res); + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) { + if(auto parent = this->manager_->parent_manager(); parent) { + auto parent_groups = parent->assignments().channel_group_of_client(mode, cldbid); + result.reserve(result.size() + parent_groups.size()); + result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); + } + } + + return result; +} + +std::deque GroupAssignmentManager::server_group_clients(GroupId group_id) { + std::deque result{}; + if constexpr(kCacheAllClients) { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) { + auto it = std::find_if(client->server_group_assignments.begin(), client->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { + return assignment.group_id == group_id; + }); + if(it == client->server_group_assignments.end()) + continue; + result.push_back(client->client_database_id); + } + } else { + auto res = sql::command(this->sql_manager(), "SELECT `cldbid` FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}) + .query([&](int length, std::string* value, std::string* column) { + ClientDbId cldbid{0}; + try { + for(int index = 0; index < length; index++) { + if(column[index] == "cldbid") { + cldbid = std::stoull(value[index]); + break; + } + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse client database id."); + return 0; + } + if(!cldbid) return 0; + + result.push_back(cldbid); + return 0; + }); + LOG_SQL_CMD(res); + } + + if(auto parent = this->manager_->parent_manager(); parent) { + auto parent_clients = parent->assignments().server_group_clients(group_id); + result.insert(result.begin(), parent_clients.begin(), parent_clients.end()); + } + + return result; +} + +GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) { + bool cache_verified{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != client) continue; + + auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { + return assignment.group_id == group; + }); + if(it != entry->server_group_assignments.end()) + return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; + entry->server_group_assignments.emplace_back(group); + cache_verified = true; + break; + } + + if(!cache_verified && kCacheAllClients) { + /* add the client to the cache */ + auto cache = std::make_unique(); + cache->client_database_id = client; + cache->server_group_assignments.emplace_back(group); + this->client_cache.push_back(std::move(cache)); + } + } + + { + + auto command = sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":gid", group}, + variable{":chid", 0}, + variable{":until", 0}); + if(cache_verified) + command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to insert group assignment into the database"}); + else { + auto result = command.execute(); + if(!result) return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; //TODO: Parse error from database? + } + } + return GroupAssignmentResult::SUCCESS; +} + +GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId client, GroupId group) { + bool cache_verified{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != client) continue; + + auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { + return assignment.group_id == group; + }); + if(it == entry->server_group_assignments.end()) + return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; + entry->server_group_assignments.erase(it); + cache_verified = true; + break; + } + + if(!cache_verified && kCacheAllClients) + return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; + } + + { + auto command = sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":gid", group}, + variable{":chid", 0}); + if(cache_verified) + command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to remove group assignment from database"}); + else { + auto result = command.execute(); + if(!result) { + return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; //TODO: Parse error from database? + } + } + } + return GroupAssignmentResult::SUCCESS; +} + +GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId client, GroupId group, ChannelId channel_id, bool temporary) { + bool cache_verified{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != client) continue; + + auto it = std::find_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel_id; + }); + if(it != entry->channel_group_assignments.end()) { + if(group) { + if(it->group_id == group) + return GroupAssignmentResult::SET_ALREADY_MEMBER_OF_GROUP; + it->group_id = group; + } else { + entry->channel_group_assignments.erase(it); + } + } else { + if(group) { + entry->channel_group_assignments.emplace_back(channel_id, group, temporary); + } + } + cache_verified = true; + break; + } + + if(!cache_verified && kCacheAllClients) { + if(group) { + /* add the client to the cache */ + auto cache = std::make_unique(); + cache->client_database_id = client; + cache->channel_group_assignments.emplace_back(channel_id, group, temporary); + this->client_cache.push_back(std::move(cache)); + } else { + return GroupAssignmentResult::SUCCESS; + } + } + } + if(temporary) + return GroupAssignmentResult::SUCCESS; + + sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":chid", channel_id}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "failed to delete old channel group assignment"}); + + if(group) { + sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":gid", group}, + variable{":chid", channel_id}, + variable{":until", 0}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "failed to insert channel group assignment"}); + } + return GroupAssignmentResult::SUCCESS; +} + +void GroupAssignmentManager::cleanup_channel_assignments(ChannelId channel_id) { + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) { + client->channel_group_assignments.erase(std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel_id; + })); + } + } + + sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = :cid", variable{":sid", this->server_id()}, variable{":cid", channel_id}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); +} + +void GroupAssignmentManager::cleanup_assignments() { + { + + std::lock_guard cache_lock{this->client_cache_lock}; + this->client_cache.clear(); + } + + sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); +} + +void GroupAssignmentManager::cleanup_channel_temporary_assignment(ClientDbId client_dbid, ChannelId channel) { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) { + if(client->client_database_id == client_dbid) { + auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel; + }); + if(assignment->temporary_assignment) + client->channel_group_assignments.erase(assignment); + break; + } + } +} \ No newline at end of file diff --git a/server/src/groups/GroupAssignmentManager.h b/server/src/groups/GroupAssignmentManager.h new file mode 100644 index 0000000..087c97b --- /dev/null +++ b/server/src/groups/GroupAssignmentManager.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sql { + class SqlManager; +} + +namespace ts::server { + class ConnectedClient; + + namespace groups { + enum struct GroupAssignmentCalculateMode { + LOCAL, /* only calculate clients groups for the local server */ + GLOBAL /* use the parent group manager as well, if existing */ + }; + + class ServerGroup; + class ChannelGroup; + class GroupManager; + + struct ChannelGroupAssignment { + ChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { } + ChannelGroupAssignment(const ChannelGroupAssignment& other) = default; + ChannelGroupAssignment(ChannelGroupAssignment&&) = default; + ChannelGroupAssignment&operator=(const ChannelGroupAssignment&) = default; + + ChannelId channel_id; + GroupId group_id; + bool temporary_assignment; + }; + + struct ServerGroupAssignment { + explicit ServerGroupAssignment(GroupId group_id) : group_id{group_id} { } + ServerGroupAssignment(const ServerGroupAssignment& other) = default; + ServerGroupAssignment(ServerGroupAssignment&&) = default; + ServerGroupAssignment&operator=(const ServerGroupAssignment&) = default; + + GroupId group_id; + }; + + enum struct GroupAssignmentResult { + SUCCESS, + ADD_ALREADY_MEMBER_OF_GROUP, + REMOVE_NOT_MEMBER_OF_GROUP, + SET_ALREADY_MEMBER_OF_GROUP + }; + + class GroupAssignmentManager { + constexpr static bool kCacheAllClients{true}; + public: + explicit GroupAssignmentManager(GroupManager* /* manager */); + ~GroupAssignmentManager(); + + /* general load/initialize methods */ + bool initialize(std::string& /* error */); + bool load_data(std::string& /* error */); + void unload_data(); + + /* client specific cache methods */ + void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + + /* info/query methods */ + [[nodiscard]] std::vector server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + [[nodiscard]] std::vector channel_group_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + + [[nodiscard]] std::deque server_group_clients(GroupId /* group id */); + //[[nodiscard]] std::deque channel_group_clients(GroupId /* group id */, ChannelId /* channel id */); + + /* change methods */ + GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */); + GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */); + + GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */); + + void cleanup_assignments(); + void cleanup_channel_assignments(ChannelId /* channel */); + void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */); + private: + struct ClientCache { + ClientDbId client_database_id{0}; + size_t use_count{0}; + + std::deque channel_group_assignments{}; + std::deque server_group_assignments{}; + }; + + GroupManager* manager_; + + std::mutex client_cache_lock; + std::deque> client_cache{}; + + + [[nodiscard]] sql::SqlManager* sql_manager(); + [[nodiscard]] ServerId server_id(); + }; + + } +} \ No newline at end of file diff --git a/server/src/groups/GroupManager.cpp b/server/src/groups/GroupManager.cpp new file mode 100644 index 0000000..4131aac --- /dev/null +++ b/server/src/groups/GroupManager.cpp @@ -0,0 +1,346 @@ +// +// Created by WolverinDEV on 03/03/2020. +// +#include +#include +#include "./GroupManager.h" +#include "../InstanceHandler.h" + +using namespace ts::server::groups; + +GroupManager::GroupManager(sql::SqlManager *sql, ServerId server_id, std::shared_ptr parent) + : virtual_server_id_{server_id}, database_{sql}, parent_manager_{std::move(parent)}, assignment_manager_{this} +{ + assert(sql); +} + +GroupManager::~GroupManager() = default; + +ts::ServerId GroupManager::server_id() { + return this->virtual_server_id_; +} + +sql::SqlManager* GroupManager::sql_manager() { + return this->database_; +} + +bool GroupManager::initialize(const std::shared_ptr& self, std::string &error) { + assert(&*self == this); + + if(this->parent_manager_) { + this->server_groups_ = std::make_shared(self, this->parent_manager_->server_groups_); + this->channel_groups_ = std::make_shared(self, this->parent_manager_->channel_groups_); + } else { + this->server_groups_ = std::make_shared(self, nullptr); + this->channel_groups_ = std::make_shared(self, nullptr); + } + + return true; +} + +/* Abstract group manager */ +AbstractGroupManager::AbstractGroupManager( + sql::SqlManager* database, + DatabaseGroupTarget database_target_, + ServerId virtual_server_id, + std::shared_ptr parent +) : database_{database}, database_target_{database_target_}, virtual_server_id_{virtual_server_id}, parent_manager_{std::move(parent)} { } + +ts::ServerId AbstractGroupManager::server_id() { + return this->virtual_server_id_; +} + +sql::SqlManager *AbstractGroupManager::sql_manager() { + return this->database_; +} + +bool AbstractGroupManager::initialize(std::string &) { + return true; +} + +GroupLoadResult AbstractGroupManager::load_data(bool initialize) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + { + std::lock_guard list_lock{this->group_mutex_}; + this->groups_.clear(); + } + + { + auto command = sql::command{this->sql_manager(), "SELECT * FROM `groups` WHERE `serverId` = :sid AND `target` = :target"}; + command.value(":sid", this->server_id()); + command.value(":target", (uint8_t) this->database_target_); + auto result = command.query(&AbstractGroupManager::insert_group_from_sql, this); + if(!result) { + LOG_SQL_CMD(result); + return GroupLoadResult::DATABASE_ERROR; + } + } + + { + std::lock_guard list_lock{this->group_mutex_}; + if(this->groups_.empty()) { + return GroupLoadResult::NO_GROUPS; + } + } + + return GroupLoadResult::SUCCESS; +} + +void AbstractGroupManager::unload_data() { + std::lock_guard manage_lock{this->group_manage_mutex_}; + { + std::lock_guard list_lock{this->group_mutex_}; + this->groups_.clear(); + } +} + +void AbstractGroupManager::reset_groups(bool db_cleanup) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + this->unload_data(); + + if(db_cleanup) { + /* FIXME: Only delete groups with our database target! */ + LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type", + variable{":serverId", this->server_id()}, + variable{":type", ts::permission::SQL_PERM_GROUP}).execute()); + LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", + variable{":serverId", this->server_id()}).execute()); + LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", + variable{":serverId", this->server_id()}).execute()); + } + + if(auto error = this->load_data(true); error != GroupLoadResult::SUCCESS) { + logCritical(this->server_id(), "Failed to load groups after group unload ({}). There might be no groups loaded now!", (int) error); + } +} + +int AbstractGroupManager::insert_group_from_sql(int length, std::string *values, std::string *names) { + GroupId group_id{0}; + GroupType group_type{GroupType::GROUP_TYPE_UNKNOWN}; + std::string group_name{}; + + for(size_t index = 0; index < length; index++) { + try { + if(names[index] == "groupId") { + group_id = std::stoull(values[index]); + } else if(names[index] == "target") { + /* group_target = (GroupTarget) std::stoull(values[index]); */ + } else if(names[index] == "type") { + group_type = (GroupType) std::stoull(values[index]); + } else if(names[index] == "displayName") { + group_name = names[index]; + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Failed to parse group from database. Failed to parse column {} (value: {})", names[index], values[index]); + return 0; + } + } + + if(!group_id) { + logWarning(this->server_id(), "Failed to query group from database. Invalid values found."); + return 0; + } + + auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) this->database_target_); + auto group = this->allocate_group(group_id, group_type, group_name, permissions); + + std::lock_guard lock{this->group_mutex_}; + this->groups_.push_back(group); + return 0; +} + +std::shared_ptr AbstractGroupManager::find_group_(GroupCalculateMode mode, GroupId group_id) { + { + std::lock_guard glock{this->group_mutex_}; + auto it = std::find_if(this->groups_.begin(), this->groups_.end(), [&](const std::shared_ptr& group) { + return group->group_id() == group_id; + }); + + if(it != this->groups_.end()) { + return *it; + } + } + if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_) { + return this->parent_manager_->find_group_(mode, group_id); + } + return nullptr; +} + +std::shared_ptr AbstractGroupManager::find_group_by_name_(GroupCalculateMode mode, const std::string &name) { + { + std::string lname{name}; + std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower); + + std::lock_guard glock{this->group_mutex_}; + auto it = std::find_if(this->groups_.begin(), this->groups_.end(), + [&](const std::shared_ptr &group) { + std::string lgroup_name{group->display_name()}; + std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(), ::tolower); + return lname == lgroup_name; + }); + + if(it != this->groups_.end()) { + return *it; + } + } + + if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_) { + return this->parent_manager_->find_group_by_name_(mode, name); + } + return nullptr; +} + +GroupCreateResult AbstractGroupManager::create_group_(GroupType type, const std::string &name, std::shared_ptr& result) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + if(this->find_group_by_name_(GroupCalculateMode::LOCAL, name)) { + return GroupCreateResult::NAME_ALREADY_IN_USED; + } + + auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `target`, `type`, `displayName`) VALUES (:sid, :target, :type, :name)", + variable{":sid", this->server_id()}, + variable{":target", (uint8_t) this->database_target_}, + variable{":type", type}, + variable{":name", name}).execute(); + + if(!res) { + LOG_SQL_CMD(res); + return GroupCreateResult::DATABASE_ERROR; + } + + auto group_id = res.last_insert_rowid(); + auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) this->database_target_); + auto group = std::make_shared(this->server_id(), group_id, type, name, permissions); + { + std::lock_guard glock{this->group_mutex_}; + this->groups_.push_back(group); + } + result = group; + return GroupCreateResult::SUCCESS; +} + +GroupCopyResult AbstractGroupManager::copy_group_(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr& result) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + + auto create_result = this->create_group_(target_type, display_name, result); + switch(create_result) { + case GroupCreateResult::SUCCESS: + break; + + case GroupCreateResult::DATABASE_ERROR: + return GroupCopyResult::DATABASE_ERROR; + + case GroupCreateResult::NAME_ALREADY_IN_USED: + return GroupCopyResult::NAME_ALREADY_IN_USE; + + case GroupCreateResult::FAILED_TO_GENERATE_ID: + return GroupCopyResult::FAILED_TO_GENERATE_ID; + } + + assert(result); + return this->copy_group_permissions_(source, result->group_id()); +} + +GroupCopyResult AbstractGroupManager::copy_group_permissions_(GroupId source, GroupId target) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + + auto source_group = this->find_group_(groups::GroupCalculateMode::GLOBAL, source); + if(!source_group) { + return GroupCopyResult::UNKNOWN_SOURCE_GROUP; + } + + auto target_group = this->find_group_(groups::GroupCalculateMode::GLOBAL, target); + if(!target_group) { + return GroupCopyResult::UNKNOWN_TARGET_GROUP; + } + + auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id", + variable{":sid", this->server_id()}, + variable{":type", ts::permission::SQL_PERM_GROUP}, + variable{":id", target}).execute(); + if(!res) { + LOG_SQL_CMD(res); + return GroupCopyResult::DATABASE_ERROR; + } + + res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source", + variable{":ssid", source_group->virtual_server_id()}, + variable{":tsid", target_group->virtual_server_id()}, + variable{":type", ts::permission::SQL_PERM_GROUP}, + variable{":source", source}, + variable{":target", target}).execute(); + + target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) this->database_target_)); + LOG_SQL_CMD(res); + + return GroupCopyResult::SUCCESS; +} + +GroupRenameResult AbstractGroupManager::rename_group_(GroupId group_id, const std::string &name) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + if(name.empty() || name.length() > 40) { + return GroupRenameResult::NAME_INVALID; + } + + auto group = this->find_group_(groups::GroupCalculateMode::GLOBAL, group_id); + if(!group) { + return GroupRenameResult::INVALID_GROUP_ID; + } + + if(this->find_group_by_name_(GroupCalculateMode::LOCAL, name)) { + return GroupRenameResult::NAME_ALREADY_USED; + } + + sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target", + variable{":server", this->server_id()}, + variable{":target", (uint8_t) this->database_target_}, + variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"}); + group->name_ = name; + return GroupRenameResult::SUCCESS; +} + +GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) { + std::lock_guard manage_lock{this->group_manage_mutex_}; + + { + std::lock_guard glock{this->group_mutex_}; + auto it = std::find_if(this->groups_.begin(), this->groups_.begin(), [&](const std::shared_ptr& group) { + return group->group_id() == group_id; + }); + + if(it == this->groups_.end()) { + return GroupDeleteResult::INVALID_GROUP_ID; + } + + this->groups_.erase(it); + } + + sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target", + variable{":server", this->server_id()}, + variable{":target", (uint8_t) this->database_target_}, + variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"}); + return GroupDeleteResult::SUCCESS; +} + + +/* Server group manager */ +ServerGroupManager::ServerGroupManager(const std::shared_ptr &handle, std::shared_ptr parent) + : AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent} +{ + +} +std::shared_ptr ServerGroupManager::allocate_group(GroupId id, GroupType type, std::string name, + std::shared_ptr permissions) { + return std::make_shared(this->server_id(), id, type, name, permissions); +} + + +/* Channel group manager */ +ChannelGroupManager::ChannelGroupManager(const std::shared_ptr &handle, std::shared_ptr parent) + : AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent} +{ + +} +std::shared_ptr ChannelGroupManager::allocate_group(GroupId id, GroupType type, std::string name, + std::shared_ptr permissions) { + return std::make_shared(this->server_id(), id, type, name, permissions); +} \ No newline at end of file diff --git a/server/src/groups/GroupManager.h b/server/src/groups/GroupManager.h new file mode 100644 index 0000000..92bdd2b --- /dev/null +++ b/server/src/groups/GroupManager.h @@ -0,0 +1,247 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "./GroupAssignmentManager.h" +#include "./Group.h" + +namespace ts::server::groups { + enum struct GroupCalculateMode { + LOCAL, /* only calculate clients groups for the local server */ + GLOBAL /* use the parent group manager as well, if existing */ + }; + + enum struct GroupLoadResult { + SUCCESS, + NO_GROUPS, + DATABASE_ERROR + }; + + enum struct GroupCreateResult { + SUCCESS, + NAME_ALREADY_IN_USED, + FAILED_TO_GENERATE_ID, + DATABASE_ERROR + }; + + enum struct GroupCopyResult { + SUCCESS, + UNKNOWN_SOURCE_GROUP, + UNKNOWN_TARGET_GROUP, + NAME_ALREADY_IN_USE, + FAILED_TO_GENERATE_ID, + DATABASE_ERROR + }; + + enum struct GroupRenameResult { + SUCCESS, + INVALID_GROUP_ID, + NAME_ALREADY_USED, + NAME_INVALID, + DATABASE_ERROR + }; + + enum struct GroupDeleteResult { + SUCCESS, + INVALID_GROUP_ID, + DATABASE_ERROR + }; + + class GroupManager; + class AbstractGroupManager { + friend class Group; + friend class GroupAssignmentManager; + public: + enum struct DatabaseGroupTarget : uint8_t { + SERVER = 0x00, + CHANNEL = 0x01 + }; + + AbstractGroupManager( + sql::SqlManager* /* database */, + DatabaseGroupTarget /* database group target */, + ServerId /* virtual server id */, + std::shared_ptr /* parent */ + ); + + virtual ~AbstractGroupManager() = default; + + bool initialize(std::string& /* error */); + GroupLoadResult load_data(bool /* initialize */ = false); + void unload_data(); + void reset_groups(bool /* cleanup database */); + protected: + std::shared_ptr parent_manager_; + DatabaseGroupTarget database_target_; + + sql::SqlManager* database_; + ServerId virtual_server_id_; + + /* recursive_mutex due to the copy group methods */ + std::recursive_mutex group_manage_mutex_{}; + std::mutex group_mutex_{}; + + /* I think std::vector is better here because we will iterate more often than add groups */ + std::vector> groups_{}; + + [[nodiscard]] sql::SqlManager* sql_manager(); + [[nodiscard]] ServerId server_id(); + + [[nodiscard]] std::shared_ptr find_group_(GroupCalculateMode /* mode */, GroupId /* group id */); + [[nodiscard]] std::shared_ptr find_group_by_name_(GroupCalculateMode /* mode */, const std::string& /* group name */); + [[nodiscard]] GroupCreateResult create_group_(GroupType type, const std::string& /* group name */, std::shared_ptr& /* result */); + [[nodiscard]] GroupCopyResult copy_group_(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr& /* result */); + [[nodiscard]] GroupCopyResult copy_group_permissions_(GroupId /* group id */, GroupId /* target group */); + [[nodiscard]] GroupRenameResult rename_group_(GroupId /* group id */, const std::string& /* target group name */); + [[nodiscard]] GroupDeleteResult delete_group_(GroupId /* group id */); + + int insert_group_from_sql(int /* length */, std::string* /* values */, std::string* /* columns */); + + virtual std::shared_ptr allocate_group( + GroupId /* id */, + GroupType /* type */, + std::string /* name */, + std::shared_ptr /* permissions */ + ) = 0; + }; + + class ServerGroupManager : public AbstractGroupManager { + public: + ServerGroupManager(const std::shared_ptr& /* owner */, std::shared_ptr /* parent */); + + [[nodiscard]] inline std::shared_ptr find_group(GroupCalculateMode mode, GroupId group_id) { + return this->cast_result(this->find_group_(mode, group_id)); + } + + [[nodiscard]] inline std::shared_ptr find_group_by_name(GroupCalculateMode mode, const std::string& group_name) { + return this->cast_result(this->find_group_by_name_(mode, group_name)); + } + + [[nodiscard]] inline GroupCreateResult create_group(GroupType type, const std::string& group_name, std::shared_ptr& result) { + std::shared_ptr result_; + auto r = this->create_group_(type, group_name, result_); + result = this->cast_result(result_); + return r; + } + + [[nodiscard]] inline GroupCopyResult copy_group(GroupId group_id, GroupType target_group_type, const std::string& target_group_name, std::shared_ptr& result) { + std::shared_ptr result_; + auto r = this->copy_group_(group_id, target_group_type, target_group_name, result_); + result = this->cast_result(result_); + return r; + } + + [[nodiscard]] inline GroupCopyResult copy_group_permissions(GroupId group_id, GroupId target_group) { + return this->copy_group_permissions_(group_id, target_group); + } + + [[nodiscard]] inline GroupRenameResult rename_group(GroupId group_id, const std::string& target_group_name) { + return this->rename_group_(group_id, target_group_name); + } + + [[nodiscard]] inline GroupDeleteResult delete_group(GroupId group_id) { + return this->delete_group_(group_id); + } + + protected: + [[nodiscard]] std::shared_ptr allocate_group(GroupId, GroupType, std::string, + std::shared_ptr) override; + + [[nodiscard]] inline std::shared_ptr cast_result(std::shared_ptr result) { + if(!result) { + return nullptr; + } + + auto casted = std::dynamic_pointer_cast(result); + assert(casted); + return casted; + } + }; + + class ChannelGroupManager : public AbstractGroupManager { + public: + ChannelGroupManager(const std::shared_ptr& /* owner */, std::shared_ptr /* parent */); + + [[nodiscard]] inline std::shared_ptr find_group(GroupCalculateMode mode, GroupId group_id) { + return this->cast_result(this->find_group_(mode, group_id)); + } + + [[nodiscard]] inline std::shared_ptr find_group_by_name(GroupCalculateMode mode, const std::string& group_name) { + return this->cast_result(this->find_group_by_name_(mode, group_name)); + } + + [[nodiscard]] inline GroupCreateResult create_group(GroupType type, const std::string& group_name, std::shared_ptr& result) { + std::shared_ptr result_; + auto r = this->create_group_(type, group_name, result_); + result = this->cast_result(result_); + return r; + } + + [[nodiscard]] inline GroupCopyResult copy_group(GroupId group_id, GroupType target_group_type, const std::string& target_group_name, std::shared_ptr& result) { + std::shared_ptr result_; + auto r = this->copy_group_(group_id, target_group_type, target_group_name, result_); + result = this->cast_result(result_); + return r; + } + + [[nodiscard]] inline GroupCopyResult copy_group_permissions(GroupId group_id, GroupId target_group) { + return this->copy_group_permissions_(group_id, target_group); + } + + [[nodiscard]] inline GroupRenameResult rename_group(GroupId group_id, const std::string& target_group_name) { + return this->rename_group_(group_id, target_group_name); + } + + [[nodiscard]] inline GroupDeleteResult delete_group(GroupId group_id) { + return this->delete_group_(group_id); + } + private: + std::shared_ptr allocate_group(GroupId, GroupType, std::string, + std::shared_ptr) override; + + [[nodiscard]] inline std::shared_ptr cast_result(std::shared_ptr result) { + if(!result) { + return nullptr; + } + + auto casted = std::dynamic_pointer_cast(result); + assert(casted); + return casted; + } + }; + + class GroupManager { + friend class Group; + friend class ServerGroupManager; + friend class ChannelGroupManager; + friend class GroupAssignmentManager; + public: + GroupManager(sql::SqlManager* /* database */, ServerId /* virtual server id */, std::shared_ptr /* parent */); + ~GroupManager(); + + bool initialize(const std::shared_ptr& /* self ref, */, std::string& /* error */); + + [[nodiscard]] inline const std::shared_ptr& parent_manager() { return this->parent_manager_; } + + [[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; } + [[nodiscard]] inline const std::shared_ptr& server_groups() { return this->server_groups_; } + [[nodiscard]] inline const std::shared_ptr& channel_groups() { return this->channel_groups_; } + private: + sql::SqlManager* database_; + ServerId virtual_server_id_; + + std::shared_ptr parent_manager_; + + std::shared_ptr server_groups_{}; + std::shared_ptr channel_groups_{}; + + GroupAssignmentManager assignment_manager_; + + [[nodiscard]] sql::SqlManager* sql_manager(); + [[nodiscard]] ServerId server_id(); + }; +} \ No newline at end of file diff --git a/server/src/server/QueryServer.cpp b/server/src/server/QueryServer.cpp index 067ee6f..dc62b6c 100644 --- a/server/src/server/QueryServer.cpp +++ b/server/src/server/QueryServer.cpp @@ -44,7 +44,7 @@ void QueryServer::unregisterConnection(const shared_ptr &client) { if(client->server) { client->server->getGroupManager()->disableCache(client->getClientDatabaseId()); } else { - serverInstance->getGroupManager()->disableCache(client->getClientDatabaseId()); + serverInstance->getOldGroupManager()->disableCache(client->getClientDatabaseId()); } /* client->handle = nullptr; */ }