Finalizing the new group manager

This commit is contained in:
WolverinDEV 2021-03-11 14:12:12 +01:00
parent 061ce4c284
commit 5991e5f4ad
46 changed files with 2086 additions and 2711 deletions

View File

@ -8,7 +8,7 @@ set(TEASPEAK_SERVER ON)
#end now
#set(MEMORY_DEBUG_FLAGS " -fsanitize=leak -fsanitize=address -fstack-protector-all ")
#set(MEMORY_DEBUG_FLAGS "-fsanitize=address -fstack-protector-all")
set(MEMORY_DEBUG_FLAGS "-fsanitize=address -fstack-protector-all")
#set(MEMORY_DEBUG_FLAGS "-fstack-protector-all")
#set(MEMORY_DEBUG_FLAGS " -fsanitize=address -static-libasan")

View File

@ -29,8 +29,9 @@ void LocalFileTransfer::shutdown_disk_io() {
{
std::unique_lock qlock{this->disk_io.queue_lock};
this->disk_io.notify_work_awaiting.notify_all();
while(this->disk_io.queue_head)
while(this->disk_io.queue_head) {
this->disk_io.notify_client_processed.wait_for(qlock, std::chrono::seconds{10});
}
if(this->disk_io.queue_head) {
logWarning(0, "Failed to flush disk IO. Force aborting.");

@ -1 +1 @@
Subproject commit d0babbc62fb657783a04737a1265ab1c36636bf6
Subproject commit 8c2608f90739a42cd727c8c8eab3d60bbb6dca53

View File

@ -117,12 +117,12 @@ bool DatabaseHandler::setup(std::string& error) {
SET_VERSION(6);
case 6:
CTBL("CREATE TABLE license_upgrade_log (`upgrade_id` INT, `timestamp` INT, `unique_id` VARCHAR(64), `server_ip` INT, `succeeded` TINYINT);");
CTBL("CREATE TABLE license_upgrade_log (`upgrade_id` INT, `timestamp` INT, `unique_id` VARCHAR(64), `server_ip` VARCHAR(32), `succeeded` TINYINT);");
CIDX("CREATE INDEX `upgrade_id_timestamp` ON `license_upgrade_log` (`upgrade_id`, `timestamp`)");
SET_VERSION(7);
default:;
}
}
return true;
}

2
rtclib

@ -1 +1 @@
Subproject commit cdf42fccd34c769fabfef7208f829c3f4b62a595
Subproject commit ea2dd197d26330850dfef795cf7db140d26194d7

View File

@ -56,6 +56,7 @@ set(SERVER_SOURCE_FILES
src/server/VoiceServer.cpp
src/server/POWHandler.cpp
src/client/voice/VoiceClientConnection.cpp
src/client/command_handler/groups.cpp
src/client/command_handler/channel.cpp
src/client/command_handler/client.cpp
src/client/command_handler/server.cpp
@ -223,7 +224,7 @@ target_link_libraries(PermMapHelper
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "5")
SET(CPACK_PACKAGE_VERSION_PATCH "2")
SET(CPACK_PACKAGE_VERSION_PATCH "3")
if (BUILD_TYPE_NAME EQUAL OFF)
SET(CPACK_PACKAGE_VERSION_DATA "beta")
elseif (BUILD_TYPE_NAME STREQUAL "")

View File

@ -172,8 +172,6 @@ inline sql::result load_permissions_v2(
sql::command& command,
bool test_channel, /* only used for client permissions (client channel permissions) */
bool is_channel) {
auto start = system_clock::now();
return command.query([&](int length, char** values, char** names){
permission::PermissionType key = permission::PermissionType::undefined;
permission::PermissionValue value = permNotGranted, granted = permNotGranted;
@ -214,10 +212,11 @@ inline sql::result load_permissions_v2(
return 0;
}
if(channel_id == 0 || is_channel)
if(channel_id == 0 || is_channel) {
manager->load_permission(key, {value, granted}, skipped, negated, value != permNotGranted, granted != permNotGranted);
else
} else {
manager->load_permission(key, {value, granted}, channel_id, skipped, negated, value != permNotGranted, granted != permNotGranted);
}
return 0;
});
@ -387,7 +386,7 @@ std::shared_ptr<permission::v2::PermissionManager> DatabaseHelper::loadGroupPerm
return result;
}
void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId group_id, uint8_t /* target */, const std::shared_ptr<ts::permission::v2::PermissionManager> &permissions) {
void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId group_id, uint8_t target, const std::shared_ptr<ts::permission::v2::PermissionManager> &permissions) {
const auto updates = permissions->flush_db_updates();
if(updates.empty())
return;
@ -398,8 +397,8 @@ void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId
auto permission_data = permission::resolvePermissionData(update.permission);
auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value;
auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant;
logTrace(server_id, "[CHANNEL] Updating group permission for group {}: {}. New value: {}. New grant: {}. Query: {}",
group_id,
logTrace(server_id, "Updating group permission for group {}/{}: {}. New value: {}. New grant: {}. Query: {}",
target, group_id,
permission_data->name,
value,
grant,
@ -608,9 +607,7 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId serverId, s
return false;
debugMessage(serverId, "Successfully registered client {} for server {} with database id {}.", cl->getUid(), serverId, cl->getClientDatabaseId());
return true;
}
return true;
}

View File

@ -12,961 +12,3 @@ using namespace std::chrono;
using namespace ts;
using namespace ts::server;
using namespace ts::permission;
extern InstanceHandler* serverInstance;
Group::Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId groupId) : _target(target), _type(type) {
memtrack::allocated<Group>(this);
this->handle = handle;
this->_properties = std::make_shared<PropertyManager>();
this->_properties->register_property_type<property::GroupProperties>();
this->setPermissionManager(make_shared<permission::v2::PermissionManager>());
this->properties()[property::GROUP_ID] = groupId;
this->properties()[property::GROUP_TYPE] = type;
/*
this->_properties->registerProperty("sgid", groupId, PROP_GROUP_INIT_SERVER);
this->_properties->registerProperty("cgid", groupId, PROP_GROUP_INIT_CHANNEL);
this->_properties->registerProperty("gid", groupId, PROP_PRIVATE_TEMP);
this->_properties->registerProperty("type", (uint8_t) type, PROP_GROUP_INIT);
this->_properties->registerProperty("name", "Undefined group #" + to_string(groupId), PROP_GROUP_INIT | PROP_SNAPSHOT);
this->_properties->registerProperty("sortid", 0, PROP_GROUP_INIT);
this->_properties->registerProperty("savedb", 0, PROP_GROUP_INIT);
this->_properties->registerProperty("namemode", 0, PROP_GROUP_INIT);
this->_properties->registerProperty("iconid", 0, PROP_GROUP_INIT);
this->_properties->registerProperty("default_group", false, PROP_PRIVATE_TEMP);
*/
this->_properties->registerNotifyHandler([&](Property& prop){
if((prop.type().flags & property::FLAG_SAVE) == 0) return;
if(!this->handle) return;
std::string sql;
if(prop.hasDbReference()){
sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :gid AND `key` = :key";
} else {
prop.setDbReference(true);
sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :gid, :key, :value)";
}
sql::command(this->handle->sql, sql,
variable{":sid", this->handle->getServerId()}, variable{":type", property::PropertyType::PROP_TYPE_GROUP}, variable{":gid", this->groupId()}, variable{":key", prop.type().name}, variable{":value", prop.value()})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
});
}
void Group::setPermissionManager(const std::shared_ptr<permission::v2::PermissionManager> &manager) {
this->_permissions = manager;
this->apply_properties_from_permissions();
}
void Group::apply_properties_from_permissions() {
{
auto permission = this->_permissions->permission_value_flagged(permission::i_icon_id);
this->properties()[property::GROUP_ICONID] = permission.has_value ? permission.value : 0;
}
{
auto permission = this->_permissions->permission_value_flagged(permission::i_group_show_name_in_tree);
this->properties()[property::GROUP_NAMEMODE] = permission.has_value ? permission.value : 0;
}
{
auto permission = this->_permissions->permission_value_flagged(permission::i_group_sort_id);
this->properties()[property::GROUP_SORTID] = permission.has_value ? permission.value : 0;
}
{
auto permission = this->_permissions->permission_value_flagged(permission::b_group_is_permanent);
this->properties()[property::GROUP_SAVEDB] = permission.has_value ? permission.value : 0;
}
}
Group::~Group() {
memtrack::freed<Group>(this);
}
GroupManager::GroupManager(const shared_ptr<VirtualServer> &server, sql::SqlManager *sql, std::shared_ptr<GroupManager> root) : server(server), sql(sql), root(std::move(root)) { }
GroupManager::~GroupManager() {}
bool GroupManager::loadGroupFormDatabase(GroupId id) {
std::lock_guard glock{this->group_lock};
if(id == 0){
this->groups.clear();
auto res = sql::command(this->sql, "SELECT * FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&GroupManager::insertGroupFromDb, this);
LOG_SQL_CMD(res);
return true;
} else {
for(const auto &e : this->groups)
if(e->groupId() == id){
this->groups.erase(find(this->groups.begin(), this->groups.end(), e));
break;
}
auto res = sql::command(this->sql, "SELECT * FROM `groups` WHERE `serverId` = :sid AND `groupId` = :gid",
variable{":sid", this->getServerId()}, variable{":gid", id}).query(&GroupManager::insertGroupFromDb, this);
auto fn = LOG_SQL_CMD;
fn(res);
return res;
}
}
std::vector<std::shared_ptr<Group>> GroupManager::availableGroups(bool root) {
std::vector<std::shared_ptr<Group>> response;
{
std::lock_guard glock{this->group_lock};
for(const auto& group : this->groups)
response.push_back(group);
}
if(root && this->root){
auto elm = this->root->availableGroups();
for(const auto& e : elm)
response.push_back(e);
}
return response;
}
std::vector<std::shared_ptr<Group>> GroupManager::availableServerGroups(bool root){
std::vector<std::shared_ptr<Group>> response;
{
std::lock_guard glock{this->group_lock};
for(const auto& group : this->groups)
if(group->target() == GroupTarget::GROUPTARGET_SERVER)
response.push_back(group);
}
if(root && this->root){
auto elm = this->root->availableServerGroups();
for(const auto& e : elm)
response.push_back(e);
}
return response;
}
std::vector<std::shared_ptr<Group>> GroupManager::availableChannelGroups(bool root) {
std::vector<std::shared_ptr<Group>> response;
{
std::lock_guard glock{this->group_lock};
for(const auto& group : this->groups)
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL)
response.push_back(group);
}
if(root && this->root){
auto elm = this->root->availableChannelGroups(true);
for(const auto& e : elm)
response.push_back(e);
}
return response;
}
int GroupManager::insertGroupFromDb(int count, char **values, char **column) {
GroupId groupId = 0;
auto target = (GroupTarget) 0xff;
auto type = (GroupType) 0xff;
std::string targetName;
for(int index = 0; index < count; index++){
if(strcmp(column[index], "target") == 0)
target = (GroupTarget) stoll(values[index]);
else if(strcmp(column[index], "type") == 0)
type = (GroupType) stoll(values[index]);
else if(strcmp(column[index], "groupId") == 0)
groupId = (GroupType) stoll(values[index]);
else if(strcmp(column[index], "displayName") == 0)
targetName = values[index];
//else cerr << "Invalid group table row " << column[index] << endl;
}
if((size_t) groupId == 0 || (size_t) target == 0xff || (size_t) type == 0xff || targetName.empty()) {
logCritical(this->getServerId(), "Found invalid group ad database! (GroupId " + to_string(groupId) + ", Target " + to_string(target) + ", Type " + to_string(type) + ", Name '" + targetName + "')");
return 0;
}
/*
assert(groupId != 0);
assert(target != 0xff);
assert(type != 0xff);
assert(!targetName.empty());
*/
shared_ptr<Group> group = std::make_shared<Group>(this, target, type, groupId);
/*
auto res = sql::command(this->sql, "SELECT `key`, `value` FROM `properties` WHERE `serverId` = :sid AND `type` = :type AND `id` = :gid",
variable{":sid", this->getServerId()}, variable{":type", property::PROP_TYPE_GROUP}, variable{":gid", group->groupId()}).query([this](Group* g, int length, char** values, char** columns){
string key, value;
for(int index = 0; index < length; index++)
if(strcmp(columns[index], "key") == 0)
key = values[index];
else if(strcmp(columns[index], "value") == 0)
value = values[index];
auto info = property::info<property::GroupProperties>(key);
if(info == property::GROUP_UNDEFINED) {
logError(this->getServerId(), "Invalid property for group: " + key);
return 0;
}
auto prop = g->properties()[info.property];
prop.setDbReference(true);
prop.value(value, false);
return 0;
}, group.get());
auto print = LOG_SQL_CMD;
print(res);
*/
//FIXME load group properties view database helper (or drop it full because no saved properties)
group->properties()[property::GROUP_NAME] = targetName;
group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0));
debugMessage(this->getServerId(), "Push back group -> " + to_string(group->groupId()) + " - " + group->name());
this->groups.push_back(group);
#if 0
auto iconId = (IconId) group->icon_id();
if(iconId != 0 && serverInstance && serverInstance->getFileServer() && !serverInstance->getFileServer()->iconExists(this->server.lock(), iconId)) {
logMessage(this->getServerId(), "[FILE] Missing group icon (" + to_string(iconId) + ").");
if(config::server::delete_missing_icon_permissions) group->permissions()->set_permission(permission::i_icon_id, {0, 0}, permission::v2::delete_value, permission::v2::do_nothing);
}
#endif
return 0;
}
void GroupManager::handleChannelDeleted(const ChannelId& channel_id) {
unique_lock cache_lock(this->cacheLock);
auto cached_clients = std::vector<shared_ptr<CachedClient>>{this->cachedClients.begin(), this->cachedClients.end()};
cache_lock.unlock();
for(auto& entry : cached_clients) {
lock_guard entry_lock(entry->lock);
entry->channel_groups.erase(channel_id);
}
}
bool GroupManager::isLocalGroup(std::shared_ptr<Group> gr) {
std::lock_guard glock{this->group_lock};
return std::find(this->groups.begin(), this->groups.end(), gr) != this->groups.end();
}
std::shared_ptr<Group> GroupManager::defaultGroup(GroupTarget type, bool enforce_property) {
threads::MutexLock lock(this->cacheLock);
if(this->groups.empty()) return nullptr;
auto server = this->server.lock();
auto id =
server ?
server->properties()[type == GroupTarget::GROUPTARGET_SERVER ? property::VIRTUALSERVER_DEFAULT_SERVER_GROUP
: property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_or<GroupId>(0) :
serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_or<GroupId>(0);
auto group = this->findGroupLocal(id);
if(group || enforce_property) return group;
{
std::lock_guard glock{this->group_lock};
for(auto elm : this->groups)
if(elm->target() == type)
return elm;
}
return nullptr; //Worst case!
}
std::shared_ptr<Group> GroupManager::findGroup(GroupId groupId) {
auto result = this->findGroupLocal(groupId);
if(!result && this->root) result = this->root->findGroup(groupId);
return result;
}
std::shared_ptr<Group> GroupManager::findGroupLocal(GroupId groupId) {
std::lock_guard glock{this->group_lock};
for(const auto& elm : this->groups)
if(elm->groupId() == groupId) return elm;
return nullptr;
}
std::vector<std::shared_ptr<Group>> GroupManager::findGroup(GroupTarget target, std::string name) {
vector<shared_ptr<Group>> res;
{
std::lock_guard glock{this->group_lock};
for(const auto &elm : this->groups)
if(elm->name() == name && elm->target() == target) res.push_back(elm);
}
if(this->root) {
auto r = root->findGroup(target, name);
for(const auto &e : r) res.push_back(e);
}
return res;
}
ServerId GroupManager::getServerId() {
auto l = this->server.lock();
return l ? l->getServerId() : 0;
}
std::shared_ptr<Group> GroupManager::createGroup(GroupTarget target, GroupType type, std::string name) {
if(type != GROUP_TYPE_NORMAL && this->root) return root->createGroup(target, type, name);
auto rawId = generateGroupId(this->sql);
if(rawId <= 0) {
logError(this->getServerId(), "Could not create a new group! ({})", "Could not generate group id");
return nullptr;
}
auto groupId = (GroupId) rawId;
auto res = sql::command(this->sql, "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", this->getServerId()}, variable{":gid", groupId}, variable{":target", target}, variable{":type", type}, variable{":name", name}).execute();
auto print = LOG_SQL_CMD;
print(res);
if(!res) return nullptr;
std::shared_ptr<Group> group = std::make_shared<Group>(this, target, type, groupId);
group->properties()[property::GROUP_NAME] = name;
group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0));
std::lock_guard glock{this->group_lock};
this->groups.push_back(group);
return group;
}
GroupId GroupManager::copyGroup(std::shared_ptr<Group> group, GroupType type, std::string name, ServerId targetServerId) {
auto group_server = group->handle->getServerId();
auto groupId = generateGroupId(this->sql);
auto print = LOG_SQL_CMD;
auto res = sql::command(this->sql, "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":target", groupId}, variable{":ssid", group_server}, variable{":tsid", targetServerId}, variable{":source", group->groupId()}, variable{":type", SQL_PERM_GROUP}).execute();
print(res);
//Properties not used currently
res = sql::command(this->sql, "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, `key`, `value` FROM `properties` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":target", groupId}, variable{":ssid", group_server}, variable{":tsid", targetServerId}, variable{":source", group->groupId()}, variable{":type", property::PropertyType::PROP_TYPE_GROUP}).execute();
print(res);
res = sql::command(this->sql, "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", targetServerId}, variable{":gid", groupId}, variable{":target", group->target()}, variable{":type", type}, variable{":name", name}).execute();
print(res);
if(targetServerId == (this->getServerId()))
this->loadGroupFormDatabase(groupId);
else if(this->root)
this->root->loadGroupFormDatabase(groupId);
return groupId;
}
bool GroupManager::copyGroupPermissions(const shared_ptr<Group> &source, const shared_ptr<Group> &target) {
auto targetServer = target->handle->getServerId();
auto sourceServer = source->handle->getServerId();
auto res = sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id", variable{":sid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":id", target->groupId()}).execute();
LOG_SQL_CMD(res);
res = sql::command(this->sql, "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":ssid", sourceServer}, variable{":tsid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":source", source->groupId()}, variable{":target", target->groupId()}).execute();
target->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(target->handle->getServerId(), target->groupId(), 0));
LOG_SQL_CMD(res);
return true;
}
bool GroupManager::reloadGroupPermissions(std::shared_ptr<Group> group) {
if(!isLocalGroup(group)){
if(this->root) return this->root->reloadGroupPermissions(group);
return false;
}
group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0));
return true;
}
bool GroupManager::renameGroup(std::shared_ptr<Group> group, std::string name) {
if(!isLocalGroup(group)){
if(this->root) return this->root->renameGroup(group, name);
return false;
}
// CREATE_TABLE("groups", "`serverId` INT NOT NULL, `groupId` INTEGER, `target` INT, `type` INT, `displayName` TEXT");
group->properties()[property::GROUP_NAME] = name;
sql::command(this->sql, "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :sid AND `groupId` = :gid AND `target` = :target",
variable{":name", name}, variable{":gid", group->groupId()}, variable{":target", group->target()}, variable{":sid", this->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed"});
return true;
}
bool GroupManager::deleteAllGroups() {
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).execute());
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).execute());
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type", variable{":sid", this->getServerId()}, variable{":type", SQL_PERM_GROUP}).execute());
{
lock_guard cache_lock(this->cacheLock);
for(const auto& entry : this->cachedClients) {
lock_guard entry_lock(entry->lock);
entry->server_groups.clear();
entry->channel_groups.clear();
}
}
this->groups.clear();
return true;
}
bool GroupManager::deleteGroup(std::shared_ptr<Group> group) {
if(!isLocalGroup(group)){
if(this->root) return this->root->deleteGroup(group);
return false;
}
{
std::lock_guard glock{this->group_lock};
this->groups.erase(std::find(this->groups.begin(), this->groups.end(), group));
}
/* erase the group out of our cache */
{
lock_guard cache_lock(this->cacheLock);
for(auto& entry : this->cachedClients) {
lock_guard entry_lock(entry->lock);
entry->server_groups.erase(std::remove_if(entry->server_groups.begin(), entry->server_groups.end(), [&](const std::shared_ptr<GroupAssignment>& group_assignment) {
return group_assignment->group == group;
}), entry->server_groups.end());
for(auto it = entry->channel_groups.begin(); it != entry->channel_groups.end();) {
if(it->second->group == group)
it = entry->channel_groups.erase(it);
else
it++;
}
}
}
bool flag_sql = false;
auto res = sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute();
LOG_SQL_CMD(res);
flag_sql |= !res;
res = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute();
LOG_SQL_CMD(res);
flag_sql |= !res;
serverInstance->databaseHelper()->deleteGroupArtifacts(this->getServerId(), group->groupId());
if(flag_sql)
logError(this->getServerId(), "Could not delete group {} ({}) from database. May leader to invalid data", group->name(), group->groupId());
return true;
}
int64_t GroupManager::generateGroupId(sql::SqlManager* sql) {
int64_t hightestGroupId = 0;
sql::command(sql, "SELECT `groupId` FROM `groups` ORDER BY `groupId` DESC LIMIT 1").query([](int64_t* ptr, int, char** values, char**){
*ptr = stoul(values[0]);
return 0;
}, &hightestGroupId);
return hightestGroupId + 1;
}
std::deque<property::ClientProperties> GroupManager::update_server_group_property(const shared_ptr<server::ConnectedClient> &client, bool channel_lock, const std::shared_ptr<BasicChannel>& channel) {
std::deque<property::ClientProperties> changed;
//Server groups
{
auto groups = this->getServerGroups(client->getClientDatabaseId(), client->getType());
string group_string;
for(const auto& group : groups)
if(group_string.empty()) group_string += to_string(group->group->groupId());
else group_string += "," + to_string(group->group->groupId());
if(client->properties()[property::CLIENT_SERVERGROUPS] != group_string) {
client->properties()[property::CLIENT_SERVERGROUPS] = group_string;
changed.push_back(property::CLIENT_SERVERGROUPS);
unique_lock chan_lock(client->channel_lock, defer_lock);
if(channel_lock)
chan_lock.lock();
client->cached_server_groups.clear();
client->cached_server_groups.reserve(groups.size());
for(const auto& group : groups)
client->cached_server_groups.push_back(group->group->groupId());
}
}
//Channel groups
if(channel){
shared_ptr<GroupAssignment> group = this->getChannelGroup(client->getClientDatabaseId(), channel, true);
if(client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] != group->channelId) {
client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = group->channelId;
changed.push_back(property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID);
}
if(client->properties()[property::CLIENT_CHANNEL_GROUP_ID] != group->group->groupId()) {
client->properties()[property::CLIENT_CHANNEL_GROUP_ID] = group->group->groupId();
changed.push_back(property::CLIENT_CHANNEL_GROUP_ID);
unique_lock chan_lock(client->channel_lock, defer_lock);
if(channel_lock)
chan_lock.lock();
client->cached_channel_group = group->group->groupId();
}
}
if(!changed.empty())
client->join_state_id++; /* groups have changed :) */
return changed;
}
void GroupManager::cleanupAssignments(ClientDbId client) {
if(this->root)
this->root->cleanupAssignments(client);
for(const auto& assignment : this->getAssignedServerGroups(client)) {
if(!assignment->group->is_permanent())
this->removeServerGroup(client, assignment->group);
}
}
void GroupManager::enableCache(const ClientDbId& client_database_id) {
if(this->root)
this->root->enableCache(client_database_id);
unique_lock cache_lock(this->cacheLock);
/* test if we're already having the client */
for(auto& entry : this->cachedClients)
if(entry->client_database_id == client_database_id) {
entry->use_count++;
return; /* client already cached, no need to cache client */
}
auto entry = std::make_shared<CachedClient>();
entry->client_database_id = client_database_id;
entry->use_count++;
this->cachedClients.push_back(entry);
lock_guard client_cache_lock(entry->lock); /* lock the client because we're currently loading the cache. */
cache_lock.unlock();
auto res = sql::command(this->sql, "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", client_database_id})
.query([&](int length, std::string* value, std::string* column) {
shared_ptr<Group> group = nullptr;
time_point<system_clock> until;
ChannelId channelId = 0;
for(int index = 0; index < length; index++){
try {
if(column[index] == "groupId"){
group = this->findGroup(stoll(value[index]));
} else if(column[index] == "until"){
until = time_point<system_clock>() + milliseconds(stoll(value[index]));
} else if(column[index] == "channelId"){
channelId = stoll(value[index]);
} else {
logError(this->getServerId(), "Unknown column in group assignment query: {}", column[index]);
continue;
}
} catch(std::exception& ex) {
logError(this->getServerId(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", client_database_id, column[index], value[index]);
return 0;
}
}
if(!group) {
return 0;
}
auto assignment = std::make_shared<GroupAssignment>();
assignment->group = group;
assignment->until = until;
assignment->parent = &*entry;
assignment->channelId = channelId;
assignment->server = this->getServerId();
if(channelId == 0)
entry->server_groups.push_back(assignment);
else
entry->channel_groups[channelId] = assignment;
return 0;
});
}
//FIXME: This method till get far more often then it should be. We should add a flag if the group cache is loaded for each std::shared_ptr<ConnectedClient> instance
void GroupManager::disableCache(const ClientDbId& client_database_id) {
if(this->root) {
this->root->disableCache(client_database_id);
}
lock_guard cache_lock(this->cacheLock);
this->cachedClients.erase(std::remove_if(this->cachedClients.begin(), this->cachedClients.end(), [&](const std::shared_ptr<CachedClient>& client) {
if(client->client_database_id != client_database_id)
return false;
lock_guard client_lock{client->lock};
return (--client->use_count) == 0;
}), this->cachedClients.end());
}
void GroupManager::clearCache() {
if(this->root) this->root->clearCache();
lock_guard lock(this->cacheLock);
this->cachedClients.clear();
}
bool GroupManager::isClientCached(const ClientDbId& client_database_id) {
return this->resolve_cached_client(client_database_id) == nullptr;
}
constexpr static auto kGroupMemberListQuery{R"(
SELECT
assignedGroups.cldbid,
clients_server.client_unique_id,
clients_server.client_nickname,
assignedGroups.channelId,
assignedGroups.until
FROM assignedGroups
INNER JOIN clients_server ON
clients_server.client_database_id = assignedGroups.cldbid AND clients_server.server_id = :sid
WHERE assignedGroups.`serverId` = :sid AND `groupId` = :gid;
)"};
typedef std::vector<std::shared_ptr<GroupMember>> ResList;
std::deque<GroupMember> GroupManager::listGroupMembers(std::shared_ptr<Group> group, bool names) {
if(!isLocalGroup(group)){
if(this->root)
return this->root->listGroupMembers(group, names);
return {};
}
std::deque<GroupMember> result{};
size_t set_index{0};
sql::command{this->sql, std::string{kGroupMemberListQuery}, variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}}
.query([&](int length, std::string* values, std::string* names) {
set_index++;
auto index{0};
try {
auto& member = result.emplace_back();
assert(names[index] == "cldbid");
member.cldbId = std::stoull(values[index++]);
assert(names[index] == "client_unique_id");
member.uid = values[index++];
assert(names[index] == "client_nickname");
member.displayName = values[index++];
assert(names[index] == "channelId");
member.channelId = std::stoull(values[index++]);
assert(names[index] == "until");
member.until = std::chrono::system_clock::time_point{} + std::chrono::milliseconds{std::stoll(values[index++])};
assert(index == length);
} catch (std::exception& ex) {
result.pop_back();
logError(this->getServerId(), "Failed to parse client group assignment for group {}: {}. Set index: {}, Column: {}",
group->groupId(),
ex.what(),
set_index - 1,
index - 1
);
return;
}
});
return result;
}
vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbId cldbId) {
vector<std::shared_ptr<GroupAssignment>> result;
sql::result res;
auto cached = resolve_cached_client(cldbId);
if(cached) {
{
lock_guard lock{cached->lock};
for(const auto &serverGroup : cached->server_groups)
result.push_back(serverGroup);
for(auto& channelGroup : cached->channel_groups)
result.push_back(channelGroup.second);
}
if(this->root){
auto append = this->root->listGroupAssignments(cldbId);
for(const auto &elm : append)
result.push_back(elm);
}
return result;
}
res = sql::command(this->sql, "SELECT `groupId`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId})
.query([&](int length, char** value, char** column){
shared_ptr<Group> group = nullptr;
time_point<system_clock> until;
uint64_t channelId = 0;
for(int index = 0; index < length; index++){
if(value[index] == nullptr) {
logError(this->getServerId(), string() + "Invalid value at " + column[index]);
continue;
}
if(strcmp(column[index], "groupId") == 0){
group = this->findGroup(stoll(value[index]));
} else if(strcmp(column[index], "until") == 0){
until = time_point<system_clock>() + milliseconds(stoll(value[index]));
} else if(strcmp(column[index], "channelId") == 0){
channelId = stoll(value[index]);
} else cerr << "Invalid column " << column[index] << endl;
}
if(!group)
return 0;
shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
assignment->parent = nullptr;
assignment->group = group;
assignment->until = until;
assignment->channelId = channelId;
assignment->server = this->getServerId();
result.push_back(assignment);
return 0;
});
(LOG_SQL_CMD)(res);
if(this->root){
auto append = this->root->listGroupAssignments(cldbId);
for(const auto &elm : append)
result.push_back(elm);
}
return result;
}
std::shared_ptr<CachedClient> GroupManager::resolve_cached_client(ClientDbId client_database_id) {
{
lock_guard lock(this->cacheLock);
for(auto& entry : this->cachedClients)
if(entry->client_database_id == client_database_id)
return entry;
}
return nullptr;
}
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGroups(ClientDbId cldbid) {
auto cached = this->resolve_cached_client(cldbid);
sql::result res;
std::vector<std::shared_ptr<GroupAssignment>> result;
if(this->root) {
auto client_groups = this->root->getAssignedServerGroups(cldbid);
result.insert(result.begin(), client_groups.begin(), client_groups.end());
}
if(cached) {
lock_guard cache_lock{cached->lock};
result.insert(result.end(), cached->server_groups.begin(), cached->server_groups.end());
return result;
}
debugMessage(this->getServerId(), "Query client groups for client {} on server {}.", cldbid, this->getServerId());
res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->getServerId()}, variable{":cldbid", cldbid}).query([&](int length, char** value, char** column){
shared_ptr<Group> group = nullptr;
time_point<system_clock> until;
for(int index = 0; index < length; index++) {
if(value[index] == nullptr) {
logError(this->getServerId(), string() + "Invalid value at " + column[index]);
continue;
}
if(strcmp(column[index], "groupId") == 0 && value[index] != nullptr){
group = this->findGroup(stoll(value[index]));
} else if(strcmp(column[index], "until") == 0){
until = time_point<system_clock>() + milliseconds(stoll(value[index] == nullptr ? "0" : value[index]));
} else cerr << "Invalid column " << column[index] << endl;
}
if(!group)
return 0;
shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
assignment->parent = nullptr;
assignment->group = group;
assignment->until = until;
assignment->server = this->getServerId();
result.push_back(assignment);
return 0;
});
LOG_SQL_CMD(res);
return result;
}
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getServerGroups(ClientDbId cldbid, server::ClientType type) {
auto result = this->getAssignedServerGroups(cldbid);
if(result.empty()) return this->defaultServerGroupGroupAssignments(cldbid, type);
return result;
}
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::defaultServerGroupGroupAssignments(ClientDbId client, ClientType type) {
std::vector<std::shared_ptr<GroupAssignment>> result;
if(type == ClientType::CLIENT_QUERY && this->root) {
auto root = this->root->defaultServerGroupGroupAssignments(client, type);
result.insert(result.begin(), root.begin(), root.end());
} else if(type == ClientType::CLIENT_MUSIC) {
threads::MutexLock lock(this->cacheLock);
auto server = this->server.lock();
auto id =
server ?
server->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP].as_or<GroupId>(0) :
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_or<GroupId>(0);
auto group = this->findGroupLocal(id);
if(group) {
result.push_back(std::make_shared<GroupAssignment>(nullptr, this->getServerId(), 0, group, time_point<system_clock>()));
return result;
}
}
result.push_back(std::make_shared<GroupAssignment>(nullptr, this->getServerId(), 0, this->defaultGroup(GroupTarget::GROUPTARGET_SERVER), time_point<system_clock>()));
return result;
}
std::shared_ptr<GroupAssignment> GroupManager::getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr<BasicChannel>& channel, bool assign_default) {
auto cached = resolve_cached_client(cldbId);
if(cached) {
lock_guard cache_lock(cached->lock);
if(cached->channel_groups.count(channel->channelId()) > 0) {
return cached->channel_groups[channel->channelId()];
} else
return assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : nullptr;
}
std::shared_ptr<GroupAssignment> result;
auto res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId}, variable{":chid", channel->channelId()}).query([&](int length, char** value, char** column){
shared_ptr<Group> group = nullptr;
time_point<system_clock> until;
for(int index = 0; index < length; index++){
if(value[index] == nullptr) {
logError(this->getServerId(), string() + "Invalid value at " + column[index]);
continue;
}
if(strcmp(column[index], "groupId") == 0){
group = this->findGroup(stoll(value[index]));
} else if(strcmp(column[index], "until") == 0){
until = time_point<system_clock>() + milliseconds(stoll(value[index]));
} else cerr << "Invalid column " << column[index] << endl;
}
if(!group)
return 0;
shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
assignment->parent = nullptr;
assignment->group = group;
assignment->until = until;
assignment->server = this->getServerId();
assignment->channelId = channel->channelId();
result = std::move(assignment);
return 0;
});
(LOG_SQL_CMD)(res);
return !result && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : result;
}
std::shared_ptr<GroupAssignment> GroupManager::getChannelGroup(ClientDbId cldbId, const shared_ptr<BasicChannel> &channel, bool assign_default) {
shared_ptr<GroupAssignment> group;
std::shared_ptr<BasicChannel> inheritance_channel = channel;
while(inheritance_channel && !group) {
group = this->getChannelGroupExact(cldbId, inheritance_channel, false);
if(!group) {
auto inheritance = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end);
if(inheritance.has_value && inheritance.value == 1)
break;
inheritance_channel = inheritance_channel->parent();
}
}
return !group && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : group;
}
std::shared_ptr<GroupAssignment> GroupManager::defaultChannelGroupAssignment(ClientDbId cldbId, const std::shared_ptr<BasicChannel> &channel) {
return std::make_shared<GroupAssignment>(nullptr, this->getServerId(), channel->channelId(), this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL), time_point<system_clock>());
}
void GroupManager::addServerGroup(ClientDbId cldbId, std::shared_ptr<Group> group, time_point<system_clock> until) {
/*
if(!this->isLocalGroup(group)) {
if(this->root) this->root->addServerGroup(cldbId, group, until);
return;
}
if(hasServerGroup(cldbId, group)) return;
*/
auto cached = resolve_cached_client(cldbId);
if(cached) {
lock_guard cache_lock(cached->lock);
cached->server_groups.push_back(std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), 0, group, until));
}
sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
variable{":sid", this->getServerId()},
variable{":cldbid", cldbId},
variable{":gid", group->groupId()},
variable{":chid", 0},
variable{":until", time_point_cast<milliseconds>(until).time_since_epoch().count()})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
}
void GroupManager::removeServerGroup(ClientDbId cldbId, std::shared_ptr<Group> group) {
if(!this->hasServerGroupAssigned(cldbId, group)) return;
auto cached = resolve_cached_client(cldbId);
if(cached) {
lock_guard cache_lock(cached->lock);
cached->server_groups.erase(std::remove_if(cached->server_groups.begin(), cached->server_groups.end(), [&](const std::shared_ptr<GroupAssignment>& group_assignment) {
return group_assignment->group == group;
}), cached->server_groups.end());
}
sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid",
variable{":sid", this->getServerId()},
variable{":cldbid", cldbId},
variable{":gid", group->groupId()},
variable{":chid", 0})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
}
void GroupManager::setChannelGroup(ClientDbId cldbId, std::shared_ptr<Group> group, std::shared_ptr<BasicChannel> channel, time_point<system_clock> until) {
auto old_group = getChannelGroupExact(cldbId, channel, false);
if(old_group) {
if(old_group->group == group) return;
} else if(!group) return;
auto default_group = !group || group == this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
auto cached = resolve_cached_client(cldbId);
if(cached) {
lock_guard cache_lock(cached->lock);
if(default_group)
cached->channel_groups.erase(channel->channelId());
else
cached->channel_groups[channel->channelId()] = std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), channel->channelId(), group, until);
}
sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid",
variable{":sid", this->getServerId()},
variable{":cldbid", cldbId},
variable{":chid", channel->channelId()})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
if(!default_group) {
sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
variable{":sid", this->getServerId()},
variable{":cldbid", cldbId},
variable{":gid", group->groupId()},
variable{":chid", channel->channelId()},
variable{":until", time_point_cast<milliseconds>(until).time_since_epoch().count()})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
}
}

View File

@ -921,7 +921,7 @@ bool InstanceHandler::validate_default_groups() {
auto& assignments = this->group_manager_->assignments();
auto client_assignments = assignments.server_groups_of_client(GroupAssignmentCalculateMode::GLOBAL, this->globalServerAdmin->getClientDatabaseId());
if(client_assignments.empty()) {
assignments.add_server_group(this->globalServerAdmin->getClientDatabaseId(), group_instance->group_id());
assignments.add_server_group(this->globalServerAdmin->getClientDatabaseId(), group_instance->group_id(), false);
}
}
}

View File

@ -4,6 +4,7 @@
#include "./InstanceHandler.h"
#include "./groups/GroupManager.h"
#include "./PermissionCalculator.h"
using namespace ts;
using namespace ts::server;
@ -24,53 +25,11 @@ permission::v2::PermissionFlaggedValue InstancePermissionHelper::calculate_permi
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue> > InstancePermissionHelper::calculate_permissions(
const std::deque<permission::PermissionType> &permissions,
ClientDbId cldbid,
ClientType /* type */,
ChannelId /* channel */,
ClientType type,
ChannelId channel,
bool granted,
std::shared_ptr<CalculateCache> /* cache */
) const {
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> result{};
/* TODO: Use same algorithm as on normal servers */
std::vector<std::shared_ptr<groups::ServerGroup>> assigned_groups{};
{
using groups::GroupAssignmentCalculateMode;
using groups::GroupCalculateMode;
auto group_manager = this->instance->group_manager();
auto& assignment_manager = group_manager->assignments();
auto assignments = assignment_manager.server_groups_of_client(GroupAssignmentCalculateMode::GLOBAL, cldbid);
if(assignments.empty()) {
assignments.push_back(this->instance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>());
}
assigned_groups.reserve(assignments.size());
for(const auto& group_id : assignments) {
auto group = group_manager->server_groups()->find_group(GroupCalculateMode::GLOBAL, group_id);
if(!group) {
continue;
}
assigned_groups.push_back(std::move(group));
}
}
for(const auto& permission : permissions) {
permission::v2::PermissionFlaggedValue value{0, false};
for(const auto &gr : assigned_groups){
auto group_permissions = gr->permissions();
auto flagged_permissions = granted ? group_permissions->permission_granted_flagged(permission) : group_permissions->permission_value_flagged(permission);
if(flagged_permissions.has_value) {
if(!value.has_value || flagged_permissions.value > value.value || flagged_permissions.value == -1) {
value = flagged_permissions;
}
}
}
result.emplace_back(permission, value);
}
return result;
ClientPermissionCalculator calculator{nullptr, cldbid, type, channel};
return calculator.calculate_permissions(permissions, granted);
}

View File

@ -1,11 +1,10 @@
#include <netdb.h>
#include <deque>
#include <tuple>
#include <fstream>
#include <log/LogUtils.h>
#include "InstanceHandler.h"
#include "src/client/InternalClient.h"
#include "src/server/QueryServer.h"
#include "./groups/GroupManager.h"
using namespace std;
using namespace std::chrono;
@ -23,9 +22,9 @@ struct GroupInfo {
*/
int target;
std::deque<string> properties;
string name;
std::string name;
/* permission type, value, granted, skip, negate */
deque<tuple<permission::PermissionType, permission::PermissionValue, permission::PermissionValue, bool, bool>> permissions;
std::deque<std::tuple<permission::PermissionType, permission::PermissionValue, permission::PermissionValue, bool, bool>> permissions;
};
/* TODO may use a transaction here? */
@ -123,14 +122,41 @@ bool InstanceHandler::setupDefaultGroups() {
for(const auto& info : groups) {
debugMessage(LOG_INSTANCE, "Creating default group {} with type {}", info->name, to_string(info->target));
//Query groups
auto group = this->group_manager_->createGroup(
info->target != 2 ? GroupTarget::GROUPTARGET_SERVER : GroupTarget::GROUPTARGET_CHANNEL,
info->target == 0 ? GroupType::GROUP_TYPE_QUERY : GroupType::GROUP_TYPE_TEMPLATE,
info->name
);
std::shared_ptr<groups::Group> created_group{};
groups::GroupCreateResult create_result{};
if(info->target == 2) {
std::shared_ptr<groups::ChannelGroup> c_group{};
create_result = serverInstance->group_manager()->channel_groups()->create_group(groups::GroupType::GROUP_TYPE_TEMPLATE, info->name, c_group);
created_group = c_group;
} else {
std::shared_ptr<groups::ChannelGroup> s_group{};
create_result = serverInstance->group_manager()->channel_groups()->create_group(info->target == 0 ? groups::GroupType::GROUP_TYPE_QUERY : groups::GroupType::GROUP_TYPE_TEMPLATE, info->name, s_group);
created_group = s_group;
}
switch(create_result) {
case groups::GroupCreateResult::SUCCESS:
break;
case groups::GroupCreateResult::DATABASE_ERROR:
logCritical(LOG_INSTANCE, "Failed to insert template group {} (Database error)", info->name);
return false;
case groups::GroupCreateResult::NAME_TOO_LONG:
case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
case groups::GroupCreateResult::NAME_TOO_SHORT:
logCritical(LOG_INSTANCE, "Failed to insert template group {} (Name issue)", info->name);
return false;
default:
logCritical(LOG_INSTANCE, "Failed to insert template group {} (Unkown error)", info->name);
return false;
}
for(auto perm : info->permissions) {
group->permissions()->set_permission(get<0>(perm), {get<1>(perm), get<2>(perm)}, get<1>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<2>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<3>(perm), get<4>(perm));
created_group->permissions()->set_permission(get<0>(perm), {get<1>(perm), get<2>(perm)}, get<1>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<2>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<3>(perm), get<4>(perm));
}
for(const auto& property : info->properties) {
@ -138,7 +164,7 @@ bool InstanceHandler::setupDefaultGroups() {
if(prop.is_undefined()) {
logCritical(LOG_INSTANCE, "Invalid template property name: " + property);
} else {
this->properties()[prop] = group->groupId();
this->properties()[prop] = created_group->group_id();
}
}
}

View File

@ -19,10 +19,18 @@ ClientPermissionCalculator::ClientPermissionCalculator(DataClient *client, Chann
auto server = client->getServer();
if(server && channel_id > 0) {
std::shared_lock channel_lock{server->get_channel_tree_lock()};
auto channel = server->getChannelTree()->findChannel(channel_id);
channel_lock.unlock();
std::shared_ptr<BasicChannel> channel{};
try {
std::shared_lock channel_lock{server->get_channel_tree_lock()};
channel = server->getChannelTree()->findChannel(channel_id);
} catch (std::system_error& e) {
if(e.code() != std::errc::resource_deadlock_would_occur) {
throw;
}
/* tree already write locked, no need to lock it again */
channel = server->getChannelTree()->findChannel(channel_id);
}
if(channel) {
this->channel_permissions = channel->permissions();
}
@ -56,10 +64,18 @@ ClientPermissionCalculator::ClientPermissionCalculator(
this->group_manager_ = server->group_manager();
this->default_channel_group = [server]{ return server->default_channel_group(); };
std::shared_lock channel_lock{server->get_channel_tree_lock()};
auto channel = server->getChannelTree()->findChannel(channel_id);
channel_lock.unlock();
std::shared_ptr<BasicChannel> channel{};
try {
std::shared_lock channel_lock{server->get_channel_tree_lock()};
channel = server->getChannelTree()->findChannel(channel_id);
} catch (std::system_error& e) {
if(e.code() != std::errc::resource_deadlock_would_occur) {
throw;
}
/* tree already write locked, no need to lock it again */
channel = server->getChannelTree()->findChannel(channel_id);
}
if(channel) {
this->channel_permissions = channel->permissions();
}

View File

@ -6,7 +6,7 @@
#include "VirtualServerManager.h"
#include "src/server/VoiceServer.h"
#include "InstanceHandler.h"
#include "InstanceHandler.h"
#include "./groups/GroupManager.h"
//TODO: When using the new command builder make sure you're using a std::deque as the underlying bulk type!
@ -979,10 +979,13 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr<Virtual
//List groups
{
cmd[index]["server_groups"] = "";
for(const auto& group : server->group_manager()->availableServerGroups(false)) {
cmd[index]["id"] = group->groupId();
cmd[index]["name"] = group->name();
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break;
auto server_groups = server->group_manager()->server_groups();
for(const auto& group : server_groups->available_groups(groups::GroupCalculateMode::LOCAL)) {
cmd[index]["id"] = group->group_id();
cmd[index]["name"] = group->display_name();
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) {
break;
}
cmd[index++]["end_group"] = "";
}
cmd[index++]["end_groups"] = "";
@ -1001,10 +1004,13 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr<Virtual
//List groups
{
cmd[index]["channel_groups"] = "";
for(const auto& group : server->group_manager()->availableChannelGroups(false)) {
cmd[index]["id"] = group->groupId();
cmd[index]["name"] = group->name();
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break;
auto server_groups = server->group_manager()->channel_groups();
for(const auto& group : server_groups->available_groups(groups::GroupCalculateMode::LOCAL)) {
cmd[index]["id"] = group->group_id();
cmd[index]["name"] = group->display_name();
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) {
break;
}
cmd[index++]["end_group"] = "";
}
cmd[index++]["end_groups"] = "";

View File

@ -550,7 +550,8 @@ void VirtualServer::client_move(
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();
this->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId());
this->group_manager()->assignments().cleanup_temporary_channel_assignment(target->getClientDatabaseId(),
s_source_channel->channelId());
auto update = target->properties()[property::CLIENT_IS_TALKER].as_or<bool>(false) ||
target->properties()[property::CLIENT_TALK_REQUEST].as_or<int64_t>(0) > 0;

View File

@ -976,8 +976,8 @@ bool VirtualServer::resetPermissions(std::string& new_permission_token) {
std::map<GroupId, GroupId> server_group_mapping{};
std::map<GroupId, GroupId> channel_group_mapping{};
{
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()->server_groups()->reset_groups(server_group_mapping);
this->group_manager()->channel_groups()->reset_groups(channel_group_mapping);
this->group_manager()->assignments().reset_all();
}
@ -1041,6 +1041,8 @@ bool VirtualServer::resetPermissions(std::string& new_permission_token) {
}
void VirtualServer::ensureValidDefaultGroups() {
/* TODO: FIXME: Impl! */
#if 0
auto default_server_group = this->group_manager()->defaultGroup(GROUPTARGET_SERVER, true);
if(!default_server_group) {
logError(this->serverId, "Missing server's default server group! (Id: {})", this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].value());
@ -1077,6 +1079,7 @@ void VirtualServer::ensureValidDefaultGroups() {
logError(this->serverId, "Using {} ({}) instead!", admin_channel_group->groupId(), admin_channel_group->name());
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = admin_channel_group->groupId();
}
#endif
}
void VirtualServer::send_text_message(const std::shared_ptr<BasicChannel> &channel, const std::shared_ptr<ConnectedClient> &sender, const std::string &message) {

View File

@ -7,6 +7,7 @@
#include "src/client/ConnectedClient.h"
#include "src/InstanceHandler.h"
#include "../manager/ConversationManager.h"
#include "../groups/GroupManager.h"
using namespace std;
using namespace ts;
@ -575,11 +576,11 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel>
auto server = this->server_ref.lock();
if(server) {
server->group_manager()->handleChannelDeleted(channel->channelId());
server->group_manager()->assignments().handle_channel_deleted(channel->channelId());
server->conversation_manager()->delete_conversation(channel->channelId());
server->rtc_server().destroy_channel(server_channel->rtc_channel_id);
} else {
serverInstance->group_manager()->handleChannelDeleted(channel->channelId());
serverInstance->group_manager()->assignments().handle_channel_deleted(channel->channelId());
}

View File

@ -61,7 +61,8 @@ void ConnectedClient::initialize_weak_reference(const std::shared_ptr<ConnectedC
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 changed{false};
self->update_displayed_client_groups(changed, changed);
}
}};
}
@ -487,6 +488,15 @@ bool ConnectedClient::notifyClientLeftView(
invoker = this->server->serverRoot;
}
break;
case ViewReasonId::VREASON_USER_ACTION:
case ViewReasonId::VREASON_SYSTEM:
case ViewReasonId::VREASON_TIMEOUT:
case ViewReasonId::VREASON_SERVER_STOPPED:
case ViewReasonId::VREASON_SERVER_LEFT:
case ViewReasonId::VREASON_CHANNEL_UPDATED:
case ViewReasonId::VREASON_EDITED:
case ViewReasonId::VREASON_SERVER_SHUTDOWN:
default:
break;
}
@ -818,7 +828,7 @@ void ConnectedClient::sendServerInit() {
command["client_myteamspeak_id"] = this->properties()[property::CLIENT_MYTEAMSPEAK_ID].value();
command["client_integrations"] = this->properties()[property::CLIENT_INTEGRATIONS].value();
if(ts::config::server::DefaultServerLicense == LicenseType::LICENSE_AUTOMATIC_INSTANCE){
if(ts::config::server::DefaultServerLicense == LicenseType::LICENSE_AUTOMATIC_INSTANCE) {
if(serverInstance->getVoiceServerManager()->usedSlots() <= 32)
command["lt"] = LicenseType::LICENSE_NONE;
else if(serverInstance->getVoiceServerManager()->usedSlots() <= 512)
@ -1037,19 +1047,28 @@ void ConnectedClient::update_displayed_client_groups(bool& server_groups_changed
if(!server_group_assignments.empty()) {
server_group_assignments = server_group_assignments.substr(1);
}
std::unique_lock view_lock{this->channel_lock};
this->cached_server_groups = server_groups;
}
{
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 if(ref_server) {
channel_group_id = ref_server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_or<GroupId>(0);
channel_inherit_id = 0;
} else {
channel_group_id = 0;
channel_inherit_id = 0;
}
this->cached_channel_group = channel_group_id;
}
server_groups_changed = false;
@ -1174,7 +1193,7 @@ void ConnectedClient::useToken(token::TokenId token_id) {
}
if(action.type == token::ActionType::AddServerGroup) {
auto result = this->server->group_manager()->assignments().add_server_group(this->getClientDatabaseId(), group->group_id());
auto result = this->server->group_manager()->assignments().add_server_group(this->getClientDatabaseId(), group->group_id(), !group->is_permanent());
switch(result) {
case GroupAssignmentResult::SUCCESS:
debugMessage(this->getServerId(), "{} Executing token action add server group for group {}.", CLIENT_STR_LOG_PREFIX, action.id1);
@ -1260,15 +1279,16 @@ void ConnectedClient::useToken(token::TokenId token_id) {
}
if(tree_registered && server_groups_changed) {
for(const auto &viewer : this->server->getClients()) {
if(viewer->isClientVisible(this->ref(), true)) {
for(const auto& group : added_server_groups) {
viewer->notifyServerGroupClientAdd(this->server->serverRoot, this->ref(), group);
}
for(const auto& group : removed_server_groups) {
viewer->notifyServerGroupClientRemove(this->server->serverRoot, this->ref(), group);
}
for(const auto& group : added_server_groups) {
std::optional<ts::command_builder> notify{};
for(const auto &viewer : this->server->getClients()) {
viewer->notifyServerGroupClientAdd(notify, this->server->serverRoot, this->ref(), group->group_id());
}
}
for(const auto& group : added_server_groups) {
std::optional<ts::command_builder> notify{};
for(const auto &viewer : this->server->getClients()) {
viewer->notifyServerGroupClientRemove(notify, this->server->serverRoot, this->ref(), group->group_id());
}
}
}

View File

@ -120,10 +120,11 @@ namespace ts {
std::deque<std::shared_ptr<BasicChannel>> unsubscribeChannel(const std::deque<std::shared_ptr<BasicChannel>>& target, bool lock_channel);
bool isClientVisible(const std::shared_ptr<ConnectedClient>&, bool /* lock channel lock */);
inline std::deque<std::weak_ptr<ConnectedClient>> getVisibleClients(bool lock_channel){
inline std::deque<std::weak_ptr<ConnectedClient>> getVisibleClients(bool lock_channel) {
std::shared_lock lock(this->channel_lock, std::defer_lock);
if(lock_channel)
if(lock_channel) {
lock.lock();
}
return this->visibleClients;
}
@ -162,20 +163,44 @@ namespace ts {
inline void sendChannelMessage(const std::shared_ptr<ConnectedClient>& sender, const std::string& textMessage){
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, this->currentChannel ? this->currentChannel->channelId() : 0, 0, std::chrono::system_clock::now(), textMessage);
}
//Group Client Groups
virtual bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group);
virtual bool notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group);
/* invalid client id causes error: invalid clientID */
/* invalid channel id causes error: invalid channelID */
/* an invalid channel or not a client's channel causes: invalid channelID */
/**
* Notify the client that a client has received a new server group.
* If the target client isn't visible no notify will be send.
*
* Note:
* 1. This method will lock the channel tree in shared mode!
* 2. For TS3 clients if the client isn't in view the client will disconnect.
*/
virtual bool notifyServerGroupClientAdd(std::optional<ts::command_builder>& /* generated notify */, const std::shared_ptr<ConnectedClient> &/* invoker */, const std::shared_ptr<ConnectedClient> &/* target client */, const GroupId& /* group id */);
/**
* Notify the client that a client has removed a server group.
* If the target client isn't visible no notify will be send.
*
* Note:
* 1. This method will lock the channel tree in shared mode!
* 2. For TS3 clients if the client isn't in view the client will disconnect.
*/
virtual bool notifyServerGroupClientRemove(std::optional<ts::command_builder>& /* generated notify */, const std::shared_ptr<ConnectedClient> &/* invoker */, const std::shared_ptr<ConnectedClient> &/* target client */, const GroupId& /* group id */);
/**
* Notify that a client has received a new channel group.
* If the target client isn't visible no notify will be send.
*
* Note:
* 1. This method will lock the channel tree in shared mode!
* 2. For TS3 clients if the client isn't in view the client will disconnect.
*/
virtual bool notifyClientChannelGroupChanged(
const std::shared_ptr<ConnectedClient>& invoker,
const std::shared_ptr<ConnectedClient>& client,
const std::shared_ptr<BasicChannel>& /*channel*/,
const std::shared_ptr<BasicChannel>& /*inherited channel*/,
const std::shared_ptr<Group>& group,
bool lock_channel_tree /* server channel tree AND client tree must be at least read locked! */
std::optional<ts::command_builder>& /* generated notify */,
const std::shared_ptr<ConnectedClient>& /* invoker */,
const std::shared_ptr<ConnectedClient>& /* target client */,
const ChannelId& /* channel */,
const ChannelId& /* inherited channel */,
const GroupId& /* group id */
);
//Group channel
virtual bool notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker);
virtual bool notifyChannelDescriptionChanged(std::shared_ptr<BasicChannel> channel);
@ -306,6 +331,8 @@ namespace ts {
bool update_client_needed_permissions();
/**
* Note: `server_groups_changed` and `channel_group_changed` could be equal.
* If so it will be true if any changes have been made.
* Attention: This method should never be called directly!
* Use `task_update_displayed_groups` instead to schedule an update.
*/
@ -696,6 +723,13 @@ namespace ts {
return std::string(notify);
return "";
}
command_result handleCommandGroupAdd(Command&, GroupTarget);
command_result handleCommandGroupCopy(Command&, GroupTarget);
command_result handleCommandGroupRename(Command&, GroupTarget);
command_result handleCommandGroupDel(Command&, GroupTarget);
command_result executeGroupPermissionEdit(Command&, const std::vector<std::shared_ptr<groups::Group>>& /* groups */, const std::shared_ptr<VirtualServer>& /* target server */, permission::v2::PermissionUpdateType /* mode */);
};
template <typename T>

View File

@ -70,9 +70,9 @@ inline void build_group_notify(ts::command_builder& notify, bool is_channel_grou
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("savedb", group->is_permanent());
bulk.put_unchecked("namemode", (uint8_t) group->name_mode());
bulk.put_unchecked("iconid", group->icon_id());
bulk.put_unchecked("iconid", (int32_t) group->icon_id());
auto modify_power = group->permissions()->permission_value_flagged(permission_modify);
auto add_power = group->permissions()->permission_value_flagged(permission_add);
@ -88,7 +88,7 @@ bool ConnectedClient::notifyServerGroupList(std::optional<ts::command_builder> &
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);
auto available_groups = group_manager->server_groups()->available_groups(groups::GroupCalculateMode::GLOBAL);
build_group_notify(generated_notify.emplace(as_notify ? "notifyservergrouplist" : ""), false, available_groups);
}
@ -225,57 +225,95 @@ bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<C
return true;
}
bool ConnectedClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {
Command cmd("notifyservergroupclientadded");
INVOKER(cmd, invoker);
cmd["sgid"] = group->groupId();
cmd["clid"] = client->getClientId();
cmd["name"] = client->getDisplayName();
cmd["cluid"] = client->getUid();
this->sendCommand(cmd);
return true;
}
bool ConnectedClient::notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) {
Command cmd("notifyservergroupclientdeleted");
INVOKER(cmd, invoker);
cmd["sgid"] = group->groupId();
cmd["clid"] = client->getClientId();
cmd["name"] = client->getDisplayName();
cmd["cluid"] = client->getUid();
this->sendCommand(cmd);
return true;
}
bool ConnectedClient::notifyClientChannelGroupChanged(
bool ConnectedClient::notifyServerGroupClientAdd(
std::optional<ts::command_builder>& notify,
const std::shared_ptr<ConnectedClient> &invoker,
const std::shared_ptr<ConnectedClient> &client,
const std::shared_ptr<BasicChannel>& channel,
const std::shared_ptr<BasicChannel>& inherited,
const std::shared_ptr<Group>& group,
bool lock_channel_tree) {
assert(!lock_channel_tree); /* not supported */
const std::shared_ptr<ConnectedClient> &target_client,
const GroupId& group_id) {
if(!this->isClientVisible(client, false) && client != this) return false;
if(client->getChannel() != channel) return false;
/* Deny any client moves 'till we've send the notify */
std::shared_lock<std::shared_mutex> channel_tree_lock{};
if(this->server) {
channel_tree_lock = std::shared_lock{this->server->channel_tree_lock};
}
assert(client);
assert(channel);
assert(inherited);
assert(group);
Command cmd("notifyclientchannelgroupchanged");
INVOKER(cmd, invoker);
if(!this->isClientVisible(target_client, true)) {
return false;
}
cmd["cgid"] = group->groupId();
cmd["clid"] = client->getClientId();
cmd["cid"] = channel->channelId();
cmd["cgi"] = inherited ? inherited->channelId() : channel->channelId(); //The inherited channel
if(!notify.has_value()) {
notify.emplace("notifyservergroupclientadded");
INVOKER_NEW((*notify), invoker);
this->sendCommand(cmd);
notify->put_unchecked(0, "sgid", group_id);
notify->put_unchecked(0, "clid", target_client->getClientId());
notify->put_unchecked(0, "name", target_client->getDisplayName());
notify->put_unchecked(0, "cluid", target_client->getUid());
}
this->sendCommand(*notify);
return true;
}
bool ConnectedClient::notifyServerGroupClientRemove(
std::optional<ts::command_builder>& notify,
const std::shared_ptr<ConnectedClient> &invoker,
const std::shared_ptr<ConnectedClient> &target_client,
const GroupId& group_id) {
/* Deny any client moves 'till we've send the notify */
std::shared_lock<std::shared_mutex> channel_tree_lock{};
if(this->server) {
channel_tree_lock = std::shared_lock{this->server->channel_tree_lock};
}
if(!this->isClientVisible(target_client, true)) {
return false;
}
if(!notify.has_value()) {
notify.emplace("notifyservergroupclientdeleted");
INVOKER_NEW((*notify), invoker);
notify->put_unchecked(0, "sgid", group_id);
notify->put_unchecked(0, "clid", target_client->getClientId());
notify->put_unchecked(0, "name", target_client->getDisplayName());
notify->put_unchecked(0, "cluid", target_client->getUid());
}
this->sendCommand(*notify);
return true;
}
bool ConnectedClient::notifyClientChannelGroupChanged(std::optional<ts::command_builder> &notify,
const std::shared_ptr<ConnectedClient> &invoker,
const std::shared_ptr<ConnectedClient> &target_client,
const ChannelId &channel_id,
const ChannelId &inherited_channel_id,
const GroupId &group_id) {
/* Deny any client moves 'till we've send the notify */
std::shared_lock<std::shared_mutex> channel_tree_lock{};
if(this->server) {
channel_tree_lock = std::shared_lock{this->server->channel_tree_lock};
}
/* No need to check if the channel is visible since if this is the case the client would not be visible as well. */
if(!this->isClientVisible(target_client, true)) {
return false;
}
if(!notify.has_value()) {
notify.emplace("notifyclientchannelgroupchanged");
INVOKER_NEW((*notify), invoker);
notify->put_unchecked(0, "cgid", group_id);
notify->put_unchecked(0, "clid", target_client->getClientId());
notify->put_unchecked(0, "name", target_client->getDisplayName());
notify->put_unchecked(0, "cid", channel_id);
notify->put_unchecked(0, "cgi", inherited_channel_id == 0 ? channel_id : inherited_channel_id);
}
this->sendCommand(*notify);
return true;
}

View File

@ -8,6 +8,8 @@
#include "../groups/GroupManager.h"
#include "../groups/GroupAssignmentManager.h"
#include "../PermissionCalculator.h"
using namespace std;
using namespace ts;
using namespace ts::server;
@ -138,6 +140,12 @@ bool DataClient::loadDataForCurrentServer() {
this->properties()[property::CLIENT_UNREAD_MESSAGES] = ref_server->letters->unread_letter_count(this->getUid());
}
if(this->server) {
this->temporary_assignments_lock = server->group_manager()->assignments().create_tmp_assignment_lock(this->getClientDatabaseId());
} else {
this->temporary_assignments_lock = serverInstance->group_manager()->assignments().create_tmp_assignment_lock(this->getClientDatabaseId());
}
return true;
}
@ -145,40 +153,16 @@ std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlag
const std::deque<permission::PermissionType> &permissions,
ChannelId channel,
bool granted,
std::shared_ptr<CalculateCache> cache) {
if(permissions.empty()) {
return {};
}
std::shared_ptr<CalculateCache>) {
if(!cache) {
cache = std::make_shared<CalculateCache>();
}
if(!cache->client_permissions) {
/* so we don't have to load that shit later */
cache->client_permissions = this->clientPermissions;
}
if(channel == -1) {
auto current_channel = this->currentChannel;
channel = current_channel ? current_channel->channelId() : 0;
}
auto ref_server = this->server;
if(ref_server) {
return ref_server->calculate_permissions(permissions, this->getClientDatabaseId(), this->getType(), channel, granted, cache);
} else {
return serverInstance->permission_helper().calculate_permissions(permissions, this->getClientDatabaseId(), this->getType(), channel, granted, cache);
}
ts::server::ClientPermissionCalculator calculator{this, channel};
return calculator.calculate_permissions(permissions, granted);
}
permission::v2::PermissionFlaggedValue DataClient::calculate_permission(
permission::PermissionType permission, ChannelId channel, bool granted, std::shared_ptr<CalculateCache> cache) {
auto result = this->calculate_permissions({permission}, channel, granted, cache);
if(result.empty()) {
return {0, false};
}
return result.back().second;
permission::PermissionType permission, ChannelId channel, bool granted, std::shared_ptr<CalculateCache>) {
ts::server::ClientPermissionCalculator calculator{this, channel};
return calculator.calculate_permission(permission, granted);
}
std::vector<std::shared_ptr<groups::ServerGroup>> DataClient::assignedServerGroups() {
@ -208,17 +192,16 @@ std::vector<std::shared_ptr<groups::ServerGroup>> DataClient::assignedServerGrou
return result;
}
std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(const std::shared_ptr<BasicChannel> &channel) {
std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(std::shared_ptr<BasicChannel> &channel) {
auto ref_server = this->server;
assert(channel);
if(!channel || !ref_server) {
return nullptr;
}
std::shared_ptr<BasicChannel> inherited_channel{channel};
std::shared_ptr<BasicChannel> original_channel{channel};
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
auto assigned_group_id = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), inherited_channel);
auto assigned_group_id = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), channel);
std::shared_ptr<groups::ChannelGroup> result{};
if(assigned_group_id.has_value()) {
@ -227,6 +210,7 @@ std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(const std
if(!result) {
result = ref_server->default_channel_group();
channel = original_channel;
}
assert(result);
@ -252,7 +236,8 @@ bool DataClient::channelGroupAssigned(const shared_ptr<groups::ChannelGroup> &gr
return false;
}
return this->assignedChannelGroup(channel) == group;
std::shared_ptr<BasicChannel> interited_channel{channel};
return this->assignedChannelGroup(interited_channel) == group;
}
std::string DataClient::getAvatarId() {

View File

@ -22,6 +22,7 @@ namespace ts {
namespace groups {
class ServerGroup;
class ChannelGroup;
typedef void TemporaryAssignmentsLock;
}
class DataClient {
@ -68,7 +69,7 @@ namespace ts {
);
virtual std::vector<std::shared_ptr<groups::ServerGroup>> assignedServerGroups();
virtual std::shared_ptr<groups::ChannelGroup> assignedChannelGroup(const std::shared_ptr<BasicChannel> &);
virtual std::shared_ptr<groups::ChannelGroup> assignedChannelGroup(std::shared_ptr<BasicChannel> &);
virtual bool serverGroupAssigned(const std::shared_ptr<groups::ServerGroup> &);
virtual bool channelGroupAssigned(const std::shared_ptr<groups::ChannelGroup> &, const std::shared_ptr<BasicChannel> &);
@ -106,6 +107,7 @@ namespace ts {
std::shared_ptr<PropertyManager> _properties;
std::shared_ptr<BasicChannel> currentChannel = nullptr;
std::shared_ptr<groups::TemporaryAssignmentsLock> temporary_assignments_lock{};
};
}
}

View File

@ -16,6 +16,7 @@
#include "../manager/ActionLogger.h"
#include "./voice/VoiceClient.h"
#include "../rtc/imports.h"
#include "../groups/GroupManager.h"
using namespace std::chrono;
using namespace ts;
@ -25,7 +26,7 @@ using namespace ts::protocol;
//#define PKT_LOG_VOICE
//#define PKT_LOG_WHISPER
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient(a, b), whisper_handler_{this} {
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient{a, b}, whisper_handler_{this} {
speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now();
};
@ -122,8 +123,6 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
}
TIMING_STEP(timings, "db assign ");
this->server->group_manager()->enableCache(this->getClientDatabaseId());
TIMING_STEP(timings, "gr cache ");
const static vector<string> available_parameters = {
"client_nickname",
@ -437,10 +436,6 @@ void SpeakingClient::processJoin() {
}
TIMING_STEP(timings, "server reg ");
ref_server->group_manager()->cleanupAssignments(this->getClientDatabaseId());
TIMING_STEP(timings, "grp cleanup");
ref_server->group_manager()->update_server_group_property(this->ref(), true, nullptr);
TIMING_STEP(timings, "grp apply ");
this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag;
if(geoloc::provider) {
@ -574,7 +569,6 @@ void SpeakingClient::processLeave() {
unique_lock server_channel_lock(this->server->channel_tree_lock);
server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
}
server->group_manager()->disableCache(ownLock->getClientDatabaseId());
server->music_manager_->cleanup_client_bots(this->getClientDatabaseId());
//ref_server = nullptr; Removed caused nullptr exceptions
}

View File

@ -9,11 +9,9 @@
#include "../../manager/ActionLogger.h"
namespace ts::command::bulk_parser {
template <bool kParseValue>
class PermissionBulkParser {
friend class PermissionBulkParser<false>;
public:
explicit PermissionBulkParser(ts::ParameterBulk& bulk) {
explicit PermissionBulkParser(ts::ParameterBulk& bulk, bool parse_value) {
if(bulk.has("permid")) {
auto type = bulk["permid"].as<permission::PermissionType>();
if ((type & PERM_ID_GRANT) != 0) {
@ -35,7 +33,7 @@ namespace ts::command::bulk_parser {
return;
}
if(kParseValue) {
if(parse_value) {
if(!bulk.has("permvalue")) {
this->error_.reset(ts::command_result{error::parameter_missing, "permvalue"});
return;
@ -180,7 +178,6 @@ namespace ts::command::bulk_parser {
#endif
};
template <bool kParseValue>
class PermissionBulksParser {
public:
PermissionBulksParser(const PermissionBulksParser&) = delete;
@ -220,7 +217,7 @@ namespace ts::command::bulk_parser {
};
struct FilteredPermissionListIterable {
typedef typename std::vector<PermissionBulkParser<kParseValue>>::const_iterator const_iterator;
typedef typename std::vector<PermissionBulkParser>::const_iterator const_iterator;
public:
FilteredPermissionListIterable(const_iterator begin, const_iterator end) noexcept : begin_{begin}, end_{end} {}
@ -236,10 +233,11 @@ namespace ts::command::bulk_parser {
const_iterator end_;
};
explicit PermissionBulksParser(ts::Command& command) {
explicit PermissionBulksParser(ts::Command& command, bool parse_value) : parse_value{parse_value} {
this->permissions_.reserve(command.bulkCount());
for(size_t index{0}; index < command.bulkCount(); index++)
this->permissions_.emplace_back(command[index]);
for(size_t index{0}; index < command.bulkCount(); index++) {
this->permissions_.emplace_back(command[index], parse_value);
}
}
[[nodiscard]] inline bool validate(const std::shared_ptr<server::ConnectedClient>& issuer, ChannelId channel_id) {
@ -247,19 +245,24 @@ namespace ts::command::bulk_parser {
if(!ignore_granted_values) {
auto max_value = issuer->calculate_permission(permission::i_permission_modify_power, channel_id, false);
if(!max_value.has_value) {
for(PermissionBulkParser<kParseValue>& permission : this->permissions_) {
if(permission.has_error()) continue;
for(PermissionBulkParser& permission : this->permissions_) {
if(permission.has_error()) {
continue;
}
permission.emplace_custom_error(ts::command_result{permission::i_permission_modify_power});
}
return false;
}
for(size_t index{0}; index < this->permissions_.size(); index++) {
PermissionBulkParser<kParseValue>& permission = this->permissions_[index];
if(permission.has_error()) continue;
PermissionBulkParser& permission = this->permissions_[index];
if(permission.has_error()) {
continue;
}
if(kParseValue && permission_require_granted_value(permission.permission_type()) && !permission::v2::permission_granted(permission.value(), max_value)) {
if(this->parse_value && permission_require_granted_value(permission.permission_type()) && !permission::v2::permission_granted(permission.value(), max_value)) {
permission.emplace_custom_error(ts::command_result{permission::i_permission_modify_power});
continue;
}
@ -281,13 +284,15 @@ namespace ts::command::bulk_parser {
assert(!std::exchange(this->result_created_, true));
ts::command_result_bulk result{};
for(auto& permission : this->permissions_)
for(auto& permission : this->permissions_) {
result.insert_result(std::forward<ts::command_result>(permission.release_error()));
}
return ts::command_result{std::move(result)};
}
private:
std::vector<PermissionBulkParser<kParseValue>> permissions_{};
bool parse_value;
std::vector<PermissionBulkParser> permissions_{};
#ifndef NDEBUG
bool result_created_{false};
#endif

View File

@ -134,418 +134,19 @@ command_result ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd)
}
command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
log::GroupType log_group_type;
auto group_type = cmd["type"].as<groups::GroupType>();
switch (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:
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"};
}
std::shared_ptr<groups::ChannelGroup> group{};
auto result = group_manager->channel_groups()->create_group(group_type, cmd["name"].string(), group);
switch(result) {
case groups::GroupCreateResult::SUCCESS:
break;
case groups::GroupCreateResult::NAME_TOO_SHORT:
case groups::GroupCreateResult::NAME_TOO_LONG:
return command_result{error::parameter_invalid, "name"};
case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
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")};
notify.put_unchecked(0, "cgid", group->group_id());
this->sendCommand(notify);
}
if (group) {
group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing);
if (this->server) {
this->server->enqueue_notify_channel_group_list();
}
} else {
return command_result{error::group_invalid_id};
}
return command_result{error::ok};
return this->handleCommandGroupAdd(cmd, GroupTarget::GROUPTARGET_CHANNEL);
}
//name=Channel\sAdmin scgid=5 tcgid=4 type=1
command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto ref_server = this->server;
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
auto source_group_id = cmd["scgid"].as<GroupId>();
auto source_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
if (!source_group) {
return command_result{error::group_invalid_id, "invalid source group id"};
}
const auto group_type_modifiable = [&](groups::GroupType type) {
switch (type) {
case groups::GroupType::GROUP_TYPE_TEMPLATE:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0))) {
return permission::b_serverinstance_modify_templates;
}
break;
case groups::GroupType::GROUP_TYPE_QUERY:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0))) {
return permission::b_serverinstance_modify_querygroup;
}
break;
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:
break;
}
return permission::undefined;
};
{
auto result = group_type_modifiable(source_group->group_type());
if (result != permission::undefined) {
return command_result{result};
}
}
auto global_update = false;
if (cmd[0].has("tcgid") && cmd["tcgid"].as<GroupId>() != 0) {
//Copy an existing group
auto target_group_id = cmd["tcgid"].as<GroupId>();
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"};
}
if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
return ts::command_result{permission::i_channel_group_needed_modify_power};
}
{
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;
switch (target_group->group_type()) {
case groups::GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY;
break;
case groups::GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE;
break;
case groups::GroupType::GROUP_TYPE_NORMAL:
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
log_group_type = log::GroupType::NORMAL;
break;
}
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->group_id(), target_group->display_name(), source_group->group_id(), source_group->display_name());
global_update = !this->server || !group_manager->channel_groups()->find_group(groups::GroupCalculateMode::LOCAL, target_group_id);
} else {
//Copy a new group
auto target_type = cmd["type"].as<groups::GroupType>();
log::GroupType log_group_type;
switch (target_type) {
case groups::GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY;
break;
case groups::GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE;
break;
case groups::GroupType::GROUP_TYPE_NORMAL:
log_group_type = log::GroupType::NORMAL;
break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
return command_result{error::parameter_invalid, "type"};
}
{
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!"};
}
std::shared_ptr<groups::ChannelGroup> created_group{};
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;
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};
}
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);
}
global_update = !this->server;
}
for (const auto &server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque<shared_ptr<VirtualServer>>{this->server})) {
if (server) {
server->enqueue_notify_channel_group_list();
}
}
return command_result{error::ok};
return this->handleCommandGroupCopy(cmd, GroupTarget::GROUPTARGET_CHANNEL);
}
command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
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};
}
ACTION_REQUIRES_GROUP_PERMISSION(channel_group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
auto type = channel_group->group_type();
log::GroupType log_group_type;
if (type == groups::GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
} else if (type == groups::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;
}
auto old_name = channel_group->display_name();
auto result = group_manager->channel_groups()->rename_group(channel_group->group_id(), cmd["name"].string());
switch(result) {
case groups::GroupRenameResult::SUCCESS:
break;
case groups::GroupRenameResult::INVALID_GROUP_ID:
return ts::command_result{error::vs_critical, "internal invalid group id"};
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 this->handleCommandGroupRename(cmd, GroupTarget::GROUPTARGET_CHANNEL);
}
command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto ref_server = this->server;
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
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->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete default channel group!"};
}
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!"};
}
}
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->group_id()) {
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 this->handleCommandGroupDel(cmd, GroupTarget::GROUPTARGET_CHANNEL);
}
command_result ConnectedClient::handleCommandChannelGroupList(Command &) {
@ -614,95 +215,39 @@ command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd)
command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_id = cmd["cgid"].as<GroupId>();
std::shared_ptr<groups::ChannelGroupManager> owning_manager{};
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
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);
auto group = group_manager->channel_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, group_id);
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
if (!pparser.validate(this->ref(), 0)) {
return pparser.build_command_result();
if(!group) {
return ts::command_result{error::group_invalid_id};
}
bool updateList{false};
for (const auto &ppermission : pparser.iterate_valid_permissions()) {
ppermission.apply_to(channelGroup->permissions(), permission::v2::PermissionUpdateType::set_value);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log::PermissionTarget::CHANNEL_GROUP,
permission::v2::PermissionUpdateType::set_value,
0, "",
channelGroup->group_id(), channelGroup->display_name());
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
updateList |= ppermission.is_group_property();
}
if (this->server) {
if (updateList) {
this->server->enqueue_notify_channel_group_list();
}
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! */
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
cl->task_update_needed_permissions.enqueue();
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
client_channel_lock.unlock();
cl->task_update_channel_client_properties.enqueue();
});
}
return pparser.build_command_result();
auto target_server = group_manager->channel_groups() == owning_manager ? this->server : nullptr;
return this->executeGroupPermissionEdit(cmd, { group }, target_server, permission::v2::PermissionUpdateType::set_value);
}
command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_id = cmd["cgid"].as<GroupId>();
std::shared_ptr<groups::ChannelGroupManager> owning_manager{};
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
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);
auto group = group_manager->channel_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, group_id);
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
if (!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
bool updateList{false};
for (const auto &ppermission : pparser.iterate_valid_permissions()) {
ppermission.apply_to(channelGroup->permissions(), permission::v2::PermissionUpdateType::delete_value);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log::PermissionTarget::CHANNEL_GROUP,
permission::v2::PermissionUpdateType::delete_value,
0, "",
channelGroup->group_id(), channelGroup->display_name());
updateList |= ppermission.is_group_property();
if(!group) {
return ts::command_result{error::group_invalid_id};
}
if (this->server) {
if (updateList)
this->server->enqueue_notify_channel_group_list();
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! */
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
cl->task_update_needed_permissions.enqueue();
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
client_channel_lock.unlock();
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
cl->task_update_channel_client_properties.enqueue();
});
}
return pparser.build_command_result();
auto target_server = group_manager->channel_groups() == owning_manager ? this->server : nullptr;
return this->executeGroupPermissionEdit(cmd, { group }, target_server, permission::v2::PermissionUpdateType::delete_value);
}
//TODO: Test if parent or previous is deleted!
@ -2236,7 +1781,7 @@ command_result ConnectedClient::handleCommandChannelAddPerm(Command &cmd) {
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
if (!pparser.validate(this->ref(), channel->channelId())) {
return pparser.build_command_result();
}
@ -2315,7 +1860,7 @@ command_result ConnectedClient::handleCommandChannelDelPerm(Command &cmd) {
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
if (!pparser.validate(this->ref(), channel->channelId()))
return pparser.build_command_result();
@ -2428,7 +1973,7 @@ command_result ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd)
ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id);
}
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
if (!pparser.validate(this->ref(), channel->channelId()))
return pparser.build_command_result();
@ -2495,7 +2040,7 @@ command_result ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd)
auto required_permissions = this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cmd["cldbid"], ClientType::CLIENT_TEAMSPEAK, channel_id);
ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id);
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
if (!pparser.validate(this->ref(), channel->channelId()))
return pparser.build_command_result();

View File

@ -985,7 +985,7 @@ command_result ConnectedClient::handleCommandClientAddPerm(Command &cmd) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_permission_modify_power, this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cldbid, ClientType::CLIENT_TEAMSPEAK, 0));
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
@ -1028,7 +1028,7 @@ command_result ConnectedClient::handleCommandClientDelPerm(Command &cmd) {
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), cldbid);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_permission_modify_power, this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cldbid, ClientType::CLIENT_TEAMSPEAK, 0));
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();

View File

@ -0,0 +1,862 @@
//
// Created by WolverinDEV on 08/03/2021.
//
#include <memory>
#include <bitset>
#include <algorithm>
#include <openssl/sha.h>
#include "../../build.h"
#include "../ConnectedClient.h"
#include "../InternalClient.h"
#include "../../server/VoiceServer.h"
#include "../voice/VoiceClient.h"
#include "../../InstanceHandler.h"
#include "../../server/QueryServer.h"
#include "../music/MusicClient.h"
#include "../query/QueryClient.h"
#include "../../manager/ConversationManager.h"
#include "../../manager/PermissionNameMapper.h"
#include "../../manager/ActionLogger.h"
#include "../../groups/GroupManager.h"
#include "helpers.h"
#include "./bulk_parsers.h"
#include <log/LogUtils.h>
#include <misc/base64.h>
#include <misc/digest.h>
#include <misc/rnd.h>
#include <misc/scope_guard.h>
#include <bbcode/bbcodes.h>
using namespace std::chrono;
using namespace std;
using namespace ts;
using namespace ts::server;
command_result ConnectedClient::handleCommandGroupAdd(Command &cmd, GroupTarget group_target) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
log::GroupTarget log_group_target;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
log_group_target = log::GroupTarget::SERVER;
break;
case GroupTarget::GROUPTARGET_CHANNEL:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_create, 1);
log_group_target = log::GroupTarget::CHANNEL;
break;
default:
return ts::command_result{error::vs_critical, "internal invalid group target"};
}
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
log::GroupType log_group_type;
auto group_type = cmd[0].has("type") ? cmd["type"].as<groups::GroupType>() : groups::GroupType::GROUP_TYPE_NORMAL;
switch (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:
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"};
}
std::shared_ptr<groups::Group> group;
groups::GroupCreateResult result;
std::string notify_name;
std::string notify_id_key;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER: {
std::shared_ptr<groups::ServerGroup> s_group;
result = group_manager->server_groups()->create_group(group_type, cmd["name"].string(), s_group);
group = s_group;
notify_name = this->notify_response_command("notifyservergroupadded");
notify_id_key = "sgid";
break;
}
case GroupTarget::GROUPTARGET_CHANNEL: {
std::shared_ptr<groups::ChannelGroup> c_group;
result = group_manager->channel_groups()->create_group(group_type, cmd["name"].string(), c_group);
group = c_group;
notify_name = this->notify_response_command("notifychannelgroupadded");
notify_id_key = "cgid";
break;
}
default:
assert(false);
result = groups::GroupCreateResult::DATABASE_ERROR;
break;
}
switch(result) {
case groups::GroupCreateResult::SUCCESS:
break;
case groups::GroupCreateResult::NAME_TOO_SHORT:
case groups::GroupCreateResult::NAME_TOO_LONG:
return command_result{error::parameter_invalid, "name"};
case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
return command_result{error::group_name_inuse};
case groups::GroupCreateResult::DATABASE_ERROR:
default:
return command_result{error::vs_critical};
}
assert(group);
assert(!notify_id_key.empty());
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log_group_target, log_group_type, group->group_id(), group->display_name(), 0, "");
{
ts::command_builder notify{notify_name};
notify.put_unchecked(0, notify_id_key, group->group_id());
this->sendCommand(notify);
}
group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing);
if (this->server) {
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
this->server->enqueue_notify_server_group_list();
break;
case GroupTarget::GROUPTARGET_CHANNEL:
this->server->enqueue_notify_channel_group_list();
break;
}
}
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
if(!this->server) {
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
} else {
server_updates.push_back(this->server);
}
for(const auto& server : server_updates) {
if(!server) {
continue;
}
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
server->enqueue_notify_server_group_list();
break;
case GroupTarget::GROUPTARGET_CHANNEL:
server->enqueue_notify_channel_group_list();
break;
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandGroupCopy(Command &cmd, GroupTarget group_target) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto ref_server = this->server;
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
log::GroupTarget log_group_target;
GroupId source_group_id, target_group_id;
std::shared_ptr<groups::Group> source_group{}, target_group{};
bool target_group_global;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER: {
std::shared_ptr<groups::ServerGroupManager> owning_manager{};
source_group_id = cmd["ssgid"].as<GroupId>();
target_group_id = cmd[0].has("tsgid") ? cmd["tsgid"].as<GroupId>() : 0;
source_group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
target_group = group_manager->server_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, target_group_id);
target_group_global = owning_manager != group_manager->server_groups();
log_group_target = log::GroupTarget::SERVER;
break;
}
case GroupTarget::GROUPTARGET_CHANNEL: {
std::shared_ptr<groups::ChannelGroupManager> owning_manager{};
source_group_id = cmd["scgid"].as<GroupId>();
target_group_id = cmd[0].has("tcgid") ? cmd["tcgid"].as<GroupId>() : 0;
source_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
target_group = group_manager->channel_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, target_group_id);
target_group_global = owning_manager != group_manager->channel_groups();
log_group_target = log::GroupTarget::CHANNEL;
break;
}
}
if (!source_group) {
return command_result{error::group_invalid_id, "invalid source group id"};
}
if (target_group_id > 0 && !target_group) {
return command_result{error::group_invalid_id, "invalid target group"};
}
const auto group_type_modifiable = [&](groups::GroupType type) {
switch (type) {
case groups::GroupType::GROUP_TYPE_TEMPLATE:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0))) {
return permission::b_serverinstance_modify_templates;
}
break;
case groups::GroupType::GROUP_TYPE_QUERY:
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0))) {
return permission::b_serverinstance_modify_querygroup;
}
break;
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:
break;
}
return permission::undefined;
};
{
auto result = group_type_modifiable(source_group->group_type());
if (result != permission::undefined) {
return command_result{result};
}
}
auto global_update = false;
if (target_group) {
if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
return ts::command_result{permission::i_channel_group_needed_modify_power};
}
{
auto result = group_type_modifiable(target_group->group_type());
if (result != permission::undefined) {
return command_result{result};
}
}
groups::GroupCopyResult result;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
result = group_manager->server_groups()->copy_group_permissions(source_group_id, target_group_id);
break;
case GroupTarget::GROUPTARGET_CHANNEL:
result = group_manager->channel_groups()->copy_group_permissions(source_group_id, target_group_id);
break;
}
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};
case groups::GroupCopyResult::NAME_INVALID:
default:
return command_result{error::vs_critical};
}
log::GroupType log_group_type;
switch (target_group->group_type()) {
case groups::GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY;
break;
case groups::GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE;
break;
case groups::GroupType::GROUP_TYPE_NORMAL:
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
log_group_type = log::GroupType::NORMAL;
break;
}
serverInstance->action_logger()->group_logger.log_group_permission_copy(target_group->group_type() != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log_group_target, log_group_type, target_group->group_id(), target_group->display_name(), source_group->group_id(), source_group->display_name());
global_update = !this->server || target_group_global;
} else {
//Copy to a new group
auto target_type = cmd["type"].as<groups::GroupType>();
log::GroupType log_group_type;
switch (target_type) {
case groups::GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY;
break;
case groups::GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE;
break;
case groups::GroupType::GROUP_TYPE_NORMAL:
log_group_type = log::GroupType::NORMAL;
break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
return command_result{error::parameter_invalid, "type"};
}
{
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!"};
}
std::shared_ptr<groups::Group> created_group{};
std::string notify_name, notify_id_key;
groups::GroupCopyResult result;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER: {
std::shared_ptr<groups::ServerGroup> s_group{};
result = group_manager->server_groups()->copy_group(source_group_id, target_type, cmd["name"].string(), s_group);
created_group = s_group;
notify_name = this->notify_response_command("notifyservergroupcopied");
notify_id_key = "sgid";
break;
}
case GroupTarget::GROUPTARGET_CHANNEL: {
std::shared_ptr<groups::ChannelGroup> c_group{};
result = group_manager->channel_groups()->copy_group(source_group_id, target_type, cmd["name"].string(), c_group);
created_group = c_group;
notify_name = this->notify_response_command("notifychannelgroupcopied");
notify_id_key = "cgid";
break;
}
default: {
result = groups::GroupCopyResult::DATABASE_ERROR;
break;
}
}
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};
case groups::GroupCopyResult::NAME_INVALID:
return command_result{error::parameter_invalid, "name"};
default:
return command_result{error::vs_critical};
}
assert(!notify_name.empty());
assert(created_group);
serverInstance->action_logger()->group_logger.log_group_create(target_type != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log_group_target, log_group_type, created_group->group_id(), cmd["name"], source_group->group_id(), source_group->display_name());
{
ts::command_builder notify{notify_name};
notify.put_unchecked(0, notify_id_key, created_group->group_id());
this->sendCommand(notify);
}
global_update = !this->server;
}
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
if(global_update) {
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
} else {
server_updates.push_back(this->server);
}
for(const auto& server : server_updates) {
if(!server) {
continue;
}
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
server->enqueue_notify_server_group_list();
break;
case GroupTarget::GROUPTARGET_CHANNEL:
server->enqueue_notify_channel_group_list();
break;
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandGroupRename(Command &cmd, GroupTarget group_target) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
log::GroupTarget log_group_target;
std::shared_ptr<groups::Group> group{};
GroupId group_id;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
group_id = cmd["sgid"].as<GroupId>();
group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
return ts::command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true);
log_group_target = log::GroupTarget::SERVER;
break;
case GroupTarget::GROUPTARGET_CHANNEL:
group_id = cmd["cgid"].as<GroupId>();
group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
return ts::command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
log_group_target = log::GroupTarget::CHANNEL;
break;
default:
return ts::command_result{error::vs_critical, "internal invalid group target"};
}
assert(group);
auto type = group->group_type();
log::GroupType log_group_type;
if (type == groups::GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
} else if (type == groups::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;
}
auto old_name = group->display_name();
groups::GroupRenameResult rename_result;
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
rename_result = group_manager->server_groups()->rename_group(group->group_id(), cmd["name"].string());
break;
case GroupTarget::GROUPTARGET_CHANNEL:
rename_result = group_manager->channel_groups()->rename_group(group->group_id(), cmd["name"].string());
break;
default:
assert(false);
rename_result = groups::GroupRenameResult::DATABASE_ERROR;
break;
}
switch(rename_result) {
case groups::GroupRenameResult::SUCCESS:
break;
case groups::GroupRenameResult::INVALID_GROUP_ID:
return ts::command_result{error::vs_critical, "internal invalid group id"};
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_group_target, log_group_type, group->group_id(), group->display_name(), old_name);
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
if(!this->server) {
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
} else {
server_updates.push_back(this->server);
}
for(const auto& server : server_updates) {
if(!server) {
continue;
}
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
server->enqueue_notify_server_group_list();
break;
case GroupTarget::GROUPTARGET_CHANNEL:
server->enqueue_notify_channel_group_list();
break;
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandGroupDel(Command &cmd, GroupTarget group_target) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto ref_server = this->server;
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
log::GroupTarget log_group_target;
std::shared_ptr<groups::Group> group{};
GroupId group_id;
switch (group_target) {
case GroupTarget::GROUPTARGET_SERVER:
group_id = cmd["sgid"].as<GroupId>();
group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
return ts::command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true);
log_group_target = log::GroupTarget::SERVER;
break;
case GroupTarget::GROUPTARGET_CHANNEL:
group_id = cmd["cgid"].as<GroupId>();
group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
return ts::command_result{error::group_invalid_id};
}
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
log_group_target = log::GroupTarget::CHANNEL;
break;
default:
return ts::command_result{error::vs_critical, "internal invalid group target"};
}
assert(group);
/* Test if the group is one of the default group */
switch (group_target) {
case GroupTarget::GROUPTARGET_SERVER:
if(this->server) {
if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete default server group!"};
}
}
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete instance default server admin group!"};
}
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete instance default server group!"};
}
if(serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete instance default guest server query group!"};
}
break;
case GroupTarget::GROUPTARGET_CHANNEL:
if (this->server) {
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete default channel group!"};
}
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete default channel admin group!"};
}
}
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete instance default channel group!"};
}
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == group->group_id()) {
return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"};
}
break;
}
log::GroupType log_group_type;
switch (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:
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_delete, 1);
break;
case GroupTarget::GROUPTARGET_CHANNEL:
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1);
break;
}
log_group_type = log::GroupType::NORMAL;
break;
case groups::GroupType::GROUP_TYPE_UNKNOWN:
default:
return ts::command_result{error::vs_critical};
}
auto force_delete = cmd[0].has("force") ? cmd["force"].as<bool>() : false;
groups::GroupDeleteResult result;
switch (group_target) {
case GroupTarget::GROUPTARGET_SERVER:
if(!force_delete && !group_manager->assignments().is_server_group_empty(group->group_id())) {
result = groups::GroupDeleteResult::GROUP_NOT_EMPTY;
break;
}
result = group_manager->server_groups()->delete_group(group->group_id());
break;
case GroupTarget::GROUPTARGET_CHANNEL:
if(!force_delete && !group_manager->assignments().is_channel_group_empty(group->group_id())) {
result = groups::GroupDeleteResult::GROUP_NOT_EMPTY;
break;
}
result = group_manager->channel_groups()->delete_group(group->group_id());
break;
}
switch(result) {
case groups::GroupDeleteResult::SUCCESS:
break;
case groups::GroupDeleteResult::GROUP_NOT_EMPTY:
return ts::command_result{error::group_not_empty};
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_group_target, log_group_type, group->group_id(), group->display_name());
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
if(!this->server) {
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
} else {
server_updates.push_back(this->server);
}
for(const auto& server : server_updates) {
if(!server) {
continue;
}
switch(group_target) {
case GroupTarget::GROUPTARGET_SERVER:
server->enqueue_notify_server_group_list();
server->group_manager()->assignments().handle_server_group_deleted(group->group_id());
server->tokenManager->handle_server_group_deleted(group->group_id());
break;
case GroupTarget::GROUPTARGET_CHANNEL:
server->enqueue_notify_channel_group_list();
server->group_manager()->assignments().handle_channel_group_deleted(group->group_id());
server->tokenManager->handle_channel_group_deleted(group->group_id());
break;
}
this->server->forEachClient([&](const std::shared_ptr<ConnectedClient>& client) {
bool groups_changed;
client->update_displayed_client_groups(groups_changed, groups_changed);
if(groups_changed) {
client->task_update_needed_permissions.enqueue();
client->task_update_channel_client_properties.enqueue();
}
});
}
return command_result{error::ok};
}
command_result ConnectedClient::executeGroupPermissionEdit(Command &cmd,
const std::vector<std::shared_ptr<groups::Group>> &groups,
const std::shared_ptr<VirtualServer> &target_server,
permission::v2::PermissionUpdateType mode) {
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, mode == permission::v2::PermissionUpdateType::set_value};
if (!pparser.validate(this->ref(), 0)) {
return pparser.build_command_result();
}
bool update_channel_group_list{false}, update_server_group_list{false};
for(const auto& group : groups) {
bool* update_group_list;
log::PermissionTarget log_group_target;
if(dynamic_pointer_cast<groups::ServerGroup>(group)) {
update_group_list = &update_server_group_list;
log_group_target = log::PermissionTarget::SERVER_GROUP;
} else {
update_group_list = &update_channel_group_list;
log_group_target = log::PermissionTarget::CHANNEL_GROUP;
}
for (const auto &ppermission : pparser.iterate_valid_permissions()) {
ppermission.apply_to(group->permissions(), mode);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log_group_target,
mode,
0, "",
group->group_id(), group->display_name());
*update_group_list |= ppermission.is_group_property();
}
}
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
if(target_server) {
server_updates.push_back(target_server);
} else {
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
}
for(const auto& server : server_updates) {
if(!server) {
continue;
}
if(update_server_group_list) {
server->enqueue_notify_server_group_list();
}
if(update_channel_group_list) {
server->enqueue_notify_channel_group_list();
}
btree::set<ClientId> updated_clients{};
for(const auto& group : groups) {
if(auto s_group{dynamic_pointer_cast<groups::ServerGroup>(group)}; s_group) {
server->forEachClient([&](const std::shared_ptr<ConnectedClient> &client) {
if(updated_clients.count(client->getClientId()) > 0) {
return;
}
if(client->serverGroupAssigned(s_group)) {
updated_clients.emplace(client->getClientId());
client->task_update_channel_client_properties.enqueue();
client->task_update_needed_permissions.enqueue();
client->join_state_id++;
}
});
} else if(auto c_group{dynamic_pointer_cast<groups::ChannelGroup>(group)}; c_group) {
server->forEachClient([&](const std::shared_ptr<ConnectedClient> &client) {
if(updated_clients.count(client->getClientId()) > 0) {
return;
}
auto channel = client->getChannel();
if(client->channelGroupAssigned(c_group, channel)) {
updated_clients.emplace(client->getClientId());
client->task_update_channel_client_properties.enqueue();
client->task_update_needed_permissions.enqueue();
client->join_state_id++;
}
});
} else {
assert(false);
}
}
}
return pparser.build_command_result();
}

View File

@ -461,7 +461,7 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd)
}
}
shared_lock server_channel_lock{this->server->channel_tree_lock}; /* ensure we dont get moved or somebody could move us */
std::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 = this->server->channelTree->findChannel(channel_id);
if (!channel) {
@ -524,17 +524,23 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd)
bool channel_group_changed, server_group_changed;
targetClient->update_displayed_client_groups(server_group_changed, channel_group_changed);
auto client_channel = targetClient->getChannel();
if(!client_channel) {
continue;
}
if(channel_group_changed) {
targetClient->task_update_needed_permissions.enqueue();
targetClient->task_update_displayed_groups.enqueue();
std::optional<ts::command_builder> notify{};
for (const auto &viewer : this->server->getClients()) {
/* if in view will be tested within that method */
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);
viewer->notifyClientChannelGroupChanged(
notify,
this->ref(), targetClient,
client_channel->channelId(),
targetClient->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID],
targetClient->properties()[property::CLIENT_CHANNEL_GROUP_ID]);
}
}
}
@ -2205,7 +2211,6 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
return command_result{error::channel_invalid_id};
}
auto channel_group = this->assignedChannelGroup(channel);
auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), client_dbid);
Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : "");
@ -2302,14 +2307,18 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
}
}
{
auto permission_manager = channel_group->group->permissions();
auto interited_channel{channel};
auto channel_group = this->assignedChannelGroup(interited_channel);
if(channel_group) {
assert(interited_channel);
auto permission_manager = channel_group->permissions();
for(const auto& permission_data : permission_manager->permissions()) {
auto& permission = std::get<1>(permission_data);
if(permission.flags.value_set) {
result[index]["t"] = 3; /* channel group */
result[index]["id1"] = channel_group->channelId;
result[index]["id2"] = channel_group->group->groupId();
result[index]["id1"] = interited_channel->channelId();
result[index]["id2"] = channel_group->group_id();
result[index]["p"] = std::get<0>(permission_data);
result[index]["v"] = permission.values.value;
@ -2319,8 +2328,8 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
}
if(permission.flags.grant_set) {
result[index]["t"] = 3; /* channel group */
result[index]["id1"] = channel_group->channelId;
result[index]["id2"] = channel_group->group->groupId();
result[index]["id1"] = interited_channel->channelId();
result[index]["id2"] = channel_group->group_id();
result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT);
result[index]["v"] = permission.values.grant;

View File

@ -557,7 +557,7 @@ command_result ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) {
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
return command_result{perr};
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
@ -587,7 +587,7 @@ command_result ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) {
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
return command_result{perr};
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
@ -719,7 +719,7 @@ command_result ConnectedClient::handleCommandPlaylistClientAddPerm(ts::Command &
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
return command_result{perr};
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
if(!pparser.validate(this->ref(), this->getClientDatabaseId()))
return pparser.build_command_result();
@ -753,7 +753,7 @@ command_result ConnectedClient::handleCommandPlaylistClientDelPerm(ts::Command &
return command_result{perr};
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
if(!pparser.validate(this->ref(), this->getClientDatabaseId()))
return pparser.build_command_result();

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
#include <pipes/errors.h>
#include <misc/std_unique_ptr.h>
#include <log/LogUtils.h>
#include "../../groups/GroupAssignmentManager.h"
using namespace std;
using namespace std::chrono;
@ -631,9 +632,6 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) {
this->currentChannel = nullptr;
}
old_server->group_manager()->disableCache(this->getClientDatabaseId());
this->loadDataForCurrentServer();
}
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
}

View File

@ -134,11 +134,21 @@ namespace ts::server {
bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override;
bool notifyClientChatClosed(const std::shared_ptr<ConnectedClient> &ptr) override;
bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point&, const std::string &textMessage) override;
bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group) override;
bool notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) override;
bool notifyClientChannelGroupChanged(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &ptr, const std::shared_ptr<BasicChannel> &shared_ptr,
const std::shared_ptr<Group> &group, bool lock_channel_tree) override;
bool notifyServerGroupClientAdd(std::optional<ts::command_builder> &anOptional,
const std::shared_ptr<ConnectedClient> &ptr,
const std::shared_ptr<ConnectedClient> &sharedPtr,
const GroupId &id) override;
bool notifyServerGroupClientRemove(std::optional<ts::command_builder> &anOptional,
const std::shared_ptr<ConnectedClient> &ptr,
const std::shared_ptr<ConnectedClient> &sharedPtr,
const GroupId &id) override;
bool notifyClientChannelGroupChanged(std::optional<ts::command_builder> &anOptional,
const std::shared_ptr<ConnectedClient> &ptr,
const std::shared_ptr<ConnectedClient> &sharedPtr, const ChannelId &id,
const ChannelId &channelId, const GroupId &groupId) override;
bool notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) override;
bool notifyChannelCreate(const std::shared_ptr<BasicChannel> &channel, ChannelId orderId,

View File

@ -13,7 +13,7 @@
#include <ThreadPool/Timer.h>
#include <numeric>
#include "src/manager/ActionLogger.h"
#include "../../groups/GroupManager.h"
#include "src/client/command_handler/helpers.h"
using namespace std;
@ -244,12 +244,12 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
if(this->server) {
{
unique_lock tree_lock(this->server->channel_tree_lock);
if(joined_channel)
if(joined_channel) {
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
}
this->server->unregisterClient(this->ref(), "login", tree_lock);
}
this->server->group_manager()->disableCache(this->getClientDatabaseId());
} else serverInstance->group_manager()->disableCache(this->getClientDatabaseId());
}
logMessage(LOG_QUERY, "Got new authenticated client. Username: {}, Unique-ID: {}, Bounded Server: {}", account->username, account->unique_id, account->bound_server);
@ -269,7 +269,6 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(target_server ? target_server->getServerId() : 0), this->ref());
if(target_server) {
target_server->group_manager()->enableCache(this->getClientDatabaseId());
target_server->registerClient(this->ref());
{
@ -293,7 +292,6 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
this->task_update_needed_permissions.enqueue();
}
} else {
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
this->task_update_needed_permissions.enqueue();
}
@ -318,15 +316,13 @@ command_result QueryClient::handleCommandLogout(Command &) {
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
this->server->unregisterClient(this->ref(), "logout", tree_lock);
}
this->server->group_manager()->disableCache(this->getClientDatabaseId());
} else serverInstance->group_manager()->disableCache(this->getClientDatabaseId());
}
this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery"; //TODO load from table
this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(ntohs(this->getPeerPort()));
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), this->ref());
if(this->server){
this->server->group_manager()->enableCache(this->getClientDatabaseId());
this->server->registerClient(this->ref());
{
@ -347,7 +343,6 @@ command_result QueryClient::handleCommandLogout(Command &) {
this->task_update_needed_permissions.enqueue();
}
} else {
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
this->task_update_needed_permissions.enqueue();
}
@ -401,7 +396,6 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) {
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), this->ref());
if(this->server) {
this->server->group_manager()->enableCache(this->getClientDatabaseId());
this->server->registerClient(this->ref());
{
@ -420,7 +414,6 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) {
this->task_update_needed_permissions.enqueue();
}
} else {
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
this->task_update_needed_permissions.enqueue();
}

View File

@ -75,20 +75,27 @@ bool QueryClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<Conne
return ConnectedClient::notifyTextMessage(mode, sender, targetId, channel_id, tp, textMessage);
}
bool QueryClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {
bool QueryClient::notifyServerGroupClientAdd(optional<ts::command_builder> &anOptional,
const shared_ptr<ConnectedClient> &ptr,
const shared_ptr<ConnectedClient> &sharedPtr, const GroupId &id) {
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_ADD);
return ConnectedClient::notifyServerGroupClientAdd(invoker, client, group);
return ConnectedClient::notifyServerGroupClientAdd(anOptional, ptr, sharedPtr, id);
}
bool QueryClient::notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) {
bool QueryClient::notifyServerGroupClientRemove(optional<ts::command_builder> &anOptional,
const shared_ptr<ConnectedClient> &ptr,
const shared_ptr<ConnectedClient> &sharedPtr, const GroupId &id) {
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_REMOVE);
return ConnectedClient::notifyServerGroupClientRemove(invoker, client, group);
return ConnectedClient::notifyServerGroupClientRemove(anOptional, ptr, sharedPtr, id);
}
bool QueryClient::notifyClientChannelGroupChanged(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &ptr, const std::shared_ptr<BasicChannel> &shared_ptr,
const std::shared_ptr<Group> &group, bool lock_channel_tree) {
bool QueryClient::notifyClientChannelGroupChanged(optional<ts::command_builder> &anOptional,
const shared_ptr <ConnectedClient> &ptr,
const shared_ptr <ConnectedClient> &sharedPtr, const ChannelId &id,
const ChannelId &channelId, const GroupId &groupId) {
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_CHANNEL_CHANGED);
return ConnectedClient::notifyClientChannelGroupChanged(invoker, client, ptr, shared_ptr, group, lock_channel_tree);
return ConnectedClient::notifyClientChannelGroupChanged(anOptional, ptr, sharedPtr, id, channelId, groupId);
}
bool QueryClient::notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) {

View File

@ -73,8 +73,8 @@ namespace ts::server::whisper {
* This also updates the last head ptr.
* @return True if the packet is a valid whisper packet. The payload ptr and payload length variables will be set
*/
bool validate_whisper_packet(const protocol::PacketParser& /* packet */, bool& /* matches last header */, void*& /* payload ptr */, size_t& /* payload length */);
ts::command_result configure_rtc_clients(uint32_t /* stream id */, const std::vector<std::shared_ptr<SpeakingClient>>& /* clients */);
[[nodiscard]] bool validate_whisper_packet(const protocol::PacketParser& /* packet */, bool& /* matches last header */, void*& /* payload ptr */, size_t& /* payload length */);
[[nodiscard]] ts::command_result configure_rtc_clients(uint32_t /* stream id */, const std::vector<std::shared_ptr<SpeakingClient>>& /* clients */);
[[nodiscard]] size_t max_whisper_targets();
};

View File

@ -144,6 +144,7 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
*packets_tail = packet;
packets_tail = &packet->next;
assert(!packet->next);
data_length -= bytes;
if(data_length == 0) {
@ -161,6 +162,7 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
packets_head = packet;
packets_tail = &packet->next;
assert(!packet->next);
}
{

View File

@ -21,7 +21,7 @@ constexpr static auto kMaxWhisperClientNameLength{30};
constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */
VoiceClient::VoiceClient(const std::shared_ptr<VoiceServer>& server, const sockaddr_storage* address) :
SpeakingClient(server->server->sql, server->server),
SpeakingClient{server->server->sql, server->server},
voice_server(server) {
assert(address);
memtrack::allocated<VoiceClient>(this);

View File

@ -227,7 +227,6 @@ bool WebClient::close_connection(const std::chrono::system_clock::time_point& ti
unique_lock server_channel_lock(this->server->channel_tree_lock);
this->server->unregisterClient(this->ref(), "disconnected", server_channel_lock);
}
this->server->group_manager()->disableCache(this->getClientDatabaseId());
//this->server = nullptr;
}

View File

@ -22,26 +22,28 @@ namespace ts::server::groups {
typedef uint32_t GroupIconId;
class AbstractGroupManager;
class Group {
friend class AbstractGroupManager;
public:
Group(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
Group(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */,
std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
virtual ~Group() = default;
/* information getters */
[[nodiscard]] inline ServerId virtual_server_id() const { return this->virtual_server_id_; }
[[nodiscard]] inline GroupId group_id() const { return this->group_id_; }
[[nodiscard]] inline GroupType group_type() const { return this->type_; }
[[nodiscard]] inline const std::string& display_name() const { return this->name_; }
[[nodiscard]] inline const std::string &display_name() const { return this->name_; }
/* we're not returning a cr here because the permissions might get reloaded */
[[nodiscard]] inline std::shared_ptr<permission::v2::PermissionManager> permissions() { return this->permissions_; }
[[nodiscard]] inline bool save_assignments() const {
assert(this->permissions_);
auto value = this->permissions_->permission_value_flagged(permission::b_group_is_permanent);
return value.has_value ? permission::v2::permission_granted(1, value) : true;
}
[[nodiscard]] inline std::shared_ptr<permission::v2::PermissionManager>
permissions() { return this->permissions_; }
[[nodiscard]] inline GroupNameMode name_mode() const {
assert(this->permissions_);
@ -75,17 +77,19 @@ namespace ts::server::groups {
return data.has_value ? data.value : 0;
}
[[nodiscard]] inline bool permission_granted(const permission::PermissionType& permission, const permission::v2::PermissionFlaggedValue& granted_value, bool require_granted_value) {
[[nodiscard]] inline bool permission_granted(const permission::PermissionType &permission,
const permission::v2::PermissionFlaggedValue &granted_value,
bool require_granted_value) {
auto permission_manager = this->permissions_;;
assert(permission_manager);
const auto data = permission_manager->permission_value_flagged(permission);
if(!data.has_value) {
if (!data.has_value) {
return !require_granted_value || granted_value.has_value;
}
if(!granted_value.has_value) {
if (!granted_value.has_value) {
return false;
}
if(data.value == -1) {
if (data.value == -1) {
return granted_value.value == -1;
}
return granted_value.value >= data.value;
@ -99,17 +103,19 @@ namespace ts::server::groups {
std::shared_ptr<permission::v2::PermissionManager> permissions_;
void set_permissions(const std::shared_ptr<permission::v2::PermissionManager>& /* permissions */);
void set_permissions(const std::shared_ptr<permission::v2::PermissionManager> & /* permissions */);
};
class ServerGroup : public Group {
public:
ServerGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
ServerGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */,
std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
};
class ChannelGroup : public Group {
public:
ChannelGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
ChannelGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */,
std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
};
}
DEFINE_TRANSFORMS(ts::server::groups::GroupType, uint8_t);

View File

@ -8,7 +8,30 @@
using namespace ts::server::groups;
GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle} { }
namespace ts::server::groups {
struct InternalChannelGroupAssignment {
InternalChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { }
InternalChannelGroupAssignment(const InternalChannelGroupAssignment& other) = default;
InternalChannelGroupAssignment(InternalChannelGroupAssignment&&) = default;
InternalChannelGroupAssignment&operator=(const InternalChannelGroupAssignment&) = default;
ChannelId channel_id;
GroupId group_id;
bool temporary_assignment;
};
struct InternalServerGroupAssignment {
explicit InternalServerGroupAssignment(GroupId group_id, bool temporary) : group_id{group_id}, temporary_assignment{temporary} { }
InternalServerGroupAssignment(const InternalServerGroupAssignment& other) = default;
InternalServerGroupAssignment(InternalServerGroupAssignment&&) = default;
InternalServerGroupAssignment&operator=(const InternalServerGroupAssignment&) = default;
GroupId group_id;
bool temporary_assignment;
};
}
GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle}, client_cache_lock{std::make_shared<std::mutex>()} { }
GroupAssignmentManager::~GroupAssignmentManager() = default;
bool GroupAssignmentManager::initialize(std::string &error) {
@ -25,8 +48,8 @@ ts::ServerId GroupAssignmentManager::server_id() {
bool GroupAssignmentManager::load_data(std::string &error) {
if constexpr(kCacheAllClients) {
std::lock_guard cache_lock{this->client_cache_lock};
std::unique_ptr<ClientCache> current_entry{nullptr};
std::lock_guard cache_lock{*this->client_cache_lock};
std::shared_ptr<ClientCache> current_entry{nullptr};
auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `cldbid`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid ORDER BY `cldbid`", variable{":sid", this->server_id()})
.query([&](int length, std::string* value, std::string* column) {
@ -55,14 +78,15 @@ bool GroupAssignmentManager::load_data(std::string &error) {
this->client_cache.push_back(std::move(current_entry));
if(!current_entry) {
current_entry = std::make_unique<ClientCache>();
current_entry = std::make_shared<ClientCache>();
current_entry->client_database_id = client_dbid;
}
if(channel_id)
current_entry->channel_group_assignments.emplace_back(channel_id, group_id, false);
else
current_entry->server_group_assignments.emplace_back(group_id);
if(channel_id) {
current_entry->channel_group_assignments.push_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group_id, false));
} else {
current_entry->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group_id, false));
}
return 0;
});
@ -79,7 +103,7 @@ bool GroupAssignmentManager::load_data(std::string &error) {
}
void GroupAssignmentManager::unload_data() {
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
this->client_cache.clear();
}
@ -87,7 +111,7 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
if constexpr(!kCacheAllClients) {
bool cache_exists{false};
{
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& client : this->client_cache)
if(client->client_database_id == cldbid) {
client->use_count++;
@ -97,7 +121,7 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
}
if(!cache_exists) {
auto cache = std::make_unique<ClientCache>();
auto cache = std::make_shared<ClientCache>();
cache->client_database_id = cldbid;
cache->use_count++;
@ -122,14 +146,15 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
if(!group_id)
return 0;
if(channel_id)
cache->channel_group_assignments.emplace_back(channel_id, group_id, false);
else
cache->server_group_assignments.emplace_back(group_id);
if(channel_id) {
cache->channel_group_assignments.push_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group_id, false));
} else {
cache->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group_id, false));
}
return 0;
});
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
#if 0 /* lets have some performance over double entries :D */
for(auto& client : this->client_cache)
if(client->client_database_id == cldbid) {
@ -152,8 +177,8 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
void GroupAssignmentManager::disable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) {
if constexpr(!kCacheAllClients) {
std::lock_guard cache_lock{this->client_cache_lock};
this->client_cache.erase(std::remove_if(this->client_cache.begin(), this->client_cache.end(), [cldbid](const std::unique_ptr<ClientCache>& client) {
std::lock_guard cache_lock{*this->client_cache_lock};
this->client_cache.erase(std::remove_if(this->client_cache.begin(), this->client_cache.end(), [cldbid](const std::shared_ptr<ClientCache>& client) {
return client->client_database_id == cldbid;
}), this->client_cache.end());
}
@ -169,13 +194,13 @@ std::vector<ts::GroupId> GroupAssignmentManager::server_groups_of_client(ts::ser
std::vector<ts::GroupId> result{};
bool cache_found{false};
{
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
if(entry->client_database_id != cldbid) continue;
result.reserve(entry->server_group_assignments.size());
for(auto& assignment : entry->server_group_assignments)
result.push_back(assignment.group_id);
result.push_back(assignment->group_id);
cache_found = true;
break;
@ -222,12 +247,18 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups
std::vector<ChannelGroupAssignment> result{};
bool cache_found{false};
{
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
if(entry->client_database_id != cldbid) continue;
result.reserve(entry->channel_group_assignments.size());
result.insert(result.begin(), entry->channel_group_assignments.begin(), entry->channel_group_assignments.end());
for(const auto& assignment : entry->channel_group_assignments) {
result.push_back(ChannelGroupAssignment{
.client_database_id = cldbid,
.channel_id = assignment->channel_id,
.group_id = assignment->group_id,
});
}
cache_found = true;
break;
}
@ -255,7 +286,12 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups
}
if(!group_id || !channel_id) return 0;
result.emplace_back(channel_id, group_id, false);
result.push_back(ChannelGroupAssignment{
.client_database_id = cldbid,
.channel_id = channel_id,
.group_id = group_id,
});
return 0;
});
LOG_SQL_CMD(res);
@ -273,7 +309,7 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups
}
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 */
auto assignments = this->exact_channel_groups_of_client(mode, client_database_id);
for(const auto& assignment : assignments) {
@ -310,44 +346,87 @@ std::optional<ts::GroupId> GroupAssignmentManager::calculate_channel_group_of_cl
return std::nullopt;
}
std::deque<ts::ClientDbId> GroupAssignmentManager::server_group_clients(GroupId group_id) {
std::deque<ts::ClientDbId> result{};
if constexpr(kCacheAllClients) {
std::lock_guard cache_lock{this->client_cache_lock};
std::deque<ServerGroupAssignment> GroupAssignmentManager::server_group_clients(GroupId group_id, bool full_info) {
std::deque<ServerGroupAssignment> result{};
if(kCacheAllClients && !full_info) {
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& client : this->client_cache) {
auto it = std::find_if(client->server_group_assignments.begin(), client->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) {
return assignment.group_id == group_id;
auto it = std::find_if(client->server_group_assignments.begin(), client->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
return assignment->group_id == group_id;
});
if(it == client->server_group_assignments.end()) {
continue;
}
result.push_back(client->client_database_id);
result.push_back(ServerGroupAssignment{
.client_database_id = client->client_database_id,
.group_id = group_id,
});
}
} else {
auto res = sql::command(this->sql_manager(), "SELECT `cldbid` FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id})
.query([&](int length, std::string* value, std::string* column) {
ClientDbId cldbid{0};
try {
for(int index = 0; index < length; index++) {
if(column[index] == "cldbid") {
cldbid = std::stoull(value[index]);
break;
}
}
} catch(std::exception& ex) {
logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse client database id.");
return 0;
}
if(!cldbid) return 0;
if(full_info) {
constexpr static auto kSqlCommand{
"SELECT `cldbid`, clients_server.client_unique_id, clients_server.client_nickname FROM `assignedGroups` "
"LEFT JOIN `clients_server` ON `assignedGroups`.`serverId` = `clients_server`.`server_id` AND `assignedGroups`.`cldbid` = `clients_server`.`client_database_id` "
"WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid;"
};
result.push_back(cldbid);
return 0;
});
LOG_SQL_CMD(res);
std::string sql_command{kSqlCommand};
auto sql = sql::command{this->sql_manager(), kSqlCommand, variable{":sid", this->server_id()}, variable{":gid", group_id}};
LOG_SQL_CMD(sql.query([&](int length, std::string* values, std::string* names) {
ServerGroupAssignment assignment{};
int index{0};
try {
assert(names[index] == "cldbid");
assignment.client_database_id = std::stoull(values[index++]);
assert(names[index] == "client_unique_id");
assignment.client_unique_id = values[index++];
assert(names[index] == "client_nickname");
assignment.client_display_name = values[index++];
assert(index == length);
} catch (std::exception& ex) {
logError(this->server_id(), "Failed to parse client server group assignment at index {}: {}",
index - 1,
ex.what()
);
return;
}
result.push_back(std::move(assignment));
}));
} else {
auto res = sql::command(this->sql_manager(), "SELECT `cldbid` FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id})
.query([&](int length, std::string* value, std::string* column) {
ClientDbId cldbid{0};
try {
for(int index = 0; index < length; index++) {
if(column[index] == "cldbid") {
cldbid = std::stoull(value[index]);
break;
}
}
} catch(std::exception& ex) {
logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse client database id.");
return 0;
}
if(!cldbid) return 0;
result.push_back(ServerGroupAssignment{
.client_database_id = cldbid,
.group_id = group_id,
});
return 0;
});
LOG_SQL_CMD(res);
}
}
if(auto parent = this->manager_->parent_manager(); parent) {
auto parent_clients = parent->assignments().server_group_clients(group_id);
auto parent_clients = parent->assignments().server_group_clients(group_id, full_info);
result.insert(result.begin(), parent_clients.begin(), parent_clients.end());
}
@ -407,60 +486,62 @@ std::deque<std::tuple<ts::GroupId, ts::ChannelId, ts::ClientDbId>> GroupAssignme
return result;
}
GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) {
bool cache_verified{false};
GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group, bool temporary) {
bool cache_registered{false};
{
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
if(entry->client_database_id != client) continue;
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) {
return assignment.group_id == group;
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
return assignment->group_id == group;
});
if(it != entry->server_group_assignments.end()) {
return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP;
}
entry->server_group_assignments.emplace_back(group);
cache_verified = true;
entry->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group, temporary));
cache_registered = true;
break;
}
if(!cache_verified && kCacheAllClients) {
if(!cache_registered && kCacheAllClients) {
/* add the client to the cache */
auto cache = std::make_unique<ClientCache>();
auto cache = std::make_shared<ClientCache>();
cache->client_database_id = client;
cache->server_group_assignments.emplace_back(group);
cache->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group, temporary));
this->client_cache.push_back(std::move(cache));
}
}
{
if(!temporary) {
auto command = sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
variable{":sid", this->server_id()},
variable{":cldbid", client},
variable{":gid", group},
variable{":chid", 0},
variable{":until", 0});
if(cache_verified)
if(cache_registered) {
command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to insert group assignment into the database"});
else {
} else {
auto result = command.execute();
if(!result) return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; //TODO: Parse error from database?
}
}
return GroupAssignmentResult::SUCCESS;
}
GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId client, GroupId group) {
bool cache_verified{false};
{
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
if(entry->client_database_id != client) continue;
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) {
return assignment.group_id == group;
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
return assignment->group_id == group;
});
if(it == entry->server_group_assignments.end()) {
return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP;
@ -495,24 +576,24 @@ GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId cli
GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId client, GroupId group, ChannelId channel_id, bool temporary) {
bool cache_verified{false};
{
std::lock_guard cache_lock{this->client_cache_lock};
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
if(entry->client_database_id != client) continue;
auto it = std::find_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) {
return assignment.channel_id == channel_id;
auto it = std::find_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
return assignment->channel_id == channel_id;
});
if(it != entry->channel_group_assignments.end()) {
if(group) {
if(it->group_id == group)
if((*it)->group_id == group)
return GroupAssignmentResult::SET_ALREADY_MEMBER_OF_GROUP;
it->group_id = group;
(*it)->group_id = group;
} else {
entry->channel_group_assignments.erase(it);
}
} else {
if(group) {
entry->channel_group_assignments.emplace_back(channel_id, group, temporary);
entry->channel_group_assignments.emplace_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group, temporary));
}
}
cache_verified = true;
@ -522,9 +603,9 @@ GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId clien
if(!cache_verified && kCacheAllClients) {
if(group) {
/* add the client to the cache */
auto cache = std::make_unique<ClientCache>();
auto cache = std::make_shared<ClientCache>();
cache->client_database_id = client;
cache->channel_group_assignments.emplace_back(channel_id, group, temporary);
cache->channel_group_assignments.emplace_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group, temporary));
this->client_cache.push_back(std::move(cache));
} else {
return GroupAssignmentResult::SUCCESS;
@ -554,47 +635,137 @@ GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId clien
return GroupAssignmentResult::SUCCESS;
}
void GroupAssignmentManager::cleanup_channel_assignments(ChannelId channel_id) {
{
std::lock_guard cache_lock{this->client_cache_lock};
for(auto& client : this->client_cache) {
client->channel_group_assignments.erase(std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) {
return assignment.channel_id == channel_id;
}));
}
}
sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = :cid", variable{":sid", this->server_id()}, variable{":cid", channel_id})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
}
void GroupAssignmentManager::cleanup_assignments() {
{
std::lock_guard cache_lock{this->client_cache_lock};
this->client_cache.clear();
}
sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->server_id()})
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
}
void GroupAssignmentManager::cleanup_channel_temporary_assignment(ClientDbId client_dbid, ChannelId channel) {
std::lock_guard cache_lock{this->client_cache_lock};
void GroupAssignmentManager::cleanup_temporary_channel_assignment(ClientDbId client_dbid, ChannelId channel) {
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& client : this->client_cache) {
if(client->client_database_id == client_dbid) {
auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) {
return assignment.channel_id == channel;
auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
return assignment->channel_id == channel;
});
if(assignment == client->channel_group_assignments.end()) {
break;
}
if(assignment->temporary_assignment) {
if((*assignment)->temporary_assignment) {
client->channel_group_assignments.erase(assignment);
}
break;
}
}
}
bool GroupAssignmentManager::is_server_group_empty(GroupId group_id) {
bool result{true};
if(kCacheAllClients) {
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
for(auto& assignment : entry->server_group_assignments) {
if(assignment->group_id == group_id) {
return false;
}
}
}
} else {
auto sql = sql::command{this->sql_manager(), "SELECT COUNT(*) FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
LOG_SQL_CMD(sql.query([&](int, std::string* values, std::string*) {
result = std::stoul(values[0]) == 0;
}));
}
return result;
}
bool GroupAssignmentManager::is_channel_group_empty(GroupId group_id) {
bool result{true};
if(kCacheAllClients) {
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
for(auto& assignment : entry->channel_group_assignments) {
if(assignment->group_id == group_id) {
return false;
}
}
}
} else {
auto sql = sql::command{this->sql_manager(), "SELECT COUNT(*) FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
LOG_SQL_CMD(sql.query([&](int, std::string* values, std::string*) {
result = std::stoul(values[0]) == 0;
}));
}
return result;
}
void GroupAssignmentManager::handle_channel_deleted(ChannelId channel_id) {
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = :cid", variable{":sid", this->server_id()}, variable{":cid", channel_id}};
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete assignments for deleted channel"});
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
entry->channel_group_assignments.erase(std::remove_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
return assignment->channel_id == channel_id;
}), entry->channel_group_assignments.end());
}
}
void GroupAssignmentManager::handle_server_group_deleted(GroupId group_id) {
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete assignments for deleted server group"});
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
entry->server_group_assignments.erase(std::remove_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
return assignment->group_id == group_id;
}), entry->server_group_assignments.end());
}
}
void GroupAssignmentManager::handle_channel_group_deleted(GroupId group_id) {
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete assignments for deleted channel group"});
std::lock_guard cache_lock{*this->client_cache_lock};
for(auto& entry : this->client_cache) {
entry->channel_group_assignments.erase(std::remove_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
return assignment->group_id == group_id;
}), entry->channel_group_assignments.end());
}
}
void GroupAssignmentManager::reset_all() {
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}};
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete all assignments"});
}
std::shared_ptr<TemporaryAssignmentsLock> GroupAssignmentManager::create_tmp_assignment_lock(ClientDbId cldbid) {
std::shared_ptr<ClientCache> cache{};
std::lock_guard cache_lock{*this->client_cache_lock};
for(const auto& entry : this->client_cache) {
if(entry->client_database_id == cldbid) {
cache = entry;
break;
}
}
if(!cache) {
cache = std::make_shared<ClientCache>();
cache->client_database_id = cldbid;
this->client_cache.push_back(cache);
}
auto cache_mutex = this->client_cache_lock;
std::shared_ptr<char> temp_assignment_lock{new char{}, [cache, cache_mutex](void* buffer) {
delete (char*) buffer;
std::lock_guard cache_lock{*cache_mutex};
cache->server_group_assignments.erase(std::remove_if(cache->server_group_assignments.begin(), cache->server_group_assignments.end(), [](const std::unique_ptr<InternalServerGroupAssignment>& assignment){
return assignment->temporary_assignment;
}), cache->server_group_assignments.end());
cache->channel_group_assignments.erase(std::remove_if(cache->channel_group_assignments.begin(), cache->channel_group_assignments.end(), [](const std::unique_ptr<InternalChannelGroupAssignment>& assignment){
return assignment->temporary_assignment;
}), cache->channel_group_assignments.end());
}};
cache->temp_assignment_lock = temp_assignment_lock;
return temp_assignment_lock;
}

View File

@ -31,25 +31,25 @@ namespace ts::server {
class GroupManager;
struct ChannelGroupAssignment {
ChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { }
ChannelGroupAssignment(const ChannelGroupAssignment& other) = default;
ChannelGroupAssignment(ChannelGroupAssignment&&) = default;
ChannelGroupAssignment&operator=(const ChannelGroupAssignment&) = default;
ClientDbId client_database_id;
ChannelId channel_id;
GroupId group_id;
bool temporary_assignment;
};
struct ServerGroupAssignment {
explicit ServerGroupAssignment(GroupId group_id) : group_id{group_id} { }
ServerGroupAssignment(const ServerGroupAssignment& other) = default;
ServerGroupAssignment(ServerGroupAssignment&&) = default;
ServerGroupAssignment&operator=(const ServerGroupAssignment&) = default;
ClientDbId client_database_id{0};
GroupId group_id{0};
GroupId group_id;
/* both fields will only be set on extended info */
std::optional<std::string> client_display_name{};
std::optional<std::string> client_unique_id{};
};
typedef void TemporaryAssignmentsLock;
struct InternalChannelGroupAssignment;
struct InternalServerGroupAssignment;
enum struct GroupAssignmentResult {
SUCCESS,
ADD_ALREADY_MEMBER_OF_GROUP,
@ -82,43 +82,44 @@ namespace ts::server {
/**
* 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.
* Note: `target channel` will be altered if the result 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<ServerGroupAssignment> server_group_clients(GroupId /* group id */, bool /* full info */);
[[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 */
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */);
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */, bool /* temporary assignment */);
GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */);
GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */);
void cleanup_assignments();
void cleanup_channel_assignments(ChannelId /* channel */);
void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */);
[[nodiscard]] std::shared_ptr<TemporaryAssignmentsLock> create_tmp_assignment_lock(ClientDbId /* client database id */);
void cleanup_temporary_channel_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:
GroupManager* manager_;
struct ClientCache {
ClientDbId client_database_id{0};
size_t use_count{0};
std::deque<ChannelGroupAssignment> channel_group_assignments{};
std::deque<ServerGroupAssignment> server_group_assignments{};
std::deque<std::unique_ptr<InternalChannelGroupAssignment>> channel_group_assignments{};
std::deque<std::unique_ptr<InternalServerGroupAssignment>> server_group_assignments{};
std::weak_ptr<TemporaryAssignmentsLock> temp_assignment_lock{};
};
GroupManager* manager_;
std::mutex client_cache_lock;
std::deque<std::unique_ptr<ClientCache>> client_cache{};
std::shared_ptr<std::mutex> client_cache_lock{};
std::deque<std::shared_ptr<ClientCache>> client_cache{};
[[nodiscard]] sql::SqlManager* sql_manager();

View File

@ -51,11 +51,13 @@ void GroupManager::save_permissions() {
auto time_server_groups = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp_1 - timestamp_0).count();
auto time_channel_groups = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp_2 - timestamp_1).count();
debugMessage(this->server_id(), "Saved {}/{} server and {}/{} channel group permissions in {}ms or {}ms",
saved_groups_server, total_groups_server,
saved_groups_channel, total_groups_channel,
time_server_groups, time_channel_groups
);
if(saved_groups_channel > 0 || saved_groups_channel > 0) {
debugMessage(this->server_id(), "Saved {}/{} server and {}/{} channel group permissions in {}ms or {}ms",
saved_groups_server, total_groups_server,
saved_groups_channel, total_groups_channel,
time_server_groups, time_channel_groups
);
}
}
/* Abstract group manager */
@ -114,47 +116,60 @@ void AbstractGroupManager::unload_data() {
}
}
void AbstractGroupManager::reset_groups(const std::shared_ptr<GroupManager> &template_provider, std::map<GroupId, GroupId> &mapping) {
void AbstractGroupManager::reset_groups(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",
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :serverId AND AND `target` = :target)",
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());
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":target", (uint8_t) this->database_target_}).execute());
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :serverId AND AND `target` = :target)",
variable{":serverId", this->server_id()},
variable{":target", (uint8_t) this->database_target_}).execute());
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId AND `target` = :target",
variable{":serverId", this->server_id()},
variable{":target", (uint8_t) this->database_target_}
).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) {
std::lock_guard manage_lock{this->group_manage_mutex_};
this->unload_data();
if(this->parent_manager_) {
for(const auto& group : this->parent_manager_->groups_) {
if(group->group_type() != GroupType::GROUP_TYPE_TEMPLATE) {
continue;
}
if(db_cleanup) {
/* FIXME: Only delete groups with our database target! */
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type",
variable{":serverId", this->server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP}).execute());
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId",
variable{":serverId", this->server_id()}).execute());
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId",
variable{":serverId", this->server_id()}).execute());
}
//GroupCopyResult
std::shared_ptr<Group> created_group{};
auto result = this->copy_group_(group->group_id(), GroupType::GROUP_TYPE_NORMAL, group->display_name(), created_group);
switch(result) {
case GroupCopyResult::SUCCESS:
break;
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);
case GroupCopyResult::NAME_INVALID:
case GroupCopyResult::NAME_ALREADY_IN_USE:
case GroupCopyResult::UNKNOWN_TARGET_GROUP:
case GroupCopyResult::UNKNOWN_SOURCE_GROUP:
case GroupCopyResult::DATABASE_ERROR:
logCritical(this->server_id(), "Failed to copy template group {}: {}", group->group_id(), (uint8_t) result);
break;
}
logTrace(this->server_id(), "Copied template group {}. New id: {}", group->group_id(), created_group->group_id());
mapping[group->group_id()] = created_group->group_id();
}
}
}
int AbstractGroupManager::insert_group_from_sql(int length, std::string *values, std::string *names) {
GroupId group_id{0};
GroupType group_type{GroupType::GROUP_TYPE_UNKNOWN};
@ -169,7 +184,7 @@ int AbstractGroupManager::insert_group_from_sql(int length, std::string *values,
} else if(names[index] == "type") {
group_type = (GroupType) std::stoull(values[index]);
} else if(names[index] == "displayName") {
group_name = names[index];
group_name = values[index];
}
} catch(std::exception& ex) {
logWarning(this->server_id(), "Failed to parse group from database. Failed to parse column {} (value: {})", names[index], values[index]);
@ -190,20 +205,24 @@ int AbstractGroupManager::insert_group_from_sql(int length, std::string *values,
return 0;
}
void AbstractGroupManager::save_permissions() {
std::unique_lock group_lock{this->groups_};
void AbstractGroupManager::save_permissions(size_t &total_groups, size_t &saved_groups) {
std::unique_lock group_lock{this->group_mutex_};
auto groups = this->groups_;
group_lock.unlock();
total_groups = groups.size();
saved_groups = 0;
for(auto& group : groups) {
auto permissions = group->permissions();
if(permissions->require_db_updates()) {
serverInstance->databaseHelper()->saveGroupPermissions(0, group->group_id(), (uint8_t) this->database_target_, permissions);
serverInstance->databaseHelper()->saveGroupPermissions(this->server_id(), group->group_id(), (uint8_t) this->database_target_, permissions);
saved_groups++;
}
}
}
std::shared_ptr<Group> AbstractGroupManager::find_group_(GroupCalculateMode mode, GroupId group_id) {
std::shared_ptr<Group> AbstractGroupManager::find_group_(std::shared_ptr<AbstractGroupManager>& owning_manager, GroupCalculateMode mode, GroupId group_id) {
{
std::lock_guard glock{this->group_mutex_};
auto it = std::find_if(this->groups_.begin(), this->groups_.end(), [&](const std::shared_ptr<Group>& group) {
@ -211,12 +230,16 @@ std::shared_ptr<Group> AbstractGroupManager::find_group_(GroupCalculateMode mode
});
if(it != this->groups_.end()) {
owning_manager = this->shared_from_this();
return *it;
}
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_) {
return this->parent_manager_->find_group_(mode, group_id);
owning_manager = this->parent_manager_;
return this->parent_manager_->find_group_(owning_manager, mode, group_id);
}
return nullptr;
}
@ -269,7 +292,8 @@ GroupCreateResult AbstractGroupManager::create_group_(GroupType type, const std:
auto group_id = res.last_insert_rowid();
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) this->database_target_);
auto group = std::make_shared<ServerGroup>(this->server_id(), group_id, type, name, permissions);
auto group = this->allocate_group(group_id, type, name, permissions);
{
std::lock_guard glock{this->group_mutex_};
this->groups_.push_back(group);
@ -291,6 +315,10 @@ GroupCopyResult AbstractGroupManager::copy_group_(GroupId source, GroupType targ
case GroupCreateResult::NAME_ALREADY_IN_USED:
return GroupCopyResult::NAME_ALREADY_IN_USE;
case GroupCreateResult::NAME_TOO_LONG:
case GroupCreateResult::NAME_TOO_SHORT:
return GroupCopyResult::NAME_INVALID;
}
assert(result);
@ -300,35 +328,49 @@ GroupCopyResult AbstractGroupManager::copy_group_(GroupId source, GroupType targ
GroupCopyResult AbstractGroupManager::copy_group_permissions_(GroupId source, GroupId target) {
std::lock_guard manage_lock{this->group_manage_mutex_};
auto source_group = this->find_group_(groups::GroupCalculateMode::GLOBAL, source);
std::shared_ptr<AbstractGroupManager> owning_manager{};
auto source_group = this->find_group_(owning_manager, groups::GroupCalculateMode::GLOBAL, source);
if(!source_group) {
return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
}
auto target_group = this->find_group_(groups::GroupCalculateMode::GLOBAL, target);
auto target_group = this->find_group_(owning_manager, groups::GroupCalculateMode::GLOBAL, target);
if(!target_group) {
return GroupCopyResult::UNKNOWN_TARGET_GROUP;
}
auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
variable{":sid", this->server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":id", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
if(target_group->permissions()->require_db_updates()) {
/* TODO: Somehow flush all pending changes */
}
res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":ssid", source_group->virtual_server_id()},
variable{":tsid", target_group->virtual_server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":source", source},
variable{":target", target}).execute();
{
auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
variable{":sid", this->server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":id", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
}
}
{
constexpr static auto kSqlCommand = "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) "
"SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source";
auto res = sql::command(this->sql_manager(), kSqlCommand,
variable{":ssid", source_group->virtual_server_id()},
variable{":tsid", target_group->virtual_server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":source", source},
variable{":target", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
}
}
target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) this->database_target_));
LOG_SQL_CMD(res);
return GroupCopyResult::SUCCESS;
}
@ -338,7 +380,8 @@ GroupRenameResult AbstractGroupManager::rename_group_(GroupId group_id, const st
return GroupRenameResult::NAME_INVALID;
}
auto group = this->find_group_(groups::GroupCalculateMode::GLOBAL, group_id);
std::shared_ptr<AbstractGroupManager> owning_manager{};
auto group = this->find_group_(owning_manager, groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
return GroupRenameResult::INVALID_GROUP_ID;
}
@ -360,7 +403,7 @@ GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) {
{
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_.end(), [&](const std::shared_ptr<Group>& group) {
return group->group_id() == group_id;
});
@ -376,17 +419,13 @@ GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) {
this->groups_.erase(it);
}
sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", (uint8_t) this->database_target_},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
return GroupDeleteResult::SUCCESS;
}
void AbstractGroupManager::reset_groups_(const std::shared_ptr<GroupManager> &source, std::map<GroupId, GroupId> &mapping) {
}
/* Server group manager */
ServerGroupManager::ServerGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ServerGroupManager> parent)
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent}
@ -401,7 +440,7 @@ std::shared_ptr<Group> ServerGroupManager::allocate_group(GroupId id, GroupType
/* Channel group manager */
ChannelGroupManager::ChannelGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ChannelGroupManager> parent)
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent}
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::CHANNEL, handle->server_id(), parent}
{
}

View File

@ -34,6 +34,7 @@ namespace ts::server::groups {
UNKNOWN_SOURCE_GROUP,
UNKNOWN_TARGET_GROUP,
NAME_ALREADY_IN_USE,
NAME_INVALID,
DATABASE_ERROR
};
@ -48,11 +49,13 @@ namespace ts::server::groups {
enum struct GroupDeleteResult {
SUCCESS,
INVALID_GROUP_ID,
/* Artificial result, not used by the delete method but needed */
GROUP_NOT_EMPTY,
DATABASE_ERROR
};
class GroupManager;
class AbstractGroupManager {
class AbstractGroupManager : public std::enable_shared_from_this<AbstractGroupManager> {
friend class Group;
friend class GroupAssignmentManager;
public:
@ -77,10 +80,9 @@ namespace ts::server::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.
* Reset all known groups and copy the template groups from our group parent (if it isn't null)
*/
void reset_groups(const std::shared_ptr<GroupManager>& /* template group provider */, std::map<GroupId, GroupId>& /* mapping */);
void reset_groups(std::map<GroupId, GroupId>& /* mapping */);
protected:
std::shared_ptr<AbstractGroupManager> parent_manager_;
DatabaseGroupTarget database_target_;
@ -98,7 +100,7 @@ namespace ts::server::groups {
[[nodiscard]] sql::SqlManager* sql_manager();
[[nodiscard]] ServerId server_id();
[[nodiscard]] std::shared_ptr<Group> find_group_(GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<Group> find_group_(std::shared_ptr<AbstractGroupManager>& /* owning manager */, GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<Group> find_group_by_name_(GroupCalculateMode /* mode */, const std::string& /* group name */);
[[nodiscard]] GroupCreateResult create_group_(GroupType type, const std::string& /* group name */, std::shared_ptr<Group>& /* result */);
[[nodiscard]] GroupCopyResult copy_group_(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<Group>& /* result */);
@ -140,7 +142,25 @@ namespace ts::server::groups {
}
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group(GroupCalculateMode mode, GroupId group_id) {
return this->cast_result(this->find_group_(mode, group_id));
std::shared_ptr<AbstractGroupManager> owning_manager{};
return this->cast_result(this->find_group_(owning_manager, mode, group_id));
}
/**
*
* @param owning_manager
* @param mode
* @param group_id
* @return the group if found. `owning_manager` will be set to the owning manager.
*/
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group_ext(std::shared_ptr<ServerGroupManager>& owning_manager, GroupCalculateMode mode, GroupId group_id) {
std::shared_ptr<AbstractGroupManager> owning_manager_;
auto result = this->cast_result(this->find_group_(owning_manager_, mode, group_id));
if(owning_manager_) {
owning_manager = std::dynamic_pointer_cast<ServerGroupManager>(owning_manager_);
assert(owning_manager);
}
return result;
}
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group_by_name(GroupCalculateMode mode, const std::string& group_name) {
@ -212,7 +232,25 @@ namespace ts::server::groups {
}
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group(GroupCalculateMode mode, GroupId group_id) {
return this->cast_result(this->find_group_(mode, group_id));
std::shared_ptr<AbstractGroupManager> owning_manager{};
return this->cast_result(this->find_group_(owning_manager, mode, group_id));
}
/**
*
* @param owning_manager
* @param mode
* @param group_id
* @return the group if found. `owning_manager` will be set to the owning manager.
*/
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group_ext(std::shared_ptr<ChannelGroupManager>& owning_manager, GroupCalculateMode mode, GroupId group_id) {
std::shared_ptr<AbstractGroupManager> owning_manager_;
auto result = this->cast_result(this->find_group_(owning_manager_, mode, group_id));
if(owning_manager_) {
owning_manager = std::dynamic_pointer_cast<ChannelGroupManager>(owning_manager_);
assert(owning_manager);
}
return result;
}
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group_by_name(GroupCalculateMode mode, const std::string& group_name) {

View File

@ -96,7 +96,6 @@ std::shared_ptr<server::MusicClient> MusicBotManager::createBot(ClientDbId owner
if(!config::music::enabled) return nullptr;
handle->group_manager()->enableCache(musicBot->getClientDatabaseId());
handle->registerClient(musicBot);
{
@ -147,7 +146,6 @@ void MusicBotManager::deleteBot(std::shared_ptr<server::MusicClient> musicBot) {
handle->client_move(musicBot, nullptr, nullptr, "Music bot deleted", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock);
handle->unregisterClient(musicBot, "bot deleted", server_channel_lock);
}
handle->group_manager()->disableCache(musicBot->getClientDatabaseId());
serverInstance->databaseHelper()->deleteClient(handle, musicBot->getClientDatabaseId());
serverInstance->databaseHelper()->deleteClient(nullptr, musicBot->getClientDatabaseId());
@ -241,7 +239,6 @@ int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::str
musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
}
handle->group_manager()->enableCache(musicBot->getClientDatabaseId());
if(musicBot->getClientDatabaseId() != botId) logCritical(handle->getServerId(),"Invalid music bot id mapping!");
{

View File

@ -41,11 +41,6 @@ void QueryServer::unregisterConnection(const shared_ptr<QueryClient> &client) {
}
}
if(client->server) {
client->server->group_manager()->disableCache(client->getClientDatabaseId());
} else {
serverInstance->group_manager()->disableCache(client->getClientDatabaseId());
}
/* client->handle = nullptr; */
}

View File

@ -13,6 +13,7 @@
#include "../InstanceHandler.h"
#include "../ShutdownHelper.h"
#include "../server/QueryServer.h"
#include "../groups/GroupManager.h"
#ifdef HAVE_JEMALLOC
#include <jemalloc/jemalloc.h>
@ -293,7 +294,7 @@ namespace terminal::chandler {
return false;
}
auto group = server->group_manager()->findGroup(groupId);
auto group = server->group_manager()->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, groupId);
if(!group) {
handle.response.emplace_back("Could not resolve server group!");
return false;

View File

@ -8,6 +8,16 @@ general server & channel groups
Notified channel & server member add/remove powers
Group edit if the notify<server/channel> group triggers
channelgroupclientlist
Icon IDs seem to be buggy with groups
group permission copy dosn't work
permission reset
TODO: Delete the group header
servergroupclientlist
server default group edit
channelgroupclientlist
temporary group/channel assignments
Test with `kCacheAllClients` and without `kCacheAllClients`
TODO:
- Delete the group header