Compare commits

..

4 Commits

Author SHA1 Message Date
Savanni D'Gerinel deb3415c30 Remove the fallibility testing code
It's being handled in a different branch
2022-11-25 12:49:26 -05:00
Savanni D'Gerinel 064b754786 Inject failpoints with the fail library 2022-11-25 12:08:41 -05:00
Savanni D'Gerinel 95a597c8ea Try to add some code for test error fuzzing
it doesn't compile, unfortunately, so it's all commented out
2022-11-20 12:43:33 -05:00
Savanni D'Gerinel 45df1233e5 Complete the test suite for the authentication DB 2022-11-20 12:10:28 -05:00
3 changed files with 185 additions and 50 deletions

View File

@ -6,9 +6,6 @@ use std::{convert::Infallible, str::FromStr};
use thiserror::Error; use thiserror::Error;
use uuid::{adapter::Hyphenated, Uuid}; use uuid::{adapter::Hyphenated, Uuid};
#[cfg(test)]
use crate::errors::maybe_fail;
#[derive(Debug, Error, PartialEq)] #[derive(Debug, Error, PartialEq)]
pub enum AuthenticationError { pub enum AuthenticationError {
#[error("username already exists")] #[error("username already exists")]
@ -144,7 +141,7 @@ impl FromSql for Username {
pub trait AuthenticationDB: Send + Sync { pub trait AuthenticationDB: Send + Sync {
fn create_user(&mut self, username: Username) -> AppResult<UserId, AuthenticationError>; fn create_user(&mut self, username: Username) -> AppResult<UserId, AuthenticationError>;
fn create_invitation(&mut self, user: UserId) -> AppResult<Invitation, AuthenticationError>; fn create_invitation(&mut self, user: &UserId) -> AppResult<Invitation, AuthenticationError>;
fn authenticate( fn authenticate(
&mut self, &mut self,
@ -179,8 +176,9 @@ pub struct MemoryAuth {
impl AuthenticationDB for MemoryAuth { impl AuthenticationDB for MemoryAuth {
fn create_user(&mut self, username: Username) -> AppResult<UserId, AuthenticationError> { fn create_user(&mut self, username: Username) -> AppResult<UserId, AuthenticationError> {
#[cfg(test)] if self.users.contains_key(&username) {
let _ = maybe_fail::<AuthenticationError>(vec![])?; return Ok(Err(AuthenticationError::DuplicateUsername));
}
let userid = UserId::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str()); let userid = UserId::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
self.users.insert(username.clone(), userid.clone()); self.users.insert(username.clone(), userid.clone());
@ -189,17 +187,14 @@ impl AuthenticationDB for MemoryAuth {
ok(userid) ok(userid)
} }
fn create_invitation(&mut self, user: UserId) -> AppResult<Invitation, AuthenticationError> { fn create_invitation(&mut self, user: &UserId) -> AppResult<Invitation, AuthenticationError> {
#[cfg(test)]
let _ = maybe_fail::<AuthenticationError>(vec![])?;
if !self.inverse_users.contains_key(&user) { if !self.inverse_users.contains_key(&user) {
return error::<Invitation, AuthenticationError>(AuthenticationError::UserNotFound); return error::<Invitation, AuthenticationError>(AuthenticationError::UserNotFound);
} }
let invitation = let invitation =
Invitation::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str()); Invitation::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
self.invitations.insert(invitation.clone(), user); self.invitations.insert(invitation.clone(), user.clone());
ok(invitation) ok(invitation)
} }
@ -207,9 +202,6 @@ impl AuthenticationDB for MemoryAuth {
&mut self, &mut self,
invitation: Invitation, invitation: Invitation,
) -> AppResult<SessionToken, AuthenticationError> { ) -> AppResult<SessionToken, AuthenticationError> {
#[cfg(test)]
let _ = maybe_fail::<AuthenticationError>(vec![])?;
if let Some(user) = self.invitations.get(&invitation) { if let Some(user) = self.invitations.get(&invitation) {
let session_token = let session_token =
SessionToken::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str()); SessionToken::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
@ -222,9 +214,6 @@ impl AuthenticationDB for MemoryAuth {
} }
fn delete_session(&mut self, session: SessionToken) -> AppResult<(), AuthenticationError> { fn delete_session(&mut self, session: SessionToken) -> AppResult<(), AuthenticationError> {
#[cfg(test)]
let _ = maybe_fail::<AuthenticationError>(vec![])?;
if let Some(_) = self.sessions.remove(&session) { if let Some(_) = self.sessions.remove(&session) {
ok(()) ok(())
} else { } else {
@ -233,9 +222,6 @@ impl AuthenticationDB for MemoryAuth {
} }
fn delete_invitation(&mut self, invitation: Invitation) -> AppResult<(), AuthenticationError> { fn delete_invitation(&mut self, invitation: Invitation) -> AppResult<(), AuthenticationError> {
#[cfg(test)]
let _ = maybe_fail::<AuthenticationError>(vec![])?;
if let Some(_) = self.invitations.remove(&invitation) { if let Some(_) = self.invitations.remove(&invitation) {
ok(()) ok(())
} else { } else {
@ -244,9 +230,6 @@ impl AuthenticationDB for MemoryAuth {
} }
fn delete_user(&mut self, user: UserId) -> AppResult<(), AuthenticationError> { fn delete_user(&mut self, user: UserId) -> AppResult<(), AuthenticationError> {
#[cfg(test)]
let _ = maybe_fail::<AuthenticationError>(vec![])?;
if let Some(username) = self.inverse_users.remove(&user) { if let Some(username) = self.inverse_users.remove(&user) {
let _ = self.users.remove(&username); let _ = self.users.remove(&username);
self.invitations = self self.invitations = self
@ -306,10 +289,11 @@ impl AuthenticationDB for MemoryAuth {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use std::panic;
fn with_memory_db<F>(test: F) fn with_memory_db<F>(test: F)
where where
F: Fn(Box<dyn AuthenticationDB>), F: Fn(Box<dyn AuthenticationDB>) -> () + panic::UnwindSafe,
{ {
let authdb: Box<MemoryAuth> = Box::new(Default::default()); let authdb: Box<MemoryAuth> = Box::new(Default::default());
test(authdb); test(authdb);
@ -327,6 +311,7 @@ mod test {
assert_eq!(authdb.get_username(&userid).unwrap(), Ok(username)); assert_eq!(authdb.get_username(&userid).unwrap(), Ok(username));
assert!(authdb.list_users().unwrap().unwrap().contains(&userid)); assert!(authdb.list_users().unwrap().unwrap().contains(&userid));
} }
with_memory_db(test_case); with_memory_db(test_case);
} }
@ -346,43 +331,197 @@ mod test {
with_memory_db(test_case); with_memory_db(test_case);
} }
#[test]
fn it_allows_multiple_invitations_per_user() {
unimplemented!()
}
#[test] #[test]
fn it_exchanges_an_invitation_for_a_session() { fn it_exchanges_an_invitation_for_a_session() {
unimplemented!() fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
} let username = Username::from("shephard");
let userid = authdb
#[test] .create_user(username.clone())
fn it_allows_multiple_sessions_per_user() { .expect("no fatal errors")
unimplemented!() .expect("user to be created");
} let invitation = authdb
.create_invitation(&userid)
#[test] .expect("no fatal errors")
fn it_disallows_invitation_reuse() { .expect("invitation to be created");
unimplemented!() let _ = authdb
.authenticate(invitation)
.expect("no fatal errors")
.expect("to receive a session token");
}
with_memory_db(test_case);
} }
#[test] #[test]
fn it_identifies_a_user_by_session() { fn it_identifies_a_user_by_session() {
unimplemented!() fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let shephard = Username::from("shephard");
let garrus = Username::from("garrus");
let (shephard_session, shephard_id) = {
let userid = authdb.create_user(shephard.clone()).unwrap().unwrap();
let invitation = authdb.create_invitation(&userid).unwrap().unwrap();
(authdb.authenticate(invitation).unwrap().unwrap(), userid)
};
let (garrus_session, garrus_id) = {
let userid = authdb.create_user(garrus.clone()).unwrap().unwrap();
let invitation = authdb.create_invitation(&userid).unwrap().unwrap();
(authdb.authenticate(invitation).unwrap().unwrap(), userid)
};
assert_eq!(
authdb.validate_session(shephard_session).unwrap().unwrap(),
(shephard, shephard_id)
);
assert_eq!(
authdb.validate_session(garrus_session).unwrap().unwrap(),
(garrus, garrus_id)
);
}
with_memory_db(test_case);
}
#[test]
fn it_allows_multiple_invitations_per_user() {
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let username = Username::from("shephard");
let userid = authdb.create_user(username).unwrap().unwrap();
let invite1 = authdb.create_invitation(&userid).unwrap().unwrap();
let invite2 = authdb.create_invitation(&userid).unwrap().unwrap();
assert_ne!(invite1, invite2);
authdb.authenticate(invite1).unwrap().unwrap();
authdb.authenticate(invite2).unwrap().unwrap();
}
with_memory_db(test_case);
}
#[test]
fn it_allows_multiple_sessions_per_user() {
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let username = Username::from("shephard");
let userid = authdb.create_user(username.clone()).unwrap().unwrap();
let invite1 = authdb.create_invitation(&userid).unwrap().unwrap();
let invite2 = authdb.create_invitation(&userid).unwrap().unwrap();
assert_ne!(invite1, invite2);
let session1 = authdb.authenticate(invite1).unwrap().unwrap();
let session2 = authdb.authenticate(invite2).unwrap().unwrap();
assert_ne!(session1, session2);
assert_eq!(
authdb.validate_session(session1).unwrap().unwrap(),
(username.clone(), userid.clone())
);
assert_eq!(
authdb.validate_session(session2).unwrap().unwrap(),
(username, userid)
);
}
with_memory_db(test_case);
}
#[test]
fn it_disallows_invitation_reuse() {
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let username = Username::from("shephard");
let userid = authdb
.create_user(username.clone())
.expect("no fatal errors")
.expect("user to be created");
let invitation = authdb
.create_invitation(&userid)
.expect("no fatal errors")
.expect("invitation to be created");
let _ = authdb
.authenticate(invitation.clone())
.expect("no fatal errors")
.expect("to receive a session token");
let reuse_result = authdb.authenticate(invitation).expect("no fatal errors");
assert_eq!(reuse_result, Err(AuthenticationError::InvalidInvitation));
}
with_memory_db(test_case);
} }
#[test] #[test]
fn it_deletes_a_user_and_invalidates_tokens() { fn it_deletes_a_user_and_invalidates_tokens() {
unimplemented!() fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let shephard = Username::from("shephard");
let garrus = Username::from("garrus");
let (shephard_session, shephard_id) = {
let userid = authdb.create_user(shephard.clone()).unwrap().unwrap();
let invitation = authdb.create_invitation(&userid).unwrap().unwrap();
(authdb.authenticate(invitation).unwrap().unwrap(), userid)
};
let (garrus_invitation, garrus_id) = {
let userid = authdb.create_user(garrus.clone()).unwrap().unwrap();
(authdb.create_invitation(&userid).unwrap().unwrap(), userid)
};
authdb.delete_user(shephard_id).unwrap().unwrap();
assert_eq!(
authdb.validate_session(shephard_session).unwrap(),
Err(AuthenticationError::InvalidSession)
);
authdb.delete_user(garrus_id).unwrap().unwrap();
assert_eq!(
authdb.authenticate(garrus_invitation).unwrap(),
Err(AuthenticationError::InvalidInvitation)
);
}
with_memory_db(test_case);
} }
#[test] #[test]
fn it_deletes_a_session() { fn it_deletes_a_session() {
unimplemented!() fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let username = Username::from("shephard");
let userid = authdb
.create_user(username.clone())
.expect("no fatal errors")
.expect("user to be created");
let invitation = authdb
.create_invitation(&userid)
.expect("no fatal errors")
.expect("invitation to be created");
let session = authdb.authenticate(invitation).unwrap().unwrap();
authdb.delete_session(session.clone()).unwrap().unwrap();
assert_eq!(
authdb.validate_session(session).unwrap(),
Err(AuthenticationError::InvalidSession)
);
}
with_memory_db(test_case);
} }
#[test] #[test]
fn it_deletes_an_invitation() { fn it_deletes_an_invitation() {
unimplemented!() fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
let username = Username::from("shephard");
let userid = authdb
.create_user(username.clone())
.expect("no fatal errors")
.expect("user to be created");
let invitation = authdb
.create_invitation(&userid)
.expect("no fatal errors")
.expect("invitation to be created");
authdb
.delete_invitation(invitation.clone())
.unwrap()
.unwrap();
assert_eq!(
authdb.authenticate(invitation).unwrap(),
Err(AuthenticationError::InvalidInvitation)
);
}
with_memory_db(test_case);
} }
} }

View File

@ -26,8 +26,3 @@ pub fn error<A, E>(err: E) -> AppResult<A, E> {
pub fn fatal<A, E>(err: FatalError) -> AppResult<A, E> { pub fn fatal<A, E>(err: FatalError) -> AppResult<A, E> {
Err(err) Err(err)
} }
#[cfg(test)]
pub fn maybe_fail<E>(possible_errors: Vec<E>) -> AppResult<(), E> {
ok(())
}

View File

@ -87,6 +87,7 @@ struct MakeInvitationResponse {
invitation: Invitation, invitation: Invitation,
} }
/*
fn make_invitation( fn make_invitation(
auth_ctx: Arc<RwLock<impl AuthenticationDB>>, auth_ctx: Arc<RwLock<impl AuthenticationDB>>,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone { ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
@ -104,6 +105,7 @@ fn make_invitation(
} }
}) })
} }
*/
#[derive(Deserialize)] #[derive(Deserialize)]
struct AuthenticateParams { struct AuthenticateParams {
@ -191,7 +193,6 @@ pub async fn main() {
.or(echo_unauthenticated); .or(echo_unauthenticated);
*/ */
let filter = make_user(auth_ctx.clone()) let filter = make_user(auth_ctx.clone())
.or(make_invitation(auth_ctx.clone()))
.or(authenticate(auth_ctx.clone())) .or(authenticate(auth_ctx.clone()))
.or(echo_authenticated) .or(echo_authenticated)
.or(echo_unauthenticated); .or(echo_unauthenticated);