chiark / gitweb /
wip new account etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 20 Oct 2020 19:55:16 +0000 (20:55 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 20 Oct 2020 19:55:16 +0000 (20:55 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Cargo.lock.example
Cargo.toml
src/accounts.rs
src/cmdlistener.rs
src/global.rs
src/imports.rs

index 2b2213d930129ba19e6086de49f1e666b526b31a..4065f5282c7e955a4628d7b2b065f7d971b2b3e6 100644 (file)
@@ -1086,6 +1086,7 @@ dependencies = [
  "chrono",
  "chrono-tz",
  "delegate",
+ "either",
  "failure",
  "fehler",
  "flexi_logger",
index 5c2283555b3a1f1edda91931b8974e92965fd640..4ac0f0b3b8c72cfc4fb0e53fdf45d546e359a104 100644 (file)
@@ -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" ] }
index 4dc20f46d4a96701304d9ce26c1e2dfc5b767352..05416f2329e746621abba32f3449ec78ba50aa54 100644 (file)
@@ -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<P: Perm> (u64, PhantomData<&'static P>);
+
+  #[derive(Debug,Clone)]
+  pub struct EffectiveAcl<'i, P: Perm> {
+    pub owner_account: Option<&'i str>,
+    pub acl: LoadedAcl<P>,
+  }
+
+  #[derive(Debug,Clone)]
+  pub struct LoadedAcl<P: Perm> (Vec<LoadedAclEntry<P>>);
+
+  #[derive(Debug,Clone)]
+  struct LoadedAclEntry<P: Perm> {
+    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<P:Perm> LoadedAcl<P> {
+    fn entries(&'s self) -> impl Iterator<Item=AclEntryRef<'s>> {
+      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<P>) {
+      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<P:Perm> From<I> for PermSet<P> where I: IntoIterator<Item=&P> {
+    fn from(i: I) -> Self {
+      i.into_iter().fold(0, |b, i| b | i.to_u64().unwrap())
+    }
+  }
+
+  fn unpack<P:Perm>(unpacked: Bitmap) -> HashSet<P> {
+    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<P:Perm> From<Acl<P>> for LoadedAcl<P> {
+    fn from(acl: Acl<P>) -> LoadedAcl<P> {
+      let ents = acl.into_iter().map(
+        |AclEntry { account_glob, allow, deny }|
+        LoadedAclEntry { account_glob, allow: allow.into(), deny: deny.into() }
+      ).collect();
+      LoadedAcl(ents)
+    }
+  }
+
+  impl<P:Perm> From<LoadedAcl<P>> for Acl<P> {
+    fn from(acl: LoadedAcl<P>) -> Acl<P> {
+      let LoadedAcl(ents) = acl;
+      ents.into_iter().map(
+        |LoadedAclEntry { account_glob, allow, deny }|
+        AclEntry { account_glob, allow: unpack(allow), deny: unpack(deny) }
+      ).collect()
+    }
+  }
+}
index f74f725570b7b29c84b2fac663f82433c4509b72..190ee91ecbf73d22ed220571d6444592f6222383 100644 (file)
@@ -43,6 +43,21 @@ impl Display for Who {
   fn fmt(&self, f: &mut Formatter) { write!(f, "The facilitator")? }
 }
 
+struct CommandStream<'d> {
+  euid : Result<Uid, ConnectionEuidDiscoverEerror>,
+  desc : &'d str,
+  account : Option<AccountSpecified>,
+  chan : MgmtChannel,
+  who: Who,
+}
+
+#[derive(Debug,Clone)]
+struct AccountSpecified {
+  name: ScopedName,
+  cooked: String,
+  auth: Authorisation<AccountScope>,
+}
+
 // ========== 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<AccountName> = 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<F: FnOnce(&mut InstanceGuard) -> ExecuteGameInsnResults>(
+    cs: &CommandStream,
+    ig: &Unauthorised<InstanceGuard>,
+    p: PermSet<TablePermission>,
+    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<Uid, ConnectionEuidDiscoverEerror>,
-  desc : &'d str,
-  account : Option<(AccountName, Authorisation<AccountScope>)>,
-  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<AccountScope> {
+fn authorise_scope_direct(cs: &CommandStream, wanted: &AccountScope)
+                          -> Authorisation<AccountScope> {
+  // Usually, use authorise_by_account
   do_authorise_scope(cs, wanted)
     .map_err(|e| cs.map_auth_err(e))?
 }
index 442897049fadcdb6523bd514611b938f8875d3c8..b597e0b25a320fbcd1c7c4c1bf1bd641f78f8fe6 100644 (file)
@@ -38,6 +38,7 @@ pub struct Instance {
   pub iplayers : SecondarySlotMap<PlayerId, PlayerRecord>,
   pub tokens_players : TokenRegistry<PlayerId>,
   pub tokens_clients : TokenRegistry<ClientId>,
+  pub acl: Acl<TablePermission>,
 }
 
 pub struct PlayerRecord {
@@ -251,6 +252,18 @@ impl InstanceRef {
   }
 }
 
+impl<A> Unauthorised<InstanceGuard<'_>, A> {
+  #[throws(MgmtError)]
+  pub fn check_acl(&mut self, p: PermSet<TablePermissions>)
+                   -> &mut InstanceGuard {
+    let auth = {
+      let acl = self.by(Authorisation::authorise_any()).acl;
+      acl.check(p)?;
+    };
+    self.by_mut(auth);
+  }
+}
+
 impl<A> Unauthorised<InstanceRef, A> {
   #[throws(InstanceLockError)]
   pub fn lock(&self) -> Unauthorised<InstanceGuard<'_>, A> {
index b8821566fa1bce69fe4da063a58ac2fde07c12d2..9c1876118ed65ce368661e964e5cdde6bacb5a14 100644 (file)
@@ -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};