chiark / gitweb /
wip new account etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 19 Oct 2020 18:12:47 +0000 (19:12 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 19 Oct 2020 18:12:47 +0000 (19:12 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/accounts.rs
src/cmdlistener.rs
src/commands.rs
src/global.rs
src/spec.rs

index e588b82cae44a37e53c228744c5e745cecc9050c..9b53b257d5385e27085cc8c3a53447ba4606364b 100644 (file)
@@ -101,19 +101,21 @@ pub fn save_accounts_now() -> Result<(), InternalError> {
 }
 
 impl AccountRecord {
-  pub fn lookup(account: &AccountName)
+  pub fn lookup(account: &AccountName,  _: Authorised<AccountName>)
             -> Option<MappedRwLockReadGuard<'static, AccountRecord>> {
     ACCOUNTS.read().map(
       |accounts| accounts?.get(account)
     )
   }
-  pub fn lookup_mut_caller_must_save(account: &AccountName)
+  pub fn lookup_mut_caller_must_save(account: &AccountName,
+                                      _: Authorised<AccountName>)
             -> Option<MappedRwLockWriteGuard<'static, AccountRecord>> {
     ACCOUNTS.write().map(
       |accounts| accounts?.get(account)
     )
   }
-  pub fn with_entry_mut<T, F>(account: &AccountName, f: F)
+  pub fn with_entry_mut<T, F>(account: &AccountName, f: F,
+                              _: Authorised<AccountName>)
                               -> Result<T, (InternalError, T)>
   where F: FnOnce(Option<&mut AccountRecord>) -> T
   {
index e7241d2740a5451abf62891edee336c1b9e49be3..f4c1a568aef4b0351ff28b1050200f6ff6750a77 100644 (file)
@@ -55,8 +55,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse {
     Noop => Fine,
 
     SetAccount(wanted_account) => {
-      let authorised : AuthorisedSatisfactory =
-        authorise_scope(cs, &wanted_account.scope)?;
+      let authorised = authorise_scope(cs, &wanted_account.scope)?;
       cs.account = ScopedName {
         scope: Some(authorised.into_inner()),
         scoped_nmae: wanted_account.scoped_name,
@@ -64,7 +63,9 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse {
       Fine
     },
 
-    CreateGame { name, insns } => {
+    CreateGame { game, insns } => {
+      let authorised = authorise_scope(cs, &game.scope)?;
+
       let gs = crate::gamestate::GameState {
         table_size : DEFAULT_TABLE_SIZE,
         pieces : Default::default(),
@@ -74,12 +75,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse {
         max_z: default(),
       };
 
-      let name = ScopedName {
-        scope : cs.get_scope()?.clone(),
-        scoped_name : name,
-      };
-
-      let gref = Instance::new(name, gs)?;
+      let gref = Instance::new(game, gs, authorised)?;
       let mut ig = gref.lock()?;
 
       execute_for_game(cs, &mut ig, insns, MgmtGameUpdateMode::Bulk)
@@ -97,15 +93,13 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse {
     },
 
     ListGames { all } => {
-      let scope = if all == Some(true) {
-        let _authorise : AuthorisedSatisfactory =
-          authorise_scope(cs, &AS::Server)?;
-        None
+      let (scope, authorised) = if all == Some(true) {
+        let auth = authorise_scope(cs, &AS::Server)?;
+        (None, auth)
       } else {
-        let scope = cs.get_scope()?;
-        Some(scope)
+        cs.current_account()?;
       };
-      let mut games = Instance::list_names(scope);
+      let mut games = Instance::list_names(scope, authorised);
       games.sort_unstable();
       GamesList(games)
     },
@@ -425,7 +419,7 @@ impl UpdateHandler {
 struct CommandStream<'d> {
   euid : Result<Uid, ConnectionEuidDiscoverEerror>,
   desc : &'d str,
-  account : Option<AccountName>,
+  account : Option<(AccountName, Authorised<AccountName>)>,
   chan : MgmtChannel,
   who: Who,
 }
@@ -449,8 +443,10 @@ impl CommandStream<'_> {
   }
 
   #[throws(MgmtError)]
-  fn get_scope(&self) -> &AccountScope {
-    self.scope.as_ref().ok_or(NoScope)?
+  fn current_account(&self) -> (&AccountName, Authorised<AccountName>) {
+    self.scope.as_ref()
+      .ok_or(ME::SpecifyAccount)?
+      .map(|(account,auth)| (account,*auth))
   }
 }
 
@@ -561,35 +557,35 @@ impl CommandStream<'_> {
 
 #[throws(MgmtError)]
 fn authorise_scope(cs: &CommandStream, wanted: &AccountScope)
-                   -> AuthorisedSatisfactory {
+                   -> Authorised<AccountScope> {
   do_authorise_scope(cs, wanted)
     .map_err(|e| cs.map_auth_err(e))?
 }
 
 #[throws(AuthorisationError)]
 fn do_authorise_scope(cs: &CommandStream, wanted: &AccountScope)
-                   -> AuthorisedSatisfactory {
-  type AS<T> = (T, AccountScope);
-
+                   -> Authorised<AccountScope> {
   match &wanted {
 
     AccountScope::Server => {
-      let y : AS<
-        Authorised<(Passwd,Uid)>,
-      > = {
-        let ok = cs.authorised_uid(None,None)?;
-        (ok,
-         AccountScope::Server)
+      let y : Authorised<(Passwd,Uid)> = {
+        cs.authorised_uid(None,None)?;
       };
-      return y.into()
+      y.therefore_ok()
     },
 
     AccountScope::Unix { user: wanted } => {
-      let y : AS<
-        Authorised<(Passwd,Uid)>,
-      > = {
+      struct InUserList;
+
+      let y : Authorised<(Passwd,Uid,InUserList)> = {
+
         struct AuthorisedIf { authorised_for : Option<Uid> };
 
+        const SERVER_ONLY : (AuthorisedIf, Authorised<InUserList>) = (
+          AuthorisedIf { authorised_for: None },
+          Authorised::authorised(InUserList),
+        );
+
         let pwent = Passwd::from_name(&wanted)
           .map_err(
             |e| anyhow!("looking up requested username {:?}: {:?}",
@@ -600,25 +596,29 @@ fn do_authorise_scope(cs: &CommandStream, wanted: &AccountScope)
               "requested username {:?} not found", &wanted
             ))
           )?;
+        let pwent_ok = Authorised::authorised(pwent);
 
-        let (in_userlist, xinfo) = (||{ <Result<_,AE>>::Ok({
+        let ((uid, in_userlist_ok), xinfo) = (||{ <Result<_,AE>>::Ok({
           let allowed = BufReader::new(match File::open(USERLIST) {
             Err(e) if e.kind() == ErrorKind::NotFound => {
               return Ok((
-                AuthorisedIf{ authorised_for: None },
+                SERVER_ONLY,
                 Some(format!(" user list {} does not exist", USERLIST))
               ))
             },
             r => r,            
           }?);
+          
           allowed
             .lines()
             .filter_map(|le| match le {
               Ok(l) if l.trim() == wanted => Some(
                 Ok((
-                  AuthorisedIf{ authorised_for: Some(
+                  (AuthorisedIf{ authorised_for: Some(
                     Uid::from_raw(pwent.uid)
-                  ) },
+                  )},
+                   Authorised::authorised(InUserList),
+                  ),
                   None
                 ))
               ),
@@ -628,20 +628,19 @@ fn do_authorise_scope(cs: &CommandStream, wanted: &AccountScope)
             .next()
             .unwrap_or_else(
               || Ok((
-                AuthorisedIf{ authorised_for: None },
+                SERVER_ONLY,
                 Some(format!(" requested username {:?} not in {}",
                              &wanted, USERLIST)),
               ))
             )?
         })})()?;
 
-        let AuthorisedIf{ authorised_for } = in_userlist;
         let info = xinfo.as_deref();
-        let ok = cs.authorised_uid(authorised_for, info)?;
-        (ok,
-         AccountScope::Unix { user: pwent.name })
+        let uid_ok = cs.authorised_uid(uid.authorised_for, info)?;
+      
+        (pwent_ok, uid_ok, in_userlist_ok).combine()
       };
-      y.into()
+      y.therefore_ok()
     },
 
   }
@@ -660,22 +659,14 @@ mod authproofs {
   pub struct AuthorisationError(pub String);
 
   pub struct Authorised<A> (PhantomData<A>);
-  //struct AuthorisedScope<A> (Authorised<A>, ManagementScope);
-  pub struct AuthorisedSatisfactory (AccountScope);
-
-  impl AuthorisedSatisfactory {
-    pub fn into_inner(self) -> AccountScope { self.0 }
-  }
 
   impl<T> Authorised<T> {
-    pub fn authorise() -> Authorised<T> { Authorised(PhantomData) }
+    pub const fn authorise_any() -> Authorised<T> { Authorised(PhantomData) }
+    pub const fn authorised(v: &T) -> Authorised<T> { Authorised(PhantomData) }
   }
 
-  impl<T> From<(Authorised<T>, AccountScope)> for AuthorisedSatisfactory {
-    fn from((_,s): (Authorised<T>, AccountScope)) -> Self { Self(s) }
-  }
-  impl<T,U> From<((Authorised<T>, Authorised<U>), AccountScope)> for AuthorisedSatisfactory {
-    fn from(((..),s): ((Authorised<T>, Authorised<U>), AccountScope)) -> Self { Self(s) }
+  impl<T> Authorised<T> {
+    pub fn therefore_ok<U>(self) -> Authorised<U> { Authorised(PhantomData) }
   }
 
   impl From<anyhow::Error> for AuthorisationError {
@@ -688,4 +679,17 @@ mod authproofs {
       AuthorisationError(format!("{}",e))
     }
   }
+
+  pub trait AuthorisedCombine {
+    type Output;
+    fn combine(self) -> Authorised<Self::Output> { Authorised(PhantomData) }
+  }
+  impl<A,B> AuthorisedCombine
+    for (Authorised<A>, Authorised<B>) {
+    type Output = Authorised<(A, B)>;
+  }
+  impl<A,B,C> AuthorisedCombine
+    for (Authorised<A>, Authorised<B>, Authorised<C>) {
+    type Output = Authorised<(A, B, C)>;
+  }
 }
index f32a174b58fda0986f59bab5fef257f09560b2ce..a02b601e80fcde2b3f00128b078d340b1d81df36 100644 (file)
@@ -137,7 +137,7 @@ pub enum MgmtGameUpdateMode {
 pub enum MgmtError {
   ParseFailed(String),
   AuthorisationError,
-  NoScope,
+  SpecifyAccount,
   AlreadyExists,
   NickCollision,
   GameBeingDestroyed,
index 77ff834970f0d721ce3869d01acaa077ade4d966..065f721cbfa2aa294906f308c12a461bf76b264c 100644 (file)
@@ -255,7 +255,7 @@ impl Instance {
   /// Returns `None` if a game with this name already exists
   #[allow(clippy::new_ret_no_self)]
   #[throws(MgmtError)]
-  pub fn new(name: InstanceName, gs: GameState)
+  pub fn new(name: InstanceName, gs: GameState, _: Authorised<InstanceName>)
              -> InstanceRef {
     let name = Arc::new(name);
 
@@ -301,7 +301,8 @@ impl Instance {
   }
 
   #[throws(MgmtError)]
-  pub fn lookup_by_name(name: &InstanceName) -> InstanceRef {
+  pub fn lookup_by_name(name: &InstanceName, _: Authorised<InstanceName>)
+                        -> InstanceRef {
     GLOBAL.games.read().unwrap()
       .get(name)
       .ok_or(MgmtError::GameNotFound)?
@@ -309,7 +310,7 @@ impl Instance {
   }
 
   #[throws(InternalError)]
-  pub fn destroy_game(mut g: InstanceGuard) {
+  pub fn destroy_game(mut g: InstanceGuard, _: Authorised<InstanceName>) {
     let a_savefile = savefilename(&g.name, "a-", "");
 
     let mut gw = GLOBAL.games.write().unwrap();
@@ -334,7 +335,7 @@ impl Instance {
       );
   }
 
-  pub fn list_names(scope: Option<&AccountScope>)
+  pub fn list_names(scope: Option<&AccountScope>, _: Authorised<AccountScope>)
                     -> Vec<Arc<InstanceName>> {
     let games = GLOBAL.games.read().unwrap();
     let out : Vec<Arc<InstanceName>> =
index ab520b59f9e3045e2b8e2150badb5380d976f75e..44772e854674d8641dd56d07524d2cb0097b126a 100644 (file)
@@ -243,11 +243,9 @@ pub mod implementation {
       // xxx check this on setting access
       None
     }
-    fn client_mgi(&self, _player: PlayerId) -> Option<MgmtGameInstruction> {
-      None
-    }
-    fn server_deliver(&self, pst: &PlayerState, token: &AccessTokenReport)
-                      -> Result<Option<&AccessTokenReport>, TDE> {
+    fn server_deliver<'t>(&self, pst: &PlayerState,
+                          token: &'t AccessTokenReport)
+                          -> Result<Option<&'t AccessTokenReport>, TDE> {
       Ok(None)
     }
     fn client_deliver(&self, pst: &PlayerState, token: &AccessTokenReport)
@@ -269,22 +267,14 @@ pub mod implementation {
     fn override_token(&self) -> Option<&RawToken> {
       Some(&self.token)
     }
-    fn client_mgi(&self, player: PlayerId) -> Option<MgmtGameInstruction> {
-      Some(Insn::SetFixedPlayerAccess {
-        player,
-        token: self.token.clone(),
-      })
-    }
   }
 
   #[typetag::serde]
   impl PlayerAccessSpec for UrlOnStdout {
-    fn client_mgi(&self, player: PlayerId) -> Option<MgmtGameInstruction> {
-      Some(Insn::ReportPlayerAccesses(player))
-    }
     #[throws(TDE)]
-    fn server_deliver(&self, ps: &PlayerState, token: &AccessTokenReport)
-                      -> Option<&AccessTokenReport> {
+    fn server_deliver<'t>(&self, ps: &PlayerState,
+                          token: &'t AccessTokenReport)
+                          -> Option<&'t AccessTokenReport> {
       Some(token)
     }
     #[throws(TDE)]