From: Ian Jackson Date: Tue, 20 Oct 2020 19:55:16 +0000 (+0100) Subject: wip new account etc. X-Git-Tag: otter-0.2.0~614 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=a9b77bd712c6921125227daf0f2e13ca5f1a7e83;p=otter.git wip new account etc. Signed-off-by: Ian Jackson --- diff --git a/Cargo.lock.example b/Cargo.lock.example index 2b2213d9..4065f528 100644 --- a/Cargo.lock.example +++ b/Cargo.lock.example @@ -1086,6 +1086,7 @@ dependencies = [ "chrono", "chrono-tz", "delegate", + "either", "failure", "fehler", "flexi_logger", diff --git a/Cargo.toml b/Cargo.toml index 5c228355..4ac0f0b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ boolinator = "2" chrono = "0.4" chrono-tz = "0.5" delegate = "0.4" +either = "1" failure = "0.1.8" # for pwd fehler = "1" flexi_logger = { version = "0.15", features = [ "specfile" ] } diff --git a/src/accounts.rs b/src/accounts.rs index 4dc20f46..05416f23 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -133,3 +133,103 @@ impl AccountRecord { panic!("xxx") } } + +//---------- acl handling ---------- + +pub mod loaded_acl { + use crate::imports::*; + + pub trait Perm : FromPrimitive + ToPrimitive + + Copy + Eq + Hash + Sync + Send { } + + #[derive(Copy,Clone,Debug)] + pub struct PermSet (u64, PhantomData<&'static P>); + + #[derive(Debug,Clone)] + pub struct EffectiveAcl<'i, P: Perm> { + pub owner_account: Option<&'i str>, + pub acl: LoadedAcl

, + } + + #[derive(Debug,Clone)] + pub struct LoadedAcl (Vec>); + + #[derive(Debug,Clone)] + struct LoadedAclEntry { + pat: glob::Pattern, + allow: Bitmap, + deny: Bitmap, + ptype: PhantomData<&'static P>, + } + + #[derive(Debug)] + struct AclEntryRef<'r, P: Perm> { + pat: Either<&'r str, &'r glob::Pattern>, + allow: u64, + deny: u64, + ptype: PhantomData<&'static P>, + } + + impl LoadedAcl

{ + fn entries(&'s self) -> impl Iterator> { + self.owner_account.map( + |owner| + AclEntryRef { pat: Left(owner), allow: !0, deny: 0, ptype } + ).chain(self.entries.map( + |LoadedAclEntry { pat, allow, deny }| + AclEntryRef { pat: Left(pat), allow: allow.0, deny: deny.0 } + )) + } + + #[throws(MgmtError)] + fn check(&self, account_name: &str, p: PermSet

) { + let mut needed = p.0; + assert!(needed != 0); + for AclEntryRef { pat, allow, deny } in self.entries() { + if !match pat { + Left(owner) => owner == account_name, + Right(pat) => pat.matches(account_name), + } { continue } + if needed & deny != 0 { break } + needed &= !allow; + if needed == 0 { return Ok(()) } + } + Err(ME::PermissionDenied) + } + } + + impl From for PermSet

where I: IntoIterator { + fn from(i: I) -> Self { + i.into_iter().fold(0, |b, i| b | i.to_u64().unwrap()) + } + } + + fn unpack(unpacked: Bitmap) -> HashSet

{ + let mut s = HashSet::new(); + for n in 0.. { + let v = match FromPrimitive::from_u64(n) { Some(v) => v, None => break }; + if unpacked & n != 0 { s.insert(v) } + } + s + } + + impl From> for LoadedAcl

{ + fn from(acl: Acl

) -> LoadedAcl

{ + let ents = acl.into_iter().map( + |AclEntry { account_glob, allow, deny }| + LoadedAclEntry { account_glob, allow: allow.into(), deny: deny.into() } + ).collect(); + LoadedAcl(ents) + } + } + + impl From> for Acl

{ + fn from(acl: LoadedAcl

) -> Acl

{ + let LoadedAcl(ents) = acl; + ents.into_iter().map( + |LoadedAclEntry { account_glob, allow, deny }| + AclEntry { account_glob, allow: unpack(allow), deny: unpack(deny) } + ).collect() + } + } +} diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index f74f7255..190ee91e 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -43,6 +43,21 @@ impl Display for Who { fn fmt(&self, f: &mut Formatter) { write!(f, "The facilitator")? } } +struct CommandStream<'d> { + euid : Result, + desc : &'d str, + account : Option, + chan : MgmtChannel, + who: Who, +} + +#[derive(Debug,Clone)] +struct AccountSpecified { + name: ScopedName, + cooked: String, + auth: Authorisation, +} + // ========== management API ========== // ---------- management command implementations @@ -55,7 +70,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { Noop => Fine, SetAccount(wanted_account) => { - let authorised = authorise_scope(cs, &wanted_account.scope)?; + let authorised = authorise_scope_direct(cs, &wanted_account.scope)?; cs.account = Some(( wanted_account, authorised.therefore_ok(), @@ -64,8 +79,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { }, CreateGame { game, insns } => { - let authorised = authorise_scope(cs, &game.scope)?; - let authorised : Authorisation = authorised.therefore_ok(); + let authorised = authorise_by_account(cs, &game.scope)?; let gs = crate::gamestate::GameState { table_size : DEFAULT_TABLE_SIZE, @@ -96,7 +110,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { ListGames { all } => { let (scope, auth) = if all == Some(true) { - let auth = authorise_scope(cs, &AS::Server)?; + let auth = authorise_scope_direct(cs, &AS::Server)?; (None, auth) } else { let (account, auth) = cs.account.as_ref() @@ -140,7 +154,15 @@ fn execute_game_insn(cs: &CommandStream, type Insn = MgmtGameInstruction; type Resp = MgmtGameResponse; let who = &cs.who; - fn readonly(_ig: &InstanceGuard, resp: Resp) -> ExecuteGameInsnResults { + + fn readonly ExecuteGameInsnResults>( + cs: &CommandStream, + ig: &Unauthorised, + p: PermSet, + f: F) -> ExecuteGameInsnResults + { + let ig = ig.check_acl(&cs.account.as_ref()?.cooked)?; + let resp = f(ig); (U{ pcs: vec![], log: vec![], raw: None }, resp) } @@ -418,14 +440,6 @@ impl UpdateHandler { // ---------- core listener implementation ---------- -struct CommandStream<'d> { - euid : Result, - desc : &'d str, - account : Option<(AccountName, Authorisation)>, - chan : MgmtChannel, - who: Who, -} - impl CommandStream<'_> { #[throws(CSE)] pub fn mainloop(mut self) { @@ -558,8 +572,9 @@ impl CommandStream<'_> { } #[throws(MgmtError)] -fn authorise_scope(cs: &CommandStream, wanted: &AccountScope) - -> Authorisation { +fn authorise_scope_direct(cs: &CommandStream, wanted: &AccountScope) + -> Authorisation { + // Usually, use authorise_by_account do_authorise_scope(cs, wanted) .map_err(|e| cs.map_auth_err(e))? } diff --git a/src/global.rs b/src/global.rs index 44289704..b597e0b2 100644 --- a/src/global.rs +++ b/src/global.rs @@ -38,6 +38,7 @@ pub struct Instance { pub iplayers : SecondarySlotMap, pub tokens_players : TokenRegistry, pub tokens_clients : TokenRegistry, + pub acl: Acl, } pub struct PlayerRecord { @@ -251,6 +252,18 @@ impl InstanceRef { } } +impl Unauthorised, A> { + #[throws(MgmtError)] + pub fn check_acl(&mut self, p: PermSet) + -> &mut InstanceGuard { + let auth = { + let acl = self.by(Authorisation::authorise_any()).acl; + acl.check(p)?; + }; + self.by_mut(auth); + } +} + impl Unauthorised { #[throws(InstanceLockError)] pub fn lock(&self) -> Unauthorised, A> { diff --git a/src/imports.rs b/src/imports.rs index b8821566..9c187611 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -87,7 +87,7 @@ pub use arrayvec::ArrayVec; pub use log::{trace,debug,info,warn,error}; pub use log::log; -pub use num_traits::Bounded; +pub use num_traits::{Bounded, FromPrimitive, ToPrimitive}; pub use flexi_logger::{LogSpecification}; @@ -123,6 +123,7 @@ pub use crate::debugreader::DebugReader; pub use crate::shapelib; pub use crate::tz::*; pub use crate::accounts::*; +pub use crate::accounts::loaded_acl::{self,LoadedAcl}; pub use zcoord::{self, ZCoord};