319 lines
15 KiB
C++
319 lines
15 KiB
C++
//
|
|
// Created by WolverinDEV on 03/03/2020.
|
|
//
|
|
|
|
#include <log/LogUtils.h>
|
|
#include "./PermissionsService.h"
|
|
#include "../InstanceHandler.h"
|
|
#include "../vserver/VirtualServerBase.h"
|
|
#include "../groups/Group.h"
|
|
#include "../groups/GroupManager.h"
|
|
#include "../groups/GroupAssignmentManager.h"
|
|
|
|
using namespace ts::permission;
|
|
using namespace ts::permission::v2;
|
|
using namespace ts::server::permissions;
|
|
|
|
PermissionService::PermissionService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {}
|
|
|
|
bool PermissionService::initialize(std::string &) { return true; }
|
|
|
|
bool PermissionService::load_data(std::string &) { return true; }
|
|
void PermissionService::unload_data() {}
|
|
|
|
ts::ServerId PermissionService::get_server_id() const {
|
|
return this->virtual_server_->server_id();
|
|
}
|
|
|
|
PermissionFlaggedValue PermissionService::calculate_client_permission(
|
|
PermissionType permission,
|
|
ClientDbId cldbid,
|
|
ClientType type,
|
|
ChannelId channel,
|
|
bool granted,
|
|
std::shared_ptr<CalculateCache> cache) {
|
|
auto result = this->calculate_client_permissions({permission}, cldbid, type, channel, granted, std::move(cache));
|
|
if(result.empty()) return {0, false};
|
|
|
|
return result.front().second;
|
|
}
|
|
|
|
|
|
std::vector<std::pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> PermissionService::calculate_client_permissions(
|
|
const std::deque<permission::PermissionType>& permissions,
|
|
ClientDbId client_dbid,
|
|
ClientType client_type,
|
|
ChannelId channel_id,
|
|
bool calculate_granted,
|
|
std::shared_ptr<CalculateCache> cache) {
|
|
if(permissions.empty()) return {};
|
|
|
|
std::vector<std::pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> result{};
|
|
result.reserve(permissions.size());
|
|
|
|
if(cache->client_database_id && cache->client_database_id != client_dbid)
|
|
cache = nullptr;
|
|
|
|
if(!cache)
|
|
cache = std::make_shared<CalculateCache>();
|
|
cache->client_type = client_type;
|
|
cache->client_database_id = client_dbid;
|
|
|
|
if(!cache->client_permissions)
|
|
cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_->server_ref(), client_dbid);
|
|
|
|
bool have_skip_permission = false;
|
|
int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */
|
|
|
|
bool have_skip;
|
|
|
|
/*
|
|
* server_group_data[0] := Server group id
|
|
* server_group_data[1] := Skip flag
|
|
* server_group_data[2] := Negate flag
|
|
* server_group_data[3] := Permission value
|
|
*/
|
|
typedef std::tuple<GroupId, bool, bool, permission::PermissionValue> GroupData;
|
|
bool server_group_data_initialized = false;
|
|
std::vector<GroupData> server_group_data;
|
|
GroupData* active_server_group;
|
|
|
|
/* function to calculate skip permission */
|
|
auto calculate_skip = [&]{
|
|
skip_permission_type = 0;
|
|
/* test for skip permission within the client permission manager */
|
|
{
|
|
auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
|
|
if(skip_value.has_value) {
|
|
have_skip_permission = skip_value.value == 1;
|
|
skip_permission_type = 1;
|
|
logTrace(this->get_server_id(), "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission);
|
|
}
|
|
}
|
|
/* test for skip permission within all server groups */
|
|
if(skip_permission_type != 1) {
|
|
this->load_group_assignments(cache);
|
|
assert(cache->assignment_server_groups_set);
|
|
for(const auto& assignment : cache->assignment_server_groups) {
|
|
auto group_permissions = assignment->permissions();
|
|
auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
|
|
if(flagged_value.has_value) {
|
|
have_skip_permission |= flagged_value.value == 1;
|
|
if(have_skip_permission) {
|
|
logTrace(this->get_server_id(), "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group_id(), assignment->display_name(), have_skip_permission);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
auto initialize_group_data = [&](const permission::PermissionType& permission_type) {
|
|
server_group_data_initialized = true;
|
|
active_server_group = nullptr;
|
|
|
|
this->load_group_assignments(cache);
|
|
assert(cache->assignment_server_groups_set);
|
|
|
|
server_group_data.resize(cache->assignment_server_groups.size());
|
|
auto it = server_group_data.begin();
|
|
for(auto& group : cache->assignment_server_groups) {
|
|
auto group_permissions = group->permissions();
|
|
auto permission_flags = group_permissions->permission_flags(permission_type);
|
|
|
|
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
|
|
if(!flag_set)
|
|
continue;
|
|
|
|
//TODO: Test if there is may a group channel permissions
|
|
auto value = group_permissions->permission_values(permission_type);
|
|
*it = std::make_tuple(group->group_id(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value);
|
|
it++;
|
|
}
|
|
if(it == server_group_data.begin())
|
|
return; /* no server group has that permission */
|
|
|
|
server_group_data.erase(it, server_group_data.end()); /* remove unneeded */
|
|
|
|
auto found_negate = false;
|
|
for(auto& group : server_group_data) {
|
|
if(std::get<2>(group)) {
|
|
found_negate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(found_negate) {
|
|
server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end());
|
|
logTrace(this->get_server_id(), "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size());
|
|
if(server_group_data.empty())
|
|
logTrace(this->get_server_id(), "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid);
|
|
permission::PermissionValue current_lowest = 0;
|
|
for(auto& group : server_group_data) {
|
|
if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) {
|
|
current_lowest = std::get<3>(group);
|
|
active_server_group = &group;
|
|
}
|
|
}
|
|
} else {
|
|
permission::PermissionValue current_highest = 0;
|
|
for(auto& group : server_group_data) {
|
|
if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) {
|
|
current_highest = std::get<3>(group);
|
|
active_server_group = &group;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
for(const auto& permission : permissions) {
|
|
if(permission == permission::b_client_skip_channelgroup_permissions) {
|
|
if(skip_permission_type == -1) /* initialize skip flag */
|
|
calculate_skip();
|
|
result.push_back({permission, {have_skip_permission, skip_permission_type == 1}});
|
|
continue;
|
|
}
|
|
|
|
server_group_data_initialized = false; /* reset all group data */
|
|
auto client_permission_flags = cache->client_permissions->permission_flags(permission);
|
|
/* lets try to resolve the channel specific permission */
|
|
if(channel_id > 0 && client_permission_flags.channel_specific) {
|
|
auto data = cache->client_permissions->channel_permission(permission, channel_id);
|
|
if(calculate_granted ? data.flags.grant_set : data.flags.value_set) {
|
|
result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}});
|
|
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
have_skip = channel_id == 0;
|
|
if(!have_skip) {
|
|
/* look if somewhere is the skip permission flag set */
|
|
if(skip_permission_type == -1) /* initialize skip flag */
|
|
calculate_skip();
|
|
have_skip = have_skip_permission;
|
|
}
|
|
if(!have_skip) {
|
|
/* okey we've no global skip. Then now lookup the groups and the client permissions */
|
|
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
|
|
/* okey the client has the permission, this counts */
|
|
have_skip = client_permission_flags.skip;
|
|
} else {
|
|
if(!server_group_data_initialized)
|
|
initialize_group_data(permission);
|
|
if(active_server_group)
|
|
have_skip = std::get<1>(*active_server_group);
|
|
}
|
|
}
|
|
|
|
if(!have_skip) {
|
|
/* lookup the channel group */
|
|
{
|
|
this->load_channel_assignment(cache, channel_id);
|
|
assert(cache->assignment_channel_group_channel == channel_id);
|
|
|
|
auto channel_assignment = cache->assignment_channel_group;
|
|
if(channel_assignment) {
|
|
auto group_permissions = channel_assignment->permissions();
|
|
auto permission_flags = group_permissions->permission_flags(permission);
|
|
|
|
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
|
|
if(flag_set) {
|
|
auto value = group_permissions->permission_values(permission);
|
|
result.push_back({permission, {calculate_granted ? value.grant : value.value, true}});
|
|
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* lookup the channel permissions. Whyever? */
|
|
{
|
|
this->load_server_channel(cache, channel_id);
|
|
if(cache->server_channel) {
|
|
auto channel_permissions = cache->server_channel->permissions();
|
|
auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission);
|
|
if(data.has_value) {
|
|
result.push_back({permission, {data.value, true}});
|
|
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
|
|
auto client_value = cache->client_permissions->permission_values(permission);
|
|
result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}});
|
|
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value);
|
|
continue;
|
|
}
|
|
|
|
if(!server_group_data_initialized)
|
|
initialize_group_data(permission);
|
|
if(active_server_group) {
|
|
result.push_back({permission, {get<3>(*active_server_group), true}});
|
|
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group));
|
|
continue;
|
|
}
|
|
|
|
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name);
|
|
result.push_back({permission, {permNotGranted, false}});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void PermissionService::load_group_assignments(const std::shared_ptr<CalculateCache> &cache) {
|
|
if(cache->assignment_server_groups_set) return;
|
|
cache->assignment_server_groups_set = true;
|
|
|
|
auto group_manager = this->virtual_server_->group_manager();
|
|
auto assignments = group_manager->assignments().server_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id);
|
|
cache->assignment_server_groups.reserve(assignments.size());
|
|
|
|
for(auto& entry : assignments) {
|
|
auto group = group_manager->find_server_group(entry);
|
|
if(!group) continue;
|
|
|
|
cache->assignment_server_groups.push_back(group);
|
|
}
|
|
|
|
if(cache->assignment_server_groups.empty())
|
|
cache->assignment_server_groups.push_back(group_manager->default_server_group(cache->client_type));
|
|
}
|
|
|
|
void PermissionService::load_channel_assignment(const std::shared_ptr<CalculateCache> &cache, ts::ChannelId channel_id) {
|
|
if(cache->assignment_channel_group_channel == channel_id) return;
|
|
cache->assignment_channel_group_channel = channel_id;
|
|
|
|
auto group_manager = this->virtual_server_->group_manager();
|
|
|
|
auto channel_groups = group_manager->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id);
|
|
auto it = std::find_if(channel_groups.begin(), channel_groups.end(), [&](const groups::ChannelGroupAssignment& assignment) {
|
|
return assignment.channel_id == channel_id;
|
|
});
|
|
|
|
GroupId group_id{0};
|
|
if(it != channel_groups.end())
|
|
group_id = it->group_id;
|
|
|
|
if(!group_id) {
|
|
auto assignment = this->virtual_server_->channel_service().calculate_channel_group(channel_id, cache->client_database_id, cache->client_type);
|
|
group_id = assignment.group_id;
|
|
}
|
|
|
|
cache->assignment_channel_group = group_manager->find_channel_group(group_id);
|
|
if(!cache->assignment_channel_group)
|
|
cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type);
|
|
}
|
|
|
|
void PermissionService::load_server_channel(const std::shared_ptr<CalculateCache> &cache, ts::ChannelId channel_id) {
|
|
if(cache->last_server_channel == channel_id) return;
|
|
cache->last_server_channel = channel_id;
|
|
|
|
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);
|
|
} |