mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
example of a initial permission system
This commit is contained in:
parent
e4c9634b76
commit
71941132ce
@ -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,
|
||||
|
150
common/src/comp/permissions.rs
Normal file
150
common/src/comp/permissions.rs
Normal 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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
58
server/src/sys/permissions.rs
Normal file
58
server/src/sys/permissions.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user