Some more changes

This commit is contained in:
WolverinDEV 2020-03-05 14:10:22 +01:00
parent 3a5c152b3c
commit 6172628247
21 changed files with 1072 additions and 106 deletions

View File

@ -160,7 +160,7 @@ if (COMPILE_WEB_CLIENT)
src/client/web/WSWebClient.cpp
src/client/web/SampleHandler.cpp
src/client/web/VoiceBridge.cpp
src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h src/groups/GroupManager.cpp src/groups/GroupManager.h src/groups/GroupAssignmentManager.cpp src/groups/GroupAssignmentManager.h src/groups/Group.cpp src/groups/Group.h)
src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h src/groups/GroupManager.cpp src/groups/GroupManager.h src/groups/GroupAssignmentManager.cpp src/groups/GroupAssignmentManager.h src/groups/Group.cpp src/groups/Group.h src/services/VirtualServerInformation.cpp src/services/VirtualServerInformation.h src/vserver/VirtualServerManager.cpp src/vserver/VirtualServerManager.h)
endif ()
add_executable(PermHelper helpers/permgen.cpp)

View File

@ -1,58 +1,85 @@
CommandResult handleCommandClientUpdate(Command&);
CommandResult handleCommandClientEdit(Command&);
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
CommandResult handleCommandClientMove(Command&);
CommandResult handleCommandClientGetVariables(Command&);
CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&);
General lock order:
- Client execute lock
- Server state lock
- Server channel tree
- Client channel tree
CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelEdit(Command&); write lock server channel tree
CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
CommandResult handleCommandChannelMove(Command&); write lock server channel tree
When executing a command:
Lock order:
- Client execute lock
- Server state lock (Server should not try to change state while a client is executing something)
Notes:
The server might be null or the default server.
This must be checked via the given macro!
CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock
Disconnect/unregister a client:
- Lock client execute lock
- Lock server state
- Unregister client from server and set server variable to nullptr
- Underlying server (UDP, Web, etc. disconnect)
CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel
CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel
Client channel tree connect/mode/disconnect:
- Lock client execute lock
- Lock server channel tree in write mode
- Lock client channel tree in write mode
- Move client & notify (notify required all clients to have their channel tree locked in shared mode)
CommandResult handleCommandPluginCmd(Command&);
Command lock overview:
CommandResult handleCommandClientUpdate(Command&);
CommandResult handleCommandClientEdit(Command&);
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
CommandResult handleCommandClientMove(Command&);
CommandResult handleCommandClientGetVariables(Command&);
CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&);
CommandResult handleCommandClientMute(Command&);
CommandResult handleCommandClientUnmute(Command&);
CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelEdit(Command&); write lock server channel tree
CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
CommandResult handleCommandChannelMove(Command&); write lock server channel tree
//Original from query but still reachable for all
CommandResult handleCommandClientList(Command&);
CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandClientFind(Command&);
CommandResult handleCommandClientInfo(Command&);
CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel
CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel
CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree
CommandResult handleCommandPluginCmd(Command&);
handleCommandChannelFind read lock: server channel tree
handleCommandChannelInfo read lock: server channel tree
CommandResult handleCommandClientMute(Command&);
CommandResult handleCommandClientUnmute(Command&);
General command handling: client_command_lock
Ensure that only one command at time will be handeled
//Original from query but still reachable for all
CommandResult handleCommandClientList(Command&);
CommandResult handleCommandClientFind(Command&);
CommandResult handleCommandClientInfo(Command&);
CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree
handleCommandChannelFind read lock: server channel tree
handleCommandChannelInfo read lock: server channel tree
General command handling: client_command_lock
Ensure that only one command at time will be handeled
Read access server channel tree: read lock channel_tree_lock
Write access server channel tree: lock channel_tree_lock
Write access client channel tree: read lock channel_tree_lock => lock client channel tree
if we write to the server channel tree no client should have their channel tree updated
Read access client channel tree: no lock required
Note: the server channel tree should not be accessed!
Read access server channel tree: read lock channel_tree_lock
Write access server channel tree: lock channel_tree_lock
Write access client channel tree: read lock channel_tree_lock => lock client channel tree
if we write to the server channel tree no client should have their channel tree updated
Read access client channel tree: no lock required
Note: the server channel tree should not be accessed!
Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree
Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree
TODO: Some kind of perm channel lock
TODO: Fix handleCommandChannelEdit
Test: Channel hide & show with clients! Multible clients as well!
Test: Channel hide & show with clients! Multiple clients as well!

View File

@ -159,10 +159,9 @@ void DatabaseHelper::deleteClient(const std::shared_ptr<VirtualServer>& server,
//TODO delete complains
}
inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& server, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) {
inline sql::result load_permissions_v2(VirtualServerId server_id, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) {
auto start = system_clock::now();
auto server_id = server ? server->getServerId() : 0;
return command.query([&](int length, char** values, char** names){
permission::PermissionType key = permission::PermissionType::undefined;
permission::PermissionValue value = permNotGranted, granted = permNotGranted;
@ -198,6 +197,8 @@ inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& ser
return 0;
}
if(channel_id > 0 && test_channel) {
//TODO: Test for invalid channels
/*
if(!server)
logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]);
else {
@ -205,6 +206,7 @@ inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& ser
if(!channel)
logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]);
}
*/
}
@ -282,7 +284,7 @@ std::shared_ptr<v2::PermissionRegister> DatabaseHelper::loadClientPermissionMana
variable{":serverId", server ? server->getServerId() : 0},
variable{":type", permission::SQL_PERM_USER},
variable{":id", cldbid});
LOG_SQL_CMD(load_permissions_v2(server, permission_manager.get(), command, true));
LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, permission_manager.get(), command, true));
}
@ -336,14 +338,14 @@ void DatabaseHelper::saveClientPermissions(const std::shared_ptr<ts::server::Vir
}
std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPermissions(const std::shared_ptr<VirtualServer>& server, ts::GroupId group_id) {
std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPermissions(VirtualServerId server, ts::GroupId group_id, uint8_t type) {
auto result = std::make_shared<v2::PermissionRegister>();
if(this->use_startup_cache && server) {
shared_ptr<StartupCacheEntry> entry;
{
threads::MutexLock lock(this->startup_lock);
for(const auto& entries : this->startup_entries) {
if(entries->sid == server->getServerId()) {
if(entries->sid == server) {
entry = entries;
break;
}
@ -361,19 +363,18 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPer
//7931
auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id",
variable{":serverId", server ? server->getServerId() : 0},
variable{":serverId", server},
variable{":type", permission::SQL_PERM_GROUP},
variable{":id", group_id});
LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
return result;
}
void DatabaseHelper::saveGroupPermissions(const std::shared_ptr<ts::server::VirtualServer> &server, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionRegister> &permissions) {
void DatabaseHelper::saveGroupPermissions(VirtualServerId server_id, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionRegister> &permissions) {
const auto updates = permissions->flush_db_updates();
if(updates.empty())
return;
auto server_id = server ? server->getServerId() : 0;
for(auto& update : updates) {
std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND);
@ -388,7 +389,7 @@ void DatabaseHelper::saveGroupPermissions(const std::shared_ptr<ts::server::Virt
query
);
sql::command(this->sql, query,
variable{":serverId", server ? server->getServerId() : 0},
variable{":serverId", server_id},
variable{":id", group_id},
variable{":chId", 0},
variable{":type", permission::SQL_PERM_GROUP},
@ -436,7 +437,7 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadPlaylist
variable{":serverId", server ? server->getServerId() : 0},
variable{":type", permission::SQL_PERM_PLAYLIST},
variable{":id", playlist_id});
LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false));
return result;
}
@ -502,7 +503,7 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadChannelP
variable{":chid", channel},
variable{":id", 0},
variable{":type", permission::SQL_PERM_CHANNEL});
LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false));
return result;
}

View File

@ -97,8 +97,8 @@ namespace ts {
std::shared_ptr<permission::v2::PermissionRegister> loadChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId);
void saveChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionRegister> loadGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId);
void saveGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionRegister> loadGroupPermissions(VirtualServerId, GroupId, uint8_t /* group type */);
void saveGroupPermissions(VirtualServerId, GroupId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionRegister> loadPlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId /* playlist id */);
void savePlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);

View File

@ -29,7 +29,9 @@ namespace ts {
enum GroupTarget {
GROUPTARGET_SERVER,
GROUPTARGET_CHANNEL
GROUPTARGET_CHANNEL,
GROUPTARGET_UNKNOWN
};
enum GroupNameMode {

View File

@ -560,6 +560,10 @@ bool ConnectedClient::notifyClientNeededPermissions() {
cmd[index]["permid"] = value.first;
cmd[index++]["permvalue"] = value.second.has_value ? value.second.value : 0;
}
if(index == 0) {
cmd[index]["permid"] = permission::i_client_talk_power;
cmd[index++]["permvalue"] = 0;
}
this->sendCommand(cmd);
return true;

View File

@ -105,7 +105,8 @@ namespace ts {
std::shared_ptr<permission::v2::PermissionRegister> clientPermissions = nullptr;
std::shared_ptr<Properties> _properties;
std::shared_ptr<BasicChannel> currentChannel = nullptr;
/* the current channel is save to access when the server channel tree has been locked (shared or exclusively) */
std::shared_ptr<BasicChannel> currentChannel{nullptr};
};
}
}

View File

@ -7,15 +7,8 @@
using namespace ts::server::groups;
Group::Group(const std::shared_ptr<GroupManager> &manager, ts::GroupId id, ts::server::groups::GroupType type, std::string name,
std::shared_ptr<permission::v2::PermissionRegister> permissions) : manager_{manager}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { }
void Group::set_display_name(const std::string &name) {
this->name_ = name;
auto handle = this->manager_.lock();
if(handle) handle->update_group_name(this);
}
Group::Group(VirtualServerId sid, ts::GroupId id, ts::server::groups::GroupType type, std::string name,
std::shared_ptr<permission::v2::PermissionRegister> permissions) : virtual_server_id_{sid}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { }
void Group::set_permissions(const std::shared_ptr<permission::v2::PermissionRegister> &permissions) {
assert(permissions);

View File

@ -7,7 +7,9 @@ namespace ts::server::groups {
enum GroupType {
GROUP_TYPE_QUERY,
GROUP_TYPE_TEMPLATE,
GROUP_TYPE_NORMAL
GROUP_TYPE_NORMAL,
GROUP_TYPE_UNKNOWN = 0xFF
};
enum GroupNameMode {
@ -23,13 +25,15 @@ namespace ts::server::groups {
class Group {
friend class GroupManager;
public:
Group(const std::shared_ptr<GroupManager>& /* manager */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
Group(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
~Group() = default;
/* information getters */
[[nodiscard]] inline VirtualServerId virtual_server_id() const { return this->virtual_server_id_; }
[[nodiscard]] inline GroupId group_id() const { return this->group_id_; }
[[nodiscard]] inline GroupType group_type() const { return this->type_; }
[[nodiscard]] inline const std::string& display_name() const { return this->name_; }
/* we're not returning a cr here because the permissions might get reloaded */
[[nodiscard]] inline std::shared_ptr<permission::v2::PermissionRegister> permissions() { return this->permissions_; }
[[nodiscard]] inline bool save_assignments() const {
@ -56,11 +60,8 @@ namespace ts::server::groups {
return value.has_value ? value.value : 0;
}
/* information setters */
void set_display_name(const std::string& name);
private:
std::weak_ptr<GroupManager> manager_;
const VirtualServerId virtual_server_id_;
const GroupId group_id_;
const GroupType type_;
std::string name_;
@ -70,6 +71,13 @@ namespace ts::server::groups {
void set_permissions(const std::shared_ptr<permission::v2::PermissionRegister>& /* permissions */);
};
class ServerGroup : public Group { };
class ChannelGroup : public Group { };
class ServerGroup : public Group {
public:
ServerGroup(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
};
class ChannelGroup : public Group {
public:
ChannelGroup(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
};
}

View File

@ -1,5 +1,612 @@
//
// Created by WolverinDEV on 03/03/2020.
//
#include <log/LogUtils.h>
#include "GroupManager.h"
#include "../vserver/VirtualServerBase.h"
#include "../InstanceHandler.h"
using namespace ts::server::groups;
GroupManager::GroupManager(ts::server::vserver::VirtualServerBase *server, std::shared_ptr<GroupManager> parent)
: virtual_server_{server}, parent_manager_{std::move(parent)}, assignment_manager_{this} {
assert(server);
}
GroupManager::~GroupManager() = default;
ts::ServerId GroupManager::server_id() {
return this->virtual_server_->server_id();
}
sql::SqlManager* GroupManager::sql_manager() {
return this->virtual_server_->sql_manager();
}
bool GroupManager::initialize(const std::shared_ptr<GroupManager>& self, std::string &error) {
assert(&*self == this);
this->weak_ref_ = self;
return this->assignment_manager_.initialize(error);
}
GroupLoadResult GroupManager::load_data(bool initialize) {
std::lock_guard manage_lock{this->group_manage_lock_};
{
std::lock_guard list_lock{this->group_lock_};
this->server_groups_.clear();
this->channel_groups_.clear();
}
auto res = sql::command(this->sql_manager(), "SELECT * FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}).query(&GroupManager::insert_group_from_sql, this);
if(!res) {
LOG_SQL_CMD(res);
return GroupLoadResult::DATABASE_ERROR;
}
if(this->server_groups_.empty() || this->channel_groups_.empty())
return GroupLoadResult::NO_GROUPS;
this->ensure_valid_default_groups(initialize);
return GroupLoadResult::SUCCESS;
}
void GroupManager::unload_data() {
std::lock_guard manage_lock{this->group_manage_lock_};
{
std::lock_guard list_lock{this->group_lock_};
this->server_groups_.clear();
this->channel_groups_.clear();
}
}
void GroupManager::reset_groups(bool db_cleanup) {
std::lock_guard manage_lock{this->group_manage_lock_};
this->unload_data();
if(db_cleanup) {
LOG_SQL_CMD(sql::command(this->virtual_server_->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->virtual_server_->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->server_id()}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->server_id()}).execute());
}
if(auto error = this->load_data(true); error != GroupLoadResult::SUCCESS)
logCritical(this->server_id(), "Failed to load groups after group unload ({}). There might be no groups loaded now!", (int) error);
}
int GroupManager::insert_group_from_sql(int length, std::string *values, std::string *names) {
GroupId group_id{0};
GroupType group_type{GroupType::GROUP_TYPE_UNKNOWN};
std::string group_name{};
GroupTarget group_target{GroupTarget::GROUPTARGET_UNKNOWN};
for(size_t index = 0; index < length; index++) {
try {
if(names[index] == "groupId")
group_id = std::stoull(values[index]);
else if(names[index] == "target")
group_target = (GroupTarget) std::stoull(values[index]);
else if(names[index] == "type")
group_type = (GroupType) std::stoull(values[index]);
else if(names[index] == "displayName")
group_name = names[index];
} catch(std::exception& ex) {
logWarning(this->server_id(), "Failed to parse group from database. Failed to parse column {} (value: {})", names[index], values[index]);
return 0;
}
}
if(!group_id || group_type == GroupType::GROUP_TYPE_UNKNOWN || group_target == GroupTarget::GROUPTARGET_UNKNOWN) {
logWarning(this->server_id(), "Failed to query group from database. Invalid values found.");
return 0;
}
auto database_target = (uint8_t) (group_target == GroupTarget::GROUPTARGET_SERVER ? DatabaseGroupTarget::SERVER : DatabaseGroupTarget::CHANNEL);
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, database_target);
if(group_target == GroupTarget::GROUPTARGET_SERVER) {
auto group = std::make_shared<ServerGroup>(this->server_id(), group_id, group_type, group_name, permissions);
std::lock_guard lock{this->group_lock_};
this->server_groups_.push_back(group);
} else {
auto group = std::make_shared<ChannelGroup>(this->server_id(), group_id, group_type, group_name, permissions);
std::lock_guard lock{this->group_lock_};
this->channel_groups_.push_back(group);
}
return 0;
}
void GroupManager::ensure_valid_default_groups(bool initialize) {
assert(!this->channel_groups_.empty() && !this->server_groups_.empty());
auto fallback_server_group = this->server_groups_.front();
auto fallback_channel_group = this->channel_groups_.front();
auto properties = this->virtual_server_->server_properties();
/* default server music group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP).as_save<GroupId>();
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default music bot group ({}). Try to reinitialize from serverinstance_template_musicdefault_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as<GroupId>();
group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_server_group;
logCritical(LOG_INSTANCE, "Failed to find default music bot group ({}). Using first available group ({}).", group_id, fallback_server_group->group_id());
}
auto group_name = group->display_name();
group = this->find_server_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_server_group;
logError(this->server_id(), "Missing default music bot group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP) = group->group_id();
}
}
/* default server guest group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP).as_save<GroupId>();
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default server guest group ({}). Try to reinitialize from serverinstance_template_serverdefault_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as<GroupId>();
group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_server_group;
logCritical(LOG_INSTANCE, "Failed to find default server guest group ({}). Using first available group ({}).", group_id, fallback_server_group->group_id());
}
auto group_name = group->display_name();
group = this->find_server_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_server_group;
logError(this->server_id(), "Missing default server guest group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP) = group->group_id();
}
}
/* default channel admin group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP).as_save<GroupId>();
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default channel admin group ({}). Try to reinitialize from serverinstance_template_channeladmin_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as<GroupId>();
group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_channel_group;
logCritical(LOG_INSTANCE, "Failed to find default channel admin group ({}). Using first available group ({}).", group_id, fallback_channel_group->group_id());
}
auto group_name = group->display_name();
group = this->find_channel_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_channel_group;
logError(this->server_id(), "Missing default channel admin group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP) = group->group_id();
}
}
/* default channel group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP).as_save<GroupId>();
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default channel guest group ({}). Try to reinitialize from serverinstance_template_channeldefault_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as<GroupId>();
group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_channel_group;
logCritical(LOG_INSTANCE, "Failed to find default channel guest group ({}). Using first available group ({}).", group_id, fallback_channel_group->group_id());
}
auto group_name = group->display_name();
group = this->find_channel_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_channel_group;
logError(this->server_id(), "Missing default channel guest group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP) = group->group_id();
}
}
}
std::shared_ptr<ServerGroup> GroupManager::default_server_group(ClientType client_type, bool enforce_id) {
auto id =
client_type != ClientType::CLIENT_QUERY ?
this->virtual_server_->server_properties()->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP).as_save<GroupId>() :
serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>();
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, id);
if(group || enforce_id) return group;
std::lock_guard glock{this->group_lock_};
return this->server_groups_.empty() ? nullptr : this->server_groups_.front();
}
std::shared_ptr<ChannelGroup> GroupManager::default_channel_group(ClientType client_type, bool enforce_id) {
auto id = this->virtual_server_->server_properties()->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP).as_save<GroupId>();
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, id);
if(group || enforce_id) return group;
std::lock_guard glock{this->group_lock_};
return this->channel_groups_.empty() ? nullptr : this->channel_groups_.front();
}
std::shared_ptr<ServerGroup> GroupManager::find_server_group(GroupCalculateMode mode, GroupId group_id) {
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.end(), [&](const std::shared_ptr<ServerGroup>& group) {
return group->group_id() == group_id;
});
if(it != this->server_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_server_group(mode, group_id);
return nullptr;
}
std::shared_ptr<ServerGroup> GroupManager::find_server_group_by_name(GroupCalculateMode mode, const std::string &name) {
{
std::string lname{name};
std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower);
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.end(),
[&](const std::shared_ptr<ServerGroup> &group) {
std::string lgroup_name{group->name_};
std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(),
::tolower);
return lname == lgroup_name;
});
if(it != this->server_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_server_group_by_name(mode, name);
return nullptr;
}
std::shared_ptr<ChannelGroup> GroupManager::find_channel_group(GroupCalculateMode mode, GroupId group_id) {
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.end(), [&](const std::shared_ptr<ChannelGroup>& group) {
return group->group_id() == group_id;
});
if(it != this->channel_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_channel_group(mode, group_id);
return nullptr;
}
std::shared_ptr<ChannelGroup> GroupManager::find_channel_group_by_name(GroupCalculateMode mode, const std::string &name) {
{
std::string lname{name};
std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower);
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.end(),
[&](const std::shared_ptr<ChannelGroup> &group) {
std::string lgroup_name{group->name_};
std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(),
::tolower);
return lname == lgroup_name;
});
if(it != this->channel_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_channel_group_by_name(mode, name);
return nullptr;
}
GroupCreateResult GroupManager::create_server_group(GroupType type, const std::string &name, std::shared_ptr<ServerGroup>& result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCreateResult::DATABASE_ERROR;
if(this->find_server_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupCreateResult::NAME_ALREADY_IN_USED;
auto group_id = this->generate_server_group_id();
if(!group_id)
return GroupCreateResult::FAILED_TO_GENERATE_ID;
auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", this->server_id()}, variable{":gid", group_id}, variable{":target", DatabaseGroupTarget::SERVER}, variable{":type", type}, variable{":name", name}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCreateResult::DATABASE_ERROR;
}
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) DatabaseGroupTarget::SERVER);
auto group = std::make_shared<ServerGroup>(self->server_id(), group_id, type, name, permissions);
{
std::lock_guard glock{this->group_lock_};
this->server_groups_.push_back(group);
}
result = group;
return GroupCreateResult::SUCCESS;
}
GroupCreateResult GroupManager::create_channel_group(GroupType type, const std::string &name, std::shared_ptr<ChannelGroup> &result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCreateResult::DATABASE_ERROR;
if(this->find_channel_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupCreateResult::NAME_ALREADY_IN_USED;
auto group_id = this->generate_channel_group_id();
if(!group_id)
return GroupCreateResult::FAILED_TO_GENERATE_ID;
auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", this->server_id()}, variable{":gid", group_id}, variable{":target", DatabaseGroupTarget::CHANNEL}, variable{":type", type}, variable{":name", name}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCreateResult::DATABASE_ERROR;
}
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) DatabaseGroupTarget::CHANNEL);
auto group = std::make_shared<ChannelGroup>(this->server_id(), group_id, type, name, permissions);
{
std::lock_guard glock{this->group_lock_};
this->channel_groups_.push_back(group);
}
result = group;
return GroupCreateResult::SUCCESS;
}
GroupCopyResult GroupManager::copy_server_group(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr<ServerGroup>& result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
std::shared_ptr<ServerGroup> group{};
auto create_result = this->create_server_group(target_type, display_name, group);
switch(create_result) {
case GroupCreateResult::SUCCESS:
break;
case GroupCreateResult::DATABASE_ERROR:
return GroupCopyResult::DATABASE_ERROR;
case GroupCreateResult::NAME_ALREADY_IN_USED:
return GroupCopyResult::NAME_ALREADY_IN_USE;
case GroupCreateResult::FAILED_TO_GENERATE_ID:
return GroupCopyResult::FAILED_TO_GENERATE_ID;
}
assert(group);
return this->copy_server_group_permissions(source, group->group_id());
}
GroupCopyResult GroupManager::copy_channel_group(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr<ChannelGroup> &result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
std::shared_ptr<ChannelGroup> group{};
auto create_result = this->create_channel_group(target_type, display_name, group);
switch(create_result) {
case GroupCreateResult::SUCCESS:
break;
case GroupCreateResult::DATABASE_ERROR:
return GroupCopyResult::DATABASE_ERROR;
case GroupCreateResult::NAME_ALREADY_IN_USED:
return GroupCopyResult::NAME_ALREADY_IN_USE;
case GroupCreateResult::FAILED_TO_GENERATE_ID:
return GroupCopyResult::FAILED_TO_GENERATE_ID;
}
assert(group);
return this->copy_channel_group_permissions(source, group->group_id());
}
GroupCopyResult GroupManager::copy_server_group_permissions(GroupId source, GroupId target) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
auto source_group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, source);
if(!source_group)
return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
auto target_group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, target);
if(!target_group)
return GroupCopyResult::UNKNOWN_TARGET_GROUP;
auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
variable{":sid", this->server_id()}, variable{":type", ts::permission::SQL_PERM_GROUP}, variable{":id", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
}
res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":ssid", source_group->virtual_server_id()},
variable{":tsid", target_group->virtual_server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":source", source},
variable{":target", target}).execute();
target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) DatabaseGroupTarget::SERVER));
LOG_SQL_CMD(res);
return GroupCopyResult::SUCCESS;
}
GroupCopyResult GroupManager::copy_channel_group_permissions(GroupId source, GroupId target) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
auto source_group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, source);
if(!source_group)
return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
auto target_group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, target);
if(!target_group)
return GroupCopyResult::UNKNOWN_TARGET_GROUP;
auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
variable{":sid", this->server_id()}, variable{":type", ts::permission::SQL_PERM_GROUP}, variable{":id", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
}
res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":ssid", source_group->virtual_server_id()},
variable{":tsid", target_group->virtual_server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":source", source},
variable{":target", target}).execute();
target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) DatabaseGroupTarget::CHANNEL));
LOG_SQL_CMD(res);
return GroupCopyResult::SUCCESS;
}
GroupRenameResult GroupManager::rename_server_group(GroupId group_id, const std::string &name) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupRenameResult::DATABASE_ERROR;
if(name.empty() || name.length() > 40)
return GroupRenameResult::NAME_INVALID;
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) return GroupRenameResult::INVALID_GROUP_ID;
if(this->find_server_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupRenameResult::NAME_ALREADY_USED;
sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::SERVER},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
group->name_ = name;
return GroupRenameResult::SUCCESS;
}
GroupRenameResult GroupManager::rename_channel_group(GroupId group_id, const std::string &name) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupRenameResult::DATABASE_ERROR;
if(name.empty() || name.length() > 40)
return GroupRenameResult::NAME_INVALID;
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) return GroupRenameResult::INVALID_GROUP_ID;
if(this->find_channel_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupRenameResult::NAME_ALREADY_USED;
sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::CHANNEL},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
group->name_ = name;
return GroupRenameResult::SUCCESS;
}
GroupDeleteResult GroupManager::delete_server_group(GroupId group_id) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupDeleteResult::DATABASE_ERROR;
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.begin(), [&](const std::shared_ptr<ServerGroup>& group) {
return group->group_id() == group_id;
});
if(it == this->server_groups_.end())
return GroupDeleteResult::INVALID_GROUP_ID;
this->server_groups_.erase(it);
}
sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::SERVER},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
return GroupDeleteResult::SUCCESS;
}
GroupDeleteResult GroupManager::delete_channel_group(GroupId group_id) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupDeleteResult::DATABASE_ERROR;
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.begin(), [&](const std::shared_ptr<ChannelGroup>& group) {
return group->group_id() == group_id;
});
if(it == this->channel_groups_.end())
return GroupDeleteResult::INVALID_GROUP_ID;
this->channel_groups_.erase(it);
}
sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::CHANNEL},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
return GroupDeleteResult::SUCCESS;
}
ts::GroupId GroupManager::generate_server_group_id() {
std::lock_guard glock{this->group_lock_};
ts::GroupId highest_group_id{0};
for(auto& group : this->channel_groups_)
highest_group_id = std::max(group->group_id(), highest_group_id);
for(auto& group : this->server_groups_)
highest_group_id = std::max(group->group_id(), highest_group_id);
if(this->parent_manager_)
highest_group_id = std::max(this->parent_manager_->generate_server_group_id(), highest_group_id);
return highest_group_id + 1;
}
ts::GroupId GroupManager::generate_channel_group_id() {
return this->generate_server_group_id();
}

View File

@ -20,63 +20,114 @@ namespace ts::server {
GLOBAL /* use the parent group manager as well, if existing */
};
enum struct ActionReturnCode {
RENAME_NAME_ALREADY_USED,
RENAME_GROUP_INVALID_ID,
enum struct GroupLoadResult {
SUCCESS,
NO_GROUPS,
DATABASE_ERROR
};
DELETE_GROUP_INVALID_ID,
enum struct GroupCreateResult {
SUCCESS,
NAME_ALREADY_IN_USED,
FAILED_TO_GENERATE_ID,
DATABASE_ERROR
};
CUSTOM_ERROR
enum struct GroupCopyResult {
SUCCESS,
UNKNOWN_SOURCE_GROUP,
UNKNOWN_TARGET_GROUP,
NAME_ALREADY_IN_USE,
FAILED_TO_GENERATE_ID,
DATABASE_ERROR
};
enum struct GroupRenameResult {
SUCCESS,
INVALID_GROUP_ID,
NAME_ALREADY_USED,
NAME_INVALID,
DATABASE_ERROR
};
enum struct GroupDeleteResult {
SUCCESS,
INVALID_GROUP_ID,
DATABASE_ERROR
};
class GroupManager {
friend class Group;
public:
enum struct DatabaseGroupTarget {
SERVER,
CHANNEL
};
GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr<GroupManager> /* parent */);
~GroupManager();
bool initialize(std::string& /* error */);
bool load_data(std::string& /* error */);
bool initialize(const std::shared_ptr<GroupManager>& /* self ref, */, std::string& /* error */);
GroupLoadResult load_data(bool /* initialize */ = false);
void unload_data();
void reset_groups(bool /* cleanup database */);
[[nodiscard]] inline vserver::VirtualServerBase* virtual_server() { return this->virtual_server_; }
[[nodiscard]] inline const std::shared_ptr<GroupManager>& parent_manager() { return this->parent_manager_; }
[[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; }
[[nodiscard]] std::deque<std::shared_ptr<ServerGroup>> server_groups(GroupCalculateMode /* mode */);
[[nodiscard]] std::deque<std::shared_ptr<ChannelGroup>> channel_groups(GroupCalculateMode /* mode */);
[[nodiscard]] std::vector<std::shared_ptr<ServerGroup>> server_groups(GroupCalculateMode /* mode */);
[[nodiscard]] std::vector<std::shared_ptr<ChannelGroup>> channel_groups(GroupCalculateMode /* mode */);
[[nodiscard]] std::shared_ptr<ServerGroup> default_server_group(ClientType /* client type */);
[[nodiscard]] std::shared_ptr<ChannelGroup> default_channel_group(ClientType /* client type */);
[[nodiscard]] std::shared_ptr<ServerGroup> default_server_group(ClientType /* client type */, bool /* enforce id */ = false);
[[nodiscard]] std::shared_ptr<ChannelGroup> default_channel_group(ClientType /* client type */, bool /* enforce id */ = false);
[[nodiscard]] std::shared_ptr<ServerGroup> find_server_group(GroupId /* group id */);
[[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(GroupId /* group id */);
[[nodiscard]] std::shared_ptr<ServerGroup> find_server_group(GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<ServerGroup> create_server_group(GroupType type, const std::string& /* group name */);
[[nodiscard]] std::shared_ptr<ServerGroup> create_channel_group(GroupType type, const std::string& /* group name */);
[[nodiscard]] std::shared_ptr<ServerGroup> find_server_group_by_name(GroupCalculateMode /* mode */, const std::string& /* group name */);
[[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group_by_name(GroupCalculateMode /* mode */, const std::string& /* group name */);
[[nodiscard]] ActionReturnCode copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */);
[[nodiscard]] ActionReturnCode copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */);
[[nodiscard]] GroupCreateResult create_server_group(GroupType type, const std::string& /* group name */, std::shared_ptr<ServerGroup>& /* result */);
[[nodiscard]] GroupCreateResult create_channel_group(GroupType type, const std::string& /* group name */, std::shared_ptr<ChannelGroup>& /* result */);
[[nodiscard]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
[[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
[[nodiscard]] GroupCopyResult copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<ServerGroup>& /* result */);
[[nodiscard]] GroupCopyResult copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<ChannelGroup>& /* result */);
[[nodiscard]] ActionReturnCode rename_server_group(GroupId /* group id */, const std::string& /* target group name */);
[[nodiscard]] ActionReturnCode rename_channel_group(GroupId /* group id */, const std::string& /* target group name */);
[[nodiscard]] GroupCopyResult copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
[[nodiscard]] GroupCopyResult copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
[[nodiscard]] ActionReturnCode delete_server_group(GroupId /* group id */);
[[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */);
[[nodiscard]] GroupRenameResult rename_server_group(GroupId /* group id */, const std::string& /* target group name */);
[[nodiscard]] GroupRenameResult rename_channel_group(GroupId /* group id */, const std::string& /* target group name */);
[[nodiscard]] GroupDeleteResult delete_server_group(GroupId /* group id */);
[[nodiscard]] GroupDeleteResult delete_channel_group(GroupId /* group id */);
private:
vserver::VirtualServerBase* virtual_server_;
std::weak_ptr<GroupManager> weak_ref_;
std::shared_ptr<GroupManager> parent_manager_;
GroupAssignmentManager assignment_manager_;
/* recursive_mutex due to the copy server/channel group methods */
std::recursive_mutex group_manage_lock_{};
std::mutex group_lock_{};
/* I think std::vector is better here because we will iterate more often than add groups */
std::vector<std::shared_ptr<ServerGroup>> server_groups_{};
std::vector<std::shared_ptr<ChannelGroup>> channel_groups_{};
[[nodiscard]] sql::SqlManager* sql_manager();
void update_group_name(Group* /* group */);
[[nodiscard]] ServerId server_id();
void ensure_valid_default_groups(bool /* initialize */);
int insert_group_from_sql(int /* length */, std::string* /* values */, std::string* /* columns */);
[[nodiscard]] GroupId generate_server_group_id();
[[nodiscard]] GroupId generate_channel_group_id();
};
}
}
}
DEFINE_TRANSFORMS(ts::server::groups::GroupManager::DatabaseGroupTarget, uint8_t);

View File

@ -19,6 +19,7 @@ ts::ServerId ClientChannelService::get_server_id() const {
}
ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) {
auto server_state_lock = this->virtual_server_->lock_server_status();
auto tree_lock = this->virtual_server_->lock_channel_clients();
tree_lock.auto_lock_shared();
@ -67,6 +68,7 @@ ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr<Connecte
if(auto err = tree_lock.auto_lock_exclusive(); err)
return ClientMoveResult::TREE_LOCK_FAILED;
auto server_state_lock = this->virtual_server_->lock_server_status();
std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
auto old_channel = target->getChannel();
target->setChannel(nullptr);
@ -105,6 +107,7 @@ ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr<Connect
if(auto err = tree_lock.auto_lock_exclusive(); err)
return ClientMoveResult::TREE_LOCK_FAILED;
auto server_state_lock = this->virtual_server_->lock_server_status();
std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
auto old_channel = target->getChannel();
target->setChannel(nullptr);
@ -147,6 +150,8 @@ ClientMoveResult ClientChannelService::client_move(const std::shared_ptr<Connect
if(auto err = tree_lock.auto_lock_exclusive(); err)
return ClientMoveResult::TREE_LOCK_FAILED;
auto server_state_lock = this->virtual_server_->lock_server_status();
TIMING_STEP(timings, "tree lock setup");
if(target->getChannel() == s_target_channel)
return ClientMoveResult::SUCCESS;

View File

@ -6,9 +6,11 @@
#include "./PermissionsService.h"
#include "../InstanceHandler.h"
#include "../vserver/VirtualServerBase.h"
#include "../vserver/VirtualServer.h"
#include "../groups/Group.h"
#include "../groups/GroupManager.h"
#include "../groups/GroupAssignmentManager.h"
#include "../client/ConnectedClient.h"
using namespace ts::permission;
using namespace ts::permission::v2;
@ -274,7 +276,7 @@ void PermissionService::load_group_assignments(const std::shared_ptr<CalculateCa
cache->assignment_server_groups.reserve(assignments.size());
for(auto& entry : assignments) {
auto group = group_manager->find_server_group(entry);
auto group = group_manager->find_server_group(groups::GroupCalculateMode::GLOBAL, entry);
if(!group) continue;
cache->assignment_server_groups.push_back(group);
@ -304,7 +306,7 @@ void PermissionService::load_channel_assignment(const std::shared_ptr<CalculateC
group_id = assignment.group_id;
}
cache->assignment_channel_group = group_manager->find_channel_group(group_id);
cache->assignment_channel_group = group_manager->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!cache->assignment_channel_group)
cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type);
}
@ -316,4 +318,55 @@ void PermissionService::load_server_channel(const std::shared_ptr<CalculateCache
auto channel_tree = this->virtual_server_->server_channel_tree();
auto tree_lock = this->virtual_server_->lock_channel_clients();
cache->server_channel = channel_tree->findChannel(channel_id);
}
PermissionResetResult PermissionService::reset_server_permissions() {
//TODO: Delete all tokens?
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
auto group_manager = this->virtual_server_->group_manager();
group_manager->reset_groups(false);
auto properties = this->virtual_server_->server_properties();
properties->find(property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY) = "";
properties->find(property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY) = true;
if(auto vs = dynamic_cast<vserver::VirtualServer*>(this->virtual_server_); vs){
auto default_admin_group_id = serverInstance->properties().find(property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP).as_save<GroupId>();
auto group = this->virtual_server_->group_manager()->find_server_group(groups::GroupCalculateMode::GLOBAL, default_admin_group_id);
if(!group) {
logCritical(LOG_INSTANCE, "Missing default template server admin group ({}). We're not generating an admin token.", default_admin_group_id);
} else {
auto group_name = group->display_name();
group = this->virtual_server_->group_manager()->find_server_group_by_name(groups::GroupCalculateMode::LOCAL, group_name);
if(!group) {
logError(this->get_server_id(), "Could not find server admin group from template name ({}). We're not generating an admin token.", group_name, group);
} else {
auto token = vs->token_manager().createToken(server::tokens::TOKEN_SERVER, group->group_id(), "Default server token for the server admin.");
if(!token) {
logError(this->get_server_id(), "Failed to generate default server admin token.");
} else {
properties->find(property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY) = token->token;
properties->find(property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY) = true;
}
}
}
}
for(const auto& client : this->virtual_server_->connected_clients(false)) {
if(client->getType() != ClientType::CLIENT_QUERY) {
client->notifyServerGroupList();
client->notifyChannelGroupList();
}
if(this->notifyClientPropertyUpdates(client, group_manager->assignments().update_client_group_properties(client, client->getChannelId()))) {
if(client->update_cached_permissions()) /* update cached calculated permissions */
client->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
}
client->updateChannelClientProperties(true, true);
}
return PermissionResetResult::SUCCESS;
}

View File

@ -29,6 +29,10 @@ namespace ts::server::permissions {
ChannelId last_server_channel{0};
};
enum struct PermissionResetResult {
SUCCESS
};
class PermissionService {
public:
explicit PermissionService(vserver::VirtualServerBase*);
@ -39,6 +43,8 @@ namespace ts::server::permissions {
bool load_data(std::string& /* error */);
void unload_data();
PermissionResetResult reset_server_permissions();
permission::v2::PermissionFlaggedValue calculate_client_permission(
permission::PermissionType,
ClientDbId,

View File

@ -0,0 +1,15 @@
//
// Created by WolverinDEV on 04/03/2020.
//
#include "./VirtualServerInformation.h"
#include "../vserver/VirtualServerBase.h"
using namespace ts::server::vserver;
InformationService::InformationService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {}
InformationService::~InformationService() {}
float InformationService::averagePing() {
}

View File

@ -0,0 +1,18 @@
#pragma once
namespace ts::server::vserver {
class VirtualServerBase;
class InformationService {
public:
explicit InformationService(VirtualServerBase*);
~InformationService();
[[nodiscard]] float averagePing();
[[nodiscard]] float averagePacketLoss();
[[nodiscard]] bool could_default_create_channel();
private:
VirtualServerBase* virtual_server_;
};
}

View File

@ -3,6 +3,9 @@
//
#include "./VirtualServerBase.h"
#include "./VirtualServerBase.h"
#include "../client/ConnectedClient.h"
#include "VirtualServerBase.h"
#include <log/LogUtils.h>
using namespace ts::server::vserver;
@ -75,4 +78,137 @@ VirtualServerStopResult VirtualServerBase::stop_server() {
VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock<ts::rw_mutex> &slock, std::string &error) {
//TODO: Load server permissions, etc
}
VirtualServerClientRegisterResult VirtualServerBase::register_client(const std::shared_ptr<ConnectedClient> &client) {
assert(client);
ts::rwshared_lock status_lock{this->status_mutex_};
if(this->server_status() != VirtualServerStatus::VIRTUAL && this->server_status() != VirtualServerStatus::RUNNING)
return VirtualServerClientRegisterResult::SERVER_NOT_ONLINE;
{
std::lock_guard client_lock{this->registered_clients.mutex};
if(client->getClientId() > 0) {
logCritical(this->server_id(), "Client {} ({}|{}) has been already registered somewhere!", client->getDisplayName(), client->getClientId(), client->getUid());
return VirtualServerClientRegisterResult::CLIENT_ALREADY_KNOWN;
}
ClientId client_id = 0;
ClientId max_client_id = this->registered_clients.clients.size();
while(client_id < max_client_id && this->registered_clients.clients[client_id])
client_id++;
if(client_id > 1024 * 8)
return VirtualServerClientRegisterResult::PROTOCOL_LIMIT_REACHED;
if(client_id == max_client_id)
this->registered_clients.clients.push_back(client);
else
this->registered_clients.clients[client_id] = client;
this->registered_clients.count++;
client->setClientId(client_id);
}
{
std::lock_guard nlock{this->client_nickname_lock_};
auto login_name = client->getDisplayName();
while(login_name.length() < 3)
login_name += ".";
if(client->getExternalType() == ClientType::CLIENT_TEAMSPEAK)
client->properties()[property::CLIENT_LOGIN_NAME] = login_name;
std::shared_ptr<ConnectedClient> found_client = nullptr;
auto client_name = login_name;
size_t counter = 0;
{
std::lock_guard clients_lock(this->registered_clients.mutex);
while(true) {
for(auto& _client : this->registered_clients.clients) {
if(!_client) continue;
if(_client->getDisplayName() == client_name && _client != client)
goto increase_name;
}
goto nickname_valid;
increase_name:
client_name = login_name + std::to_string(++counter);
}
}
nickname_valid:
client->setDisplayName(client_name);
}
{
auto properties = this->server_properties();
if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) {
properties->find(property::VIRTUALSERVER_CLIENT_CONNECTIONS) ++; //increase client connections
properties->find(property::VIRTUALSERVER_LAST_CLIENT_CONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
} else if(client->getType() == ClientType::CLIENT_QUERY) {
properties->find(property::VIRTUALSERVER_LAST_QUERY_CONNECT) ++; //increase query connections
properties->find(property::VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
}
return VirtualServerClientRegisterResult::SUCCESS;
}
void VirtualServerBase::unregister_client(const std::shared_ptr<ConnectedClient> &client) {
ts::rwshared_lock status_lock{this->status_mutex_, ts::rw_lock_defered};
{
auto execute_lock = client->lock_command_handling();
if(client->getChannel()) {
auto tree_lock = this->lock_channel_clients();
auto result = this->channel_service().client_move(client, nullptr, nullptr, "", ViewReasonId::VREASON_SERVER_LEFT, false, tree_lock);
if(result != channels::ClientMoveResult::SUCCESS)
logCritical(this->server_id(), "Failed to unregister client from channel tree on client unregister ({}).", (int) result);
}
status_lock.auto_lock_shared();
auto client_id = client->getClientId();
if(client_id == 0) return; /* client hasn't been registered */
{
std::lock_guard lock(this->registered_clients.mutex);
if(client_id >= this->registered_clients.clients.size()) {
logCritical(this->server_id(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister client.", client->getDisplayName(), client_id, client->getUid());
} else {
auto& client_container = this->registered_clients.clients[client_id];
if(client_container != client) {
logCritical(this->server_id(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister client.", client->getDisplayName(), client_id, client->getUid());
} else {
client_container.reset();
this->registered_clients.count--;
}
}
}
}
{
auto properties = this->server_properties();
if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) {
properties->find(property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
} else if(client->getType() == ClientType::CLIENT_QUERY) {
properties->find(property::VIRTUALSERVER_LAST_QUERY_DISCONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
}
{
if(!chan_tree_lock.owns_lock())
chan_tree_lock.lock();
if(cl->currentChannel) //We dont have to make him invisible if he hasnt even a channel
this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock);
}
serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions);
cl->setClientId(0);
return true;
}

View File

@ -57,6 +57,19 @@ namespace ts::server::vserver {
STATE_LOCK_FAILED
};
enum struct VirtualServerClientRegisterResult {
SUCCESS,
PROTOCOL_LIMIT_REACHED,
CLIENT_ALREADY_KNOWN,
SERVER_NOT_ONLINE
};
#define var_access_requires_server_loaded(variable) \
do { \
assert(this->status_ != VirtualServerStatus::STOPPED); \
return variable; \
} while(0);
class VirtualServerBase {
public:
//TODO: Constructor
@ -85,15 +98,15 @@ namespace ts::server::vserver {
[[nodiscard]] inline ts::rwshared_lock<ts::rw_mutex> lock_channel_clients() { return ts::rwshared_lock{this->channel_clients_lock_, ts::rw_lock_shared}; }
[[nodiscard]] inline std::mutex& client_nickname_lock() { return this->client_nickname_lock_; }
[[nodiscard]] inline const std::shared_ptr<Properties>& server_properties() { return this->server_properties_; }
[[nodiscard]] inline const std::shared_ptr<ServerChannelTree>& server_channel_tree() { return this->server_channel_tree_; }
[[nodiscard]] inline const std::shared_ptr<Properties>& server_properties() { var_access_requires_server_loaded(this->server_properties_); }
[[nodiscard]] inline const std::shared_ptr<ServerChannelTree>& server_channel_tree() { var_access_requires_server_loaded(this->server_channel_tree_); }
[[nodiscard]] inline const std::shared_ptr<groups::GroupManager>& group_manager() { return this->group_manager_; }
[[nodiscard]] inline const std::shared_ptr<groups::GroupManager>& group_manager() { var_access_requires_server_loaded(this->group_manager_); }
/* client management */
/* this may block until the current command of the client has been finished executed */
void unregister_client(const std::shared_ptr<ConnectedClient>& /* client */);
void register_client(const std::shared_ptr<ConnectedClient>& /* client */);
VirtualServerClientRegisterResult register_client(const std::shared_ptr<ConnectedClient>& /* client */);
/* ATTENTION: lock_channel_clients should be acquired for the following methods. Else the client might be disconnected while working with him. */
[[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> connected_clients(bool /* must be registered within the channel tree */);

View File

@ -0,0 +1,5 @@
//
// Created by WolverinDEV on 04/03/2020.
//
#include "VirtualServerManager.h"

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
namespace ts::server::vserver {
enum struct VirtualServerStartResult {
};
class VirtualServerManager {
public:
explicit VirtualServerManager();
~VirtualServerManager();
bool initialize(std::string& /* error */);
private:
};
}

2
shared

@ -1 +1 @@
Subproject commit ae4751ee6fcb857771ff5f4e57459a75638e2b1f
Subproject commit 0d0d7dd1924ee93bcbe4875c3cf007117d05e4d7