example of a initial permission system

This commit is contained in:
Marcel Märtens 2022-08-19 17:03:01 +02:00
parent e4c9634b76
commit 71941132ce
6 changed files with 216 additions and 0 deletions

View File

@ -33,6 +33,8 @@ pub mod loot_owner;
#[cfg(not(target_arch = "wasm32"))] pub mod melee;
#[cfg(not(target_arch = "wasm32"))] mod misc;
#[cfg(not(target_arch = "wasm32"))] pub mod ori;
#[cfg(not(target_arch = "wasm32"))]
pub mod permissions;
#[cfg(not(target_arch = "wasm32"))] pub mod pet;
#[cfg(not(target_arch = "wasm32"))] mod phys;
#[cfg(not(target_arch = "wasm32"))] mod player;
@ -81,6 +83,7 @@ pub use self::{
fluid_dynamics::Fluid,
group::Group,
inputs::CanBuild,
permissions::RuleSet,
inventory::{
item::{
self,

View File

@ -0,0 +1,150 @@
use bitflags::bitflags;
use num_traits::FromPrimitive;
use specs::{hibitset::DrainableBitSet, BitSet, Component};
#[derive(Clone, Copy, PartialEq, num_derive::FromPrimitive)]
#[repr(u8)]
pub enum Action {
Read = 1,
Write = 2,
}
bitflags! {
struct Actions: u8 {
const READ = Action::Read as u8;
const WRITE = Action::Write as u8;
const READ_WRITE = Self::READ.bits | Self::WRITE.bits;
}
}
const OBJ_MULTIPLIER: u32 = 16;
#[derive(Clone, Copy, PartialEq, num_derive::FromPrimitive)]
#[repr(u16)]
pub enum Object {
ChatGlobal = 0x0001,
ChatWorld = 0x0002,
ChatRegion = 0x0003,
ChatLocal = 0x0004,
CommandTeleport = 0x0005,
CommandKick = 0x0006,
CommandBan = 0x0007,
CommandMute = 0x0008,
BuildCreateBlock = 0x0009,
BuildCreateSprite = 0x000A,
BuildDestroy = 0x000B,
}
impl Actions {
pub const fn allows(&self, action: Action) -> bool {
match action {
Action::Read => self.contains(Actions::READ),
Action::Write => self.contains(Actions::WRITE),
}
}
}
impl Object {
const fn get_valid_actions(&self) -> Actions {
match self {
Self::ChatGlobal => Actions::READ_WRITE,
Self::ChatWorld => Actions::READ_WRITE,
Self::ChatRegion => Actions::READ_WRITE,
Self::ChatLocal => Actions::READ_WRITE,
Self::CommandTeleport => Actions::WRITE,
Self::CommandKick => Actions::WRITE,
Self::CommandBan => Actions::WRITE,
Self::CommandMute => Actions::WRITE,
Self::BuildCreateBlock => Actions::WRITE,
Self::BuildCreateSprite => Actions::WRITE,
Self::BuildDestroy => Actions::WRITE,
}
}
}
pub struct ObjectAction {
obj: Object,
act: Action,
}
impl ObjectAction {
pub fn new(obj: Object, act: Action) -> Self { Self { obj, act } }
const fn id(&self) -> u32 { self.obj as u32 * OBJ_MULTIPLIER + self.act as u32 }
pub fn from_id(id: u32) -> Option<Self> {
let act = FromPrimitive::from_u32(id.rem_euclid(OBJ_MULTIPLIER));
let obj = FromPrimitive::from_u32(id.div_euclid(OBJ_MULTIPLIER));
if let (Some(act), Some(obj)) = (act, obj) {
Some(Self::new(obj, act))
} else {
None
}
}
const fn valid(&self) -> bool { self.obj.get_valid_actions().allows(self.act) }
}
#[derive(Clone, Debug, Default)]
pub struct RuleSet {
allowed: BitSet,
}
impl Component for RuleSet {
type Storage = specs::VecStorage<Self>;
}
impl RuleSet {
pub fn is_allowed(&self, object_action: ObjectAction) -> bool {
self.allowed.contains(object_action.id())
}
pub fn add(&mut self, object_action: ObjectAction) {
if object_action.valid() {
self.allowed.add(object_action.id());
}
}
pub fn remove(&mut self, object_action: ObjectAction) {
self.allowed.remove(object_action.id());
}
pub fn append(&mut self, mut other: RuleSet) {
for o in other.allowed.drain() {
self.allowed.add(o);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_max_action() {
assert!(OBJ_MULTIPLIER >= Actions::all().bits as u32 + 1);
}
#[test]
fn ser_and_deser() {
let oa = ObjectAction::new(Object::ChatGlobal, Action::Write);
let i = oa.id();
let oa2 = ObjectAction::from_id(i).unwrap();
assert_eq!(oa.id(), oa2.id());
}
#[test]
fn check_permissions() {
let mut ruleset = RuleSet::default();
ruleset.add(ObjectAction::new(Object::ChatGlobal, Action::Read));
ruleset.add(ObjectAction::new(Object::ChatRegion, Action::Read));
ruleset.add(ObjectAction::new(Object::ChatGlobal, Action::Write));
let oa = ObjectAction::new(Object::ChatGlobal, Action::Write);
let oa2 = ObjectAction::new(Object::CommandBan, Action::Write);
let oa3 = ObjectAction::new(Object::ChatGlobal, Action::Read);
let oa4 = ObjectAction::new(Object::ChatRegion, Action::Write);
assert!(ruleset.is_allowed(oa));
assert!(!ruleset.is_allowed(oa2));
assert!(ruleset.is_allowed(oa3));
assert!(!ruleset.is_allowed(oa4));
}
}

View File

@ -203,6 +203,7 @@ impl State {
ecs.register::<comp::invite::Invite>();
ecs.register::<comp::invite::PendingInvites>();
ecs.register::<comp::Beam>();
ecs.register::<comp::RuleSet>();
// Register synced resources used by the ECS.
ecs.insert(TimeOfDay(0.0));

View File

@ -68,6 +68,7 @@ use crate::{
rtsim::RtSim,
state_ext::StateExt,
sys::sentinel::{DeletedEntities, TrackedStorages},
sys::permissions::UserRoles,
};
use censor::Censor;
#[cfg(not(feature = "worldgen"))]
@ -343,6 +344,7 @@ impl Server {
state.ecs_mut().register::<comp::Pet>();
state.ecs_mut().register::<login_provider::PendingLogin>();
state.ecs_mut().register::<RepositionOnChunkLoad>();
state.ecs_mut().register::<UserRoles>();
// Load banned words list
let banned_words = settings.moderation.load_banned_words(data_dir);

View File

@ -7,6 +7,7 @@ pub mod loot;
pub mod metrics;
pub mod msg;
pub mod object;
pub mod permissions;
pub mod persistence;
pub mod pets;
pub mod sentinel;
@ -40,6 +41,7 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch::<chunk_serialize::Sys>(dispatch_builder, &[]);
// don't depend on chunk_serialize, as we assume everything is done in a SlowJow
dispatch::<chunk_send::Sys>(dispatch_builder, &[]);
dispatch::<permissions::Sys>(dispatch_builder, &[]);
}
pub fn run_sync_systems(ecs: &mut specs::World) {

View File

@ -0,0 +1,58 @@
use chrono::Utc;
use common::comp::permissions::RuleSet;
use common_ecs::{Job, Origin, Phase, System};
use specs::{Component, Entities, Join, ReadStorage, WriteStorage};
use std::sync::Arc;
pub struct Role {
pub name: String,
rules: RuleSet,
}
pub struct RoleAccess {
role: Arc<Role>,
valid_until: chrono::DateTime<chrono::Utc>,
}
pub struct UserRoles(Vec<RoleAccess>);
impl Component for UserRoles {
type Storage = specs::VecStorage<Self>;
}
// This system manages loot that exists in the world
#[derive(Default)]
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
WriteStorage<'a, RuleSet>,
ReadStorage<'a, UserRoles>,
);
const NAME: &'static str = "permissions";
const ORIGIN: Origin = Origin::Server;
const PHASE: Phase = Phase::Create;
fn run(_job: &mut Job<Self>, (entities, mut rule_sets, user_roles): Self::SystemData) {
// recalculate the role_access on top of all individual roles assigned
let now = Utc::now();
// Add PreviousPhysCache for all relevant entities
for entity in (&entities, !&rule_sets)
.join()
.map(|(e, _)| e)
.collect::<Vec<_>>()
{
let _ = rule_sets.insert(entity, RuleSet::default());
}
for (rule_set, user_roles) in (&mut rule_sets, &user_roles).join() {
*rule_set = RuleSet::default();
for access in &user_roles.0 {
if access.valid_until > now {
rule_set.append(access.role.rules.clone())
}
}
}
}
}