Refactor authentication into distant-auth

pull/191/head
Chip Senkbeil 12 months ago
parent 2042684c97
commit 8cf7f11269
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -40,6 +40,8 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Check Cargo availability
run: cargo --version
- name: distant-auth (all features)
run: cargo clippy -p distant-auth --all-targets --verbose --all-features
- name: distant-net (all features)
run: cargo clippy -p distant-net --all-targets --verbose --all-features
- name: distant-core (all features)
@ -158,6 +160,8 @@ jobs:
if: matrix.os == 'windows-latest'
run: echo "NEXTEST_RETRIES=9" >> $GITHUB_ENV
shell: bash
- name: Run auth tests (all features)
run: cargo nextest run --profile ci --release --all-features -p distant-net
- name: Run net tests (default features)
run: cargo nextest run --profile ci --release -p distant-net
- name: Build core (default features)

@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- CLI `--lsp [<SCHEME>]` scheme now expects just the scheme and not `://`
- Moved `distant_net::common::authentication` to separate crate `distant-auth`
- Moved `distant_net::common::authentication::Keychain` to
`distant_net::common::Keychain`
- Moved `distant_net::common::transport::framed::codec::encryption::SecretKey`
and similar to `distant_net::common::SecretKey`
## [0.20.0-alpha.6]

14
Cargo.lock generated

@ -849,6 +849,19 @@ dependencies = [
"winsplit",
]
[[package]]
name = "distant-auth"
version = "0.20.0-alpha.7"
dependencies = [
"async-trait",
"derive_more",
"env_logger",
"log",
"serde",
"test-log",
"tokio",
]
[[package]]
name = "distant-core"
version = "0.20.0-alpha.7"
@ -896,6 +909,7 @@ dependencies = [
"bytes",
"chacha20poly1305",
"derive_more",
"distant-auth",
"dyn-clone",
"env_logger",
"flate2",

@ -12,7 +12,7 @@ readme = "README.md"
license = "MIT OR Apache-2.0"
[workspace]
members = ["distant-core", "distant-net", "distant-ssh2"]
members = ["distant-auth", "distant-core", "distant-net", "distant-ssh2"]
[profile.release]
opt-level = 'z'

@ -0,0 +1,27 @@
[package]
name = "distant-auth"
description = "Authentication library for distant, providing various implementations"
categories = ["authentication"]
keywords = ["auth", "authentication", "async"]
version = "0.20.0-alpha.7"
authors = ["Chip Senkbeil <chip@senkbeil.org>"]
edition = "2021"
homepage = "https://github.com/chipsenkbeil/distant"
repository = "https://github.com/chipsenkbeil/distant"
readme = "README.md"
license = "MIT OR Apache-2.0"
[features]
default = []
tests = []
[dependencies]
async-trait = "0.1.68"
derive_more = { version = "0.99.17", default-features = false, features = ["display", "from", "error"] }
log = "0.4.17"
serde = { version = "1.0.159", features = ["derive"] }
[dev-dependencies]
env_logger = "0.10.0"
test-log = "0.2.11"
tokio = { version = "1.27.0", features = ["full"] }

@ -0,0 +1,110 @@
use std::io;
use async_trait::async_trait;
use crate::handler::AuthHandler;
use crate::msg::*;
/// Represents an interface for authenticating with a server.
#[async_trait]
pub trait Authenticate {
/// Performs authentication by leveraging the `handler` for any received challenge.
async fn authenticate(&mut self, mut handler: impl AuthHandler + Send) -> io::Result<()>;
}
/// Represents an interface for submitting challenges for authentication.
#[async_trait]
pub trait Authenticator: Send {
/// Issues an initialization notice and returns the response indicating which authentication
/// methods to pursue
async fn initialize(
&mut self,
initialization: Initialization,
) -> io::Result<InitializationResponse>;
/// Issues a challenge and returns the answers to the `questions` asked.
async fn challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse>;
/// Requests verification of some `kind` and `text`, returning true if passed verification.
async fn verify(&mut self, verification: Verification) -> io::Result<VerificationResponse>;
/// Reports information with no response expected.
async fn info(&mut self, info: Info) -> io::Result<()>;
/// Reports an error occurred during authentication, consuming the authenticator since no more
/// challenges should be issued.
async fn error(&mut self, error: Error) -> io::Result<()>;
/// Reports that the authentication has started for a specific method.
async fn start_method(&mut self, start_method: StartMethod) -> io::Result<()>;
/// Reports that the authentication has finished successfully, consuming the authenticator
/// since no more challenges should be issued.
async fn finished(&mut self) -> io::Result<()>;
}
/// Represents an implementator of [`Authenticator`] used purely for testing purposes.
#[cfg(any(test, feature = "tests"))]
pub struct TestAuthenticator {
pub initialize: Box<dyn FnMut(Initialization) -> io::Result<InitializationResponse> + Send>,
pub challenge: Box<dyn FnMut(Challenge) -> io::Result<ChallengeResponse> + Send>,
pub verify: Box<dyn FnMut(Verification) -> io::Result<VerificationResponse> + Send>,
pub info: Box<dyn FnMut(Info) -> io::Result<()> + Send>,
pub error: Box<dyn FnMut(Error) -> io::Result<()> + Send>,
pub start_method: Box<dyn FnMut(StartMethod) -> io::Result<()> + Send>,
pub finished: Box<dyn FnMut() -> io::Result<()> + Send>,
}
#[cfg(any(test, feature = "tests"))]
impl Default for TestAuthenticator {
fn default() -> Self {
Self {
initialize: Box::new(|x| Ok(InitializationResponse { methods: x.methods })),
challenge: Box::new(|x| {
Ok(ChallengeResponse {
answers: x.questions.into_iter().map(|x| x.text).collect(),
})
}),
verify: Box::new(|_| Ok(VerificationResponse { valid: true })),
info: Box::new(|_| Ok(())),
error: Box::new(|_| Ok(())),
start_method: Box::new(|_| Ok(())),
finished: Box::new(|| Ok(())),
}
}
}
#[cfg(any(test, feature = "tests"))]
#[async_trait]
impl Authenticator for TestAuthenticator {
async fn initialize(
&mut self,
initialization: Initialization,
) -> io::Result<InitializationResponse> {
(self.initialize)(initialization)
}
async fn challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
(self.challenge)(challenge)
}
async fn verify(&mut self, verification: Verification) -> io::Result<VerificationResponse> {
(self.verify)(verification)
}
async fn info(&mut self, info: Info) -> io::Result<()> {
(self.info)(info)
}
async fn error(&mut self, error: Error) -> io::Result<()> {
(self.error)(error)
}
async fn start_method(&mut self, start_method: StartMethod) -> io::Result<()> {
(self.start_method)(start_method)
}
async fn finished(&mut self) -> io::Result<()> {
(self.finished)()
}
}

@ -1,11 +1,11 @@
use std::collections::HashMap;
use std::fmt::Display;
use std::io;
use async_trait::async_trait;
use super::msg::*;
use crate::common::authentication::Authenticator;
use crate::common::HeapSecretKey;
use crate::authenticator::Authenticator;
use crate::msg::*;
mod methods;
pub use methods::*;
@ -176,7 +176,10 @@ impl AuthHandlerMap {
impl AuthHandlerMap {
/// Consumes the map, returning a new map that supports the `static_key` method.
pub fn with_static_key(mut self, key: impl Into<HeapSecretKey>) -> Self {
pub fn with_static_key<K>(mut self, key: K) -> Self
where
K: Display + Send + 'static,
{
self.insert_method_handler("static_key", StaticKeyAuthMethodHandler::simple(key));
self
}
@ -343,3 +346,77 @@ impl<'a> AuthMethodHandler for DynAuthHandler<'a> {
self.0.on_error(error).await
}
}
/// Represents an implementator of [`AuthHandler`] used purely for testing purposes.
#[cfg(any(test, feature = "tests"))]
pub struct TestAuthHandler {
pub on_initialization:
Box<dyn FnMut(Initialization) -> io::Result<InitializationResponse> + Send>,
pub on_challenge: Box<dyn FnMut(Challenge) -> io::Result<ChallengeResponse> + Send>,
pub on_verification: Box<dyn FnMut(Verification) -> io::Result<VerificationResponse> + Send>,
pub on_info: Box<dyn FnMut(Info) -> io::Result<()> + Send>,
pub on_error: Box<dyn FnMut(Error) -> io::Result<()> + Send>,
pub on_start_method: Box<dyn FnMut(StartMethod) -> io::Result<()> + Send>,
pub on_finished: Box<dyn FnMut() -> io::Result<()> + Send>,
}
#[cfg(any(test, feature = "tests"))]
impl Default for TestAuthHandler {
fn default() -> Self {
Self {
on_initialization: Box::new(|x| Ok(InitializationResponse { methods: x.methods })),
on_challenge: Box::new(|x| {
Ok(ChallengeResponse {
answers: x.questions.into_iter().map(|x| x.text).collect(),
})
}),
on_verification: Box::new(|_| Ok(VerificationResponse { valid: true })),
on_info: Box::new(|_| Ok(())),
on_error: Box::new(|_| Ok(())),
on_start_method: Box::new(|_| Ok(())),
on_finished: Box::new(|| Ok(())),
}
}
}
#[cfg(any(test, feature = "tests"))]
#[async_trait]
impl AuthHandler for TestAuthHandler {
async fn on_initialization(
&mut self,
initialization: Initialization,
) -> io::Result<InitializationResponse> {
(self.on_initialization)(initialization)
}
async fn on_start_method(&mut self, start_method: StartMethod) -> io::Result<()> {
(self.on_start_method)(start_method)
}
async fn on_finished(&mut self) -> io::Result<()> {
(self.on_finished)()
}
}
#[cfg(any(test, feature = "tests"))]
#[async_trait]
impl AuthMethodHandler for TestAuthHandler {
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
(self.on_challenge)(challenge)
}
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse> {
(self.on_verification)(verification)
}
async fn on_info(&mut self, info: Info) -> io::Result<()> {
(self.on_info)(info)
}
async fn on_error(&mut self, error: Error) -> io::Result<()> {
(self.on_error)(error)
}
}

@ -2,9 +2,7 @@ use std::io;
use async_trait::async_trait;
use super::{
Challenge, ChallengeResponse, Error, Info, Verification, VerificationKind, VerificationResponse,
};
use crate::msg::{Challenge, ChallengeResponse, Error, Info, Verification, VerificationResponse};
/// Interface for a handler of authentication requests for a specific authentication method.
#[async_trait]

@ -3,9 +3,9 @@ use std::io;
use async_trait::async_trait;
use log::*;
use super::{
AuthMethodHandler, Challenge, ChallengeResponse, Error, Info, Verification, VerificationKind,
VerificationResponse,
use crate::handler::AuthMethodHandler;
use crate::msg::{
Challenge, ChallengeResponse, Error, Info, Verification, VerificationKind, VerificationResponse,
};
/// Blocking implementation of [`AuthMethodHandler`] that uses prompts to communicate challenge &

@ -1,28 +1,26 @@
use std::fmt::Display;
use std::io;
use async_trait::async_trait;
use log::*;
use super::{
AuthMethodHandler, Challenge, ChallengeResponse, Error, Info, Verification,
VerificationResponse,
};
use crate::common::HeapSecretKey;
use crate::handler::AuthMethodHandler;
use crate::msg::{Challenge, ChallengeResponse, Error, Info, Verification, VerificationResponse};
/// Implementation of [`AuthMethodHandler`] that answers challenge requests using a static
/// [`HeapSecretKey`]. All other portions of method authentication are handled by another
/// [`AuthMethodHandler`].
pub struct StaticKeyAuthMethodHandler {
key: HeapSecretKey,
pub struct StaticKeyAuthMethodHandler<K> {
key: K,
handler: Box<dyn AuthMethodHandler>,
}
impl StaticKeyAuthMethodHandler {
impl<K> StaticKeyAuthMethodHandler<K> {
/// Creates a new [`StaticKeyAuthMethodHandler`] that responds to challenges using a static
/// `key`. All other requests are passed to the `handler`.
pub fn new<T: AuthMethodHandler + 'static>(key: impl Into<HeapSecretKey>, handler: T) -> Self {
pub fn new<T: AuthMethodHandler + 'static>(key: K, handler: T) -> Self {
Self {
key: key.into(),
key,
handler: Box::new(handler),
}
}
@ -30,7 +28,7 @@ impl StaticKeyAuthMethodHandler {
/// Creates a new [`StaticKeyAuthMethodHandler`] that responds to challenges using a static
/// `key`. All other requests are passed automatically, meaning that verification is always
/// approvide and info/errors are ignored.
pub fn simple(key: impl Into<HeapSecretKey>) -> Self {
pub fn simple(key: K) -> Self {
Self::new(key, {
struct __AuthMethodHandler;
@ -62,7 +60,10 @@ impl StaticKeyAuthMethodHandler {
}
#[async_trait]
impl AuthMethodHandler for StaticKeyAuthMethodHandler {
impl<K> AuthMethodHandler for StaticKeyAuthMethodHandler<K>
where
K: Display + Send,
{
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
trace!("on_challenge({challenge:?})");
let mut answers = Vec::new();
@ -103,11 +104,11 @@ mod tests {
use test_log::test;
use super::*;
use crate::common::authentication::msg::{ErrorKind, Question, VerificationKind};
use crate::msg::{ErrorKind, Question, VerificationKind};
#[test(tokio::test)]
async fn on_challenge_should_fail_if_non_key_question_received() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let mut handler = StaticKeyAuthMethodHandler::simple(String::from("secret-key"));
handler
.on_challenge(Challenge {
@ -120,7 +121,7 @@ mod tests {
#[test(tokio::test)]
async fn on_challenge_should_answer_with_stringified_key_for_key_questions() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let mut handler = StaticKeyAuthMethodHandler::simple(String::from("secret-key"));
let response = handler
.on_challenge(Challenge {
@ -135,7 +136,7 @@ mod tests {
#[test(tokio::test)]
async fn on_verification_should_leverage_fallback_handler() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let mut handler = StaticKeyAuthMethodHandler::simple(String::from("secret-key"));
let response = handler
.on_verification(Verification {
@ -149,7 +150,7 @@ mod tests {
#[test(tokio::test)]
async fn on_info_should_leverage_fallback_handler() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let mut handler = StaticKeyAuthMethodHandler::simple(String::from("secret-key"));
handler
.on_info(Info {
@ -161,7 +162,7 @@ mod tests {
#[test(tokio::test)]
async fn on_error_should_leverage_fallback_handler() {
let mut handler = StaticKeyAuthMethodHandler::simple(HeapSecretKey::generate(32).unwrap());
let mut handler = StaticKeyAuthMethodHandler::simple(String::from("secret-key"));
handler
.on_error(Error {

@ -0,0 +1,14 @@
mod authenticator;
mod handler;
mod methods;
pub mod msg;
pub use authenticator::*;
pub use handler::*;
pub use methods::*;
#[cfg(any(test, feature = "tests"))]
pub mod tests {
pub use crate::TestAuthHandler;
pub use crate::TestAuthenticator;
}

@ -1,12 +1,12 @@
use std::collections::HashMap;
use std::io;
use std::str::FromStr;
use async_trait::async_trait;
use log::*;
use super::super::HeapSecretKey;
use super::msg::*;
use super::Authenticator;
use crate::authenticator::Authenticator;
use crate::msg::*;
mod none;
mod static_key;
@ -48,7 +48,10 @@ impl Verifier {
}
/// Creates a verifier that uses the [`StaticKeyAuthenticationMethod`] exclusively.
pub fn static_key(key: impl Into<HeapSecretKey>) -> Self {
pub fn static_key<K>(key: K) -> Self
where
K: FromStr + PartialEq + Send + Sync + 'static,
{
Self::new(vec![
Box::new(StaticKeyAuthenticationMethod::new(key)) as Box<dyn AuthenticationMethod>
])
@ -117,10 +120,12 @@ pub trait AuthenticationMethod: Send + Sync {
#[cfg(test)]
mod tests {
use std::sync::mpsc;
use test_log::test;
use super::*;
use crate::common::FramedTransport;
use crate::authenticator::TestAuthenticator;
struct SuccessAuthenticationMethod;
@ -150,147 +155,131 @@ mod tests {
#[test(tokio::test)]
async fn verifier_should_fail_to_verify_if_initialization_fails() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame(b"invalid initialization response")
.await
.unwrap();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| Err(io::Error::from(io::ErrorKind::Other))),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> =
vec![Box::new(SuccessAuthenticationMethod)];
let verifier = Verifier::from(methods);
verifier.verify(&mut t1).await.unwrap_err();
verifier.verify(&mut authenticator).await.unwrap_err();
}
#[test(tokio::test)]
async fn verifier_should_fail_to_verify_if_fails_to_send_finished_indicator_after_success() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![SuccessAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
},
))
.await
.unwrap();
// Then drop the transport so it cannot receive anything else
drop(t2);
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![SuccessAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
})
}),
finished: Box::new(|| Err(io::Error::new(io::ErrorKind::Other, "test error"))),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> =
vec![Box::new(SuccessAuthenticationMethod)];
let verifier = Verifier::from(methods);
assert_eq!(
verifier.verify(&mut t1).await.unwrap_err().kind(),
io::ErrorKind::WriteZero
);
let err = verifier.verify(&mut authenticator).await.unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::Other);
assert_eq!(err.to_string(), "test error");
}
#[test(tokio::test)]
async fn verifier_should_fail_to_verify_if_has_no_authentication_methods() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![SuccessAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
},
))
.await
.unwrap();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![SuccessAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
})
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> = vec![];
let verifier = Verifier::from(methods);
verifier.verify(&mut t1).await.unwrap_err();
verifier.verify(&mut authenticator).await.unwrap_err();
}
#[test(tokio::test)]
async fn verifier_should_fail_to_verify_if_initialization_yields_no_valid_authentication_methods(
) {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec!["other".to_string()].into_iter().collect(),
},
))
.await
.unwrap();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec!["other".to_string()].into_iter().collect(),
})
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> =
vec![Box::new(SuccessAuthenticationMethod)];
let verifier = Verifier::from(methods);
verifier.verify(&mut t1).await.unwrap_err();
verifier.verify(&mut authenticator).await.unwrap_err();
}
#[test(tokio::test)]
async fn verifier_should_fail_to_verify_if_no_authentication_method_succeeds() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![FailAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
},
))
.await
.unwrap();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![FailAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
})
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> = vec![Box::new(FailAuthenticationMethod)];
let verifier = Verifier::from(methods);
verifier.verify(&mut t1).await.unwrap_err();
verifier.verify(&mut authenticator).await.unwrap_err();
}
#[test(tokio::test)]
async fn verifier_should_return_id_of_authentication_method_upon_success() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![SuccessAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
},
))
.await
.unwrap();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![SuccessAuthenticationMethod.id().to_string()]
.into_iter()
.collect(),
})
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> =
vec![Box::new(SuccessAuthenticationMethod)];
let verifier = Verifier::from(methods);
assert_eq!(
verifier.verify(&mut t1).await.unwrap(),
verifier.verify(&mut authenticator).await.unwrap(),
SuccessAuthenticationMethod.id()
);
}
#[test(tokio::test)]
async fn verifier_should_try_authentication_methods_in_order_until_one_succeeds() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![
FailAuthenticationMethod.id().to_string(),
SuccessAuthenticationMethod.id().to_string(),
]
.into_iter()
.collect(),
},
))
.await
.unwrap();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![
FailAuthenticationMethod.id().to_string(),
SuccessAuthenticationMethod.id().to_string(),
]
.into_iter()
.collect(),
})
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> = vec![
Box::new(FailAuthenticationMethod),
@ -298,84 +287,79 @@ mod tests {
];
let verifier = Verifier::from(methods);
assert_eq!(
verifier.verify(&mut t1).await.unwrap(),
verifier.verify(&mut authenticator).await.unwrap(),
SuccessAuthenticationMethod.id()
);
}
#[test(tokio::test)]
async fn verifier_should_send_start_method_before_attempting_each_method() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![
FailAuthenticationMethod.id().to_string(),
SuccessAuthenticationMethod.id().to_string(),
]
.into_iter()
.collect(),
},
))
.await
.unwrap();
let (tx, rx) = mpsc::channel();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![
FailAuthenticationMethod.id().to_string(),
SuccessAuthenticationMethod.id().to_string(),
]
.into_iter()
.collect(),
})
}),
start_method: Box::new(move |method| {
tx.send(method.method).unwrap();
Ok(())
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> = vec![
Box::new(FailAuthenticationMethod),
Box::new(SuccessAuthenticationMethod),
];
Verifier::from(methods).verify(&mut t1).await.unwrap();
Verifier::from(methods)
.verify(&mut authenticator)
.await
.unwrap();
// Check that we get a start method for each of the attempted methods
match t2.read_frame_as::<Authentication>().await.unwrap().unwrap() {
Authentication::Initialization(_) => (),
x => panic!("Unexpected response: {x:?}"),
}
match t2.read_frame_as::<Authentication>().await.unwrap().unwrap() {
Authentication::StartMethod(x) => assert_eq!(x.method, FailAuthenticationMethod.id()),
x => panic!("Unexpected response: {x:?}"),
}
match t2.read_frame_as::<Authentication>().await.unwrap().unwrap() {
Authentication::StartMethod(x) => {
assert_eq!(x.method, SuccessAuthenticationMethod.id())
}
x => panic!("Unexpected response: {x:?}"),
}
assert_eq!(rx.try_recv().unwrap(), FailAuthenticationMethod.id());
assert_eq!(rx.try_recv().unwrap(), SuccessAuthenticationMethod.id());
assert_eq!(rx.try_recv().unwrap_err(), mpsc::TryRecvError::Empty);
}
#[test(tokio::test)]
async fn verifier_should_send_finished_when_a_method_succeeds() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Initialization(
InitializationResponse {
methods: vec![
FailAuthenticationMethod.id().to_string(),
SuccessAuthenticationMethod.id().to_string(),
]
.into_iter()
.collect(),
},
))
.await
.unwrap();
let (tx, rx) = mpsc::channel();
let mut authenticator = TestAuthenticator {
initialize: Box::new(|_| {
Ok(InitializationResponse {
methods: vec![
FailAuthenticationMethod.id().to_string(),
SuccessAuthenticationMethod.id().to_string(),
]
.into_iter()
.collect(),
})
}),
finished: Box::new(move || {
tx.send(()).unwrap();
Ok(())
}),
..Default::default()
};
let methods: Vec<Box<dyn AuthenticationMethod>> = vec![
Box::new(FailAuthenticationMethod),
Box::new(SuccessAuthenticationMethod),
];
Verifier::from(methods).verify(&mut t1).await.unwrap();
// Clear out the initialization and start methods
t2.read_frame_as::<Authentication>().await.unwrap().unwrap();
t2.read_frame_as::<Authentication>().await.unwrap().unwrap();
t2.read_frame_as::<Authentication>().await.unwrap().unwrap();
Verifier::from(methods)
.verify(&mut authenticator)
.await
.unwrap();
match t2.read_frame_as::<Authentication>().await.unwrap().unwrap() {
Authentication::Finished => (),
x => panic!("Unexpected response: {x:?}"),
}
rx.try_recv().unwrap();
assert_eq!(rx.try_recv().unwrap_err(), mpsc::TryRecvError::Empty);
}
}

@ -2,7 +2,8 @@ use std::io;
use async_trait::async_trait;
use super::{AuthenticationMethod, Authenticator};
use crate::authenticator::Authenticator;
use crate::methods::AuthenticationMethod;
/// Authenticaton method for a static secret key
#[derive(Clone, Debug)]

@ -0,0 +1,131 @@
use std::io;
use std::str::FromStr;
use async_trait::async_trait;
use crate::authenticator::Authenticator;
use crate::methods::AuthenticationMethod;
use crate::msg::{Challenge, Error, Question};
/// Authenticaton method for a static secret key
#[derive(Clone, Debug)]
pub struct StaticKeyAuthenticationMethod<T> {
key: T,
}
impl<T> StaticKeyAuthenticationMethod<T> {
#[inline]
pub fn new(key: T) -> Self {
Self { key }
}
}
#[async_trait]
impl<T> AuthenticationMethod for StaticKeyAuthenticationMethod<T>
where
T: FromStr + PartialEq + Send + Sync,
{
fn id(&self) -> &'static str {
"static_key"
}
async fn authenticate(&self, authenticator: &mut dyn Authenticator) -> io::Result<()> {
let response = authenticator
.challenge(Challenge {
questions: vec![Question {
label: "key".to_string(),
text: "Provide a key: ".to_string(),
options: Default::default(),
}],
options: Default::default(),
})
.await?;
if response.answers.is_empty() {
return Err(Error::non_fatal("missing answer").into_io_permission_denied());
}
match response.answers.into_iter().next().unwrap().parse::<T>() {
Ok(key) if key == self.key => Ok(()),
_ => Err(Error::non_fatal("answer does not match key").into_io_permission_denied()),
}
}
}
#[cfg(test)]
mod tests {
use test_log::test;
use super::*;
use crate::authenticator::TestAuthenticator;
use crate::msg::*;
#[test(tokio::test)]
async fn authenticate_should_fail_if_key_challenge_fails() {
let method = StaticKeyAuthenticationMethod::new(String::new());
let mut authenticator = TestAuthenticator {
challenge: Box::new(|_| Err(io::Error::new(io::ErrorKind::InvalidData, "test error"))),
..Default::default()
};
let err = method.authenticate(&mut authenticator).await.unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::InvalidData);
assert_eq!(err.to_string(), "test error");
}
#[test(tokio::test)]
async fn authenticate_should_fail_if_no_answer_included_in_challenge_response() {
let method = StaticKeyAuthenticationMethod::new(String::new());
let mut authenticator = TestAuthenticator {
challenge: Box::new(|_| {
Ok(ChallengeResponse {
answers: Vec::new(),
})
}),
..Default::default()
};
let err = method.authenticate(&mut authenticator).await.unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
assert_eq!(err.to_string(), "Error: missing answer");
}
#[test(tokio::test)]
async fn authenticate_should_fail_if_answer_does_not_match_key() {
let method = StaticKeyAuthenticationMethod::new(String::from("answer"));
let mut authenticator = TestAuthenticator {
challenge: Box::new(|_| {
Ok(ChallengeResponse {
answers: vec![String::from("other")],
})
}),
..Default::default()
};
let err = method.authenticate(&mut authenticator).await.unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
assert_eq!(err.to_string(), "Error: answer does not match key");
}
#[test(tokio::test)]
async fn authenticate_should_succeed_if_answer_matches_key() {
let method = StaticKeyAuthenticationMethod::new(String::from("answer"));
let mut authenticator = TestAuthenticator {
challenge: Box::new(|_| {
Ok(ChallengeResponse {
answers: vec![String::from("answer")],
})
}),
..Default::default()
};
method.authenticate(&mut authenticator).await.unwrap();
}
}

@ -1,8 +1,8 @@
use std::net::SocketAddr;
use std::time::Duration;
use distant_core::net::auth::{DummyAuthHandler, Verifier};
use distant_core::net::client::{Client, TcpConnector};
use distant_core::net::common::authentication::{DummyAuthHandler, Verifier};
use distant_core::net::common::PortRange;
use distant_core::net::server::Server;
use distant_core::{DistantApiServerHandler, DistantClient, LocalDistantApi};

@ -16,6 +16,7 @@ async-trait = "0.1.68"
bytes = "1.4.0"
chacha20poly1305 = "0.10.1"
derive_more = { version = "0.99.17", default-features = false, features = ["as_mut", "as_ref", "deref", "deref_mut", "display", "from", "error", "into", "into_iterator", "is_variant", "try_into"] }
distant-auth = { version = "=0.20.0-alpha.7", path = "../distant-auth" }
dyn-clone = "1.0.11"
flate2 = "1.0.25"
hex = "0.4.3"
@ -35,6 +36,7 @@ tokio = { version = "1.27.0", features = ["full"] }
schemars = { version = "0.8.12", optional = true }
[dev-dependencies]
distant-auth = { version = "=0.20.0-alpha.7", path = "../distant-auth", features = ["tests"] }
env_logger = "0.10.0"
serde_json = "1.0.95"
tempfile = "3.5.0"

@ -1,49 +1,10 @@
use std::io;
use crate::common::utils;
use crate::common::{FramedTransport, Transport};
use async_trait::async_trait;
use distant_auth::msg::*;
use distant_auth::{AuthHandler, Authenticate, Authenticator};
use log::*;
use super::msg::*;
use super::AuthHandler;
use crate::common::{utils, FramedTransport, Transport};
/// Represents an interface for authenticating with a server.
#[async_trait]
pub trait Authenticate {
/// Performs authentication by leveraging the `handler` for any received challenge.
async fn authenticate(&mut self, mut handler: impl AuthHandler + Send) -> io::Result<()>;
}
/// Represents an interface for submitting challenges for authentication.
#[async_trait]
pub trait Authenticator: Send {
/// Issues an initialization notice and returns the response indicating which authentication
/// methods to pursue
async fn initialize(
&mut self,
initialization: Initialization,
) -> io::Result<InitializationResponse>;
/// Issues a challenge and returns the answers to the `questions` asked.
async fn challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse>;
/// Requests verification of some `kind` and `text`, returning true if passed verification.
async fn verify(&mut self, verification: Verification) -> io::Result<VerificationResponse>;
/// Reports information with no response expected.
async fn info(&mut self, info: Info) -> io::Result<()>;
/// Reports an error occurred during authentication, consuming the authenticator since no more
/// challenges should be issued.
async fn error(&mut self, error: Error) -> io::Result<()>;
/// Reports that the authentication has started for a specific method.
async fn start_method(&mut self, start_method: StartMethod) -> io::Result<()>;
/// Reports that the authentication has finished successfully, consuming the authenticator
/// since no more challenges should be issued.
async fn finished(&mut self) -> io::Result<()>;
}
use std::io;
macro_rules! write_frame {
($transport:expr, $data:expr) => {{
@ -203,161 +164,20 @@ where
#[cfg(test)]
mod tests {
use distant_auth::tests::TestAuthHandler;
use test_log::test;
use tokio::sync::mpsc;
use super::*;
use crate::common::authentication::AuthMethodHandler;
#[async_trait]
trait TestAuthHandler {
async fn on_initialization(
&mut self,
_: Initialization,
) -> io::Result<InitializationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_start_method(&mut self, _: StartMethod) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_finished(&mut self) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_verification(&mut self, _: Verification) -> io::Result<VerificationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_info(&mut self, _: Info) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_error(&mut self, _: Error) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
}
#[async_trait]
impl<T: TestAuthHandler + Send> AuthHandler for T {
async fn on_initialization(
&mut self,
x: Initialization,
) -> io::Result<InitializationResponse> {
TestAuthHandler::on_initialization(self, x).await
}
async fn on_start_method(&mut self, x: StartMethod) -> io::Result<()> {
TestAuthHandler::on_start_method(self, x).await
}
async fn on_finished(&mut self) -> io::Result<()> {
TestAuthHandler::on_finished(self).await
}
}
#[async_trait]
impl<T: TestAuthHandler + Send> AuthMethodHandler for T {
async fn on_challenge(&mut self, x: Challenge) -> io::Result<ChallengeResponse> {
TestAuthHandler::on_challenge(self, x).await
}
async fn on_verification(&mut self, x: Verification) -> io::Result<VerificationResponse> {
TestAuthHandler::on_verification(self, x).await
}
async fn on_info(&mut self, x: Info) -> io::Result<()> {
TestAuthHandler::on_info(self, x).await
}
async fn on_error(&mut self, x: Error) -> io::Result<()> {
TestAuthHandler::on_error(self, x).await
}
}
macro_rules! auth_handler {
(@no_challenge @no_verification @tx($tx:ident, $ty:ty) $($methods:item)*) => {
auth_handler! {
@tx($tx, $ty)
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
async fn on_verification(
&mut self,
_: Verification,
) -> io::Result<VerificationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
$($methods)*
}
};
(@no_challenge @tx($tx:ident, $ty:ty) $($methods:item)*) => {
auth_handler! {
@tx($tx, $ty)
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
$($methods)*
}
};
(@no_verification @tx($tx:ident, $ty:ty) $($methods:item)*) => {
auth_handler! {
@tx($tx, $ty)
async fn on_verification(
&mut self,
_: Verification,
) -> io::Result<VerificationResponse> {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
$($methods)*
}
};
(@tx($tx:ident, $ty:ty) $($methods:item)*) => {{
#[allow(dead_code)]
struct __InlineAuthHandler {
tx: mpsc::Sender<$ty>,
}
#[async_trait]
impl TestAuthHandler for __InlineAuthHandler {
$($methods)*
}
__InlineAuthHandler { tx: $tx }
}};
}
#[test(tokio::test)]
async fn authenticator_initialization_should_be_able_to_successfully_complete_round_trip() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
let (tx, _) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@no_verification
@tx(tx, ())
async fn on_initialization(
&mut self,
initialization: Initialization,
) -> io::Result<InitializationResponse> {
Ok(InitializationResponse {
methods: initialization.methods,
})
}
t2.authenticate(TestAuthHandler {
on_initialization: Box::new(|x| Ok(InitializationResponse { methods: x.methods })),
..Default::default()
})
.await
.unwrap()
@ -386,29 +206,34 @@ mod tests {
#[test(tokio::test)]
async fn authenticator_challenge_should_be_able_to_successfully_complete_round_trip() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
let (tx, _) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_verification
@tx(tx, ())
async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
assert_eq!(challenge.questions, vec![Question {
label: "label".to_string(),
text: "text".to_string(),
options: vec![("question_key".to_string(), "question_value".to_string())]
t2.authenticate(TestAuthHandler {
on_challenge: Box::new(|challenge| {
assert_eq!(
challenge.questions,
vec![Question {
label: "label".to_string(),
text: "text".to_string(),
options: vec![(
"question_key".to_string(),
"question_value".to_string()
)]
.into_iter()
.collect(),
}]);
}]
);
assert_eq!(
challenge.options,
vec![("key".to_string(), "value".to_string())].into_iter().collect(),
vec![("key".to_string(), "value".to_string())]
.into_iter()
.collect(),
);
Ok(ChallengeResponse {
answers: vec!["some answer".to_string()].into_iter().collect(),
})
}
}),
..Default::default()
})
.await
.unwrap()
@ -446,23 +271,15 @@ mod tests {
#[test(tokio::test)]
async fn authenticator_verification_should_be_able_to_successfully_complete_round_trip() {
let (mut t1, mut t2) = FramedTransport::test_pair(100);
let (tx, _) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@tx(tx, ())
async fn on_verification(
&mut self,
verification: Verification,
) -> io::Result<VerificationResponse> {
t2.authenticate(TestAuthHandler {
on_verification: Box::new(|verification| {
assert_eq!(verification.kind, VerificationKind::Host);
assert_eq!(verification.text, "some text");
Ok(VerificationResponse {
valid: true,
})
}
Ok(VerificationResponse { valid: true })
}),
..Default::default()
})
.await
.unwrap()
@ -490,18 +307,12 @@ mod tests {
let (tx, mut rx) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@no_verification
@tx(tx, Info)
async fn on_info(
&mut self,
info: Info,
) -> io::Result<()> {
self.tx.send(info).await.unwrap();
t2.authenticate(TestAuthHandler {
on_info: Box::new(move |info| {
tx.try_send(info).unwrap();
Ok(())
}
}),
..Default::default()
})
.await
.unwrap()
@ -532,15 +343,12 @@ mod tests {
let (tx, mut rx) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@no_verification
@tx(tx, Error)
async fn on_error(&mut self, error: Error) -> io::Result<()> {
self.tx.send(error).await.unwrap();
t2.authenticate(TestAuthHandler {
on_error: Box::new(move |error| {
tx.try_send(error).unwrap();
Ok(())
}
}),
..Default::default()
})
.await
.unwrap()
@ -573,15 +381,12 @@ mod tests {
let (tx, mut rx) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@no_verification
@tx(tx, Error)
async fn on_error(&mut self, error: Error) -> io::Result<()> {
self.tx.send(error).await.unwrap();
t2.authenticate(TestAuthHandler {
on_error: Box::new(move |error| {
tx.try_send(error).unwrap();
Ok(())
}
}),
..Default::default()
})
.await
.unwrap()
@ -612,15 +417,12 @@ mod tests {
let (tx, mut rx) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@no_verification
@tx(tx, StartMethod)
async fn on_start_method(&mut self, start_method: StartMethod) -> io::Result<()> {
self.tx.send(start_method).await.unwrap();
t2.authenticate(TestAuthHandler {
on_start_method: Box::new(move |start_method| {
tx.try_send(start_method).unwrap();
Ok(())
}
}),
..Default::default()
})
.await
.unwrap()
@ -651,15 +453,12 @@ mod tests {
let (tx, mut rx) = mpsc::channel(1);
let task = tokio::spawn(async move {
t2.authenticate(auth_handler! {
@no_challenge
@no_verification
@tx(tx, ())
async fn on_finished(&mut self) -> io::Result<()> {
self.tx.send(()).await.unwrap();
t2.authenticate(TestAuthHandler {
on_finished: Box::new(move || {
tx.try_send(()).unwrap();
Ok(())
}
}),
..Default::default()
})
.await
.unwrap()

@ -14,12 +14,12 @@ use std::time::Duration;
use std::{convert, io};
use async_trait::async_trait;
use distant_auth::AuthHandler;
#[cfg(windows)]
pub use windows::*;
use super::ClientConfig;
use crate::client::{Client, UntypedClient};
use crate::common::authentication::AuthHandler;
use crate::common::{Connection, Transport};
/// Interface that performs the connection to produce a [`Transport`] for use by the [`Client`].

@ -1,7 +1,8 @@
mod any;
pub mod authentication;
mod connection;
mod destination;
mod key;
mod keychain;
mod listener;
mod map;
mod packet;
@ -13,6 +14,8 @@ pub use any::*;
pub(crate) use connection::Connection;
pub use connection::ConnectionId;
pub use destination::*;
pub use key::*;
pub use keychain::*;
pub use listener::*;
pub use map::*;
pub use packet::*;

@ -1,10 +0,0 @@
mod authenticator;
mod handler;
mod keychain;
mod methods;
pub mod msg;
pub use authenticator::*;
pub use handler::*;
pub use keychain::*;
pub use methods::*;

@ -1,130 +0,0 @@
use std::io;
use async_trait::async_trait;
use super::{AuthenticationMethod, Authenticator, Challenge, Error, Question};
use crate::common::HeapSecretKey;
/// Authenticaton method for a static secret key
#[derive(Clone, Debug)]
pub struct StaticKeyAuthenticationMethod {
key: HeapSecretKey,
}
impl StaticKeyAuthenticationMethod {
#[inline]
pub fn new(key: impl Into<HeapSecretKey>) -> Self {
Self { key: key.into() }
}
}
#[async_trait]
impl AuthenticationMethod for StaticKeyAuthenticationMethod {
fn id(&self) -> &'static str {
"static_key"
}
async fn authenticate(&self, authenticator: &mut dyn Authenticator) -> io::Result<()> {
let response = authenticator
.challenge(Challenge {
questions: vec![Question {
label: "key".to_string(),
text: "Provide a key: ".to_string(),
options: Default::default(),
}],
options: Default::default(),
})
.await?;
if response.answers.is_empty() {
return Err(Error::non_fatal("missing answer").into_io_permission_denied());
}
match response
.answers
.into_iter()
.next()
.unwrap()
.parse::<HeapSecretKey>()
{
Ok(key) if key == self.key => Ok(()),
_ => Err(Error::non_fatal("answer does not match key").into_io_permission_denied()),
}
}
}
#[cfg(test)]
mod tests {
use test_log::test;
use super::*;
use crate::common::authentication::msg::{AuthenticationResponse, ChallengeResponse};
use crate::common::FramedTransport;
#[test(tokio::test)]
async fn authenticate_should_fail_if_key_challenge_fails() {
let method = StaticKeyAuthenticationMethod::new(b"".to_vec());
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up an invalid frame for our challenge to ensure it fails
t2.write_frame(b"invalid initialization response")
.await
.unwrap();
assert_eq!(
method.authenticate(&mut t1).await.unwrap_err().kind(),
io::ErrorKind::InvalidData
);
}
#[test(tokio::test)]
async fn authenticate_should_fail_if_no_answer_included_in_challenge_response() {
let method = StaticKeyAuthenticationMethod::new(b"".to_vec());
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Challenge(ChallengeResponse {
answers: Vec::new(),
}))
.await
.unwrap();
assert_eq!(
method.authenticate(&mut t1).await.unwrap_err().kind(),
io::ErrorKind::PermissionDenied
);
}
#[test(tokio::test)]
async fn authenticate_should_fail_if_answer_does_not_match_key() {
let method = StaticKeyAuthenticationMethod::new(b"answer".to_vec());
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Challenge(ChallengeResponse {
answers: vec![HeapSecretKey::from(b"some key".to_vec()).to_string()],
}))
.await
.unwrap();
assert_eq!(
method.authenticate(&mut t1).await.unwrap_err().kind(),
io::ErrorKind::PermissionDenied
);
}
#[test(tokio::test)]
async fn authenticate_should_succeed_if_answer_matches_key() {
let method = StaticKeyAuthenticationMethod::new(b"answer".to_vec());
let (mut t1, mut t2) = FramedTransport::test_pair(100);
// Queue up a response to the initialization request
t2.write_frame_for(&AuthenticationResponse::Challenge(ChallengeResponse {
answers: vec![HeapSecretKey::from(b"answer".to_vec()).to_string()],
}))
.await
.unwrap();
method.authenticate(&mut t1).await.unwrap();
}
}

@ -2,14 +2,16 @@ use std::io;
use std::ops::{Deref, DerefMut};
use async_trait::async_trait;
use distant_auth::{AuthHandler, Authenticate, Verifier};
use log::*;
use serde::{Deserialize, Serialize};
use tokio::sync::oneshot;
use super::authentication::{AuthHandler, Authenticate, Keychain, KeychainResult, Verifier};
#[cfg(test)]
use super::InmemoryTransport;
use super::{Backup, FramedTransport, HeapSecretKey, Reconnectable, Transport};
use crate::common::InmemoryTransport;
use crate::common::{
Backup, FramedTransport, HeapSecretKey, Keychain, KeychainResult, Reconnectable, Transport,
};
/// Id of the connection
pub type ConnectionId = u32;
@ -455,11 +457,11 @@ impl<T: Transport> Connection<T> {
mod tests {
use std::sync::Arc;
use distant_auth::msg::Challenge;
use distant_auth::{Authenticator, DummyAuthHandler};
use test_log::test;
use super::*;
use crate::common::authentication::msg::Challenge;
use crate::common::authentication::{Authenticator, DummyAuthHandler};
use crate::common::Frame;
#[test(tokio::test)]

@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
use super::{InmemoryTransport, Interest, Ready, Reconnectable, Transport};
use crate::common::utils;
use crate::common::SecretKey32;
mod backup;
mod codec;

@ -3,9 +3,7 @@ use std::{fmt, io};
use derive_more::Display;
use super::{Codec, Frame};
mod key;
pub use key::*;
use crate::common::{SecretKey, SecretKey32};
/// Represents the type of encryption for a [`EncryptionCodec`]
#[derive(

@ -6,7 +6,7 @@ use p256::PublicKey;
use rand::rngs::OsRng;
use sha2::Sha256;
use super::SecretKey32;
use crate::common::SecretKey32;
mod pkb;
pub use pkb::PublicKeyBytes;

@ -1,3 +1,4 @@
mod authentication;
pub mod client;
pub mod common;
pub mod manager;
@ -6,3 +7,6 @@ pub mod server;
pub use client::{Client, ReconnectStrategy};
pub use server::Server;
pub use {log, paste};
/// Authentication functionality tied to network operations.
pub use distant_auth as auth;

@ -1,10 +1,10 @@
use std::io;
use distant_auth::msg::{Authentication, AuthenticationResponse};
use distant_auth::AuthHandler;
use log::*;
use crate::client::Client;
use crate::common::authentication::msg::{Authentication, AuthenticationResponse};
use crate::common::authentication::AuthHandler;
use crate::common::{ConnectionId, Destination, Map, Request};
use crate::manager::data::{
ConnectionInfo, ConnectionList, ManagerCapabilities, ManagerRequest, ManagerResponse,
@ -298,9 +298,10 @@ impl ManagerClient {
#[cfg(test)]
mod tests {
use distant_auth::DummyAuthHandler;
use super::*;
use crate::client::UntypedClient;
use crate::common::authentication::DummyAuthHandler;
use crate::common::{Connection, InmemoryTransport, Request, Response};
fn setup() -> (ManagerClient, Connection<InmemoryTransport>) {

@ -1,9 +1,9 @@
use derive_more::IsVariant;
use distant_auth::msg::AuthenticationResponse;
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumDiscriminants, EnumIter, EnumMessage, EnumString};
use super::{ManagerAuthenticationId, ManagerChannelId};
use crate::common::authentication::msg::AuthenticationResponse;
use crate::common::{ConnectionId, Destination, Map, UntypedRequest};
#[allow(clippy::large_enum_variant)]

@ -1,9 +1,9 @@
use distant_auth::msg::Authentication;
use serde::{Deserialize, Serialize};
use super::{
ConnectionInfo, ConnectionList, ManagerAuthenticationId, ManagerCapabilities, ManagerChannelId,
};
use crate::common::authentication::msg::Authentication;
use crate::common::{ConnectionId, Destination, UntypedResponse};
#[derive(Clone, Debug, Serialize, Deserialize)]

@ -3,10 +3,10 @@ use std::io;
use std::sync::Arc;
use async_trait::async_trait;
use distant_auth::msg::AuthenticationResponse;
use log::*;
use tokio::sync::{oneshot, RwLock};
use crate::common::authentication::msg::AuthenticationResponse;
use crate::common::{ConnectionId, Destination, Map};
use crate::manager::{
ConnectionInfo, ConnectionList, ManagerAuthenticationId, ManagerCapabilities, ManagerChannelId,

@ -3,10 +3,10 @@ use std::io;
use std::sync::Arc;
use async_trait::async_trait;
use distant_auth::msg::*;
use distant_auth::Authenticator;
use tokio::sync::{oneshot, RwLock};
use crate::common::authentication::msg::*;
use crate::common::authentication::Authenticator;
use crate::manager::data::{ManagerAuthenticationId, ManagerResponse};
use crate::server::ServerReply;

@ -2,9 +2,9 @@ use std::future::Future;
use std::io;
use async_trait::async_trait;
use distant_auth::Authenticator;
use crate::client::UntypedClient;
use crate::common::authentication::Authenticator;
use crate::common::{Destination, Map};
pub type BoxedLaunchHandler = Box<dyn LaunchHandler>;
@ -67,19 +67,15 @@ macro_rules! boxed_launch_handler {
let x: $crate::manager::BoxedLaunchHandler = Box::new(
|$destination: &$crate::common::Destination,
$options: &$crate::common::Map,
$authenticator: &mut dyn $crate::common::authentication::Authenticator| async {
$body
},
$authenticator: &mut dyn $crate::auth::Authenticator| async { $body },
);
x
}};
(move |$destination:ident, $options:ident, $authenticator:ident| $(async)? $body:block) => {{
let x: $crate::manager::BoxedLaunchHandler = Box::new(
move |$destination: &$crate::common::Destination,
$options: &$crate::common::Map,
$authenticator: &mut dyn $crate::common::authentication::Authenticator| async move {
$body
},
$options: &$crate::common::Map,
$authenticator: &mut dyn $crate::auth::Authenticator| async move { $body },
);
x
}};
@ -141,19 +137,15 @@ macro_rules! boxed_connect_handler {
let x: $crate::manager::BoxedConnectHandler = Box::new(
|$destination: &$crate::common::Destination,
$options: &$crate::common::Map,
$authenticator: &mut dyn $crate::common::authentication::Authenticator| async {
$body
},
$authenticator: &mut dyn $crate::auth::Authenticator| async { $body },
);
x
}};
(move |$destination:ident, $options:ident, $authenticator:ident| $(async)? $body:block) => {{
let x: $crate::manager::BoxedConnectHandler = Box::new(
move |$destination: &$crate::common::Destination,
$options: &$crate::common::Map,
$authenticator: &mut dyn $crate::common::authentication::Authenticator| async move {
$body
},
$options: &$crate::common::Map,
$authenticator: &mut dyn $crate::auth::Authenticator| async move { $body },
);
x
}};

@ -3,12 +3,12 @@ use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use distant_auth::Verifier;
use log::*;
use serde::de::DeserializeOwned;
use serde::Serialize;
use tokio::sync::{broadcast, RwLock};
use crate::common::authentication::Verifier;
use crate::common::{Listener, Response, Transport};
mod builder;
@ -246,13 +246,11 @@ mod tests {
use std::time::Duration;
use async_trait::async_trait;
use distant_auth::{AuthenticationMethod, DummyAuthHandler, NoneAuthenticationMethod};
use test_log::test;
use tokio::sync::mpsc;
use super::*;
use crate::common::authentication::{
AuthenticationMethod, DummyAuthHandler, NoneAuthenticationMethod,
};
use crate::common::{Connection, InmemoryTransport, MpscListener, Request, Response};
pub struct TestServerHandler;

@ -1,10 +1,10 @@
use std::io;
use std::net::IpAddr;
use distant_auth::Verifier;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::common::authentication::Verifier;
use crate::common::{PortRange, TcpListener};
use crate::server::{Server, ServerConfig, ServerHandler, TcpServerRef};
@ -60,11 +60,11 @@ mod tests {
use std::net::{Ipv6Addr, SocketAddr};
use async_trait::async_trait;
use distant_auth::DummyAuthHandler;
use test_log::test;
use super::*;
use crate::client::Client;
use crate::common::authentication::DummyAuthHandler;
use crate::common::Request;
use crate::server::ServerCtx;

@ -1,10 +1,10 @@
use std::io;
use std::path::Path;
use distant_auth::Verifier;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::common::authentication::Verifier;
use crate::common::UnixSocketListener;
use crate::server::{Server, ServerConfig, ServerHandler, UnixSocketServerRef};
@ -59,12 +59,12 @@ where
#[cfg(test)]
mod tests {
use async_trait::async_trait;
use distant_auth::DummyAuthHandler;
use tempfile::NamedTempFile;
use test_log::test;
use super::*;
use crate::client::Client;
use crate::common::authentication::DummyAuthHandler;
use crate::common::Request;
use crate::server::ServerCtx;

@ -5,6 +5,7 @@ use std::sync::{Arc, Weak};
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
use distant_auth::Verifier;
use log::*;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -15,8 +16,9 @@ use super::{
ConnectionCtx, ConnectionState, ServerCtx, ServerHandler, ServerReply, ServerState,
ShutdownTimer,
};
use crate::common::authentication::{Keychain, Verifier};
use crate::common::{Backup, Connection, Frame, Interest, Response, Transport, UntypedRequest};
use crate::common::{
Backup, Connection, Frame, Interest, Keychain, Response, Transport, UntypedRequest,
};
pub type ServerKeychain = Keychain<oneshot::Receiver<Backup>>;
@ -591,10 +593,10 @@ mod tests {
use std::sync::atomic::{AtomicBool, Ordering};
use async_trait::async_trait;
use distant_auth::DummyAuthHandler;
use test_log::test;
use super::*;
use crate::common::authentication::DummyAuthHandler;
use crate::common::{
HeapSecretKey, InmemoryTransport, Ready, Reconnectable, Request, Response,
};

@ -3,8 +3,7 @@ use std::collections::HashMap;
use tokio::sync::{mpsc, oneshot, RwLock};
use tokio::task::JoinHandle;
use crate::common::authentication::Keychain;
use crate::common::{Backup, ConnectionId};
use crate::common::{Backup, ConnectionId, Keychain};
/// Contains all top-level state for the server
pub struct ServerState<T> {

@ -1,9 +1,9 @@
use std::io;
use async_trait::async_trait;
use distant_net::auth::{DummyAuthHandler, Verifier};
use distant_net::boxed_connect_handler;
use distant_net::client::Client;
use distant_net::common::authentication::{DummyAuthHandler, Verifier};
use distant_net::common::{Destination, InmemoryTransport, Map, OneshotListener};
use distant_net::manager::{Config, ManagerClient, ManagerServer};
use distant_net::server::{Server, ServerCtx, ServerHandler};

@ -1,6 +1,6 @@
use async_trait::async_trait;
use distant_auth::{DummyAuthHandler, Verifier};
use distant_net::client::Client;
use distant_net::common::authentication::{DummyAuthHandler, Verifier};
use distant_net::common::{InmemoryTransport, OneshotListener};
use distant_net::server::{Server, ServerCtx, ServerHandler};
use log::*;

@ -1,6 +1,6 @@
use async_trait::async_trait;
use distant_auth::{DummyAuthHandler, Verifier};
use distant_net::client::Client;
use distant_net::common::authentication::{DummyAuthHandler, Verifier};
use distant_net::common::{InmemoryTransport, OneshotListener, Request};
use distant_net::server::{Server, ServerCtx, ServerHandler};
use log::*;

@ -12,8 +12,8 @@ use std::time::Duration;
use async_compat::CompatExt;
use async_once_cell::OnceCell;
use async_trait::async_trait;
use distant_core::net::auth::{AuthHandlerMap, DummyAuthHandler, Verifier};
use distant_core::net::client::{Client, ClientConfig};
use distant_core::net::common::authentication::{AuthHandlerMap, DummyAuthHandler, Verifier};
use distant_core::net::common::{Host, InmemoryTransport, OneshotListener};
use distant_core::net::server::{Server, ServerRef};
use distant_core::{DistantApiServerHandler, DistantClient, DistantSingleKeyCredentials};

@ -5,12 +5,12 @@ use std::process::Stdio;
use std::time::Duration;
use async_trait::async_trait;
use distant_core::net::client::{Client, ClientConfig, ReconnectStrategy, UntypedClient};
use distant_core::net::common::authentication::msg::*;
use distant_core::net::common::authentication::{
use distant_core::net::auth::msg::*;
use distant_core::net::auth::{
AuthHandler, Authenticator, DynAuthHandler, ProxyAuthHandler, SingleAuthHandler,
StaticKeyAuthMethodHandler,
};
use distant_core::net::client::{Client, ClientConfig, ReconnectStrategy, UntypedClient};
use distant_core::net::common::{Destination, Map, SecretKey32};
use distant_core::net::manager::{ConnectHandler, LaunchHandler};
use log::*;

@ -1,7 +1,7 @@
use std::io::{self, Read, Write};
use anyhow::Context;
use distant_core::net::common::authentication::Verifier;
use distant_core::net::auth::Verifier;
use distant_core::net::common::{Host, SecretKey32};
use distant_core::net::server::{Server, ServerConfig as NetServerConfig, ServerRef};
use distant_core::{DistantApiServerHandler, DistantSingleKeyCredentials};

@ -2,11 +2,11 @@ use std::io;
use std::time::Duration;
use async_trait::async_trait;
use distant_core::net::client::{Client as NetClient, ClientConfig, ReconnectStrategy};
use distant_core::net::common::authentication::msg::*;
use distant_core::net::common::authentication::{
use distant_core::net::auth::msg::*;
use distant_core::net::auth::{
AuthHandler, AuthMethodHandler, PromptAuthMethodHandler, SingleAuthHandler,
};
use distant_core::net::client::{Client as NetClient, ClientConfig, ReconnectStrategy};
use distant_core::net::manager::ManagerClient;
use log::*;

@ -1,5 +1,5 @@
use anyhow::Context;
use distant_core::net::common::authentication::Verifier;
use distant_core::net::auth::Verifier;
use distant_core::net::manager::{Config as ManagerConfig, ManagerServer};
use distant_core::net::server::ServerRef;
use log::*;

Loading…
Cancel
Save