Introduced the new group manager

This commit is contained in:
WolverinDEV 2021-02-26 21:32:57 +01:00
parent ee52f4b8d9
commit 6f2d8ab6e4
23 changed files with 1395 additions and 91 deletions

View File

@ -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

View File

@ -167,14 +167,13 @@ void DatabaseHelper::deleteClient(const std::shared_ptr<VirtualServer>& server,
}
inline sql::result load_permissions_v2(
const std::shared_ptr<VirtualServer>& 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<v2::PermissionManager> 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<ts::server::Vir
}
std::shared_ptr<permission::v2::PermissionManager> DatabaseHelper::loadGroupPermissions(const std::shared_ptr<VirtualServer>& server, ts::GroupId group_id) {
std::shared_ptr<permission::v2::PermissionManager> DatabaseHelper::loadGroupPermissions(const ServerId& server_id, ts::GroupId group_id, uint8_t /* target */) {
auto result = std::make_shared<v2::PermissionManager>();
if(this->use_startup_cache && server) {
if(this->use_startup_cache && server_id > 0) {
shared_ptr<StartupCacheEntry> 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<permission::v2::PermissionManager> 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<ts::server::VirtualServer> &server, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionManager> &permissions) {
void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId group_id, uint8_t /* target */, const std::shared_ptr<ts::permission::v2::PermissionManager> &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_ptr<ts::server::Virt
query
);
sql::command(this->sql, 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<permission::v2::PermissionManager> 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<permission::v2::PermissionManager> 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;
}

View File

@ -98,8 +98,8 @@ namespace ts::server {
std::shared_ptr<permission::v2::PermissionManager> loadChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId);
void saveChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId, const std::shared_ptr<permission::v2::PermissionManager>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionManager> loadGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId);
void saveGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId, const std::shared_ptr<permission::v2::PermissionManager>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionManager> loadGroupPermissions(const ServerId& server_id, GroupId, uint8_t /* target */);
void saveGroupPermissions(const ServerId&, GroupId, uint8_t /* target */, const std::shared_ptr<permission::v2::PermissionManager>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionManager> loadPlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId /* playlist id */);
void savePlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId, const std::shared_ptr<permission::v2::PermissionManager>& /* permission manager */);

View File

@ -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<Group> GroupManager::createGroup(GroupTarget target, GroupType t
std::shared_ptr<Group> group = std::make_shared<Group>(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<Group> &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> 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;
}

View File

@ -128,10 +128,10 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) {
dynamic_pointer_cast<InternalClient>(this->_musicRoot)->initialize_weak_reference(this->_musicRoot);
{
this->groupManager = std::make_shared<GroupManager>(nullptr, this->getSql());
this->groupManager->loadGroupFormDatabase();
this->__old__groupManager = std::make_shared<GroupManager>(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<GroupId>()));
auto instance_server_admin = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as<GroupId>());
auto instance_server_admin = this->__old__groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as<GroupId>());
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<GroupId>()));
auto instance_server_guest = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>());
auto instance_server_guest = this->__old__groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>());
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<GroupId>()));
auto instance_server_music = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save<GroupId>());
auto instance_server_music = this->__old__groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save<GroupId>());
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<milliseconds>(end - begin).count());
}
@ -899,7 +899,7 @@ std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlag
//TODO: Negate?
//TODO: May move this part to the instance?
auto server_groups = this->getGroupManager()->getServerGroups(cldbid, type);
auto server_groups = this->getOldGroupManager()->getServerGroups(cldbid, type);
for(const auto& permission : permissions) {
permission::v2::PermissionFlaggedValue value{0, false};

View File

@ -47,7 +47,7 @@ namespace ts {
}
std::shared_ptr<ts::server::InternalClient> getInitialServerAdmin(){ return globalServerAdmin; }
std::shared_ptr<ts::GroupManager> getGroupManager(){ return groupManager; }
std::shared_ptr<ts::GroupManager> getOldGroupManager(){ return __old__groupManager; }
std::shared_ptr<ts::ServerChannelTree> getChannelTree() { return this->default_tree; }
std::shared_mutex& getChannelTreeLock() { return this->default_tree_lock; }
@ -143,7 +143,7 @@ namespace ts {
std::shared_ptr<ts::Properties> default_server_properties = nullptr;
std::shared_ptr<ts::ServerChannelTree> default_tree = nullptr;
std::shared_mutex default_tree_lock;
std::shared_ptr<ts::GroupManager> groupManager = nullptr;
std::shared_ptr<ts::GroupManager> __old__groupManager = nullptr;
std::shared_ptr<ts::server::InternalClient> globalServerAdmin = nullptr;
std::shared_ptr<ConnectedClient> _musicRoot = nullptr;

View File

@ -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

View File

@ -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<milliseconds>(end - begin).count());
}

View File

@ -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<shared_ptr<Group>> 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<GroupId>());
auto default_server_music = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as<GroupId>());
auto default_server_guest = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as<GroupId>());
auto default_server_admin = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP].as<GroupId>());
auto default_server_music = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as<GroupId>());
auto default_server_guest = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as<GroupId>());
auto default_channel_admin = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as<GroupId>());
auto default_channel_guest = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as<GroupId>());
auto default_channel_admin = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as<GroupId>());
auto default_channel_guest = serverInstance->getOldGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as<GroupId>());
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);
}

View File

@ -578,7 +578,7 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel>
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());
}

View File

@ -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 {

View File

@ -177,7 +177,7 @@ permission::v2::PermissionFlaggedValue DataClient::calculate_permission(
}
std::vector<std::shared_ptr<GroupAssignment>> 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());
}

View File

@ -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>() == 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<GroupId>();
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<GroupId>());
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<GroupId>());
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<GroupId>());
auto channelGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["cgid"].as<GroupId>());
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<GroupId>());
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<GroupId>());
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);

View File

@ -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<GroupId>();
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<GroupId>());
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<GroupId>());
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<GroupId>());
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<ClientDbId>();
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<ClientDbId>();
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<GroupId>());
auto serverGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["sgid"].as<GroupId>());
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<GroupId>());
auto serverGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["sgid"].as<GroupId>());
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<GroupId>());
auto serverGroup = (this->server ? this->server->groups : serverInstance->getOldGroupManager().get())->findGroup(cmd["sgid"].as<GroupId>());
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<shared_ptr<Group>> 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<shared_ptr<Group>> 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;

View File

@ -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());
}

View File

@ -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();
}

View File

@ -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<permission::v2::PermissionManager> 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<permission::v2::PermissionManager> &permissions) {
assert(permissions);
this->permissions_ = permissions;
}
ServerGroup::ServerGroup(ServerId sid, GroupId id, GroupType type, std::string name,
std::shared_ptr<permission::v2::PermissionManager> 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<permission::v2::PermissionManager> permissions)
: Group{sid, id, type, std::move(name), std::move(permissions)}
{
}

86
server/src/groups/Group.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include <memory>
#include <PermissionManager.h>
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<permission::v2::PermissionManager> /* 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<permission::v2::PermissionManager> 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<permission::v2::PermissionManager> permissions_;
void set_permissions(const std::shared_ptr<permission::v2::PermissionManager>& /* permissions */);
};
class ServerGroup : public Group {
public:
ServerGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
};
class ChannelGroup : public Group {
public:
ChannelGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
};
}
DEFINE_TRANSFORMS(ts::server::groups::GroupType, uint8_t);
DEFINE_TRANSFORMS(ts::server::groups::GroupNameMode, uint8_t);

View File

@ -0,0 +1,497 @@
//
// Created by WolverinDEV on 03/03/2020.
//
#include <log/LogUtils.h>
#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<ClientCache> 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<ClientCache>();
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<ClientCache>();
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<ClientCache>& 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<ts::GroupId> GroupAssignmentManager::server_groups_of_client(ts::server::groups::GroupAssignmentCalculateMode mode,
ts::ClientDbId cldbid) {
std::vector<ts::GroupId> 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<ChannelGroupAssignment> GroupAssignmentManager::channel_group_of_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) {
std::vector<ChannelGroupAssignment> 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<ts::ClientDbId> GroupAssignmentManager::server_group_clients(GroupId group_id) {
std::deque<ts::ClientDbId> 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<ClientCache>();
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<ClientCache>();
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;
}
}
}

View File

@ -0,0 +1,106 @@
#pragma once
#include <mutex>
#include <deque>
#include <string>
#include <memory>
#include <utility>
#include <Definitions.h>
#include <Properties.h>
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<GroupId> server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
[[nodiscard]] std::vector<ChannelGroupAssignment> channel_group_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
[[nodiscard]] std::deque<ClientDbId> server_group_clients(GroupId /* group id */);
//[[nodiscard]] std::deque<ClientDbId> 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<ChannelGroupAssignment> channel_group_assignments{};
std::deque<ServerGroupAssignment> server_group_assignments{};
};
GroupManager* manager_;
std::mutex client_cache_lock;
std::deque<std::unique_ptr<ClientCache>> client_cache{};
[[nodiscard]] sql::SqlManager* sql_manager();
[[nodiscard]] ServerId server_id();
};
}
}

View File

@ -0,0 +1,346 @@
//
// Created by WolverinDEV on 03/03/2020.
//
#include <string>
#include <log/LogUtils.h>
#include "./GroupManager.h"
#include "../InstanceHandler.h"
using namespace ts::server::groups;
GroupManager::GroupManager(sql::SqlManager *sql, ServerId server_id, std::shared_ptr<GroupManager> 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<GroupManager>& self, std::string &error) {
assert(&*self == this);
if(this->parent_manager_) {
this->server_groups_ = std::make_shared<ServerGroupManager>(self, this->parent_manager_->server_groups_);
this->channel_groups_ = std::make_shared<ChannelGroupManager>(self, this->parent_manager_->channel_groups_);
} else {
this->server_groups_ = std::make_shared<ServerGroupManager>(self, nullptr);
this->channel_groups_ = std::make_shared<ChannelGroupManager>(self, nullptr);
}
return true;
}
/* Abstract group manager */
AbstractGroupManager::AbstractGroupManager(
sql::SqlManager* database,
DatabaseGroupTarget database_target_,
ServerId virtual_server_id,
std::shared_ptr<AbstractGroupManager> 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<Group> 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>& 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<Group> 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> &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<Group>& 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<ServerGroup>(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<Group>& 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>& 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<GroupManager> &handle, std::shared_ptr<ServerGroupManager> parent)
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent}
{
}
std::shared_ptr<Group> ServerGroupManager::allocate_group(GroupId id, GroupType type, std::string name,
std::shared_ptr<permission::v2::PermissionManager> permissions) {
return std::make_shared<ServerGroup>(this->server_id(), id, type, name, permissions);
}
/* Channel group manager */
ChannelGroupManager::ChannelGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ChannelGroupManager> parent)
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent}
{
}
std::shared_ptr<Group> ChannelGroupManager::allocate_group(GroupId id, GroupType type, std::string name,
std::shared_ptr<permission::v2::PermissionManager> permissions) {
return std::make_shared<ChannelGroup>(this->server_id(), id, type, name, permissions);
}

View File

@ -0,0 +1,247 @@
#pragma once
#include <mutex>
#include <deque>
#include <string>
#include <memory>
#include <Definitions.h>
#include <sql/SqlQuery.h>
#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<AbstractGroupManager> /* 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<AbstractGroupManager> 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<std::shared_ptr<Group>> groups_{};
[[nodiscard]] sql::SqlManager* sql_manager();
[[nodiscard]] ServerId server_id();
[[nodiscard]] std::shared_ptr<Group> find_group_(GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<Group> 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<Group>& /* result */);
[[nodiscard]] GroupCopyResult copy_group_(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<Group>& /* 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<Group> allocate_group(
GroupId /* id */,
GroupType /* type */,
std::string /* name */,
std::shared_ptr<permission::v2::PermissionManager> /* permissions */
) = 0;
};
class ServerGroupManager : public AbstractGroupManager {
public:
ServerGroupManager(const std::shared_ptr<GroupManager>& /* owner */, std::shared_ptr<ServerGroupManager> /* parent */);
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group(GroupCalculateMode mode, GroupId group_id) {
return this->cast_result(this->find_group_(mode, group_id));
}
[[nodiscard]] inline std::shared_ptr<ServerGroup> 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<ServerGroup>& result) {
std::shared_ptr<Group> 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<ServerGroup>& result) {
std::shared_ptr<Group> 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<Group> allocate_group(GroupId, GroupType, std::string,
std::shared_ptr<permission::v2::PermissionManager>) override;
[[nodiscard]] inline std::shared_ptr<ServerGroup> cast_result(std::shared_ptr<Group> result) {
if(!result) {
return nullptr;
}
auto casted = std::dynamic_pointer_cast<ServerGroup>(result);
assert(casted);
return casted;
}
};
class ChannelGroupManager : public AbstractGroupManager {
public:
ChannelGroupManager(const std::shared_ptr<GroupManager>& /* owner */, std::shared_ptr<ChannelGroupManager> /* parent */);
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group(GroupCalculateMode mode, GroupId group_id) {
return this->cast_result(this->find_group_(mode, group_id));
}
[[nodiscard]] inline std::shared_ptr<ChannelGroup> 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<ChannelGroup>& result) {
std::shared_ptr<Group> 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<ChannelGroup>& result) {
std::shared_ptr<Group> 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<Group> allocate_group(GroupId, GroupType, std::string,
std::shared_ptr<permission::v2::PermissionManager>) override;
[[nodiscard]] inline std::shared_ptr<ChannelGroup> cast_result(std::shared_ptr<Group> result) {
if(!result) {
return nullptr;
}
auto casted = std::dynamic_pointer_cast<ChannelGroup>(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<GroupManager> /* parent */);
~GroupManager();
bool initialize(const std::shared_ptr<GroupManager>& /* self ref, */, std::string& /* error */);
[[nodiscard]] inline const std::shared_ptr<GroupManager>& parent_manager() { return this->parent_manager_; }
[[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; }
[[nodiscard]] inline const std::shared_ptr<ServerGroupManager>& server_groups() { return this->server_groups_; }
[[nodiscard]] inline const std::shared_ptr<ChannelGroupManager>& channel_groups() { return this->channel_groups_; }
private:
sql::SqlManager* database_;
ServerId virtual_server_id_;
std::shared_ptr<GroupManager> parent_manager_;
std::shared_ptr<ServerGroupManager> server_groups_{};
std::shared_ptr<ChannelGroupManager> channel_groups_{};
GroupAssignmentManager assignment_manager_;
[[nodiscard]] sql::SqlManager* sql_manager();
[[nodiscard]] ServerId server_id();
};
}

View File

@ -44,7 +44,7 @@ void QueryServer::unregisterConnection(const shared_ptr<QueryClient> &client) {
if(client->server) {
client->server->getGroupManager()->disableCache(client->getClientDatabaseId());
} else {
serverInstance->getGroupManager()->disableCache(client->getClientDatabaseId());
serverInstance->getOldGroupManager()->disableCache(client->getClientDatabaseId());
}
/* client->handle = nullptr; */
}