Some more changes
This commit is contained in:
parent
3a5c152b3c
commit
6172628247
@ -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)
|
||||
|
@ -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!
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */);
|
||||
|
@ -29,7 +29,9 @@ namespace ts {
|
||||
|
||||
enum GroupTarget {
|
||||
GROUPTARGET_SERVER,
|
||||
GROUPTARGET_CHANNEL
|
||||
GROUPTARGET_CHANNEL,
|
||||
|
||||
GROUPTARGET_UNKNOWN
|
||||
};
|
||||
|
||||
enum GroupNameMode {
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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 */);
|
||||
};
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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,
|
||||
|
15
server/src/services/VirtualServerInformation.cpp
Normal file
15
server/src/services/VirtualServerInformation.cpp
Normal 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() {
|
||||
|
||||
}
|
18
server/src/services/VirtualServerInformation.h
Normal file
18
server/src/services/VirtualServerInformation.h
Normal 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_;
|
||||
};
|
||||
}
|
@ -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;
|
||||
}
|
@ -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 */);
|
||||
|
5
server/src/vserver/VirtualServerManager.cpp
Normal file
5
server/src/vserver/VirtualServerManager.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by WolverinDEV on 04/03/2020.
|
||||
//
|
||||
|
||||
#include "VirtualServerManager.h"
|
21
server/src/vserver/VirtualServerManager.h
Normal file
21
server/src/vserver/VirtualServerManager.h
Normal 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
2
shared
@ -1 +1 @@
|
||||
Subproject commit ae4751ee6fcb857771ff5f4e57459a75638e2b1f
|
||||
Subproject commit 0d0d7dd1924ee93bcbe4875c3cf007117d05e4d7
|
Loading…
x
Reference in New Issue
Block a user