Some more work for the new group manager

This commit is contained in:
WolverinDEV 2021-03-07 19:17:20 +01:00
parent 449df23a4b
commit 2d0b9da08d
19 changed files with 956 additions and 834 deletions

View File

@ -421,7 +421,7 @@ int main(int argc, char** argv) {
{ {
rlimit rlimit{0, 0}; rlimit rlimit{0, 0};
//forum.teaspeak.de/index.php?threads/2570/ //forum.teaspeak.de/index.php?threads/2570/
constexpr auto seek_help_message = "Fore more help visit the forum and read this thread (https://forum.teaspeak.de/index.php?threads/2570/)."; constexpr auto seek_help_message = "For more help visit the forum and read this thread (https://forum.teaspeak.de/index.php?threads/2570/).";
if(getrlimit(RLIMIT_NOFILE, &rlimit) != 0) { if(getrlimit(RLIMIT_NOFILE, &rlimit) != 0) {
//prlimit -n4096 -p pid_of_process //prlimit -n4096 -p pid_of_process
logWarningFmt(true, LOG_INSTANCE, "Failed to get open file rlimit ({}). Please ensure its over 16384.", strerror(errno)); logWarningFmt(true, LOG_INSTANCE, "Failed to get open file rlimit ({}). Please ensure its over 16384.", strerror(errno));

View File

@ -84,6 +84,16 @@ void ClientPermissionCalculator::initialize_client(DataClient* client) {
} }
} }
PermissionFlaggedValue ClientPermissionCalculator::calculate_permission(permission::PermissionType permission,
bool granted) {
auto result = this->calculate_permissions({permission}, granted);
if(result.empty()) {
return { permNotGranted, false };
}
return result.front().second;
}
std::vector<std::pair<PermissionType, PermissionFlaggedValue>> ClientPermissionCalculator::calculate_permissions( std::vector<std::pair<PermissionType, PermissionFlaggedValue>> ClientPermissionCalculator::calculate_permissions(
const std::deque<permission::PermissionType> &permissions, const std::deque<permission::PermissionType> &permissions,
bool calculate_granted bool calculate_granted
@ -328,7 +338,8 @@ const std::shared_ptr<groups::ChannelGroup>& ClientPermissionCalculator::assigne
return *this->assigned_channel_group_; return *this->assigned_channel_group_;
} }
auto channel_group_assignment = this->group_manager_->assignments().channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->client_database_id, this->channel_id_); auto channel_group_assignment = this->group_manager_->assignments().exact_channel_group_of_client(
groups::GroupAssignmentCalculateMode::GLOBAL, this->client_database_id, this->channel_id_);
if(!channel_group_assignment.has_value()) { if(!channel_group_assignment.has_value()) {
return *this->assigned_channel_group_; return *this->assigned_channel_group_;
} }

View File

@ -8,6 +8,7 @@
#include <misc/sassert.h> #include <misc/sassert.h>
#include <src/manager/ActionLogger.h> #include <src/manager/ActionLogger.h>
#include "InstanceHandler.h" #include "InstanceHandler.h"
#include "./groups/GroupManager.h"
using namespace std; using namespace std;
using namespace ts::server; using namespace ts::server;
@ -264,33 +265,6 @@ void VirtualServer::testBanStateChange(const std::shared_ptr<ConnectedClient>& i
}); });
} }
bool VirtualServer::could_default_create_channel() {
{
auto default_group = this->group_manager()->defaultGroup(GroupTarget::GROUPTARGET_SERVER);
if(default_group) {
auto flag = default_group->permissions()->permission_value_flagged(permission::b_channel_create_temporary).value == 1;
flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_semi_permanent).value == 1;
flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_permanent).value == 1;
if(flag)
return true;
}
}
{
auto default_group = this->group_manager()->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
if(default_group) {
auto flag = default_group->permissions()->permission_value_flagged(permission::b_channel_create_temporary).value == 1;
flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_semi_permanent).value == 1;
flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_permanent).value == 1;
if(flag)
return true;
}
}
return false;
}
void VirtualServer::notify_client_ban(const shared_ptr<ConnectedClient> &target, const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::string &reason, size_t time) { void VirtualServer::notify_client_ban(const shared_ptr<ConnectedClient> &target, const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::string &reason, size_t time) {
/* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */
lock_guard command_lock(target->command_lock); lock_guard command_lock(target->command_lock);
@ -394,11 +368,13 @@ void VirtualServer::delete_channel(shared_ptr<ts::ServerChannel> channel, const
command_locks.push_back(move(unique_lock(client->command_lock))); command_locks.push_back(move(unique_lock(client->command_lock)));
} }
for(const auto& client : clients) for(const auto& client : clients) {
this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock);
}
if(!tree_lock.owns_lock()) if(!tree_lock.owns_lock()) {
tree_lock.lock(); /* no clients left within that tree */ tree_lock.lock(); /* no clients left within that tree */
}
command_locks.clear(); command_locks.clear();
auto deleted_channels = this->channelTree->delete_channel_root(channel); auto deleted_channels = this->channelTree->delete_channel_root(channel);
@ -412,7 +388,25 @@ void VirtualServer::delete_channel(shared_ptr<ts::ServerChannel> channel, const
client->notifyChannelDeleted(client->channels->delete_channel_root(channel), invoker); client->notifyChannelDeleted(client->channels->delete_channel_root(channel), invoker);
}); });
this->tokenManager->handle_channel_deleted(channel->channelId()); {
std::vector<ChannelId> deleted_channel_ids{};
deleted_channel_ids.reserve(deleted_channels.size());
for(const auto& deleted_channel : deleted_channels) {
deleted_channel_ids.push_back(deleted_channel->channelId());
}
auto ref_self = this->ref();
task_id task_id{};
serverInstance->general_task_executor()->schedule(task_id, "database cleanup after channel delete", [ref_self, deleted_channel_ids]{
for(const auto& deleted_channel_id : deleted_channel_ids) {
ref_self->tokenManager->handle_channel_deleted(deleted_channel_id);
}
for(const auto& deleted_channel_id : deleted_channel_ids) {
ref_self->group_manager()->assignments().handle_channel_deleted(deleted_channel_id);
}
});
}
} }
void VirtualServer::client_move( void VirtualServer::client_move(
@ -450,10 +444,6 @@ void VirtualServer::client_move(
s_target_channel = dynamic_pointer_cast<ServerChannel>(target_channel); s_target_channel = dynamic_pointer_cast<ServerChannel>(target_channel);
assert(s_target_channel); assert(s_target_channel);
} }
/* update the group properties here already, so for all enter views we could just send the new props directly */
changed_groups = this->groups->update_server_group_property(target, true, s_target_channel);
client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter?
} }
auto l_target_channel = s_target_channel ? this->channelTree->findLinkedChannel(s_target_channel->channelId()) : nullptr; auto l_target_channel = s_target_channel ? this->channelTree->findLinkedChannel(s_target_channel->channelId()) : nullptr;
auto l_source_channel = s_source_channel ? this->channelTree->findLinkedChannel(s_source_channel->channelId()) : nullptr; auto l_source_channel = s_source_channel ? this->channelTree->findLinkedChannel(s_source_channel->channelId()) : nullptr;
@ -560,12 +550,7 @@ void VirtualServer::client_move(
if (s_source_channel) { if (s_source_channel) {
s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
auto source_channel_group = this->groups->getChannelGroupExact(target->getClientDatabaseId(), s_source_channel, false); this->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId());
if(source_channel_group) {
auto default_data = source_channel_group->group->permissions()->permission_value_flagged(permission::b_group_is_permanent);
if(!default_data.has_value || default_data.value != 1)
this->groups->setChannelGroup(target->getClientDatabaseId(), nullptr, s_source_channel);
}
auto update = target->properties()[property::CLIENT_IS_TALKER].as_or<bool>(false) || auto update = target->properties()[property::CLIENT_IS_TALKER].as_or<bool>(false) ||
target->properties()[property::CLIENT_TALK_REQUEST].as_or<int64_t>(0) > 0; target->properties()[property::CLIENT_TALK_REQUEST].as_or<int64_t>(0) > 0;
@ -582,15 +567,18 @@ void VirtualServer::client_move(
if (s_target_channel) { if (s_target_channel) {
target->task_update_needed_permissions.enqueue(); target->task_update_needed_permissions.enqueue();
target->task_update_displayed_groups.enqueue();
TIMING_STEP(timings, "perm gr upd"); TIMING_STEP(timings, "perm gr upd");
if(s_source_channel) { if(s_source_channel) {
deque<ChannelId> deleted; deque<ChannelId> deleted;
for(const auto& channel : target->channels->test_channel(l_source_channel, l_target_channel)) for(const auto& channel : target->channels->test_channel(l_source_channel, l_target_channel)) {
deleted.push_back(channel->channelId()); deleted.push_back(channel->channelId());
}
if(!deleted.empty()) if(!deleted.empty()) {
target->notifyChannelHide(deleted, false); target->notifyChannelHide(deleted, false);
}
auto i_source_channel = s_source_channel->channelId(); auto i_source_channel = s_source_channel->channelId();
if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) {

View File

@ -6,6 +6,7 @@
#include "VirtualServer.h" #include "VirtualServer.h"
#include "./manager/ConversationManager.h" #include "./manager/ConversationManager.h"
#include "./music/MusicBotManager.h" #include "./music/MusicBotManager.h"
#include "./groups/GroupManager.h"
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@ -234,18 +235,7 @@ void VirtualServer::executeServerTick() {
{ {
BEGIN_TIMINGS(); BEGIN_TIMINGS();
this->group_manager()->save_permissions();
auto groups = this->group_manager()->availableGroups(false);
for(auto& group : groups) {
auto permissions = group->permissions();
if(permissions->require_db_updates()) {
auto begin = system_clock::now();
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());
}
}
END_TIMINGS(timing_groups); END_TIMINGS(timing_groups);
} }

View File

@ -1,7 +1,6 @@
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <protocol/buffers.h> #include <protocol/buffers.h>
#include <openssl/sha.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <bitset> #include <bitset>
#include <tomcrypt.h> #include <tomcrypt.h>
@ -28,6 +27,7 @@
#include <misc/sassert.h> #include <misc/sassert.h>
#include <src/manager/ActionLogger.h> #include <src/manager/ActionLogger.h>
#include "./groups/GroupManager.h" #include "./groups/GroupManager.h"
#include "./PermissionCalculator.h"
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@ -364,13 +364,35 @@ bool VirtualServer::initialize(bool test_properties) {
/* lets cleanup the conversations for not existent channels */ /* lets cleanup the conversations for not existent channels */
this->conversation_manager_->synchronize_channels(); this->conversation_manager_->synchronize_channels();
{
auto ref_self = this->self;
this->task_notify_channel_group_list = multi_shot_task{serverInstance->general_task_executor(), "server notify channel group list", [ref_self]{
auto this_ = ref_self.lock();
if(this_) {
this_->forEachClient([](const std::shared_ptr<ConnectedClient>& client) {
std::optional<ts::command_builder> generated_notify{};
client->notifyChannelGroupList(generated_notify, true);
});
}
}};
this->task_notify_server_group_list = multi_shot_task{serverInstance->general_task_executor(), "server notify server group list", [ref_self]{
auto this_ = ref_self.lock();
if(this_) {
this_->forEachClient([](const std::shared_ptr<ConnectedClient>& client) {
std::optional<ts::command_builder> generated_notify{};
client->notifyServerGroupList(generated_notify, true);
});
}
}};
}
return true; return true;
} }
VirtualServer::~VirtualServer() { VirtualServer::~VirtualServer() {
memtrack::freed<VirtualServer>(this); memtrack::freed<VirtualServer>(this);
delete this->tokenManager; delete this->tokenManager;
delete this->groups;
delete this->channelTree; delete this->channelTree;
delete this->letters; delete this->letters;
delete this->complains; delete this->complains;
@ -653,10 +675,6 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) {
this->webControlServer = nullptr; this->webControlServer = nullptr;
#endif #endif
if(this->groups) {
this->groups->clearCache();
}
properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = 0;
properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = 0;
properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = 0; properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = 0;
@ -778,10 +796,12 @@ std::shared_ptr<ConnectedClient> VirtualServer::findClient(std::string name, boo
bool VirtualServer::forEachClient(std::function<void(std::shared_ptr<ConnectedClient>)> function) { bool VirtualServer::forEachClient(std::function<void(std::shared_ptr<ConnectedClient>)> function) {
for(const auto& elm : this->getClients()) { for(const auto& elm : this->getClients()) {
shared_lock close_lock(elm->finalDisconnectLock, try_to_lock_t{}); shared_lock close_lock(elm->finalDisconnectLock, try_to_lock_t{});
if(close_lock.owns_lock()) //If not locked than client is on the way to disconnect if(close_lock.owns_lock()) {
//If not locked than client is on the way to disconnect
if(elm->state == ConnectionState::CONNECTED && elm->getType() != ClientType::CLIENT_INTERNAL) { if(elm->state == ConnectionState::CONNECTED && elm->getType() != ClientType::CLIENT_INTERNAL) {
function(elm); function(elm);
} }
}
} }
return true; return true;
} }
@ -883,32 +903,6 @@ void VirtualServer::broadcastMessage(std::shared_ptr<ConnectedClient> invoker, s
}); });
} }
std::vector<std::shared_ptr<GroupAssignment>> CalculateCache::getGroupAssignments(VirtualServer* server, ClientDbId cldbid, ClientType type) {
if(assignment_server_groups_set) return assignment_server_groups;
assignment_server_groups = server->group_manager()->getServerGroups(cldbid, type);
assignment_server_groups_set = true;
return assignment_server_groups;
}
std::shared_ptr<GroupAssignment> CalculateCache::getChannelAssignment(VirtualServer* server, ClientDbId client_dbid, ChannelId channel_id) {
if(this->assignment_channel_group_set && this->assignment_channel_group_channel == channel_id) return this->assignment_channel_group;
auto channel = this->getServerChannel(server, channel_id);
assignment_channel_group = channel ? server->group_manager()->getChannelGroup(client_dbid, channel, true) : nullptr;
assignment_channel_group_set = true;
assignment_channel_group_channel = channel_id;
return assignment_channel_group;
}
std::shared_ptr<BasicChannel> CalculateCache::getServerChannel(ts::server::VirtualServer *server, ts::ChannelId channel_id) {
if(this->last_server_channel == channel_id)
return this->server_channel;
this->last_server_channel = channel_id;
this->server_channel = server && channel_id > 0 ? server->getChannelTree()->findChannel(channel_id) : nullptr;
return this->server_channel;
}
ts_always_inline bool channel_ignore_permission(ts::permission::PermissionType type) { ts_always_inline bool channel_ignore_permission(ts::permission::PermissionType type) {
return permission::i_icon_id == type; return permission::i_icon_id == type;
} }
@ -920,207 +914,8 @@ vector<pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlagge
ChannelId channel_id, ChannelId channel_id,
bool calculate_granted, bool calculate_granted,
std::shared_ptr<CalculateCache> cache) { std::shared_ptr<CalculateCache> cache) {
if(permissions.empty()) return {}; ClientPermissionCalculator calculator{this->ref(), client_dbid, client_type, channel_id};
return calculator.calculate_permissions(permissions, calculate_granted);
vector<pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> result;
result.reserve(permissions.size());
if(!cache) {
cache = make_shared<CalculateCache>();
}
if(!cache->client_permissions) {
cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), client_dbid);
}
bool have_skip_permission = false;
int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */
/*
* server_group_data[0] := Server group id
* server_group_data[1] := Skip flag
* server_group_data[2] := Negate flag
* server_group_data[3] := Permission value
*/
typedef std::tuple<GroupId, bool, bool, permission::PermissionValue> GroupData;
bool server_group_data_initialized = false;
vector<GroupData> server_group_data;
GroupData* active_server_group;
/* function to calculate skip permission */
auto calculate_skip = [&]{
skip_permission_type = 0;
/* test for skip permission within the client permission manager */
{
auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
if(skip_value.has_value) {
have_skip_permission = skip_value.value == 1;
skip_permission_type = 1;
logTrace(this->serverId, "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission);
}
}
/* test for skip permission within all server groups */
if(skip_permission_type != 1) {
for(const auto& assignment : cache->getGroupAssignments(this, client_dbid, client_type)) {
auto group_permissions = assignment->group->permissions();
auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
if(flagged_value.has_value) {
have_skip_permission |= flagged_value.value == 1;
if(have_skip_permission) {
logTrace(this->serverId, "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group->groupId(), assignment->group->name(), have_skip_permission);
break;
}
}
}
}
};
auto initialize_group_data = [&](const permission::PermissionType& permission_type) {
server_group_data_initialized = true;
active_server_group = nullptr;
auto assigned_groups = cache->getGroupAssignments(this, client_dbid, client_type);
server_group_data.resize(assigned_groups.size());
auto it = server_group_data.begin();
for(auto& group : assigned_groups) {
auto group_permissions = group->group->permissions();
auto permission_flags = group_permissions->permission_flags(permission_type);
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
if(!flag_set)
continue;
//TODO: Test if there is may a group channel permissions
auto value = group_permissions->permission_values(permission_type);
*it = std::make_tuple(group->group->groupId(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value);
it++;
}
if(it == server_group_data.begin())
return; /* no server group has that permission */
server_group_data.erase(it, server_group_data.end()); /* remove unneeded */
auto found_negate = false;
for(auto& group : server_group_data) {
if(std::get<2>(group)) {
found_negate = true;
break;
}
}
if(found_negate) {
server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end());
logTrace(this->serverId, "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size());
if(server_group_data.empty())
logTrace(this->serverId, "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid);
permission::PermissionValue current_lowest = 0;
for(auto& group : server_group_data) {
if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) {
current_lowest = std::get<3>(group);
active_server_group = &group;
}
}
} else {
permission::PermissionValue current_highest = 0;
for(auto& group : server_group_data) {
if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) {
current_highest = std::get<3>(group);
active_server_group = &group;
}
}
}
};
for(const auto& permission : permissions) {
server_group_data_initialized = false; /* reset all group data */
auto client_permission_flags = cache->client_permissions->permission_flags(permission);
/* lets try to resolve the channel specific permission */
if(channel_id > 0 && client_permission_flags.channel_specific) {
auto data = cache->client_permissions->channel_permission(permission, channel_id);
if(calculate_granted ? data.flags.grant_set : data.flags.value_set) {
result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}});
logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value);
continue;
}
}
bool skip_channel_permissions = channel_id == 0;
if(!skip_channel_permissions) {
/* look if somewhere is the skip permission flag set */
if(skip_permission_type == -1) {/* initialize skip flag */
calculate_skip();
}
skip_channel_permissions = have_skip_permission;
}
if(!skip_channel_permissions) {
/* okey we've no global skip. Then now lookup the groups and the client permissions */
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
/* okey the client has the permission, this counts */
skip_channel_permissions = client_permission_flags.skip;
} else {
if(!server_group_data_initialized)
initialize_group_data(permission);
if(active_server_group)
skip_channel_permissions = std::get<1>(*active_server_group);
}
}
if(!skip_channel_permissions) {
/* lookup the channel group */
{
auto channel_assignment = cache->getChannelAssignment(this, client_dbid, channel_id);
if(channel_assignment) {
auto group_permissions = channel_assignment->group->permissions();
auto permission_flags = group_permissions->permission_flags(permission);
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
if(flag_set) {
auto value = group_permissions->permission_values(permission);
result.push_back({permission, {calculate_granted ? value.grant : value.value, true}});
logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value);
continue;
}
}
}
/* lookup the channel permissions. Whyever? */
{
auto channel = cache->getServerChannel(this, channel_id);
if(channel) {
auto channel_permissions = channel->permissions();
auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission);
if(data.has_value) {
result.push_back({permission, {data.value, true}});
logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value);
continue;
}
}
}
}
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
auto client_value = cache->client_permissions->permission_values(permission);
result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}});
logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value);
continue;
}
if(!server_group_data_initialized)
initialize_group_data(permission);
if(active_server_group) {
result.push_back({permission, {get<3>(*active_server_group), true}});
logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group));
continue;
}
logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name);
result.push_back({permission, {permNotGranted, false}});
}
return result;
} }
permission::v2::PermissionFlaggedValue VirtualServer::calculate_permission( permission::v2::PermissionFlaggedValue VirtualServer::calculate_permission(
@ -1137,13 +932,16 @@ permission::v2::PermissionFlaggedValue VirtualServer::calculate_permission(
} }
bool VirtualServer::verifyServerPassword(std::string password, bool hashed) { bool VirtualServer::verifyServerPassword(std::string password, bool hashed) {
if(!this->properties()[property::VIRTUALSERVER_FLAG_PASSWORD].as_or<bool>(false)) return true; if(!this->properties()[property::VIRTUALSERVER_FLAG_PASSWORD].as_or<bool>(false)) {
if(password.empty()) return false; return true;
}
if(!hashed){ if(password.empty()) {
char buffer[SHA_DIGEST_LENGTH]; return false;
SHA1(reinterpret_cast<const unsigned char *>(password.data()), password.length(), reinterpret_cast<unsigned char *>(buffer)); }
password = base64_encode(string(buffer, SHA_DIGEST_LENGTH));
if(!hashed) {
password = base64::encode(digest::sha1(password));
} }
return password == this->properties()[property::VIRTUALSERVER_PASSWORD].value(); return password == this->properties()[property::VIRTUALSERVER_PASSWORD].value();
@ -1175,105 +973,70 @@ VirtualServer::NetworkReport VirtualServer::generate_network_report() {
} }
bool VirtualServer::resetPermissions(std::string& new_permission_token) { bool VirtualServer::resetPermissions(std::string& new_permission_token) {
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` != :channel_type", variable{":serverId", this->serverId}, variable{":channel_type", permission::SQL_PERM_CHANNEL}).execute()); std::map<GroupId, GroupId> server_group_mapping{};
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->serverId}).execute()); std::map<GroupId, GroupId> channel_group_mapping{};
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->serverId}).execute()); {
this->group_manager()->server_groups()->reset_groups(serverInstance->group_manager(), server_group_mapping);
this->group_manager()->channel_groups()->reset_groups(serverInstance->group_manager(), channel_group_mapping);
this->group_manager()->assignments().reset_all();
}
/* assign the properties */
{
this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] =
server_group_mapping[serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as_or<GroupId>(0)];
this->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP] =
server_group_mapping[serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_or<GroupId>(0)];
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] =
channel_group_mapping[serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as_or<GroupId>(0)];
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] =
channel_group_mapping[serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as_or<GroupId>(0)];
}
{ {
threads::MutexLock lock(this->group_manager()->cacheLock); this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false;
this->group_manager()->deleteAllGroups();
deque<shared_ptr<Group>> saved_groups;
for(const auto& group : serverInstance->group_manager()->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()); auto admin_group_id = server_group_mapping[serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP].as_or<GroupId>(0)];
this->group_manager()->copyGroup(group, GroupType::GROUP_TYPE_NORMAL, group->name(), this->serverId); auto admin_group = this->group_manager()->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, admin_group_id);
if(!admin_group) {
logCritical(this->getServerId(), "Missing default server admin group. Don't generate a new token.");
new_permission_token = "missing server admin group";
} else {
auto token = this->tokenManager->create_token(0, "default server admin token", 1, std::chrono::system_clock::time_point{});
if(!token) {
logCritical(this->serverId, "Failed to register the default server admin token.");
} else {
std::vector<token::TokenAction> actions{};
actions.push_back(token::TokenAction{
.id = 0,
.type = token::ActionType::AddServerGroup,
.id1 = admin_group->group_id(),
.id2 = 0
});
this->tokenManager->add_token_actions(token->id, actions);
new_permission_token = token->token;
this->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = token->token;
this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = true;
}
} }
} }
//Server admin
auto default_server_admin = serverInstance->group_manager()->findGroup(
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP].as_or<GroupId>(0));
auto default_server_music = serverInstance->group_manager()->findGroup(
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_or<GroupId>(0));
auto default_server_guest = serverInstance->group_manager()->findGroup(
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as_or<GroupId>(0));
auto default_channel_admin = serverInstance->group_manager()->findGroup(
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as_or<GroupId>(0));
auto default_channel_guest = serverInstance->group_manager()->findGroup(
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as_or<GroupId>(0));
if(!default_server_guest) {
logCritical(0, "Missing default server guest template group!");
assert(!serverInstance->group_manager()->availableChannelGroups().empty());
default_server_guest = serverInstance->group_manager()->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->group_manager()->availableChannelGroups().empty());
default_channel_admin = serverInstance->group_manager()->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->group_manager()->availableChannelGroups().empty());
default_server_music = serverInstance->group_manager()->availableChannelGroups().front();
logCritical(0, "Using group {} as channel server guest group for server {}.", default_server_music->name(), this->serverId);
}
if(!default_server_admin) {
logCritical(0, "Missing default server admin template group! Using default guest group ({})", default_server_guest->name());
default_server_admin = default_server_guest;
}
if(!default_channel_admin) {
logCritical(0, "Missing default channel admin template group! Using default guest group ({})", default_channel_guest->name());
default_channel_admin = default_channel_guest;
}
this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = this->group_manager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_guest->name()).front()->groupId();
this->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP] = this->group_manager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_music->name()).front()->groupId();
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = this->group_manager()->findGroup(GroupTarget::GROUPTARGET_CHANNEL, default_channel_admin->name()).front()->groupId();
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = this->group_manager()->findGroup(GroupTarget::GROUPTARGET_CHANNEL, default_channel_guest->name()).front()->groupId();
auto server_admin_group_id = this->group_manager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_admin->name()).front()->groupId();
auto token = this->tokenManager->create_token(0, "Default server admin token", 1, std::chrono::system_clock::time_point{});
if(!token) {
logCritical(this->serverId, "Failed to register the default server admin token.");
this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false;
} else {
std::vector<token::TokenAction> actions{};
actions.push_back(token::TokenAction{
.id = 0,
.type = token::ActionType::AddServerGroup,
.id1 = server_admin_group_id,
.id2 = 0
});
this->tokenManager->add_token_actions(token->id, actions);
new_permission_token = token->token;
this->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = token->token;
this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = true;
}
this->ensureValidDefaultGroups(); this->ensureValidDefaultGroups();
for(const auto& client : this->getClients()) { for(const auto& client : this->getClients()) {
if(client->getType() != ClientType::CLIENT_QUERY) { client->task_update_displayed_groups.enqueue();
client->notifyServerGroupList(); client->task_update_needed_permissions.enqueue();
client->notifyChannelGroupList();
}
if(this->notifyClientPropertyUpdates(client, this->group_manager()->update_server_group_property(client, true, client->getChannel()))) {
client->task_update_needed_permissions.enqueue();
}
client->task_update_channel_client_properties.enqueue(); client->task_update_channel_client_properties.enqueue();
} }
this->task_notify_channel_group_list.enqueue();
this->task_notify_server_group_list.enqueue();
return true; return true;
} }
@ -1395,15 +1158,16 @@ void VirtualServer::update_channel_from_permissions(const std::shared_ptr<BasicC
deleted.push_back(channel->channelId()); deleted.push_back(channel->channelId());
} }
} }
if(!deleted.empty()) if(!deleted.empty()) {
cl->notifyChannelHide(deleted, false); cl->notifyChannelHide(deleted, false);
}
} }
}); });
} }
} }
std::shared_ptr<groups::ChannelGroup> VirtualServer::default_channel_group() { std::shared_ptr<groups::ChannelGroup> VirtualServer::default_channel_group() {
auto group_id = this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_save<GroupId>(); auto group_id = this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_or<GroupId>(0);
auto group = this->group_manager()->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id); auto group = this->group_manager()->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) { if(!group) {
auto groups = this->group_manager()->channel_groups()->available_groups(groups::GroupCalculateMode::GLOBAL); auto groups = this->group_manager()->channel_groups()->available_groups(groups::GroupCalculateMode::GLOBAL);

View File

@ -110,25 +110,7 @@ namespace ts {
uint16_t bots = 0; uint16_t bots = 0;
}; };
struct CalculateCache { struct CalculateCache {};
bool global_skip = false;
bool global_skip_set = false;
std::shared_ptr<permission::v2::PermissionManager> client_permissions;
std::vector<std::shared_ptr<GroupAssignment>> assignment_server_groups;
bool assignment_server_groups_set = false;
ChannelId assignment_channel_group_channel;
std::shared_ptr<GroupAssignment> assignment_channel_group;
bool assignment_channel_group_set = false;
std::shared_ptr<BasicChannel> server_channel;
ChannelId last_server_channel = 0;
inline std::vector<std::shared_ptr<GroupAssignment>> getGroupAssignments(VirtualServer* server, ClientDbId cldbid, ClientType type);
inline std::shared_ptr<GroupAssignment> getChannelAssignment(VirtualServer* server, ClientDbId client_dbid, ChannelId channel);
inline std::shared_ptr<BasicChannel> getServerChannel(VirtualServer*, ChannelId);
};
class VirtualServer { class VirtualServer {
friend class WebClient; friend class WebClient;
@ -258,8 +240,6 @@ namespace ts {
ServerState::value getState() { return this->state; } ServerState::value getState() { return this->state; }
bool could_default_create_channel();
inline std::shared_ptr<VirtualServer> ref() { return this->self.lock(); } inline std::shared_ptr<VirtualServer> ref() { return this->self.lock(); }
inline bool disable_ip_saving() { return this->_disable_ip_saving; } inline bool disable_ip_saving() { return this->_disable_ip_saving; }
inline std::chrono::system_clock::time_point start_timestamp() { return this->startTimestamp; }; inline std::chrono::system_clock::time_point start_timestamp() { return this->startTimestamp; };
@ -299,6 +279,9 @@ namespace ts {
inline auto& get_channel_tree_lock() { return this->channel_tree_lock; } inline auto& get_channel_tree_lock() { return this->channel_tree_lock; }
void update_channel_from_permissions(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* issuer */); void update_channel_from_permissions(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* issuer */);
inline void enqueue_notify_channel_group_list() { this->task_notify_channel_group_list.enqueue(); }
inline void enqueue_notify_server_group_list() { this->task_notify_server_group_list.enqueue(); }
protected: protected:
bool registerClient(std::shared_ptr<ConnectedClient>); bool registerClient(std::shared_ptr<ConnectedClient>);
bool unregisterClient(std::shared_ptr<ConnectedClient>, std::string, std::unique_lock<std::shared_mutex>& channel_tree_lock); bool unregisterClient(std::shared_ptr<ConnectedClient>, std::string, std::unique_lock<std::shared_mutex>& channel_tree_lock);
@ -363,6 +346,9 @@ namespace ts {
std::chrono::milliseconds spoken_time{0}; std::chrono::milliseconds spoken_time{0};
std::chrono::system_clock::time_point spoken_time_timestamp; std::chrono::system_clock::time_point spoken_time_timestamp;
multi_shot_task task_notify_channel_group_list{};
multi_shot_task task_notify_server_group_list{};
bool _disable_ip_saving = false; bool _disable_ip_saving = false;
}; };
} }

View File

@ -57,6 +57,13 @@ void ConnectedClient::initialize_weak_reference(const std::shared_ptr<ConnectedC
self->updateChannelClientProperties(true, true); self->updateChannelClientProperties(true, true);
} }
}}; }};
this->task_update_displayed_groups = multi_shot_task{serverInstance->general_task_executor(), "update displayed groups for " + this->getLoggingPeerIp(), [weak_self]{
auto self = weak_self.lock();
if(self) {
self->update_displayed_client_groups();
}
}};
} }
bool ConnectedClient::loadDataForCurrentServer() { bool ConnectedClient::loadDataForCurrentServer() {
@ -1012,6 +1019,63 @@ bool ConnectedClient::update_client_needed_permissions() {
return updated; return updated;
} }
void ConnectedClient::update_displayed_client_groups(bool& server_groups_changed, bool& channel_group_changed) {
auto ref_server = this->server;
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
GroupId channel_group_id{0};
ChannelId channel_inherit_id{0};
std::string server_group_assignments{};
{
auto server_groups = group_manager->assignments().server_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId());
for(const auto& group_id : server_groups) {
server_group_assignments += ",";
server_group_assignments += std::to_string(group_id);
}
if(!server_group_assignments.empty()) {
server_group_assignments = server_group_assignments.substr(1);
}
}
{
std::shared_ptr<BasicChannel> inherited_channel{this->currentChannel};
auto channel_group = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), inherited_channel);
if(channel_group.has_value()) {
assert(inherited_channel);
channel_group_id = *channel_group;
channel_inherit_id = inherited_channel->channelId();
} else {
channel_group_id = 0;
channel_inherit_id = 0;
}
}
server_groups_changed = false;
channel_group_changed = false;
std::deque<property::ClientProperties> updated_properties{};
if(this->properties()[property::CLIENT_SERVERGROUPS].update_value(server_group_assignments)) {
updated_properties.push_back(property::CLIENT_SERVERGROUPS);
server_groups_changed = true;
}
if(this->properties()[property::CLIENT_CHANNEL_GROUP_ID].update_value(channel_group_id)) {
updated_properties.push_back(property::CLIENT_CHANNEL_GROUP_ID);
channel_group_changed = true;
}
if(this->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID].update_value(channel_inherit_id)) {
updated_properties.push_back(property::CLIENT_CHANNEL_GROUP_ID);
channel_group_changed = true;
}
if(!updated_properties.empty() && ref_server) {
ref_server->notifyClientPropertyUpdates(this->ref(), updated_properties);
}
}
void ConnectedClient::sendTSPermEditorWarning() { void ConnectedClient::sendTSPermEditorWarning() {
if(config::voice::warn_on_permission_editor) { if(config::voice::warn_on_permission_editor) {
if(system_clock::now() - this->command_times.servergrouplist > milliseconds(1000)) return; if(system_clock::now() - this->command_times.servergrouplist > milliseconds(1000)) return;
@ -1192,11 +1256,7 @@ void ConnectedClient::useToken(token::TokenId token_id) {
} }
if(tree_registered && (server_groups_changed || channel_group_changed)) { if(tree_registered && (server_groups_changed || channel_group_changed)) {
/* TODO: Set "Update server groups" as task */ this->task_update_displayed_groups.enqueue();
auto updated_properties = this->getServer()->group_manager()->update_server_group_property(this->ref(), true, this->currentChannel);
if(!updated_properties.empty()) {
this->getServer()->notifyClientPropertyUpdates(this->ref(), updated_properties);
}
} }
if(tree_registered && server_groups_changed) { if(tree_registered && server_groups_changed) {

View File

@ -57,6 +57,12 @@ namespace ts {
class WebClient; class WebClient;
class MusicClient; class MusicClient;
namespace groups {
class Group;
class ServerGroup;
class ChannelGroup;
}
struct ConnectionInfoData { struct ConnectionInfoData {
std::chrono::time_point<std::chrono::system_clock> timestamp; std::chrono::time_point<std::chrono::system_clock> timestamp;
std::map<std::string, std::string> properties; std::map<std::string, std::string> properties;
@ -74,7 +80,6 @@ namespace ts {
friend class DataClient; friend class DataClient;
friend class SpeakingClient; friend class SpeakingClient;
friend class connection::VoiceClientConnection; friend class connection::VoiceClientConnection;
friend class ts::GroupManager;
friend class VirtualServerManager; friend class VirtualServerManager;
public: public:
explicit ConnectedClient(sql::SqlManager*, const std::shared_ptr<VirtualServer>& server); explicit ConnectedClient(sql::SqlManager*, const std::shared_ptr<VirtualServer>& server);
@ -129,14 +134,15 @@ namespace ts {
/** Notifies (after request) */ /** Notifies (after request) */
bool sendNeededPermissions(bool /* force an update */); /* invoke this because it dosn't spam the client */ bool sendNeededPermissions(bool /* force an update */); /* invoke this because it dosn't spam the client */
virtual bool notifyClientNeededPermissions(); virtual bool notifyClientNeededPermissions();
virtual bool notifyServerGroupList(bool as_notify = true); virtual bool notifyGroupPermList(const std::shared_ptr<groups::Group>&, bool);
virtual bool notifyGroupPermList(const std::shared_ptr<Group>&, bool);
virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr<permission::v2::PermissionManager>&, bool); virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr<permission::v2::PermissionManager>&, bool);
virtual bool notifyChannelGroupList(bool as_notify = true);
virtual bool notifyConnectionInfo(const std::shared_ptr<ConnectedClient> &target, const std::shared_ptr<ConnectionInfoData> &info); virtual bool notifyConnectionInfo(const std::shared_ptr<ConnectedClient> &target, const std::shared_ptr<ConnectionInfoData> &info);
virtual bool notifyChannelSubscribed(const std::deque<std::shared_ptr<BasicChannel>> &); virtual bool notifyChannelSubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
virtual bool notifyChannelUnsubscribed(const std::deque<std::shared_ptr<BasicChannel>> &); virtual bool notifyChannelUnsubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
virtual bool notifyServerGroupList(std::optional<ts::command_builder>& /* generated notify */, bool /* as notify */);
virtual bool notifyChannelGroupList(std::optional<ts::command_builder>& /* generated notify */, bool /* as notify */);
/** Notifies (without request) */ /** Notifies (without request) */
//Group server //Group server
virtual bool notifyServerUpdated(std::shared_ptr<ConnectedClient>); virtual bool notifyServerUpdated(std::shared_ptr<ConnectedClient>);
@ -299,6 +305,12 @@ namespace ts {
*/ */
bool update_client_needed_permissions(); bool update_client_needed_permissions();
/**
* Attention: This method should never be called directly!
* Use `task_update_displayed_groups` instead to schedule an update.
*/
void update_displayed_client_groups(bool& server_groups_changed, bool& channel_group_changed);
std::shared_lock<std::shared_mutex> require_connected_state(bool blocking = false) { std::shared_lock<std::shared_mutex> require_connected_state(bool blocking = false) {
//try_to_lock_t //try_to_lock_t
std::shared_lock<std::shared_mutex> disconnect_lock{}; std::shared_lock<std::shared_mutex> disconnect_lock{};
@ -398,6 +410,7 @@ namespace ts {
multi_shot_task task_update_needed_permissions{}; multi_shot_task task_update_needed_permissions{};
multi_shot_task task_update_channel_client_properties{}; multi_shot_task task_update_channel_client_properties{};
multi_shot_task task_update_displayed_groups{};
bool loadDataForCurrentServer() override; bool loadDataForCurrentServer() override;

View File

@ -9,6 +9,7 @@
#include <misc/sassert.h> #include <misc/sassert.h>
#include <log/LogUtils.h> #include <log/LogUtils.h>
#include "./web/WebClient.h" #include "./web/WebClient.h"
#include "../groups/GroupManager.h"
using namespace std::chrono; using namespace std::chrono;
using namespace std; using namespace std;
@ -41,41 +42,83 @@ do { \
} \ } \
} while(0) } while(0)
bool ConnectedClient::notifyServerGroupList(bool as_notify) { template <typename T>
Command cmd(as_notify ? "notifyservergrouplist" : ""); inline void build_group_notify(ts::command_builder& notify, bool is_channel_groups, const std::vector<std::shared_ptr<T>>& available_groups) {
int index = 0; std::string group_id_key{};
permission::PermissionType permission_modify{};
permission::PermissionType permission_add{};
permission::PermissionType permission_remove{};
for (const auto& group : (this->server ? this->server->group_manager() : serverInstance->group_manager().get())->availableServerGroups(true)) { if(is_channel_groups) {
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { group_id_key = "cgid";
cmd[index]["cgid"] = group->groupId();
} else {
cmd[index]["sgid"] = group->groupId();
}
for (const auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
cmd[index][std::string{prop.type().name}] = prop.value();
permission_modify = permission::i_channel_group_needed_modify_power;
permission_add = permission::i_channel_group_needed_member_add_power;
permission_remove = permission::i_channel_group_needed_member_remove_power;
} else {
group_id_key = "sgid";
auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); permission_modify = permission::i_server_group_needed_modify_power;
auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power); permission_add = permission::i_server_group_needed_member_add_power;
auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power); permission_remove = permission::i_server_group_needed_member_remove_power;
cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0;
cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0;
cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0;
index++;
} }
this->sendCommand(cmd); size_t index{0};
for(const auto& group : available_groups) {
auto bulk = notify.bulk(index++);
bulk.put_unchecked(group_id_key, group->group_id());
bulk.put_unchecked("type", (uint8_t) group->group_type());
bulk.put_unchecked("name", group->display_name());
bulk.put_unchecked("sortid", group->sort_id());
bulk.put_unchecked("savedb", group->save_assignments());
bulk.put_unchecked("namemode", (uint8_t) group->name_mode());
bulk.put_unchecked("iconid", group->icon_id());
auto modify_power = group->permissions()->permission_value_flagged(permission_modify);
auto add_power = group->permissions()->permission_value_flagged(permission_add);
auto remove_power = group->permissions()->permission_value_flagged(permission_remove);
bulk.put_unchecked("n_modifyp", modify_power.has_value ? modify_power.value : 0);
bulk.put_unchecked("n_member_addp", add_power.has_value ? add_power.value : 0);
bulk.put_unchecked("n_member_removep", remove_power.has_value ? remove_power.value : 0);
}
}
bool ConnectedClient::notifyServerGroupList(std::optional<ts::command_builder> &generated_notify, bool as_notify) {
if(!generated_notify.has_value()) {
auto server_ref = this->server;
auto group_manager = server_ref ? server_ref->group_manager() : serverInstance->group_manager();
auto available_groups = group_manager->channel_groups()->available_groups(groups::GroupCalculateMode::GLOBAL);
build_group_notify(generated_notify.emplace(as_notify ? "notifyservergrouplist" : ""), false, available_groups);
}
this->sendCommand(*generated_notify);
return true; return true;
} }
bool ConnectedClient::notifyChannelGroupList(std::optional<ts::command_builder>& generated_notify, bool as_notify) {
if(!generated_notify.has_value()) {
auto server_ref = this->server;
auto group_manager = server_ref ? server_ref->group_manager() : serverInstance->group_manager();
auto available_groups = group_manager->channel_groups()->available_groups(groups::GroupCalculateMode::GLOBAL);
bool ConnectedClient::notifyGroupPermList(const std::shared_ptr<Group>& group, bool as_sid) { build_group_notify(generated_notify.emplace(as_notify ? "notifychannelgrouplist" : ""), true, available_groups);
ts::command_builder result{this->getExternalType() == CLIENT_TEAMSPEAK ? group->target() == GROUPTARGET_SERVER ? "notifyservergrouppermlist" : "notifychannelgrouppermlist" : ""}; }
if (group->target() == GROUPTARGET_SERVER) this->sendCommand(*generated_notify);
result.put_unchecked(0, "sgid", group->groupId()); return true;
else }
result.put_unchecked(0, "cgid", group->groupId());
bool ConnectedClient::notifyGroupPermList(const std::shared_ptr<groups::Group>& group, bool as_sid) {
auto is_channel_group = !!dynamic_pointer_cast<groups::ChannelGroup>(group);
ts::command_builder result{this->notify_response_command(is_channel_group ? "notifychannelgrouppermlist" : "notifyservergrouppermlist")};
if (!is_channel_group) {
result.put_unchecked(0, "sgid", group->group_id());
} else {
result.put_unchecked(0, "cgid", group->group_id());
}
int index = 0; int index = 0;
@ -168,32 +211,6 @@ bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_
return true; return true;
} }
bool ConnectedClient::notifyChannelGroupList(bool as_notify) {
Command cmd(as_notify ? "notifychannelgrouplist" : "");
int index = 0;
for (const auto &group : (this->server ? this->server->group_manager() : serverInstance->group_manager().get())->availableChannelGroups(true)) {
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {
cmd[index]["cgid"] = group->groupId();
} else {
cmd[index]["sgid"] = group->groupId();
}
for (auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
cmd[index][std::string{prop.type().name}] = prop.value();
auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power);
auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power);
auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power);
cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0;
cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0;
cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0;
index++;
}
if(index == 0) return false;
this->sendCommand(cmd);
return true;
}
bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &invoker, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point& timestamp, const string &textMessage) { bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &invoker, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point& timestamp, const string &textMessage) {
//notifytextmessage targetmode=1 msg=asdasd target=2 invokerid=1 invokername=WolverinDEV invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw= //notifytextmessage targetmode=1 msg=asdasd target=2 invokerid=1 invokername=WolverinDEV invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw=
Command cmd("notifytextmessage"); Command cmd("notifytextmessage");

View File

@ -6,6 +6,7 @@
#include "./DataClient.h" #include "./DataClient.h"
#include "../InstanceHandler.h" #include "../InstanceHandler.h"
#include "../groups/GroupManager.h" #include "../groups/GroupManager.h"
#include "../groups/GroupAssignmentManager.h"
using namespace std; using namespace std;
using namespace ts; using namespace ts;
@ -196,22 +197,32 @@ std::vector<std::shared_ptr<groups::ServerGroup>> DataClient::assignedServerGrou
} }
} }
if(result.empty() && ref_server) {
auto default_group_id = ref_server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].as_or<GroupId>(0);
auto default_group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, default_group_id);
if(default_group) {
result.push_back(default_group);
}
}
return result; return result;
} }
std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(const shared_ptr<BasicChannel> &channel) { std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(const std::shared_ptr<BasicChannel> &channel) {
auto ref_server = this->server; auto ref_server = this->server;
assert(channel); assert(channel);
if(!channel || !ref_server) { if(!channel || !ref_server) {
return nullptr; return nullptr;
} }
std::shared_ptr<groups::ChannelGroup> result{}; std::shared_ptr<BasicChannel> inherited_channel{channel};
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager(); auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
auto channel_group_assignment = group_manager->assignments().channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), channel->channelId()); auto assigned_group_id = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), inherited_channel);
if(channel_group_assignment.has_value()) {
result = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, channel_group_assignment->group_id); std::shared_ptr<groups::ChannelGroup> result{};
if(assigned_group_id.has_value()) {
result = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, *assigned_group_id);
} }
if(!result) { if(!result) {

View File

@ -517,8 +517,14 @@ void SpeakingClient::processJoin() {
TIMING_STEP(timings, "join move "); TIMING_STEP(timings, "join move ");
this->properties()->triggerAllModified(); this->properties()->triggerAllModified();
this->notifyServerGroupList(); {
this->notifyChannelGroupList(); std::optional<ts::command_builder> generated_notify{};
this->notifyServerGroupList(generated_notify, true);
}
{
std::optional<ts::command_builder> generated_notify{};
this->notifyChannelGroupList(generated_notify, true);
}
TIMING_STEP(timings, "notify grou"); TIMING_STEP(timings, "notify grou");
logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} joined.", logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} joined.",
this->getClientDatabaseId(), this->getClientDatabaseId(),

View File

@ -7,6 +7,7 @@
#include "../../manager/PermissionNameMapper.h" #include "../../manager/PermissionNameMapper.h"
#include "../../server/QueryServer.h" #include "../../server/QueryServer.h"
#include "../../server/VoiceServer.h" #include "../../server/VoiceServer.h"
#include "../../groups/GroupManager.h"
#include "../ConnectedClient.h" #include "../ConnectedClient.h"
#include "../InternalClient.h" #include "../InternalClient.h"
#include "../music/MusicClient.h" #include "../music/MusicClient.h"
@ -137,58 +138,63 @@ command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager().get(); auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
log::GroupType log_group_type; log::GroupType log_group_type;
if (cmd["type"].as<GroupType>() == GroupType::GROUP_TYPE_QUERY) { auto group_type = cmd["type"].as<groups::GroupType>();
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1); switch (group_type) {
log_group_type = log::GroupType::QUERY; case groups::GroupType::GROUP_TYPE_QUERY:
} else if (cmd["type"].as<GroupType>() == GroupType::GROUP_TYPE_TEMPLATE) { ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1); log_group_type = log::GroupType::QUERY;
log_group_type = log::GroupType::TEMPLATE; break;
} else {
if (!this->server) {
return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"};
}
log_group_type = log::GroupType::NORMAL; case groups::GroupType::GROUP_TYPE_TEMPLATE:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
log_group_type = log::GroupType::TEMPLATE;
break;
case groups::GroupType::GROUP_TYPE_NORMAL:
if (!this->server) {
return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"};
}
log_group_type = log::GroupType::NORMAL;
break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
return command_result{error::parameter_invalid, "type"};
} }
if (cmd["name"].string().empty()) { std::shared_ptr<groups::ChannelGroup> group{};
return command_result{error::parameter_invalid, "invalid group name"}; auto result = group_manager->channel_groups()->create_group(group_type, cmd["name"].string(), group);
} switch(result) {
case groups::GroupCreateResult::SUCCESS:
break;
for (const auto &gr : group_manager->availableServerGroups(true)) { case groups::GroupCreateResult::NAME_TOO_SHORT:
if (gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_CHANNEL) { case groups::GroupCreateResult::NAME_TOO_LONG:
return command_result{error::parameter_invalid, "Group already exists"}; return command_result{error::parameter_invalid, "name"};
}
}
auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["type"].as<GroupType>(), cmd["name"].string()); case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, group->groupId(), group->name(), 0, ""); return command_result{error::group_name_inuse};
case groups::GroupCreateResult::DATABASE_ERROR:
default:
return command_result{error::vs_critical};
}
assert(group);
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, group->group_id(), group->display_name(), 0, "");
{ {
ts::command_builder notify{this->notify_response_command("notifychannelgroupadded")}; ts::command_builder notify{this->notify_response_command("notifychannelgroupadded")};
notify.put_unchecked(0, "cgid", group->groupId()); notify.put_unchecked(0, "cgid", group->group_id());
this->sendCommand(notify); this->sendCommand(notify);
} }
if (group) { if (group) {
group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing); group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing);
if (this->server) { if (this->server) {
if (this->getType() == ClientType::CLIENT_QUERY) { this->server->enqueue_notify_channel_group_list();
this->server->forEachClient([&](const std::shared_ptr<ConnectedClient> &cl) {
if (cl == this) {
return;
}
cl->notifyChannelGroupList();
});
} else {
this->server->forEachClient([](const std::shared_ptr<ConnectedClient> &cl) {
cl->notifyChannelGroupList();
});
}
} }
} else { } else {
return command_result{error::group_invalid_id}; return command_result{error::group_invalid_id};
@ -200,30 +206,40 @@ command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) {
command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) { command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) {
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
auto ref_server = this->server; auto ref_server = this->server;
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager().get(); auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
auto source_group_id = cmd["scgid"].as<GroupId>(); auto source_group_id = cmd["scgid"].as<GroupId>();
auto source_group = group_manager->findGroup(source_group_id); auto source_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
if (!source_group || source_group->target() != GROUPTARGET_CHANNEL) if (!source_group) {
return command_result{error::group_invalid_id, "invalid source group"}; return command_result{error::group_invalid_id, "invalid source group id"};
}
const auto group_type_modificable = [&](GroupType type) { const auto group_type_modifiable = [&](groups::GroupType type) {
switch (type) { switch (type) {
case GroupType::GROUP_TYPE_TEMPLATE: case groups::GroupType::GROUP_TYPE_TEMPLATE:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0))) if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0))) {
return permission::b_serverinstance_modify_templates; return permission::b_serverinstance_modify_templates;
}
break; break;
case GroupType::GROUP_TYPE_QUERY: case groups::GroupType::GROUP_TYPE_QUERY:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0))) if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0))) {
return permission::b_serverinstance_modify_querygroup; return permission::b_serverinstance_modify_querygroup;
}
break; break;
case GroupType::GROUP_TYPE_NORMAL: case groups::GroupType::GROUP_TYPE_NORMAL:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_channelgroup_create, 0))) {
return permission::b_virtualserver_channelgroup_create;
}
break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default: default:
break; break;
} }
@ -231,107 +247,149 @@ command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) {
}; };
{ {
auto result = group_type_modificable(source_group->type()); auto result = group_type_modifiable(source_group->group_type());
if (result != permission::undefined) if (result != permission::undefined) {
return command_result{result}; return command_result{result};
}
} }
auto global_update = false; auto global_update = false;
if (cmd[0].has("tcgid") && cmd["tcgid"].as<GroupId>() != 0) { if (cmd[0].has("tcgid") && cmd["tcgid"].as<GroupId>() != 0) {
//Copy an existing group //Copy an existing group
auto target_group = group_manager->findGroup(cmd["tcgid"]); auto target_group_id = cmd["tcgid"].as<GroupId>();
if (!target_group || target_group->target() != GROUPTARGET_CHANNEL) auto target_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, target_group_id);
if (!target_group) {
return command_result{error::group_invalid_id, "invalid target group"}; return command_result{error::group_invalid_id, "invalid target group"};
{
auto result = group_type_modificable(target_group->type());
if (result != permission::undefined)
return command_result{result};
} }
if (!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
return command_result{permission::i_channel_group_modify_power}; return ts::command_result{permission::i_channel_group_needed_modify_power};
}
if (!group_manager->copyGroupPermissions(source_group, target_group)) {
return command_result{error::vs_critical, "failed to copy group permissions"}; auto result = group_type_modifiable(target_group->group_type());
if (result != permission::undefined) {
return command_result{result};
}
}
auto result = group_manager->channel_groups()->copy_group_permissions(source_group_id, target_group_id);
switch(result) {
case groups::GroupCopyResult::SUCCESS:
break;
case groups::GroupCopyResult::UNKNOWN_SOURCE_GROUP:
return command_result{error::vs_critical, "internal unknown source group"};
case groups::GroupCopyResult::UNKNOWN_TARGET_GROUP:
return command_result{error::vs_critical, "internal unknown target group"};
case groups::GroupCopyResult::DATABASE_ERROR:
return command_result{error::vs_critical, "database error"};
case groups::GroupCopyResult::NAME_ALREADY_IN_USE:
return command_result{error::group_name_inuse};
default:
return command_result{error::vs_critical};
}
log::GroupType log_group_type; log::GroupType log_group_type;
switch (target_group->type()) { switch (target_group->group_type()) {
case GroupType::GROUP_TYPE_QUERY: case groups::GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY; log_group_type = log::GroupType::QUERY;
break; break;
case GroupType::GROUP_TYPE_TEMPLATE: case groups::GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE; log_group_type = log::GroupType::TEMPLATE;
break; break;
case GroupType::GROUP_TYPE_NORMAL: case groups::GroupType::GROUP_TYPE_NORMAL:
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
log_group_type = log::GroupType::NORMAL; log_group_type = log::GroupType::NORMAL;
break; break;
default:
return command_result{error::parameter_invalid, "type"};
} }
serverInstance->action_logger()->group_logger.log_group_permission_copy(target_group->type() != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(), serverInstance->action_logger()->group_logger.log_group_permission_copy(target_group->group_type() != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log::GroupTarget::CHANNEL, log_group_type, target_group->groupId(), target_group->name(), source_group->groupId(), source_group->name()); this->ref(), log::GroupTarget::CHANNEL, log_group_type, target_group->group_id(), target_group->display_name(), source_group->group_id(), source_group->display_name());
global_update = !this->server || !group_manager->isLocalGroup(target_group); global_update = !this->server || !group_manager->channel_groups()->find_group(groups::GroupCalculateMode::LOCAL, target_group_id);
} else { } else {
//Copy a new group //Copy a new group
auto target_type = cmd["type"].as<GroupType>(); auto target_type = cmd["type"].as<groups::GroupType>();
{
auto result = group_type_modificable(target_type);
if (result != permission::undefined)
return command_result{result};
}
log::GroupType log_group_type; log::GroupType log_group_type;
switch (target_type) { switch (target_type) {
case GroupType::GROUP_TYPE_QUERY: case groups::GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY; log_group_type = log::GroupType::QUERY;
break; break;
case GroupType::GROUP_TYPE_TEMPLATE: case groups::GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE; log_group_type = log::GroupType::TEMPLATE;
break; break;
case GroupType::GROUP_TYPE_NORMAL: case groups::GroupType::GROUP_TYPE_NORMAL:
log_group_type = log::GroupType::NORMAL; log_group_type = log::GroupType::NORMAL;
break; break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default: default:
return command_result{error::parameter_invalid, "type"}; return command_result{error::parameter_invalid, "type"};
} }
if (!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) {
auto result = group_type_modifiable(target_type);
if (result != permission::undefined) {
return command_result{result};
}
}
if (!ref_server && target_type == groups::GroupType::GROUP_TYPE_NORMAL) {
return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"}; return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"};
}
if (!group_manager->findGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["name"].string()).empty()) std::shared_ptr<groups::ChannelGroup> created_group{};
return command_result{error::group_name_inuse, "You cant create normal groups on the template server!"}; auto result = group_manager->channel_groups()->copy_group(source_group_id, target_type, cmd["name"].string(), created_group);
switch(result) {
case groups::GroupCopyResult::SUCCESS:
break;
auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); case groups::GroupCopyResult::UNKNOWN_SOURCE_GROUP:
if (target_group_id == 0) return command_result{error::vs_critical, "internal unknown source group"};
return command_result{error::vs_critical, "failed to copy group"};
serverInstance->action_logger()->group_logger.log_group_create(target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log::GroupTarget::CHANNEL, log_group_type, target_group_id, cmd["name"], source_group->groupId(), source_group->name());
if (this->getType() == ClientType::CLIENT_QUERY) { case groups::GroupCopyResult::UNKNOWN_TARGET_GROUP:
Command notify(""); return command_result{error::vs_critical, "internal unknown target group"};
notify["cgid"] = target_group_id;
case groups::GroupCopyResult::DATABASE_ERROR:
return command_result{error::vs_critical, "database error"};
case groups::GroupCopyResult::NAME_ALREADY_IN_USE:
return command_result{error::group_name_inuse};
default:
return command_result{error::vs_critical};
}
assert(created_group);
serverInstance->action_logger()->group_logger.log_group_create(target_type != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log::GroupTarget::CHANNEL, log_group_type, created_group->group_id(), cmd["name"], source_group->group_id(), source_group->display_name());
{
ts::command_builder notify{this->notify_response_command("notifychannelgroupcopied")};
notify.put_unchecked(0, "cgid", created_group->group_id());
this->sendCommand(notify); this->sendCommand(notify);
} }
global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); global_update = !this->server;
} }
for (const auto &server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque<shared_ptr<VirtualServer>>{this->server})) for (const auto &server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque<shared_ptr<VirtualServer>>{this->server})) {
if (server) if (server) {
server->forEachClient([](shared_ptr<ConnectedClient> cl) { server->enqueue_notify_channel_group_list();
cl->notifyChannelGroupList(); }
}); }
return command_result{error::ok}; return command_result{error::ok};
} }
@ -339,32 +397,50 @@ command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) {
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager().get(); auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
auto channel_group = group_manager->findGroup(cmd["cgid"].as<GroupId>()); auto channel_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) if (!channel_group) {
return command_result{error::parameter_invalid, "invalid channel group id"}; return command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(channel_group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true); ACTION_REQUIRES_GROUP_PERMISSION(channel_group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
auto type = channel_group->type(); auto type = channel_group->group_type();
log::GroupType log_group_type; log::GroupType log_group_type;
if (type == GroupType::GROUP_TYPE_QUERY) { if (type == groups::GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY; log_group_type = log::GroupType::QUERY;
} else if (type == GroupType::GROUP_TYPE_TEMPLATE) { } else if (type == groups::GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
log_group_type = log::GroupType::TEMPLATE; log_group_type = log::GroupType::TEMPLATE;
} else { } else {
log_group_type = log::GroupType::NORMAL; log_group_type = log::GroupType::NORMAL;
} }
auto old_name = channel_group->name(); auto old_name = channel_group->display_name();
group_manager->renameGroup(channel_group, cmd["name"].string()); auto result = group_manager->channel_groups()->rename_group(channel_group->group_id(), cmd["name"].string());
serverInstance->action_logger()->group_logger.log_group_rename(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, channel_group->groupId(), channel_group->name(), old_name); switch(result) {
case groups::GroupRenameResult::SUCCESS:
break;
if (this->server) case groups::GroupRenameResult::INVALID_GROUP_ID:
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) { return ts::command_result{error::vs_critical, "internal invalid group id"};
cl->notifyChannelGroupList();
}); case groups::GroupRenameResult::NAME_INVALID:
return ts::command_result{error::parameter_invalid, "name"};
case groups::GroupRenameResult::NAME_ALREADY_USED:
return ts::command_result{error::group_name_inuse};
case groups::GroupRenameResult::DATABASE_ERROR:
default:
return ts::command_result{error::vs_critical};
}
serverInstance->action_logger()->group_logger.log_group_rename(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, channel_group->group_id(), channel_group->display_name(), old_name);
if (this->server) {
this->server->enqueue_notify_channel_group_list();
}
return command_result{error::ok}; return command_result{error::ok};
} }
@ -372,54 +448,101 @@ command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) {
command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) { command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) {
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager().get(); auto ref_server = this->server;
auto channel_group = group_manager->findGroup(cmd["cgid"].as<GroupId>()); auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; auto channel_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
if (!channel_group) {
return command_result{error::group_invalid_id};
}
if(!channel_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
return command_result{permission::i_channel_group_needed_modify_power};
}
if (this->server) { if (this->server) {
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->groupId()) if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete default channel group!"}; return command_result{error::parameter_invalid, "Could not delete default channel group!"};
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->groupId()) }
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete default channel admin group!"}; return command_result{error::parameter_invalid, "Could not delete default channel admin group!"};
}
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->groupId())
return command_result{error::parameter_invalid, "Could not delete instance default channel group!"};
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->groupId())
return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"};
auto type = channel_group->type();
log::GroupType log_group_type;
if (type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
} else if (type == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
log_group_type = log::GroupType::TEMPLATE;
} else {
log_group_type = log::GroupType::NORMAL;
}
if (!cmd["force"].as<bool>())
if (!group_manager->listGroupMembers(channel_group, false).empty())
return command_result{error::database_empty_result, "group not empty!"};
if (group_manager->deleteGroup(channel_group)) {
serverInstance->action_logger()->group_logger.log_group_delete(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, channel_group->groupId(), channel_group->name());
if (this->server) {
this->server->forEachClient([&](shared_ptr<ConnectedClient> cl) {
if (this->server->notifyClientPropertyUpdates(cl, this->server->group_manager()->update_server_group_property(cl, true, cl->getChannel()))) {
cl->task_update_needed_permissions.enqueue();
}
cl->notifyChannelGroupList();
});
} }
} }
if(this->server) { if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->group_id()) {
this->server->tokenManager->handle_channel_group_deleted(channel_group->groupId()); return command_result{error::parameter_invalid, "Could not delete instance default channel group!"};
}
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"};
}
log::GroupType log_group_type;
switch (channel_group->group_type()) {
case groups::GroupType::GROUP_TYPE_QUERY:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
break;
case groups::GroupType::GROUP_TYPE_TEMPLATE:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
log_group_type = log::GroupType::TEMPLATE;
break;
case groups::GroupType::GROUP_TYPE_NORMAL:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1);
log_group_type = log::GroupType::NORMAL;
break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
return ts::command_result{error::vs_critical};
}
if (!cmd["force"].as<bool>()) {
if(!group_manager->assignments().is_channel_group_empty(channel_group->group_id())) {
return command_result{error::group_not_empty};
}
}
auto result = group_manager->channel_groups()->delete_group(channel_group->group_id());
switch(result) {
case groups::GroupDeleteResult::SUCCESS:
break;
case groups::GroupDeleteResult::INVALID_GROUP_ID:
case groups::GroupDeleteResult::DATABASE_ERROR:
default:
return ts::command_result{error::vs_critical};
}
serverInstance->action_logger()->group_logger.log_group_delete(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, channel_group->group_id(), channel_group->display_name());
if(ref_server) {
auto channel_group_id = channel_group->group_id();
std::deque<std::shared_ptr<ConnectedClient>> affected_clients{};
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> client) {
/* TODO: It might be faster to query all clients of the group and search within that list only */
auto assigned_group = group_manager->assignments().exact_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, client->getClientDatabaseId(), client->getChannelId());
if(assigned_group.has_value() && assigned_group->group_id == channel_group_id) {
affected_clients.push_back(client);
}
});
ref_server->enqueue_notify_channel_group_list();
ref_server->group_manager()->assignments().handle_channel_group_deleted(channel_group_id);
for(const auto& client : affected_clients) {
/*
* Now we can enqueue all the updates since handle_channel_group_deleted has already been called.
* If not done in that order, the group might already got updated before we actiually deleted it from the group manager.
*/
client->task_update_displayed_groups.enqueue();
client->task_update_needed_permissions.enqueue();
client->task_update_channel_client_properties.enqueue();
}
ref_server->tokenManager->handle_channel_group_deleted(channel_group_id);
} }
return command_result{error::ok}; return command_result{error::ok};
@ -430,7 +553,9 @@ command_result ConnectedClient::handleCommandChannelGroupList(Command &) {
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_list, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_list, 1);
this->notifyChannelGroupList(this->getType() != ClientType::CLIENT_QUERY); std::optional<ts::command_builder> notify{};
this->notifyChannelGroupList(notify, this->getType() != ClientType::CLIENT_QUERY);
this->command_times.servergrouplist = system_clock::now(); this->command_times.servergrouplist = system_clock::now();
return command_result{error::ok}; return command_result{error::ok};
} }
@ -439,69 +564,29 @@ command_result ConnectedClient::handleCommandChannelGroupClientList(Command &cmd
CMD_REQ_SERVER; CMD_REQ_SERVER;
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto target_channel_id = cmd[0].has("cid") ? cmd["cid"].as<ChannelId>() : 0; auto target_channel_id = cmd[0].has("cid") ? cmd["cid"].as<ChannelId>() : 0;
if (target_channel_id > 0) { auto target_client_database_id = cmd[0].has("cldbid") ? cmd["cldbid"].as<ClientDbId>() : 0;
ACTION_REQUIRES_PERMISSION(permission::b_virtualserver_channelgroup_client_list, 1, target_channel_id); auto target_group_id = cmd[0].has("cgid") ? cmd["cgid"].as<GroupId>() : 0;
} else {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_client_list, 1);
}
Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifychannelgroupclientlist" : "");
deque<variable> variables{variable{":sid", this->getServerId()}}; ACTION_REQUIRES_PERMISSION(permission::b_virtualserver_channelgroup_client_list, 1, target_channel_id);
string query = "SELECT `groupId`, `cldbid`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid";
if (cmd[0].has("cgid") && cmd["cgid"].as<GroupId>() > 0) { auto result = this->server->group_manager()->assignments().channel_group_list(target_group_id, target_channel_id, target_client_database_id);
auto group = this->server->group_manager()->findGroup(cmd["cgid"]); if(result.empty()) {
if (!group || group->target() != GroupTarget::GROUPTARGET_CHANNEL) return ts::command_result{error::database_empty_result};
return command_result{error::parameter_invalid, "invalid channel group id"};
query += " AND `groupId` = :groupId";
variables.push_back({":groupId", cmd["cgid"].as<GroupId>()});
} else {
query += " AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target)";
variables.push_back({":target", GroupTarget::GROUPTARGET_CHANNEL});
} }
if (cmd[0].has("cldbid") && cmd["cldbid"].as<ClientDbId>() > 0) {
query += " AND `cldbid` = :cldbid";
variables.push_back({":cldbid", cmd["cldbid"].as<ClientDbId>()});
}
if (cmd[0].has("cid") && cmd["cid"].as<ChannelId>() > 0) {
auto channel = this->server->getChannelTree()->findChannel(cmd["cid"]);
if (!channel)
return command_result{error::parameter_invalid, "invalid channel id"};
query += " AND `channelId` = :cid";
variables.push_back({":cid", cmd["cid"].as<ChannelId>()});
}
debugMessage(this->getServerId(), "Command channelgroupclientlist sql: {}", query);
auto command = sql::command(this->sql, query);
for (const auto &variable : variables)
command.value(variable);
int index = 0; size_t index{0};
command.query([&](Command &command, int &index, int length, string *values, string *names) { ts::command_builder notify{this->notify_response_command("notifychannelgroupclientlist"), 64, result.size()};
GroupId group_id = 0; for(const auto& entry : result) {
ChannelId channel_id = 0; auto bulk = notify.bulk(index++);
ClientDbId cldbid = 0; bulk.put_unchecked("cgid", std::get<0>(entry));
for (int i = 0; i < length; i++) { bulk.put_unchecked("cid", std::get<1>(entry));
try { bulk.put_unchecked("cldbid", std::get<2>(entry));
if (names[i] == "groupId") }
group_id = stoll(values[i]); this->sendCommand(notify);
else if (names[i] == "cldbid")
cldbid = stoll(values[i]);
else if (names[i] == "channelId")
channel_id = stoll(values[i]);
} catch (std::exception &ex) {
logError(this->getServerId(), "Failed to parse db field {}", names[i]);
}
}
result[index]["cid"] = channel_id;
result[index]["cgid"] = group_id;
result[index]["cldbid"] = cldbid;
index++;
},
result, index);
if (index == 0) return command_result{error::database_empty_result};
this->sendCommand(result);
return command_result{error::ok}; return command_result{error::ok};
} }
@ -509,9 +594,15 @@ command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd)
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_permission_list, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_permission_list, 1);
auto channelGroup = (this->server ? this->server->group_manager() : serverInstance->group_manager().get())->findGroup(cmd["cgid"].as<GroupId>()); auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; auto channelGroup = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
if (!this->notifyGroupPermList(channelGroup, cmd.hasParm("permsid"))) return command_result{error::database_empty_result}; if (!channelGroup) {
return command_result{error::group_invalid_id};
}
if (!this->notifyGroupPermList(channelGroup, cmd.hasParm("permsid"))) {
return command_result{error::database_empty_result};
}
if (this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { if (this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) {
this->sendTSPermEditorWarning(); this->sendTSPermEditorWarning();
@ -523,9 +614,11 @@ command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd)
command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) { command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager().get(); auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
auto channelGroup = group_manager->findGroup(cmd["cgid"].as<GroupId>()); auto channelGroup = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; if (!channelGroup) {
return command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true); ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd}; ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
@ -542,20 +635,14 @@ command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
log::PermissionTarget::CHANNEL_GROUP, log::PermissionTarget::CHANNEL_GROUP,
permission::v2::PermissionUpdateType::set_value, permission::v2::PermissionUpdateType::set_value,
0, "", 0, "",
channelGroup->groupId(), channelGroup->name()); channelGroup->group_id(), channelGroup->display_name());
updateList |= ppermission.is_group_property(); updateList |= ppermission.is_group_property();
} }
if (updateList) {
channelGroup->apply_properties_from_permissions();
}
if (this->server) { if (this->server) {
if (updateList) { if (updateList) {
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) { this->server->enqueue_notify_channel_group_list();
cl->notifyChannelGroupList();
});
} }
this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) { this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) {
@ -575,9 +662,12 @@ command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) { command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager().get();
auto channelGroup = group_manager->findGroup(cmd["cgid"].as<GroupId>()); auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; auto channelGroup = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
if (!channelGroup) {
return command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true); ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd}; ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
@ -593,18 +683,13 @@ command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) {
log::PermissionTarget::CHANNEL_GROUP, log::PermissionTarget::CHANNEL_GROUP,
permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::delete_value,
0, "", 0, "",
channelGroup->groupId(), channelGroup->name()); channelGroup->group_id(), channelGroup->display_name());
updateList |= ppermission.is_group_property(); updateList |= ppermission.is_group_property();
} }
if (updateList)
channelGroup->apply_properties_from_permissions();
if (this->server) { if (this->server) {
if (updateList) if (updateList)
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) { this->server->enqueue_notify_channel_group_list();
cl->notifyChannelGroupList();
});
this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) { this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) {
unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
@ -632,7 +717,7 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) {
#define test_permission(required, permission_type) \ #define test_permission(required, permission_type) \
do { \ do { \
if (!permission::v2::permission_granted(required, this->calculate_permission(permission_type, parent_channel_id, false))) \ if (!permission::v2::permission_granted(required, this->calculate_permission(permission_type, parent_channel_id, false))) \
return command_result{permission_type}; \ return command_result{permission_type}; \
} while (0) } while (0)
@ -842,22 +927,25 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) {
max_channels = this->calculate_permission(permission::i_client_max_permanent_channels, parent_channel_id, false); max_channels = this->calculate_permission(permission::i_client_max_permanent_channels, parent_channel_id, false);
if (max_channels.has_value) { if (max_channels.has_value) {
if (!permission::v2::permission_granted(created_perm + 1, max_channels)) if (!permission::v2::permission_granted(created_perm + 1, max_channels)) {
return command_result{permission::i_client_max_permanent_channels}; return command_result{permission::i_client_max_permanent_channels};
}
} }
} else if (cmd[0]["channel_flag_semi_permanent"].as<bool>()) { } else if (cmd[0]["channel_flag_semi_permanent"].as<bool>()) {
max_channels = this->calculate_permission(permission::i_client_max_semi_channels, parent_channel_id, false); max_channels = this->calculate_permission(permission::i_client_max_semi_channels, parent_channel_id, false);
if (max_channels.has_value) { if (max_channels.has_value) {
if (!permission::v2::permission_granted(created_semi + 1, max_channels)) if (!permission::v2::permission_granted(created_semi + 1, max_channels)) {
return command_result{permission::i_client_max_semi_channels}; return command_result{permission::i_client_max_semi_channels};
}
} }
} else { } else {
max_channels = this->calculate_permission(permission::i_client_max_temporary_channels, parent_channel_id, false); max_channels = this->calculate_permission(permission::i_client_max_temporary_channels, parent_channel_id, false);
if (max_channels.has_value) { if (max_channels.has_value) {
if (!permission::v2::permission_granted(created_tmp + 1, max_channels)) if (!permission::v2::permission_granted(created_tmp + 1, max_channels)) {
return command_result{permission::i_client_max_temporary_channels}; return command_result{permission::i_client_max_temporary_channels};
}
} }
} }
} }
@ -873,7 +961,9 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) {
channel_deep++; channel_deep++;
{ {
const auto typed_parent = dynamic_pointer_cast<ServerChannel>(local_parent->entry); const auto typed_parent = dynamic_pointer_cast<ServerChannel>(local_parent->entry);
if (typed_parent->deleted) return command_result{error::channel_is_deleted, "One of the parents has been deleted"}; if (typed_parent->deleted) {
return command_result{error::channel_is_deleted, "One of the parents has been deleted"};
}
} }
local_parent = local_parent->parent.lock(); local_parent = local_parent->parent.lock();
} }
@ -955,15 +1045,23 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) {
if (this->server) { if (this->server) {
const auto self_lock = this->ref(); const auto self_lock = this->ref();
GroupId adminGroup = this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP]; auto admin_group_id = this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as_or<GroupId>(0);
auto channel_admin_group = this->server->group_manager()->findGroup(adminGroup); auto admin_group = this->server->group_manager()->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, admin_group_id);
if (!channel_admin_group) { if(!admin_group) {
logError(this->getServerId(), "Missing server's default channel admin group! Using default channel group!"); logError(this->getServerId(), "Missing servers default channel admin group {}. Using the default channel gropup.", admin_group_id);
channel_admin_group = this->server->group_manager()->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); admin_group = this->server->default_channel_group();
} }
/* FIXME: Log group assignment */ /* admin_group might still be null since default_channel_group() could return nullptr */
this->server->group_manager()->setChannelGroup(this->getClientDatabaseId(), channel_admin_group, created_channel); if(admin_group) {
this->server->group_manager()->assignments().set_channel_group(this->getClientDatabaseId(), created_channel->channelId(), admin_group->group_id(), false);
serverInstance->action_logger()->group_assignment_logger.log_group_assignment_remove(
this->getServerId(), this->server->getServerRoot(),
log::GroupTarget::CHANNEL,
admin_group->group_id(), admin_group->display_name(),
this->getClientDatabaseId(), this->getDisplayName()
);
}
if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB || this->getType() == ClientType::CLIENT_TEASPEAK)) { if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB || this->getType() == ClientType::CLIENT_TEASPEAK)) {
channel_tree_read_lock.unlock(); channel_tree_read_lock.unlock();

View File

@ -20,6 +20,8 @@
#include "../../manager/ConversationManager.h" #include "../../manager/ConversationManager.h"
#include "../../manager/PermissionNameMapper.h" #include "../../manager/PermissionNameMapper.h"
#include "../../manager/ActionLogger.h" #include "../../manager/ActionLogger.h"
#include "../../groups/GroupManager.h"
#include "../../absl/btree/map.h"
#include <experimental/filesystem> #include <experimental/filesystem>
#include <cstdint> #include <cstdint>
#include <StringVariable.h> #include <StringVariable.h>
@ -443,28 +445,41 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd)
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25); CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto serverGroup = this->server->group_manager()->findGroup(cmd["cgid"].as<GroupId>()); auto target_channel_group_id = cmd["cgid"].as<GroupId>();
if (!serverGroup && cmd["cgid"].as<GroupId>() == 0) std::shared_ptr<groups::ChannelGroup> target_channel_group{}, default_channel_group{};
serverGroup = this->server->group_manager()->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
if (!serverGroup || serverGroup->target() != GROUPTARGET_CHANNEL) default_channel_group = this->server->default_channel_group();
return command_result{error::group_invalid_id}; if(target_channel_group_id == 0) {
target_channel_group = default_channel_group;
if(!target_channel_group) {
return command_result{error::vs_critical};
}
} else {
target_channel_group = this->server->group_manager()->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
if(!target_channel_group) {
return command_result{error::group_invalid_id};
}
}
shared_lock server_channel_lock(this->server->channel_tree_lock); /* ensure we dont get moved or somebody could move us */ shared_lock server_channel_lock{this->server->channel_tree_lock}; /* ensure we dont get moved or somebody could move us */
auto channel_id = cmd["cid"].as<ChannelId>(); auto channel_id = cmd["cid"].as<ChannelId>();
auto channel = this->server->channelTree->findChannel(channel_id); auto channel = this->server->channelTree->findChannel(channel_id);
if (!channel) return command_result{error::channel_invalid_id}; if (!channel) {
return command_result{error::channel_invalid_id};
}
auto target_cldbid = cmd["cldbid"].as<ClientDbId>(); auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
{ {
auto channel_group_member_add_power = this->calculate_permission(permission::i_channel_group_member_add_power, channel_id); auto channel_group_member_add_power = this->calculate_permission(permission::i_channel_group_member_add_power, channel_id);
if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_member_add_power, true)) { if(!target_channel_group->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_member_add_power, true)) {
if(target_cldbid != this->getClientDatabaseId()) if(target_cldbid != this->getClientDatabaseId()) {
return command_result{permission::i_channel_group_member_add_power}; return command_result{permission::i_channel_group_member_add_power};
}
auto channel_group_self_add_power = this->calculate_permission(permission::i_channel_group_self_add_power, channel_id); auto channel_group_self_add_power = this->calculate_permission(permission::i_channel_group_self_add_power, channel_id);
if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_self_add_power, true)) if(!target_channel_group->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_self_add_power, true)) {
return command_result{permission::i_channel_group_self_add_power}; return command_result{permission::i_channel_group_self_add_power};
}
} }
@ -474,66 +489,68 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd)
if(client_needed_permission_modify_power.has_value) { if(client_needed_permission_modify_power.has_value) {
if(!permission::v2::permission_granted(client_needed_permission_modify_power, client_permission_modify_power)) if(!permission::v2::permission_granted(client_needed_permission_modify_power, client_permission_modify_power)) {
return command_result{permission::i_client_permission_modify_power}; return command_result{permission::i_client_permission_modify_power};
}
}
std::shared_ptr<GroupAssignment> old_group;
{
old_group = this->server->group_manager()->getChannelGroupExact(target_cldbid, channel, false);
if(old_group) {
auto channel_group_member_remove_power = this->calculate_permission(permission::i_channel_group_member_remove_power, channel_id);
if(!old_group->group->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_member_remove_power, true)) {
if(target_cldbid != this->getClientDatabaseId())
return command_result{permission::i_channel_group_member_remove_power};
auto channel_group_self_remove_power = this->calculate_permission(permission::i_channel_group_self_remove_power, channel_id);
if(!old_group->group->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_self_remove_power, true))
return command_result{permission::i_channel_group_self_remove_power};
} }
} }
} }
this->server->group_manager()->setChannelGroup(target_cldbid, serverGroup, channel); auto old_group_id = this->server->group_manager()->assignments().exact_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, target_cldbid, channel->channelId());
std::shared_ptr<groups::ChannelGroup> old_group{};
if(old_group_id.has_value()) {
old_group = this->server->group_manager()->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, old_group_id->group_id);
if(old_group) {
auto channel_group_member_remove_power = this->calculate_permission(permission::i_channel_group_member_remove_power, channel_id);
if(!old_group->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_member_remove_power, true)) {
if(target_cldbid != this->getClientDatabaseId()) {
return command_result{permission::i_channel_group_member_remove_power};
}
auto channel_group_self_remove_power = this->calculate_permission(permission::i_channel_group_self_remove_power, channel_id);
if(!old_group->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_self_remove_power, true)) {
return command_result{permission::i_channel_group_self_remove_power};
}
}
}
}
this->server->group_manager()->assignments().set_channel_group(target_cldbid, target_channel_group->group_id(), channel->channelId(), !target_channel_group->is_permanent());
std::shared_ptr<ConnectedClient> connected_client{}; std::shared_ptr<ConnectedClient> connected_client{};
for (const auto &targetClient : this->server->findClientsByCldbId(target_cldbid)) { for (const auto &targetClient : this->server->findClientsByCldbId(target_cldbid)) {
connected_client = targetClient; connected_client = targetClient;
unique_lock client_channel_lock_w(targetClient->channel_lock); bool channel_group_changed, server_group_changed;
auto updates = this->server->group_manager()->update_server_group_property(targetClient, false, targetClient->getChannel()); /* needs a write lock */ targetClient->update_displayed_client_groups(server_group_changed, channel_group_changed);
client_channel_lock_w.unlock();
shared_lock client_channel_lock_r(targetClient->channel_lock);
auto result = this->server->notifyClientPropertyUpdates(targetClient, updates);
if (result) {
targetClient->task_update_needed_permissions.enqueue();
if(targetClient->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] == channel->channelId()) { //Only if group assigned over the channel if(channel_group_changed) {
for (const auto &viewer : this->server->getClients()) { targetClient->task_update_needed_permissions.enqueue();
/* if in view will be tested within that method */ targetClient->task_update_displayed_groups.enqueue();
shared_lock viewer_channel_lock(viewer->channel_lock, defer_lock);
if(viewer != targetClient) for (const auto &viewer : this->server->getClients()) {
viewer_channel_lock.lock(); /* if in view will be tested within that method */
viewer->notifyClientChannelGroupChanged(this->ref(), targetClient, targetClient->getChannel(), channel, serverGroup, false); std::shared_lock viewer_channel_lock{viewer->channel_lock, defer_lock};
if(viewer != targetClient) {
viewer_channel_lock.lock();
} }
viewer->notifyClientChannelGroupChanged(this->ref(), targetClient, targetClient->getChannel(), channel, target_channel_group, false);
} }
} }
targetClient->task_update_channel_client_properties.enqueue();
} }
if(old_group) { if(old_group) {
serverInstance->action_logger()->group_assignment_logger.log_group_assignment_remove(this->getServerId(), serverInstance->action_logger()->group_assignment_logger.log_group_assignment_remove(this->getServerId(),
this->ref(), log::GroupTarget::CHANNEL, this->ref(), log::GroupTarget::CHANNEL,
old_group->group->groupId(), old_group->group->name(), old_group->group_id(), old_group->display_name(),
target_cldbid, connected_client ? connected_client->getDisplayName() : "" target_cldbid, connected_client ? connected_client->getDisplayName() : ""
); );
} }
if(serverGroup != this->server->group_manager()->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL)) {
if(target_channel_group != default_channel_group) {
serverInstance->action_logger()->group_assignment_logger.log_group_assignment_add(this->getServerId(), serverInstance->action_logger()->group_assignment_logger.log_group_assignment_add(this->getServerId(),
this->ref(), log::GroupTarget::CHANNEL, this->ref(), log::GroupTarget::CHANNEL,
serverGroup->groupId(), serverGroup->name(), target_channel_group->group_id(), target_channel_group->display_name(),
target_cldbid, connected_client ? connected_client->getDisplayName() : "" target_cldbid, connected_client ? connected_client->getDisplayName() : ""
); );
} }
@ -1241,8 +1258,8 @@ void check_token_action_permissions(const std::shared_ptr<ConnectedClient>& clie
switch(action.type) { switch(action.type) {
case ActionType::AddServerGroup: { case ActionType::AddServerGroup: {
auto target_group = client->getServer()->group_manager()->findGroup(action.id1); auto target_group = client->getServer()->group_manager()->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, action.id1);
if(!target_group || target_group->type() == GroupType::GROUP_TYPE_TEMPLATE || target_group->target() != GroupTarget::GROUPTARGET_SERVER) { if(!target_group || target_group->group_type() == groups::GroupType::GROUP_TYPE_TEMPLATE) {
action.type = ActionType::ActionIgnore; action.type = ActionType::ActionIgnore;
result.set_result(index, ts::command_result{error::group_invalid_id}); result.set_result(index, ts::command_result{error::group_invalid_id});
break; break;
@ -1258,8 +1275,8 @@ void check_token_action_permissions(const std::shared_ptr<ConnectedClient>& clie
} }
case ActionType::RemoveServerGroup: { case ActionType::RemoveServerGroup: {
auto target_group = client->getServer()->group_manager()->findGroup(action.id1); auto target_group = client->getServer()->group_manager()->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, action.id1);
if(!target_group || target_group->type() == GroupType::GROUP_TYPE_TEMPLATE || target_group->target() != GroupTarget::GROUPTARGET_SERVER) { if(!target_group || target_group->group_type() == groups::GroupType::GROUP_TYPE_TEMPLATE) {
action.type = ActionType::ActionIgnore; action.type = ActionType::ActionIgnore;
result.set_result(index, ts::command_result{error::group_invalid_id}); result.set_result(index, ts::command_result{error::group_invalid_id});
break; break;
@ -1275,8 +1292,8 @@ void check_token_action_permissions(const std::shared_ptr<ConnectedClient>& clie
} }
case ActionType::SetChannelGroup: { case ActionType::SetChannelGroup: {
auto target_group = client->getServer()->group_manager()->findGroup(action.id1); auto target_group = client->getServer()->group_manager()->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, action.id1);
if(!target_group || target_group->type() == GroupType::GROUP_TYPE_TEMPLATE || target_group->target() != GroupTarget::GROUPTARGET_CHANNEL) { if(!target_group || target_group->group_type() == groups::GroupType::GROUP_TYPE_TEMPLATE) {
action.type = ActionType::ActionIgnore; action.type = ActionType::ActionIgnore;
result.set_result(index, ts::command_result{error::group_invalid_id}); result.set_result(index, ts::command_result{error::group_invalid_id});
break; break;
@ -2029,8 +2046,10 @@ command_result ConnectedClient::handleCommandPermFind(Command &cmd) {
} }
return 0; return 0;
}); });
if(entries.empty())
if(entries.empty()) {
return command_result{error::database_empty_result}; return command_result{error::database_empty_result};
}
struct CommandPerm { struct CommandPerm {
permission::PermissionType p; permission::PermissionType p;
@ -2044,7 +2063,16 @@ command_result ConnectedClient::handleCommandPermFind(Command &cmd) {
perms.resize(entries.size()); perms.resize(entries.size());
size_t index{0}; size_t index{0};
auto all_groups = this->server->group_manager()->availableGroups(true);
/* 1 := Server | 2 := Channel */
btree::map<GroupId, int> group_types{};
for(const auto& s_group : this->server->group_manager()->server_groups()->available_groups(groups::GroupCalculateMode::GLOBAL)) {
group_types[s_group->group_id()] = 1;
}
for(const auto& c_group : this->server->group_manager()->channel_groups()->available_groups(groups::GroupCalculateMode::GLOBAL)) {
group_types[c_group->group_id()] = 2;
}
for(const auto& entry : entries) { for(const auto& entry : entries) {
auto& perm = perms[index++]; auto& perm = perms[index++];
@ -2090,19 +2118,24 @@ command_result ConnectedClient::handleCommandPermFind(Command &cmd) {
perm.id2 = 0; perm.id2 = 0;
perm.t = 2; /* channel permission */ perm.t = 2; /* channel permission */
} else if(entry->type == permission::SQL_PERM_GROUP) { } else if(entry->type == permission::SQL_PERM_GROUP) {
auto group = std::find_if(all_groups.begin(), all_groups.end(), [&](const auto& group) { return group->groupId() == entry->group_id; }); auto group_type = group_types[entry->group_id];
if(group == all_groups.end()) { switch(group_type) {
index--; /* unknown group */ case 1:
continue; perm.id1 = entry->group_id;
} perm.id2 = 0;
if((*group)->target() == GroupTarget::GROUPTARGET_CHANNEL) { perm.t = 0; /* server group */
perm.id1 = 0; break;
perm.id2 = entry->group_id;
perm.t = 3; /* channel group */ case 2:
} else { perm.id1 = 0;
perm.id1 = entry->group_id; perm.id2 = entry->group_id;
perm.id2 = 0; perm.t = 3; /* channel group */
perm.t = 0; /* server group */ break;
case 0:
default:
/* unknown group */
break;
} }
} }
#endif #endif
@ -2128,21 +2161,6 @@ command_result ConnectedClient::handleCommandPermFind(Command &cmd) {
return &a > &b; return &a > &b;
}); });
#if 0
Command result(this->notify_response_command("notifypermfind"));
index = 0;
// http://yat.qa/ressourcen/server-query-kommentare/#permfind
for(const auto& e : perms) {
result[index]["p"] = e.p;
result[index]["v"] = e.v;
result[index]["id1"] = e.id1;
result[index]["id2"] = e.id2;
result[index]["t"] = e.t;
index++;
}
this->sendCommand(result);
#else
command_builder result{this->notify_response_command("notifypermfind"), 64, perms.size()}; command_builder result{this->notify_response_command("notifypermfind"), 64, perms.size()};
index = 0; index = 0;
@ -2155,7 +2173,6 @@ command_result ConnectedClient::handleCommandPermFind(Command &cmd) {
bulk.put("id2", e.id2); bulk.put("id2", e.id2);
} }
this->sendCommand(result); this->sendCommand(result);
#endif
return command_result{error::ok}; return command_result{error::ok};
} }
@ -2171,7 +2188,9 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto client_dbid = cmd["cldbid"].as<ClientDbId>(); auto client_dbid = cmd["cldbid"].as<ClientDbId>();
if(!serverInstance->databaseHelper()->validClientDatabaseId(this->getServer(), client_dbid)) return command_result{error::client_invalid_id}; if(!serverInstance->databaseHelper()->validClientDatabaseId(this->getServer(), client_dbid)) {
return command_result{error::client_invalid_id};
}
if(client_dbid == this->getClientDatabaseId()) { if(client_dbid == this->getClientDatabaseId()) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_client_permissionoverview_own, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_client_permissionoverview_own, 1);
@ -2182,26 +2201,28 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
string channel_query, perm_query; string channel_query, perm_query;
auto channel = this->server ? this->server->channelTree->findChannel(cmd["cid"]) : serverInstance->getChannelTree()->findChannel(cmd["cid"]); auto channel = this->server ? this->server->channelTree->findChannel(cmd["cid"]) : serverInstance->getChannelTree()->findChannel(cmd["cid"]);
if(!channel) return command_result{error::channel_invalid_id}; if(!channel) {
return command_result{error::channel_invalid_id};
}
auto server_groups = this->server->group_manager()->getServerGroups(client_dbid, ClientType::CLIENT_TEAMSPEAK); auto channel_group = this->assignedChannelGroup(channel);
auto channel_group = this->server->group_manager()->getChannelGroup(client_dbid, channel, true);
auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), client_dbid); auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), client_dbid);
Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : ""); Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : "");
size_t index = 0; size_t index = 0;
result["cldbid"] = client_dbid; result["cldbid"] = client_dbid;
result["cid"] = channel->channelId(); result["cid"] = channel->channelId();
if(cmd["return_code"].size() > 0) if(cmd["return_code"].size() > 0) {
result["return_code"] = cmd["return_code"].string(); result["return_code"] = cmd["return_code"].string();
}
for(const auto& server_group : server_groups) { for(const auto& server_group : this->assignedServerGroups()) {
auto permission_manager = server_group->group->permissions(); auto permission_manager = server_group->permissions();
for(const auto& permission_data : permission_manager->permissions()) { for(const auto& permission_data : permission_manager->permissions()) {
auto& permission = std::get<1>(permission_data); auto& permission = std::get<1>(permission_data);
if(permission.flags.value_set) { if(permission.flags.value_set) {
result[index]["t"] = 0; /* server group */ result[index]["t"] = 0; /* server group */
result[index]["id1"] = server_group->group->groupId(); result[index]["id1"] = server_group->group_id();
result[index]["id2"] = 0; result[index]["id2"] = 0;
result[index]["p"] = std::get<0>(permission_data); result[index]["p"] = std::get<0>(permission_data);
@ -2212,7 +2233,7 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
} }
if(permission.flags.grant_set) { if(permission.flags.grant_set) {
result[index]["t"] = 0; /* server group */ result[index]["t"] = 0; /* server group */
result[index]["id1"] = server_group->group->groupId(); result[index]["id1"] = server_group->group_id();
result[index]["id2"] = 0; result[index]["id2"] = 0;
result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT);

View File

@ -5,17 +5,17 @@
namespace ts::server::groups { namespace ts::server::groups {
enum GroupType { enum GroupType {
GROUP_TYPE_QUERY, GROUP_TYPE_TEMPLATE = 0x00,
GROUP_TYPE_TEMPLATE, GROUP_TYPE_NORMAL = 0x01,
GROUP_TYPE_NORMAL, GROUP_TYPE_QUERY = 0x02,
GROUP_TYPE_UNKNOWN = 0xFF GROUP_TYPE_UNKNOWN = 0xFF
}; };
enum GroupNameMode { enum GroupNameMode {
GROUP_NAME_MODE_HIDDEN, GROUP_NAME_MODE_HIDDEN = 0x00,
GROUP_NAME_MODE_BEFORE, GROUP_NAME_MODE_BEFORE = 0x01,
GROUP_NAME_MODE_BEHIND GROUP_NAME_MODE_BEHIND = 0x02
}; };
typedef uint32_t GroupSortId; typedef uint32_t GroupSortId;

View File

@ -4,6 +4,7 @@
#include <log/LogUtils.h> #include <log/LogUtils.h>
#include "./GroupAssignmentManager.h" #include "./GroupAssignmentManager.h"
#include "./GroupManager.h" #include "./GroupManager.h"
#include "BasicChannel.h"
using namespace ts::server::groups; using namespace ts::server::groups;
@ -217,7 +218,7 @@ std::vector<ts::GroupId> GroupAssignmentManager::server_groups_of_client(ts::ser
return result; return result;
} }
std::vector<ChannelGroupAssignment> GroupAssignmentManager::channel_groups_of_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups_of_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) {
std::vector<ChannelGroupAssignment> result{}; std::vector<ChannelGroupAssignment> result{};
bool cache_found{false}; bool cache_found{false};
{ {
@ -262,7 +263,7 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::channel_groups_of_cl
if(mode == GroupAssignmentCalculateMode::GLOBAL) { if(mode == GroupAssignmentCalculateMode::GLOBAL) {
if(auto parent = this->manager_->parent_manager(); parent) { if(auto parent = this->manager_->parent_manager(); parent) {
auto parent_groups = parent->assignments().channel_groups_of_client(mode, cldbid); auto parent_groups = parent->assignments().exact_channel_groups_of_client(mode, cldbid);
result.reserve(result.size() + parent_groups.size()); result.reserve(result.size() + parent_groups.size());
result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); result.insert(result.begin(), parent_groups.begin(), parent_groups.end());
} }
@ -271,10 +272,10 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::channel_groups_of_cl
return result; return result;
} }
std::optional<ChannelGroupAssignment> GroupAssignmentManager::channel_group_of_client(GroupAssignmentCalculateMode mode, std::optional<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_group_of_client(GroupAssignmentCalculateMode mode,
ClientDbId client_database_id, ChannelId channel_id) { ClientDbId client_database_id, ChannelId channel_id) {
/* TODO: Improve performance by not querying all groups */ /* TODO: Improve performance by not querying all groups */
auto assignments = this->channel_groups_of_client(mode, client_database_id); auto assignments = this->exact_channel_groups_of_client(mode, client_database_id);
for(const auto& assignment : assignments) { for(const auto& assignment : assignments) {
if(assignment.channel_id != channel_id) { if(assignment.channel_id != channel_id) {
continue; continue;
@ -286,6 +287,29 @@ std::optional<ChannelGroupAssignment> GroupAssignmentManager::channel_group_of_c
return std::nullopt; return std::nullopt;
} }
std::optional<ts::GroupId> GroupAssignmentManager::calculate_channel_group_of_client(GroupAssignmentCalculateMode mode,
ClientDbId client_database_id,
std::shared_ptr<BasicChannel> &channel) {
auto assignments = this->exact_channel_groups_of_client(mode, client_database_id);
while(channel) {
for(const auto& assignment : assignments) {
if(assignment.channel_id != channel->channelId()) {
continue;
}
return std::make_optional(assignment.group_id);
}
if(permission::v2::permission_granted(1, channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end))) {
break;
}
channel = channel->parent();
}
return std::nullopt;
}
std::deque<ts::ClientDbId> GroupAssignmentManager::server_group_clients(GroupId group_id) { std::deque<ts::ClientDbId> GroupAssignmentManager::server_group_clients(GroupId group_id) {
std::deque<ts::ClientDbId> result{}; std::deque<ts::ClientDbId> result{};
if constexpr(kCacheAllClients) { if constexpr(kCacheAllClients) {
@ -330,6 +354,59 @@ std::deque<ts::ClientDbId> GroupAssignmentManager::server_group_clients(GroupId
return result; return result;
} }
std::deque<std::tuple<ts::GroupId, ts::ChannelId, ts::ClientDbId>> GroupAssignmentManager::channel_group_list(
GroupId group_id,
ChannelId channel_id,
ClientDbId client_database_id
) {
std::string sql_query{};
sql_query += "SELECT `groupId`, `cldbid`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid";
if(group_id > 0) {
sql_query += " AND `groupId` = :groupId";
}
if(channel_id > 0) {
sql_query += " AND `channelId` = :cid";
}
if(client_database_id > 0) {
sql_query += " AND `cldbid` = :cldbid";
}
sql::command sql{this->sql_manager(), sql_query};
sql.value(":groupId", group_id);
sql.value(":cid", channel_id);
sql.value(":cldbid", client_database_id);
std::deque<std::tuple<ts::GroupId, ts::ChannelId, ts::ClientDbId>> result{};
LOG_SQL_CMD(sql.query([&](int length, std::string* values, std::string* names) {
GroupId group_id;
ChannelId channel_id;
ClientDbId client_database_id;
int index{0};
try {
assert(names[index] == "groupId");
group_id = std::stoull(values[index++]);
assert(names[index] == "cldbid");
channel_id = std::stoull(values[index++]);
assert(names[index] == "channelId");
client_database_id = std::stoull(values[index++]);
assert(index == length);
} catch (std::exception& ex) {
logError(this->server_id(), "Failed to parse client group assignment at index {}: {}",
index - 1,
ex.what()
);
return;
}
result.emplace_back(group_id, channel_id, client_database_id);
}));
return result;
}
GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) { GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) {
bool cache_verified{false}; bool cache_verified{false};
{ {
@ -340,8 +417,9 @@ GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) {
return assignment.group_id == group; return assignment.group_id == group;
}); });
if(it != entry->server_group_assignments.end()) if(it != entry->server_group_assignments.end()) {
return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP;
}
entry->server_group_assignments.emplace_back(group); entry->server_group_assignments.emplace_back(group);
cache_verified = true; cache_verified = true;
break; break;
@ -384,8 +462,9 @@ GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId cli
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) {
return assignment.group_id == group; return assignment.group_id == group;
}); });
if(it == entry->server_group_assignments.end()) if(it == entry->server_group_assignments.end()) {
return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP;
}
entry->server_group_assignments.erase(it); entry->server_group_assignments.erase(it);
cache_verified = true; cache_verified = true;
break; break;
@ -507,8 +586,14 @@ void GroupAssignmentManager::cleanup_channel_temporary_assignment(ClientDbId cli
auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) {
return assignment.channel_id == channel; return assignment.channel_id == channel;
}); });
if(assignment->temporary_assignment)
if(assignment == client->channel_group_assignments.end()) {
break;
}
if(assignment->temporary_assignment) {
client->channel_group_assignments.erase(assignment); client->channel_group_assignments.erase(assignment);
}
break; break;
} }
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <optional>
#include <mutex> #include <mutex>
#include <deque> #include <deque>
#include <string> #include <string>
@ -12,6 +13,10 @@ namespace sql {
class SqlManager; class SqlManager;
} }
namespace ts {
class BasicChannel;
}
namespace ts::server { namespace ts::server {
class ConnectedClient; class ConnectedClient;
@ -63,17 +68,30 @@ namespace ts::server {
bool load_data(std::string& /* error */); bool load_data(std::string& /* error */);
void unload_data(); void unload_data();
void reset_all();
/* client specific cache methods */ /* client specific cache methods */
void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
/* info/query methods */ /* info/query methods */
[[nodiscard]] std::vector<GroupId> server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); [[nodiscard]] std::vector<GroupId> server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
[[nodiscard]] std::vector<ChannelGroupAssignment> channel_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); [[nodiscard]] std::vector<ChannelGroupAssignment> exact_channel_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
[[nodiscard]] std::optional<ChannelGroupAssignment> channel_group_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */, ChannelId /* channel id */); [[nodiscard]] std::optional<ChannelGroupAssignment> exact_channel_group_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */, ChannelId /* channel id */);
/**
* Calculate the target channel group for the client.
* The parameters `target channel` will contain the channel where the group has been inherited from.
* Note: `target channel` will be altered if the resutl is empty.
* @return The target channel group id
*/
[[nodiscard]] std::optional<GroupId> calculate_channel_group_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */, std::shared_ptr<BasicChannel>& /* target channel */);
[[nodiscard]] std::deque<ClientDbId> server_group_clients(GroupId /* group id */); [[nodiscard]] std::deque<ClientDbId> server_group_clients(GroupId /* group id */);
//[[nodiscard]] std::deque<ClientDbId> channel_group_clients(GroupId /* group id */, ChannelId /* channel id */); [[nodiscard]] std::deque<std::tuple<GroupId, ChannelId, ClientDbId>> channel_group_list(GroupId /* group id */, ChannelId /* channel id */, ClientDbId /* client database id */);
[[nodiscard]] bool is_server_group_empty(GroupId /* group id */);
[[nodiscard]] bool is_channel_group_empty(GroupId /* group id */);
/* change methods */ /* change methods */
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */); GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */);
@ -84,6 +102,10 @@ namespace ts::server {
void cleanup_assignments(); void cleanup_assignments();
void cleanup_channel_assignments(ChannelId /* channel */); void cleanup_channel_assignments(ChannelId /* channel */);
void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */); void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */);
void handle_channel_deleted(ChannelId /* channel id */);
void handle_server_group_deleted(GroupId /* group id */);
void handle_channel_group_deleted(GroupId /* group id */);
private: private:
struct ClientCache { struct ClientCache {
ClientDbId client_database_id{0}; ClientDbId client_database_id{0};

View File

@ -114,6 +114,27 @@ void AbstractGroupManager::unload_data() {
} }
} }
void AbstractGroupManager::reset_groups(const std::shared_ptr<GroupManager> &template_provider, std::map<GroupId, GroupId> &mapping) {
std::lock_guard manage_lock{this->group_manage_mutex_};
this->unload_data();
/* Delete all old groups */
{
/* 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);
}
}
void AbstractGroupManager::reset_groups(bool db_cleanup) { void AbstractGroupManager::reset_groups(bool db_cleanup) {
std::lock_guard manage_lock{this->group_manage_mutex_}; std::lock_guard manage_lock{this->group_manage_mutex_};
this->unload_data(); this->unload_data();
@ -224,6 +245,12 @@ std::shared_ptr<Group> AbstractGroupManager::find_group_by_name_(GroupCalculateM
} }
GroupCreateResult AbstractGroupManager::create_group_(GroupType type, const std::string &name, std::shared_ptr<Group>& result) { GroupCreateResult AbstractGroupManager::create_group_(GroupType type, const std::string &name, std::shared_ptr<Group>& result) {
if(name.empty()) {
return GroupCreateResult::NAME_TOO_SHORT;
} else if(name.length() > 30) {
return GroupCreateResult::NAME_TOO_LONG;
}
std::lock_guard manage_lock{this->group_manage_mutex_}; std::lock_guard manage_lock{this->group_manage_mutex_};
if(this->find_group_by_name_(GroupCalculateMode::LOCAL, name)) { if(this->find_group_by_name_(GroupCalculateMode::LOCAL, name)) {
return GroupCreateResult::NAME_ALREADY_IN_USED; return GroupCreateResult::NAME_ALREADY_IN_USED;
@ -264,9 +291,6 @@ GroupCopyResult AbstractGroupManager::copy_group_(GroupId source, GroupType targ
case GroupCreateResult::NAME_ALREADY_IN_USED: case GroupCreateResult::NAME_ALREADY_IN_USED:
return GroupCopyResult::NAME_ALREADY_IN_USE; return GroupCopyResult::NAME_ALREADY_IN_USE;
case GroupCreateResult::FAILED_TO_GENERATE_ID:
return GroupCopyResult::FAILED_TO_GENERATE_ID;
} }
assert(result); assert(result);
@ -335,12 +359,17 @@ GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) {
std::lock_guard manage_lock{this->group_manage_mutex_}; std::lock_guard manage_lock{this->group_manage_mutex_};
{ {
std::lock_guard glock{this->group_mutex_}; std::unique_lock glock{this->group_mutex_};
auto it = std::find_if(this->groups_.begin(), this->groups_.begin(), [&](const std::shared_ptr<Group>& group) { auto it = std::find_if(this->groups_.begin(), this->groups_.begin(), [&](const std::shared_ptr<Group>& group) {
return group->group_id() == group_id; return group->group_id() == group_id;
}); });
if(it == this->groups_.end()) { if(it == this->groups_.end()) {
if(this->parent_manager_) {
glock.unlock();
return this->parent_manager_->delete_group_(group_id);
}
return GroupDeleteResult::INVALID_GROUP_ID; return GroupDeleteResult::INVALID_GROUP_ID;
} }
@ -354,6 +383,9 @@ GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) {
return GroupDeleteResult::SUCCESS; return GroupDeleteResult::SUCCESS;
} }
void AbstractGroupManager::reset_groups_(const std::shared_ptr<GroupManager> &source, std::map<GroupId, GroupId> &mapping) {
}
/* Server group manager */ /* Server group manager */
ServerGroupManager::ServerGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ServerGroupManager> parent) ServerGroupManager::ServerGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ServerGroupManager> parent)

View File

@ -24,7 +24,8 @@ namespace ts::server::groups {
enum struct GroupCreateResult { enum struct GroupCreateResult {
SUCCESS, SUCCESS,
NAME_ALREADY_IN_USED, NAME_ALREADY_IN_USED,
FAILED_TO_GENERATE_ID, NAME_TOO_SHORT,
NAME_TOO_LONG,
DATABASE_ERROR DATABASE_ERROR
}; };
@ -33,7 +34,6 @@ namespace ts::server::groups {
UNKNOWN_SOURCE_GROUP, UNKNOWN_SOURCE_GROUP,
UNKNOWN_TARGET_GROUP, UNKNOWN_TARGET_GROUP,
NAME_ALREADY_IN_USE, NAME_ALREADY_IN_USE,
FAILED_TO_GENERATE_ID,
DATABASE_ERROR DATABASE_ERROR
}; };
@ -73,9 +73,14 @@ namespace ts::server::groups {
bool initialize(std::string& /* error */); bool initialize(std::string& /* error */);
GroupLoadResult load_data(bool /* initialize */ = false); GroupLoadResult load_data(bool /* initialize */ = false);
void unload_data(); void unload_data();
void reset_groups(bool /* cleanup database */);
void save_permissions(size_t& /* total groups */, size_t& /* saved groups */); void save_permissions(size_t& /* total groups */, size_t& /* saved groups */);
/**
* Reset all known groups.
* If the template group provider is empty no new groups will be created.
*/
void reset_groups(const std::shared_ptr<GroupManager>& /* template group provider */, std::map<GroupId, GroupId>& /* mapping */);
protected: protected:
std::shared_ptr<AbstractGroupManager> parent_manager_; std::shared_ptr<AbstractGroupManager> parent_manager_;
DatabaseGroupTarget database_target_; DatabaseGroupTarget database_target_;

13
server/things_to_test Normal file
View File

@ -0,0 +1,13 @@
permission calculation
channel group inheritance
general server & channel groups
- assignments
- removement
- temporary groups
Notified channel & server member add/remove powers
Group edit if the notify<server/channel> group triggers
channelgroupclientlist
TODO: Delete the group header