A huge permission improvement

This commit is contained in:
WolverinDEV 2019-07-17 18:24:43 +02:00
parent 49220489ca
commit 6ee207aaad
8 changed files with 781 additions and 47 deletions

View File

@ -241,5 +241,8 @@ if(BUILD_TESTS)
add_executable(LinkedTest test/LinkedTest.cpp ${SOURCE_FILES})
target_link_libraries(LinkedTest ${TEST_LIBRARIES})
add_executable(PermissionTest test/PermissionTest.cpp ${SOURCE_FILES})
target_link_libraries(PermissionTest ${TEST_LIBRARIES})
endif()
endif()

View File

@ -21,34 +21,9 @@ BasicChannel::BasicChannel(ChannelId parentId, ChannelId channelId) {
this->properties()[property::CHANNEL_PID] = parentId;
}
void BasicChannel::setPermissionManager(const std::shared_ptr<ts::permission::PermissionManager>& manager) {
auto handler = [&](std::shared_ptr<permission::Permission> perm){
if(perm->type->type == permission::i_icon_id)
this->properties()[property::CHANNEL_ICON_ID] = (IconId) (perm->hasValue() ? perm->value : 0);
else if(perm->type->type == permission::i_client_needed_talk_power) {
this->properties()[property::CHANNEL_NEEDED_TALK_POWER] = (perm->hasValue() ? perm->value : 0);
}
};
manager->registerUpdateHandler(handler);
//Update channel properties
for(const auto& perm : manager->getPermission(permission::i_icon_id, nullptr)) handler(perm);
for(const auto& perm : manager->getPermission(permission::i_client_needed_talk_power, nullptr)) handler(perm);
void BasicChannel::setPermissionManager(const std::shared_ptr<permission::v2::PermissionManager>& manager) {
this->_permissions = manager;
/*
this->_permissions->registerPermission(permission::i_channel_needed_permission_modify_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_channel_needed_join_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_channel_needed_subscribe_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_channel_needed_description_view_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_channel_needed_modify_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_channel_needed_delete_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_ft_needed_file_browse_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_ft_needed_file_upload_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_ft_needed_file_download_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_ft_needed_file_rename_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_ft_needed_directory_create_power, 0, nullptr, PERM_FLAG_PUBLIC);
this->_permissions->registerPermission(permission::i_icon_id, 0, nullptr, PERM_FLAG_PUBLIC);
*/
this->update_properties_from_permissions();
}
void BasicChannel::setProperties(const std::shared_ptr<ts::Properties>& props) {
@ -73,6 +48,37 @@ void BasicChannel::setProperties(const std::shared_ptr<ts::Properties>& props) {
this->_channel_id = this->channelId();
}
std::vector<property::ChannelProperties> BasicChannel::update_properties_from_permissions() {
std::vector<property::ChannelProperties> result;
result.reserve(2);
auto permission_manager = this->permissions(); /* keeps the manager until we've finished our calculations */
/* update the icon id */
{
IconId target_icon_id = 0;
auto& permission_icon_flags = permission_manager->permission_flags(permission::i_icon_id);
if(permission_icon_flags.value_set)
target_icon_id = (IconId) permission_manager->permission_values(permission::i_icon_id).value;
if(this->properties()[property::CHANNEL_ICON_ID] != target_icon_id) {
this->properties()[property::CHANNEL_ICON_ID] = target_icon_id;
result.push_back(property::CHANNEL_ICON_ID);
}
}
/* update the channel talk power */
{
permission::PermissionValue talk_power = 0;
auto& permission_tp_flags = permission_manager->permission_flags(permission::i_client_needed_talk_power);
if(permission_tp_flags.value_set)
talk_power = (IconId) permission_manager->permission_values(permission::i_client_needed_talk_power).value;
if(this->properties()[property::CHANNEL_NEEDED_TALK_POWER] != talk_power) {
this->properties()[property::CHANNEL_NEEDED_TALK_POWER] = talk_power;
result.push_back(property::CHANNEL_NEEDED_TALK_POWER);
}
}
return result;
}
std::shared_ptr<BasicChannel> BasicChannel::parent() {
auto link_lock = this->_link.lock();
if(!link_lock)

View File

@ -58,8 +58,32 @@ namespace ts {
return std::chrono::system_clock::time_point() + std::chrono::milliseconds(this->properties()[property::CHANNEL_CREATED_AT].as<int64_t>());
}
inline std::shared_ptr<permission::PermissionManager> permissions(){ return this->_permissions; }
virtual void setPermissionManager(const std::shared_ptr<permission::PermissionManager>&);
ts_always_inline bool permission_require_property_update(const permission::PermissionType& permission) {
return permission == permission::i_icon_id || permission == permission::i_client_needed_talk_power;
}
std::vector<property::ChannelProperties> update_properties_from_permissions();
inline bool permission_granted(const permission::PermissionType& permission, const permission::v2::PermissionFlaggedValue& granted_value, bool require_granted_value) {
auto permission_manager = this->permissions(); /* copy the manager */
assert(permission_manager);
const auto data = permission_manager->permission_value_flagged(permission);
if(!data.has_value) {
return !require_granted_value || granted_value.has_value;
}
if(!granted_value.has_value) {
return false;
}
if(data.value == -1) {
return granted_value.value == -1;
}
return granted_value.value >= data.value;
}
ts_always_inline bool talk_power_granted(const permission::v2::PermissionFlaggedValue& granted_value) {
return this->permission_granted(permission::i_client_needed_talk_power, granted_value, false);
}
ts_always_inline std::shared_ptr<permission::v2::PermissionManager> permissions(){ return this->_permissions; }
virtual void setPermissionManager(const std::shared_ptr<permission::v2::PermissionManager>&);
virtual void setProperties(const std::shared_ptr<Properties>&);
protected:
public:
@ -71,7 +95,7 @@ namespace ts {
protected:
std::weak_ptr<TreeView::LinkedTreeEntry> _link;
std::shared_ptr<Properties> _properties;
std::shared_ptr<permission::PermissionManager> _permissions;
std::shared_ptr<permission::v2::PermissionManager> _permissions;
ChannelId _channel_order = 0;
ChannelId _channel_id = 0;

View File

@ -152,4 +152,6 @@ DEFINE_TRANSFORMS(ts::server::ClientType, uint8_t);
DEFINE_TRANSFORMS(ts::LicenseType, uint8_t);
DEFINE_TRANSFORMS(ts::PluginTargetMode, uint8_t);
DEFINE_TRANSFORMS(ts::ViewReasonId, uint8_t);
DEFINE_TRANSFORMS(ts::ChatMessageMode, uint8_t);
DEFINE_TRANSFORMS(ts::ChatMessageMode, uint8_t);
#define ts_always_inline inline __attribute__((always_inline))

View File

@ -10,7 +10,6 @@ using namespace ts::permission;
deque<std::shared_ptr<PermissionTypeEntry>> ts::permission::availablePermissions = deque<std::shared_ptr<PermissionTypeEntry>>{
make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_help_view, PermissionGroup::global_info, "b_serverinstance_help_view", "Retrieve information about ServerQuery commands"),
//make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_help_view_teespeack, PermissionGroup::group_7, "b_serverinstance_help_view_teespeack", "Costume teespeck help command idk ist just a test!"),
make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_version_view, PermissionGroup::global_info, "b_serverinstance_version_view", "Retrieve global server version (including platform and build number)"),
make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_info_view, PermissionGroup::global_info, "b_serverinstance_info_view", "Retrieve global server information"),
make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_virtualserver_list, PermissionGroup::global_info, "b_serverinstance_virtualserver_list", "List virtual servers stored in the sql"),
@ -18,7 +17,6 @@ deque<std::shared_ptr<PermissionTypeEntry>> ts::permission::availablePermissions
//Removed due its useless
make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_permission_list, PermissionGroup::global_info, "b_serverinstance_permission_list", "List permissions available available on the server instance"),
make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_permission_find, PermissionGroup::global_info, "b_serverinstance_permission_find", "Search permission assignments by name or ID"),
//make_shared<PermissionTypeEntry>(PermissionType::b_serverinstance_allow_teaspeak_plugin, PermissionGroup::group_7, "b_serverinstance_allow_teespeak_plugin", "Allow a user to use the TeaSpeak plugin extension"),
make_shared<PermissionTypeEntry>(PermissionType::b_virtualserver_create, PermissionGroup::global_vsmanage, "b_virtualserver_create", "Create virtual servers"),
make_shared<PermissionTypeEntry>(PermissionType::b_virtualserver_delete, PermissionGroup::global_vsmanage, "b_virtualserver_delete", "Delete virtual servers"),
make_shared<PermissionTypeEntry>(PermissionType::b_virtualserver_start_any, PermissionGroup::global_vsmanage, "b_virtualserver_start_any", "Start any virtual server in the server instance"),
@ -620,20 +618,39 @@ std::deque<PermissionGroup> permission::availableGroups = {
std::shared_ptr<PermissionTypeEntry> PermissionTypeEntry::unknown = make_shared<PermissionTypeEntry>(PermissionType::unknown, PermissionGroup::global, "unknown", "unknown");
std::shared_ptr<PermissionTypeEntry> permission::resolvePermissionData(PermissionType type){
if((type & PERM_ID_GRANT) > 0) type &= ~PERM_ID_GRANT;
for(auto& elm : availablePermissions)
if(elm->type == type) return elm;
return PermissionTypeEntry::unknown;
vector<std::shared_ptr<PermissionTypeEntry>> permission_id_map;
void permission::setup_permission_resolve() {
permission_id_map.resize(permission::permission_id_max);
for(auto& permission : availablePermissions) {
if(!permission->clientSupported || permission->type < 0 || permission->type > permission::permission_id_max)
continue;
permission_id_map[permission->type] = permission;
}
/* fix "holes" as well set the permission id 0 (unknown) */
for(auto& permission : permission_id_map) {
if(permission)
continue;
permission = PermissionTypeEntry::unknown;
}
}
std::shared_ptr<PermissionTypeEntry> permission::resolvePermissionData(PermissionType type) {
if((type & PERM_ID_GRANT) > 0)
type &= ~PERM_ID_GRANT;
assert(!permission_id_map.empty());
if(type < 0 || type >= permission::permission_id_max)
return PermissionTypeEntry::unknown;
return permission_id_map[type];
}
std::shared_ptr<PermissionTypeEntry> permission::resolvePermissionData(const std::string& name) {
for(auto& elm : availablePermissions) {
if(elm->name == name || elm->grant_name == name) {
for(auto& elm : availablePermissions)
if(elm->name == name || elm->grant_name == name)
return elm;
}
}
debugMessage(LOG_GENERAL, "Could not find permission {}.", name);
return PermissionTypeEntry::unknown;
}
@ -1007,4 +1024,445 @@ deque<update::UpdateEntry> update::migrate = {
AQB("b_channel_ignore_join_power")
AQB("b_virtualserver_select_godmode")
AQB("b_client_ban_trigger_list")
};
};
v2::PermissionManager::PermissionManager() {
memset(this->block_use_count, 0, sizeof(this->block_use_count));
memset(this->block_containers, 0, sizeof(this->block_containers));
}
v2::PermissionManager::~PermissionManager() {
for(auto& block : this->block_containers)
delete block;
}
void v2::PermissionManager::load_permission(const ts::permission::PermissionType &permission, const ts::permission::v2::PermissionValues &values, bool flag_skip, bool flag_negate, bool flag_value, bool flag_grant) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return;
const auto block = this->calculate_block(permission);
this->ref_allocate_block(block);
auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)];
data.values = values;
data.flags.database_reference = true;
data.flags.skip = flag_skip;
data.flags.negate = flag_negate;
data.flags.value_set = flag_value;
data.flags.grant_set = flag_grant;
this->unref_block(block);
}
void v2::PermissionManager::load_permission(const ts::permission::PermissionType &permission, const ts::permission::v2::PermissionValues &values, ChannelId channel_id, bool flag_skip, bool flag_negate, bool flag_value, bool flag_grant) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return;
unique_lock channel_perm_lock(this->channel_list_lock);
ChannelPermissionContainer* permission_container = nullptr;
for(auto& entry : this->_channel_permissions)
if(entry->permission == permission && entry->channel_id == channel_id) {
permission_container = &*entry;
break;
}
if(!permission_container) {
auto container = make_unique<ChannelPermissionContainer>();
container->permission = permission;
container->channel_id = channel_id;
permission_container = &*container;
this->_channel_permissions.push_back(std::move(container));
/* now set the channel flag for that permission */
const auto block = this->calculate_block(permission);
this->ref_allocate_block(block);
auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)];
data.flags.channel_specific = true;
this->unref_block(block);
}
permission_container->values = values;
permission_container->flags.database_reference = true;
permission_container->flags.skip = flag_skip;
permission_container->flags.negate = flag_negate;
permission_container->flags.value_set = flag_value;
permission_container->flags.grant_set = flag_grant;
}
static constexpr v2::PermissionFlags empty_flags = {false, false, false, false, false, false, 0};
const v2::PermissionFlags v2::PermissionManager::permission_flags(const ts::permission::PermissionType &permission) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return empty_flags;
const auto block = this->calculate_block(permission);
if(!this->ref_block(block))
return empty_flags;
PermissionFlags result{this->block_containers[block]->permissions[this->calculate_block_index(permission)].flags};
this->unref_block(block);
return result;
}
const v2::PermissionValues v2::PermissionManager::permission_values(const ts::permission::PermissionType &permission) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return v2::empty_permission_values;
const auto block = this->calculate_block(permission);
if(!this->ref_block(block))
return v2::empty_permission_values; /* TODO: may consider to throw an exception because the existence should be checked by getting the permission flags */
v2::PermissionValues data{this->block_containers[block]->permissions[this->calculate_block_index(permission)].values};
this->unref_block(block);
return data;
}
const v2::PermissionFlaggedValue v2::PermissionManager::permission_value_flagged(const ts::permission::PermissionType &permission) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return v2::empty_permission_flagged_value;
const auto block = this->calculate_block(permission);
if(!this->ref_block(block))
return v2::empty_permission_flagged_value;
auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)];
v2::PermissionFlaggedValue result{data.values.value, data.flags.value_set};
this->unref_block(block);
return result;
}
const v2::PermissionFlaggedValue v2::PermissionManager::permission_granted_flagged(const ts::permission::PermissionType &permission) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return v2::empty_permission_flagged_value;
const auto block = this->calculate_block(permission);
if(!this->ref_block(block))
return v2::empty_permission_flagged_value;
auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)];
v2::PermissionFlaggedValue result{data.values.grant, data.flags.grant_set};
this->unref_block(block);
return result;
}
static constexpr v2::PermissionContainer empty_channel_permission = {empty_flags, v2::empty_permission_values};
const v2::PermissionContainer v2::PermissionManager::channel_permission(const PermissionType &permission, ts::ChannelId channel_id) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return empty_channel_permission;
shared_lock channel_perm_lock(this->channel_list_lock);
for(auto& entry : this->_channel_permissions)
if(entry->permission == permission && entry->channel_id == channel_id)
return v2::PermissionContainer{entry->flags, entry->values};
return empty_channel_permission;
}
void v2::PermissionManager::set_permission(const PermissionType &permission, const v2::PermissionValues &values, const v2::PermissionUpdateType &action_value, const v2::PermissionUpdateType &action_grant, int flag_skip, int flag_negate) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return;
const auto block = this->calculate_block(permission);
this->ref_allocate_block(block);
auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)];
if(action_value == v2::PermissionUpdateType::set_value) {
data.flags.value_set = true;
data.flags.flag_value_update = true;
data.values.value = values.value;
} else if(action_value == v2::PermissionUpdateType::delete_value) {
data.flags.value_set = false;
data.flags.flag_value_update = true;
data.values.grant = permNotGranted; /* required for the database else it does not "deletes" the value */
}
if(action_grant == v2::PermissionUpdateType::set_value) {
data.flags.grant_set = true;
data.flags.flag_grant_update = true;
data.values.grant = values.value;
} else if(action_grant == v2::PermissionUpdateType::delete_value) {
data.flags.grant_set = false;
data.flags.flag_grant_update = true;
data.values.grant = permNotGranted; /* required for the database else it does not "deletes" the value */
}
if(flag_skip >= 0)
data.flags.skip = flag_skip == 1;
if(flag_negate >= 0)
data.flags.skip = flag_negate == 1;
this->unref_block(block);
this->trigger_db_update();
}
void v2::PermissionManager::set_channel_permission(const PermissionType &permission, ChannelId channel_id, const v2::PermissionValues &values, const v2::PermissionUpdateType &action_value, const v2::PermissionUpdateType &action_grant, int flag_skip, int flag_negate) {
if(permission < 0 || permission >= PermissionType::permission_id_max)
return;
unique_lock channel_perm_lock(this->channel_list_lock);
ChannelPermissionContainer* permission_container = nullptr;
for(auto& entry : this->_channel_permissions)
if(entry->permission == permission && entry->channel_id == channel_id) {
permission_container = &*entry;
break;
}
/* register a new permission if we have no permission already*/
if(!permission_container || !permission_container->flags.permission_set()) { /* if the permission isn't set then we have to register it again */
if(action_value != v2::PermissionUpdateType::set_value && action_grant == v2::PermissionUpdateType::set_value) {
return; /* we were never willing to set this permission */
}
if(!permission_container) {
auto container = make_unique<ChannelPermissionContainer>();
container->permission = permission;
container->channel_id = channel_id;
permission_container = &*container;
this->_channel_permissions.push_back(std::move(container));
}
/* now set the channel flag for that permission */
const auto block = this->calculate_block(permission);
this->ref_allocate_block(block);
auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)];
data.flags.channel_specific = true;
this->unref_block(block);
}
if(action_value == v2::PermissionUpdateType::set_value) {
permission_container->flags.value_set = true;
permission_container->flags.flag_value_update = true;
permission_container->values.value = values.value;
} else if(action_value == v2::PermissionUpdateType::delete_value) {
permission_container->flags.value_set = false;
permission_container->flags.flag_value_update = true;
permission_container->values.grant = permNotGranted; /* required for the database else it does not "deletes" the value */
}
if(action_grant == v2::PermissionUpdateType::set_value) {
permission_container->flags.grant_set = true;
permission_container->flags.flag_grant_update = true;
permission_container->values.grant = values.value;
} else if(action_grant == v2::PermissionUpdateType::delete_value) {
permission_container->flags.grant_set = false;
permission_container->flags.flag_grant_update = true;
permission_container->values.grant = permNotGranted; /* required for the database else it does not "deletes" the value */
}
if(flag_skip >= 0)
permission_container->flags.skip = flag_skip == 1;
if(flag_negate >= 0)
permission_container->flags.skip = flag_negate == 1;
if(!permission_container->flags.permission_set()) { /* unregister the permission again because its unset, we delete the channel permission as soon we've flushed the updates */
auto other_channel_permission = std::find_if(this->_channel_permissions.begin(), this->_channel_permissions.end(), [&](unique_ptr<ChannelPermissionContainer>& perm) { return perm->permission == permission && perm->flags.permission_set(); });
if(other_channel_permission == this->_channel_permissions.end()) { /* no more channel specific permissions c*/
const auto block = this->calculate_block(permission);
if(this->ref_block(block)) {
this->block_containers[block]->permissions[this->calculate_block_index(permission)].flags.channel_specific = false;
this->unref_block(block);
}
}
}
this->trigger_db_update();
}
const std::vector<std::tuple<PermissionType, const v2::PermissionContainer>> v2::PermissionManager::permissions() {
std::unique_lock use_lock(this->block_use_count_lock);
decltype(this->block_containers) block_containers; /* save the states/nullptr's */
memcpy(block_containers, this->block_containers, sizeof(this->block_containers));
size_t block_count = 0;
for(size_t index = 0; index < BULK_COUNT; index++) {
if(block_containers[index]) {
block_count++;
this->block_use_count[index]++;
}
}
use_lock.unlock();
vector<tuple<PermissionType, const v2::PermissionContainer>> result;
result.reserve(block_count * PERMISSIONS_BULK_ENTRY_COUNT);
for(size_t block_index = 0; block_index < BULK_COUNT; block_index++) {
auto& block = block_containers[block_index];
if(!block)
continue;
for(size_t permission_index = 0; permission_index < PERMISSIONS_BULK_ENTRY_COUNT; permission_index++) {
auto& permission = block->permissions[permission_index];
if(!permission.flags.permission_set())
continue;
result.emplace_back((PermissionType) (block_index * PERMISSIONS_BULK_ENTRY_COUNT + permission_index), permission);
}
}
result.shrink_to_fit();
use_lock.lock();
for(size_t index = 0; index < BULK_COUNT; index++) {
if(block_containers[index])
this->block_use_count[index]--;
}
use_lock.unlock();
return result;
}
const vector<tuple<PermissionType, const v2::PermissionContainer>> v2::PermissionManager::channel_permissions(ts::ChannelId channel_id) {
shared_lock channel_perm_lock(this->channel_list_lock);
vector<tuple<PermissionType, const v2::PermissionContainer>> result;
for(auto& entry : this->_channel_permissions)
if((channel_id == entry->channel_id) && (entry->flags.value_set || entry->flags.grant_set))
result.emplace_back(entry->permission, v2::PermissionContainer{entry->flags, entry->values});
return result;
}
const vector<tuple<PermissionType, ChannelId, const v2::PermissionContainer>> v2::PermissionManager::channel_permissions() {
shared_lock channel_perm_lock(this->channel_list_lock);
vector<tuple<PermissionType, ChannelId, const v2::PermissionContainer>> result;
for(auto& entry : this->_channel_permissions)
if(entry->flags.value_set || entry->flags.grant_set)
result.emplace_back(entry->permission, entry->channel_id, v2::PermissionContainer{entry->flags, entry->values});
return result;
}
const std::vector<v2::PermissionDBUpdateEntry> v2::PermissionManager::flush_db_updates() {
if(!this->requires_db_save)
return {};
this->requires_db_save = false;
std::vector<v2::PermissionDBUpdateEntry> result;
{
lock_guard use_lock(this->block_use_count_lock);
size_t block_count = 0;
for (auto &block_container : block_containers)
if (block_container)
block_count++;
result.reserve(block_count * PERMISSIONS_BULK_ENTRY_COUNT);
for(size_t block_index = 0; block_index < BULK_COUNT; block_index++) {
auto& block = block_containers[block_index];
if(!block)
continue;
for(size_t permission_index = 0; permission_index < PERMISSIONS_BULK_ENTRY_COUNT; permission_index++) {
auto& permission = block->permissions[permission_index];
if(!permission.flags.flag_value_update && !permission.flags.flag_grant_update)
continue;
/* we only need an update it the permission has a DB reference or we will set the permission */
if(permission.flags.database_reference || permission.flags.permission_set()) {
/*
PermissionType permission;
ChannelId channel_id;
PermissionValues values;
PermissionUpdateType update_value;
PermissionUpdateType update_grant;
bool flag_new: 1;
bool flag_skip: 1;
bool flag_negate: 1;
*/
result.push_back(v2::PermissionDBUpdateEntry{
(PermissionType) (block_index * PERMISSIONS_BULK_ENTRY_COUNT + permission_index),
(ChannelId) 0,
permission.values,
(PermissionUpdateType) (permission.flags.flag_value_update ? (permission.flags.value_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing),
(PermissionUpdateType) (permission.flags.flag_grant_update ? (permission.flags.grant_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing),
(bool) permission.flags.database_reference,
(bool) !permission.flags.permission_set(), /* db delete */
(bool) permission.flags.skip,
(bool) permission.flags.negate
});
permission.flags.database_reference = permission.flags.permission_set();
}
permission.flags.flag_value_update = false;
permission.flags.flag_grant_update = false;
}
}
}
{
lock_guard chanel_lock(this->channel_list_lock);
for(size_t index = 0; index < this->_channel_permissions.size(); index++) {
auto& permission = this->_channel_permissions[index];
if(!permission->flags.flag_value_update && !permission->flags.flag_grant_update)
continue;
/* we only need an update it the permission has a DB reference or we will set the permission */
if(permission->flags.database_reference || permission->flags.permission_set()) {
result.push_back(
v2::PermissionDBUpdateEntry{
permission->permission,
permission->channel_id,
permission->values,
permission->flags.flag_value_update ? (permission->flags.value_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing,
permission->flags.flag_grant_update ? (permission->flags.grant_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing,
(bool) permission->flags.database_reference,
(bool) !permission->flags.permission_set(), /* db delete */
(bool) permission->flags.skip,
(bool) permission->flags.negate
}
);
permission->flags.database_reference = permission->flags.permission_set();
}
permission->flags.flag_value_update = false;
permission->flags.flag_grant_update = false;
if(!permission->flags.permission_set()) {
this->_channel_permissions.erase(this->_channel_permissions.begin() + index);
index--;
}
}
}
return result;
}
size_t v2::PermissionManager::used_memory() {
size_t result = sizeof(*this);
for (auto &block_container : block_containers) {
if (block_container)
result += sizeof(PermissionContainerBulk<PERMISSIONS_BULK_ENTRY_COUNT>);
}
{
shared_lock channel_lock(this->channel_list_lock);
result += this->_channel_permissions.size() * (sizeof(ChannelPermissionContainer) + sizeof(unique_ptr<ChannelPermissionContainer>));
}
return result;
}
void v2::PermissionManager::cleanup() {
lock_guard use_lock(this->block_use_count_lock);
for (auto &block_container : block_containers) {
if (!block_container) continue;
bool used = false;
for(auto& permission : block_container->permissions) {
if(permission.flags.value_set || permission.flags.grant_set || permission.flags.channel_specific) {
used = true;
break;
}
}
if(used)
continue;
delete block_container;
block_container = nullptr;
}
}

View File

@ -1,11 +1,18 @@
#pragma once
#include <map>
#include <functional>
#include <deque>
#include <string>
#include <utility>
#include <vector>
#include <memory>
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <cassert>
#include <cstring> /* for memset */
#include "./misc/spin_lock.h"
#include "Definitions.h"
#include "Variable.h"
@ -447,7 +454,9 @@ namespace ts {
i_ft_directory_create_power,
i_ft_needed_directory_create_power,
i_ft_quota_mb_download_per_client,
i_ft_quota_mb_upload_per_client
i_ft_quota_mb_upload_per_client,
permission_id_max
};
inline PermissionType& operator&=(PermissionType& a, int b) { return a = (PermissionType) ((int) a & b); }
@ -603,6 +612,7 @@ namespace ts {
extern std::deque<PermissionType> neededPermissions;
extern std::deque<PermissionGroup> availableGroups;
void setup_permission_resolve();
std::shared_ptr<PermissionTypeEntry> resolvePermissionData(PermissionType);
std::shared_ptr<PermissionTypeEntry> resolvePermissionData(const std::string&);
@ -620,7 +630,6 @@ namespace ts {
}
Permission(Permission &) = delete;
Permission(const Permission &) = delete;
@ -717,6 +726,178 @@ namespace ts {
std::deque<std::shared_ptr<Permission>> permissions;
std::deque<std::function<void(std::shared_ptr<Permission>)>> updateHandler;
};
namespace v2 {
#pragma pack(push, 1)
struct PermissionFlags {
bool database_reference: 1; /* if set the permission is known within the database, else it has tp be inserted */
bool channel_specific: 1; /* set if there are channel specific permissions */
bool value_set: 1;
bool grant_set: 1;
bool skip: 1;
bool negate: 1;
bool flag_value_update: 1;
bool flag_grant_update: 1;
ts_always_inline bool permission_set() { return this->value_set || this->grant_set; }
};
static_assert(sizeof(PermissionFlags) == 1);
struct PermissionValues {
PermissionValue value;
PermissionValue grant;
};
static_assert(sizeof(PermissionValues) == 8);
static constexpr PermissionValues empty_permission_values{0, 0};
struct PermissionContainer {
PermissionFlags flags;
PermissionValues values;
};
static_assert(sizeof(PermissionContainer) == 9);
struct ChannelPermissionContainer : public PermissionContainer {
PermissionType permission;
ChannelId channel_id;
};
static_assert(sizeof(ChannelPermissionContainer) == 19);
#pragma pack(pop)
#pragma pack(push, 1)
template <size_t element_count>
struct PermissionContainerBulk {
PermissionContainer permissions[element_count];
PermissionContainerBulk() {
memset(this->permissions, 0, sizeof(this->permissions));
}
};
#pragma pack(pop)
enum PermissionUpdateType {
do_nothing,
set_value,
delete_value
};
struct PermissionDBUpdateEntry {
PermissionType permission;
ChannelId channel_id;
PermissionValues values;
PermissionUpdateType update_value;
PermissionUpdateType update_grant;
bool flag_db: 1; /* only needs an update if set */
bool flag_delete: 1;
bool flag_skip: 1;
bool flag_negate: 1;
};
struct PermissionFlaggedValue {
PermissionValue value;
bool has_value;
};
static constexpr PermissionFlaggedValue empty_permission_flagged_value{0, false};
class PermissionManager {
public:
static constexpr size_t PERMISSIONS_BULK_BITS = 4; /* 16 permissions per block */
static constexpr size_t PERMISSIONS_BULK_ENTRY_COUNT = 1 << PERMISSIONS_BULK_BITS;
static constexpr size_t BULK_COUNT = (PermissionType::permission_id_max / (1 << PERMISSIONS_BULK_BITS)) + ((PermissionType::permission_id_max % PERMISSIONS_BULK_ENTRY_COUNT == 0) ? 0 : 1);
static_assert(PERMISSIONS_BULK_ENTRY_COUNT * BULK_COUNT >= PermissionType::permission_id_max);
PermissionManager();
virtual ~PermissionManager();
/* load permissions from the database */
void load_permission(const PermissionType&, const PermissionValues& /* values */, bool /* flag negate */, bool /* flag skip */, bool /* value present */,bool /* grant present */);
void load_permission(const PermissionType&, const PermissionValues& /* values */, ChannelId /* channel */, bool /* flag negate */, bool /* flag skip */, bool /* value present */,bool /* grant present */);
/* general getters/setters */
const PermissionFlags permission_flags(const PermissionType&); /* we return a "copy" because the actual permission could be deleted while we're analyzing the flags */
ts_always_inline const PermissionFlags permission_flags(const std::shared_ptr<PermissionTypeEntry>& permission_info) { return this->permission_flags(permission_info->type); }
const PermissionValues permission_values(const PermissionType&);
ts_always_inline const PermissionValues permission_values(const std::shared_ptr<PermissionTypeEntry>& permission_info) { return this->permission_values(permission_info->type); }
const PermissionFlaggedValue permission_value_flagged(const PermissionType&);
ts_always_inline const PermissionFlaggedValue permission_value_flagged(const std::shared_ptr<PermissionTypeEntry>& permission_info) { return this->permission_value_flagged(permission_info->type); }
const PermissionFlaggedValue permission_granted_flagged(const PermissionType&);
ts_always_inline const PermissionFlaggedValue permission_granted_flagged(const std::shared_ptr<PermissionTypeEntry>& permission_info) { return this->permission_granted_flagged(permission_info->type); }
/* only worth looking up if channel_specific is set */
const PermissionContainer channel_permission(const PermissionType& /* permission */, ChannelId /* channel id */);
ts_always_inline const PermissionContainer channel_permission(const std::shared_ptr<PermissionTypeEntry>& permission_info, ChannelId channel_id) { return this->channel_permission(permission_info->type, channel_id); }
/* modifiers */
void set_permission(const PermissionType& /* permission */, const PermissionValues& /* values */, const PermissionUpdateType& /* update value */, const PermissionUpdateType& /* update grant */, int /* flag negate */ = -1, int /* flag skip */ = -1);
void set_channel_permission(const PermissionType& /* permission */, ChannelId /* channel id */, const PermissionValues& /* values */, const PermissionUpdateType& /* update value */, const PermissionUpdateType& /* update grant */, int /* flag negate */ = -1, int /* flag skip */ = -1);
/* bulk info */
const std::vector<std::tuple<PermissionType, const PermissionContainer>> permissions();
const std::vector<std::tuple<PermissionType, const PermissionContainer>> channel_permissions(ChannelId /* channel id */);
const std::vector<std::tuple<PermissionType, ChannelId, const PermissionContainer>> channel_permissions();
size_t used_memory();
void cleanup();
ts_always_inline bool require_db_updates() { return this->requires_db_save; }
const std::vector<PermissionDBUpdateEntry> flush_db_updates();
private:
static constexpr size_t PERMISSIONS_BULK_BLOCK_MASK = (~(1 << PERMISSIONS_BULK_BITS)) & ((1 << PERMISSIONS_BULK_BITS) - 1);
bool requires_db_save = false;
ts_always_inline void trigger_db_update() { this->requires_db_save = true; } /* todo: pull some kind of trigger? */
spin_lock block_use_count_lock{};
int16_t block_use_count[BULK_COUNT];
PermissionContainerBulk<PERMISSIONS_BULK_ENTRY_COUNT>* block_containers[BULK_COUNT];
std::shared_mutex channel_list_lock{};
std::deque<std::unique_ptr<ChannelPermissionContainer>> _channel_permissions{};
ts_always_inline size_t calculate_block(const PermissionType& permission) {
return permission >> PERMISSIONS_BULK_BITS;
}
ts_always_inline size_t calculate_block_index(const PermissionType& permission) {
return permission & PERMISSIONS_BULK_BLOCK_MASK;
}
/**
* @param block
* @return true if block exists else false
*/
ts_always_inline bool ref_block(size_t block) {
std::lock_guard use_lock(this->block_use_count_lock);
if(!this->block_containers[block])
return false;
this->block_use_count[block]++;
assert(this->block_use_count[block] > 0);
return true;
}
ts_always_inline void unref_block(size_t block) {
std::lock_guard use_lock(this->block_use_count_lock);
this->block_use_count[block]--;
assert(this->block_use_count[block] >= 0);
}
ts_always_inline void ref_allocate_block(size_t block) {
std::lock_guard use_lock(this->block_use_count_lock);
if(!this->block_containers[block])
this->block_containers[block] = new PermissionContainerBulk<PERMISSIONS_BULK_ENTRY_COUNT>();
this->block_use_count[block]++;
assert(this->block_use_count[block] > 0);
}
};
}
}
}

View File

@ -34,4 +34,6 @@ class spin_lock {
always_inline void unlock() {
locked.store(false, std::memory_order_release);
}
};
};
#undef always_inline

58
test/PermissionTest.cpp Normal file
View File

@ -0,0 +1,58 @@
//
// Created by wolverindev on 15.07.19.
//
#include "PermissionManager.h"
#include <iostream>
using namespace std;
using namespace ts::permission::v2;
using PermissionType = ts::permission::PermissionType;
void print_permissions(PermissionManager& manager) {
{
auto permissions = manager.permissions();
cout << "Permissions: " << permissions.size() << endl;
for(const auto& permission : permissions) {
cout << " - " << ts::permission::resolvePermissionData(std::get<0>(permission))->name + ": ";
cout << (std::get<1>(permission).flags.value_set ? to_string(std::get<1>(permission).values.value) : "no value") << " negate: " << std::get<1>(permission).flags.negate << " skip: " << std::get<1>(permission).flags.skip << " ";
cout << "chan permission: " << std::get<1>(permission).flags.channel_specific << endl;
}
}
cout << "Used memory: " << manager.used_memory() << endl;
}
void print_updates(PermissionManager& manager) {
const auto updates = manager.flush_db_updates();
cout << "Permission updates: " << updates.size() << endl;
for(auto& update : updates) {
cout << "Permission: " << ts::permission::resolvePermissionData(update.permission)->name << "; Channel: " << update.channel_id << "; DB Ref: " << update.flag_db << endl;
cout << " value: " << (update.update_value == PermissionUpdateType::do_nothing ? "do nothing" : update.update_value == PermissionUpdateType::set_value ? "set value to " + to_string(update.values.value) : "delete") << endl;
cout << " grant: " << (update.update_grant == PermissionUpdateType::do_nothing ? "do nothing" : update.update_grant == PermissionUpdateType::set_value ? "set value to " + to_string(update.values.grant) : "delete") << endl;
}
}
int main() {
ts::permission::setup_permission_resolve();
/*
*
Structure size of PermissionManager: 176
Structure size of PermissionContainerBulk<16>: 192
Structure size of PermissionContainer: 12
*/
cout << "Structure size of PermissionManager: " << sizeof(PermissionManager) << endl;
cout << "Structure size of PermissionContainerBulk<16>: " << sizeof(PermissionContainerBulk<16>) << endl;
cout << "Structure size of PermissionContainer: " << sizeof(PermissionContainer) << endl;
cout << "Permissions/bulk: " << PermissionManager::PERMISSIONS_BULK_ENTRY_COUNT << ". Bulks: " << PermissionManager::BULK_COUNT << " (Max permissions: " << (PermissionManager::PERMISSIONS_BULK_ENTRY_COUNT * PermissionManager::BULK_COUNT) << "; Avl: " << (uint32_t) PermissionType::permission_id_max << ")" << endl;
PermissionManager manager{};
print_permissions(manager);
manager.set_permission(PermissionType::b_client_ban_ip, {1, 0}, PermissionUpdateType::set_value, PermissionUpdateType::do_nothing);
manager.set_channel_permission(PermissionType::b_client_ban_ip, 2, {1, 0}, PermissionUpdateType::set_value, PermissionUpdateType::do_nothing);
manager.set_channel_permission(PermissionType::b_client_ban_ip, 2, {1, 0}, PermissionUpdateType::delete_value, PermissionUpdateType::do_nothing);
print_updates(manager);
//manager.set_permission(PermissionType::b_client_ban_ip, {1, 0}, PermissionUpdateType::delete_value, PermissionUpdateType::do_nothing);
//manager.cleanup();
print_permissions(manager);
return 0;
}