From a7b27502d853d2eb724153f2f1dbc772ee4fb3ed Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Sat, 30 Dec 2023 23:19:52 +0800
Subject: [PATCH] Improved docs, particularly for Behaviour

---
 embassy/demos/picow/src/main.rs |  3 ++
 embassy/src/embassy_sunset.rs   |  7 ++++
 src/behaviour.rs                | 57 +++++++++++++++++++++++++++------
 3 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs
index de0c517..e6cd1b6 100644
--- a/embassy/demos/picow/src/main.rs
+++ b/embassy/demos/picow/src/main.rs
@@ -342,8 +342,11 @@ impl DemoServer for PicoServer {
             let username = self.username.lock().await;
             let mut stdio2 = stdio.clone();
             match username.as_str() {
+                // Open the config menu
                 "config" => menu(&mut stdio, &mut stdio2, false, self.global).await,
+                // Open the usb serial directly
                 "usb" => serial(&mut stdio, &mut stdio2, self.global.usb_pipe).await,
+                // Open the normal (?) serial directly (either usb or uart)
                 _ => serial(&mut stdio, &mut stdio2, default_pipe).await,
             }
         };
diff --git a/embassy/src/embassy_sunset.rs b/embassy/src/embassy_sunset.rs
index 9a229c4..689ed05 100644
--- a/embassy/src/embassy_sunset.rs
+++ b/embassy/src/embassy_sunset.rs
@@ -120,6 +120,11 @@ impl<'a> EmbassySunset<'a> {
          }
     }
 
+    /// Runs the session to completion
+    ///
+    /// `b` is a bit tricky, it allows passing either a Mutex<CliBehaviour> or
+    /// Mutex<ServBehaviour> (to be converted into a Behaviour in progress() after 
+    /// the mutex is locked).
     pub async fn run<B: ?Sized, M: RawMutex, C: CliBehaviour, S: ServBehaviour>(&self,
         rsock: &mut impl Read,
         wsock: &mut impl Write,
@@ -294,6 +299,8 @@ impl<'a> EmbassySunset<'a> {
     }
 
     /// Returns ControlFlow::Break on session exit.
+    ///
+    /// B will be either a CliBehaviour or ServBehaviour
     async fn progress<B: ?Sized, M: RawMutex, C: CliBehaviour, S: ServBehaviour>(&self,
         b: &Mutex<M, B>)
         -> Result<ControlFlow<()>>
diff --git a/src/behaviour.rs b/src/behaviour.rs
index 9b2ff61..085c9df 100644
--- a/src/behaviour.rs
+++ b/src/behaviour.rs
@@ -21,13 +21,15 @@ use sshwire::TextString;
 // Result, it can return an error or other options like Disconnect?
 pub type BhResult<T> = core::result::Result<T, BhError>;
 
+/// At present only a single failure type is implemented
 #[derive(Debug,Snafu)]
 pub enum BhError {
     Fail,
 }
 
 /// A stack-allocated string to store responses for usernames or passwords.
-// 100 bytes is an arbitrary size.
+///
+/// Presently limited to 100 bytes as an arbitrary fixed size.
 // TODO this might get replaced with something better
 pub type ResponseString = heapless::String<100>;
 
@@ -41,6 +43,12 @@ pub type ResponseString = heapless::String<100>;
 // TODO: another interim option would to split the async trait methods
 // into a separate trait (which impls the non-async trait)
 
+/// Provides either client or server application behaviour
+///
+/// The actual behaviour is provided by [`CliBehaviour`] or [`ServBehaviour`].
+///
+/// Applications using sunset-embassy don't need to use this directly,
+/// instead passing a CliBehaviour or ServBehaviour.
 pub enum Behaviour<'a, C: CliBehaviour, S: ServBehaviour> {
     Client(&'a mut C),
     Server(&'a mut S),
@@ -61,13 +69,13 @@ impl<'a, S: ServBehaviour> From<&'a mut S> for Behaviour<'a, UnusedCli, S> {
 impl<'a, C, S> Behaviour<'a, C, S>
     where C: CliBehaviour, S: ServBehaviour
 {
+    /// Create a new client `Behaviour` instance
     pub fn new_client(b: &'a mut C) -> Behaviour<C, UnusedServ> {
         Behaviour::<C, UnusedServ>::Client(b)
-        // b.into()
     }
 
+    /// Create a new server `Behaviour` instance
     pub fn new_server(b: &'a mut S) -> Behaviour<UnusedCli, S> {
-        // b.into()
         Behaviour::<UnusedCli, S>::Server(b)
     }
 
@@ -116,6 +124,15 @@ impl<'a, C, S> Behaviour<'a, C, S>
     }
 }
 
+/// Defines application behaviour as a client
+///
+/// The trait methods are called by the Sunset runner during the connection. Some
+/// methods request information, such as [`username()`][Self::username]. Other
+/// methods inform of events that have occurred, such as [`session_opened()`][Self::session_opened]
+/// or [`disconnected()`][Self::disconnected].
+///
+/// When running async with `sunset-embassy`, the `CliBehaviour` will be passed inside
+/// a `SunsetMutex` to allow external mutability if needed.
 pub trait CliBehaviour {
     /// Provide the user to use for authentication. Will only be called once
     /// per session.
@@ -201,6 +218,17 @@ pub trait CliBehaviour {
     }
 }
 
+/// Defines application behaviour as a server
+///
+/// The trait methods are called by the Sunset runner during the connection. The response
+/// from these methods changes how the connection is handled, for example whether to allow authentication
+/// or opening a channel.
+///
+/// Channel requests also provide a [`ChanHandle`] argument which can be passed to IO methods
+/// to read/write data.
+///
+/// When running async with `sunset-embassy`, the `ServBehaviour` will be passed inside
+/// a `SunsetMutex` to allow external mutability if needed.
 pub trait ServBehaviour {
     // TODO: load keys on demand?
     // at present `async` isn't very useful here, since it can't load keys
@@ -209,10 +237,14 @@ pub trait ServBehaviour {
     // Also could make it take a closure to call with the key, lets it just
     // be loaded on the stack rather than kept in memory for the whole lifetime.
     // TODO: a slice of references is a bit awkward?
+
+    /// Requests hostkeys for the server
     fn hostkeys(&mut self) -> BhResult<heapless::Vec<&SignKey, 2>>;
 
     #[allow(unused)]
     // TODO: or return a slice of enums
+    /// Queries whether password authentication should be allowed
+    ///
     /// Implementations should take care to avoid leaking user existence
     /// based on timing.
     fn have_auth_password(&self, username: TextString) -> bool {
@@ -220,6 +252,8 @@ pub trait ServBehaviour {
     }
 
     #[allow(unused)]
+    /// Queries whether pubkey authentication should be allowed
+    ///
     /// Implementations should take care to avoid leaking user existence
     /// based on timing.
     fn have_auth_pubkey(&self, username: TextString) -> bool {
@@ -229,6 +263,8 @@ pub trait ServBehaviour {
     #[allow(unused)]
     /// Return true to allow the user to log in with no authentication
     ///
+    /// This obviously has security implications.
+    ///
     /// Implementations may need to take care to avoid leaking user existence
     /// based on timing.
     async fn auth_unchallenged(&mut self, username: TextString<'_>) -> bool {
@@ -237,9 +273,10 @@ pub trait ServBehaviour {
 
     #[allow(unused)]
     // TODO: change password
-    /// Implementations should perform password hash comparisons
-    /// in constant time, using
-    /// [`subtle::ConstantTimeEq`] or similar.
+    /// Test if a password is valid.
+    ///
+    /// Implementations should store passwords hashed, and perform password hash comparisons
+    /// in constant time, using [`subtle::ConstantTimeEq`] or similar.
     ///
     /// Implementations may need to take care to avoid leaking user existence
     /// based on timing.
@@ -258,7 +295,7 @@ pub trait ServBehaviour {
         false
     }
 
-    /// Returns whether a session can be opened
+    /// Returns whether a session (shell or command) can be opened
     fn open_session(&mut self, chan: ChanHandle) -> channel::ChanOpened;
 
     #[allow(unused)]
@@ -271,19 +308,19 @@ pub trait ServBehaviour {
         ChanOpened::Failure((ChanFail::SSH_OPEN_UNKNOWN_CHANNEL_TYPE, chan))
     }
 
-    /// Returns a boolean request success status
+    /// Called when a shell is requested, returns a boolean request success status
     #[allow(unused)]
     fn sess_shell(&mut self, chan: ChanNum) -> bool {
         false
     }
 
-    /// Returns a boolean request success status
+    /// Called when a command execution is requested, returns a boolean request success status
     #[allow(unused)]
     fn sess_exec(&mut self, chan: ChanNum, cmd: TextString) -> bool {
         false
     }
 
-    /// Returns a boolean request success status
+    /// Called when a PTY (interactive terminal) is requested, returns a boolean request success status
     #[allow(unused)]
     fn sess_pty(&mut self, chan: ChanNum, pty: &Pty) -> bool {
         false
-- 
GitLab