Skip to content

Commit 14b94d0

Browse files
authored
Merge pull request parallaxsecond#207 from joechrisellis/master
Create `Connection` abstraction for client communication
2 parents adaf587 + d0cc8b5 commit 14b94d0

File tree

6 files changed

+57
-18
lines changed

6 files changed

+57
-18
lines changed

src/authenticators/direct_authenticator/mod.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
use super::ApplicationName;
1212
use super::Authenticate;
13+
use crate::front::listener::ConnectionMetadata;
1314
use log::error;
1415
use parsec_interface::requests::request::RequestAuth;
1516
use parsec_interface::requests::{ResponseStatus, Result};
@@ -20,7 +21,11 @@ use std::str;
2021
pub struct DirectAuthenticator;
2122

2223
impl Authenticate for DirectAuthenticator {
23-
fn authenticate(&self, auth: &RequestAuth) -> Result<ApplicationName> {
24+
fn authenticate(
25+
&self,
26+
auth: &RequestAuth,
27+
_: Option<ConnectionMetadata>,
28+
) -> Result<ApplicationName> {
2429
if auth.buffer.expose_secret().is_empty() {
2530
error!("The direct authenticator does not expect empty authentication values.");
2631
Err(ResponseStatus::AuthenticationError)
@@ -49,9 +54,10 @@ mod test {
4954

5055
let app_name = "app_name".to_string();
5156
let req_auth = RequestAuth::new(app_name.clone().into_bytes());
57+
let conn_metadata = None;
5258

5359
let auth_name = authenticator
54-
.authenticate(&req_auth)
60+
.authenticate(&req_auth, conn_metadata)
5561
.expect("Failed to authenticate");
5662

5763
assert_eq!(auth_name.get_name(), app_name);
@@ -60,8 +66,9 @@ mod test {
6066
#[test]
6167
fn failed_authentication() {
6268
let authenticator = DirectAuthenticator {};
69+
let conn_metadata = None;
6370
let status = authenticator
64-
.authenticate(&RequestAuth::new(vec![0xff; 5]))
71+
.authenticate(&RequestAuth::new(vec![0xff; 5]), conn_metadata)
6572
.expect_err("Authentication should have failed");
6673

6774
assert_eq!(status, ResponseStatus::AuthenticationError);
@@ -70,8 +77,9 @@ mod test {
7077
#[test]
7178
fn empty_auth() {
7279
let authenticator = DirectAuthenticator {};
80+
let conn_metadata = None;
7381
let status = authenticator
74-
.authenticate(&RequestAuth::new(Vec::new()))
82+
.authenticate(&RequestAuth::new(Vec::new()), conn_metadata)
7583
.expect_err("Empty auth should have failed");
7684

7785
assert_eq!(status, ResponseStatus::AuthenticationError);

src/authenticators/mod.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
pub mod direct_authenticator;
1515

16+
use crate::front::listener::ConnectionMetadata;
1617
use parsec_interface::requests::request::RequestAuth;
1718
use parsec_interface::requests::Result;
1819

@@ -24,12 +25,19 @@ pub struct ApplicationName(String);
2425
///
2526
/// Interface that must be implemented for each authentication type available for the service.
2627
pub trait Authenticate {
27-
/// Authenticates a `RequestAuth` payload and returns the `ApplicationName` if successfull.
28+
/// Authenticates a `RequestAuth` payload and returns the `ApplicationName` if successful. A
29+
/// optional `ConnectionMetadata` object is passed in too, since it is sometimes possible to
30+
/// perform authentication based on the connection's metadata (i.e. as is the case for UNIX
31+
/// domain sockets with peer credentials).
2832
///
2933
/// # Errors
3034
///
3135
/// If the authentification fails, returns a `ResponseStatus::AuthenticationError`.
32-
fn authenticate(&self, auth: &RequestAuth) -> Result<ApplicationName>;
36+
fn authenticate(
37+
&self,
38+
auth: &RequestAuth,
39+
meta: Option<ConnectionMetadata>,
40+
) -> Result<ApplicationName>;
3341
}
3442

3543
impl ApplicationName {

src/bin/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ fn main() -> Result<()> {
125125
info!("Parsec configuration reloaded.");
126126
}
127127

128-
if let Some(stream) = listener.accept() {
128+
if let Some(connection) = listener.accept() {
129129
let front_end_handler = front_end_handler.clone();
130130
threadpool.execute(move || {
131-
front_end_handler.handle_request(stream);
131+
front_end_handler.handle_request(connection);
132132
trace!("handle_request egress");
133133
});
134134
} else {

src/front/domain_socket.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
//! Expose Parsec functionality using Unix domain sockets as an IPC layer.
66
//! The local socket is created at a predefined location.
77
use super::listener;
8+
use listener::Connection;
89
use listener::Listen;
9-
use listener::ReadWrite;
1010
use log::error;
1111
use std::fs;
1212
use std::fs::Permissions;
@@ -91,7 +91,7 @@ impl Listen for DomainSocketListener {
9191
self.timeout = duration;
9292
}
9393

94-
fn accept(&self) -> Option<Box<dyn ReadWrite + Send>> {
94+
fn accept(&self) -> Option<Connection> {
9595
let stream_result = self.listener.accept();
9696
match stream_result {
9797
Ok((stream, _)) => {
@@ -105,7 +105,12 @@ impl Listen for DomainSocketListener {
105105
format_error!("Failed to set stream as blocking", err);
106106
None
107107
} else {
108-
Some(Box::from(stream))
108+
Some(Connection {
109+
stream: Box::new(stream),
110+
// TODO: when possible, we want to replace this with the (uid, gid, pid)
111+
// triple for peer credentials. See listener.rs.
112+
metadata: None,
113+
})
109114
}
110115
}
111116
Err(err) => {

src/front/front_end.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
//! pass them to the rest of the service and write the responses back.
77
use crate::authenticators::Authenticate;
88
use crate::back::dispatcher::Dispatcher;
9+
use crate::front::listener::Connection;
910
use derivative::Derivative;
1011
use log::{info, trace};
1112
use parsec_interface::requests::AuthType;
1213
use parsec_interface::requests::ResponseStatus;
1314
use parsec_interface::requests::{Request, Response};
1415
use std::collections::HashMap;
1516
use std::io::{Error, ErrorKind, Result};
16-
use std::io::{Read, Write};
1717

1818
/// Read and verify request from IPC stream
1919
///
@@ -40,17 +40,17 @@ impl FrontEndHandler {
4040
///
4141
/// If an error occurs during (un)marshalling, no operation will be performed and the
4242
/// method will return.
43-
pub fn handle_request<T: Read + Write>(&self, mut stream: T) {
43+
pub fn handle_request(&self, mut connection: Connection) {
4444
trace!("handle_request ingress");
4545
// Read bytes from stream
4646
// De-Serialise bytes into a request
47-
let request = match Request::read_from_stream(&mut stream, self.body_len_limit) {
47+
let request = match Request::read_from_stream(&mut connection.stream, self.body_len_limit) {
4848
Ok(request) => request,
4949
Err(status) => {
5050
format_error!("Failed to read request", status);
5151

5252
let response = Response::from_status(status);
53-
if let Err(status) = response.write_to_stream(&mut stream) {
53+
if let Err(status) = response.write_to_stream(&mut connection.stream) {
5454
format_error!("Failed to write response", status);
5555
}
5656
return;
@@ -63,7 +63,7 @@ impl FrontEndHandler {
6363
// Otherwise find an authenticator that is capable to authenticate the request
6464
} else if let Some(authenticator) = self.authenticators.get(&request.header.auth_type) {
6565
// Authenticate the request
66-
match authenticator.authenticate(&request.auth) {
66+
match authenticator.authenticate(&request.auth, connection.metadata) {
6767
// Send the request to the dispatcher
6868
// Get a response back
6969
Ok(app_name) => (Some(app_name), None),
@@ -102,7 +102,7 @@ impl FrontEndHandler {
102102

103103
// Serialise the response into bytes
104104
// Write bytes to stream
105-
match response.write_to_stream(&mut stream) {
105+
match response.write_to_stream(&mut connection.stream) {
106106
Ok(_) => {
107107
if crate::utils::GlobalConfig::log_error_details() {
108108
if let Some(app_name_string) = app_name {

src/front/listener.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! The [`Listen`](https://parallaxsecond.github.io/parsec-book/parsec_service/listeners.html)
66
//! trait acts as an interface for the operations that must be supported by any implementation
77
//! of the IPC mechanism used as a Parsec front.
8+
use derivative::Derivative;
89
use serde::Deserialize;
910
use std::time::Duration;
1011

@@ -25,6 +26,23 @@ pub struct ListenerConfig {
2526
pub timeout: u64,
2627
}
2728

29+
/// Specifies metadata associated with a connection, if any.
30+
#[derive(Copy, Clone, Debug)]
31+
pub enum ConnectionMetadata {
32+
// TODO: nothing here right now. Metadata types will be added as needed.
33+
}
34+
35+
/// Represents a connection to a single client. Contains a stream, used for communication with the
36+
/// client, and some metadata associated with the connection that might be useful elsewhere (i.e.
37+
/// authentication, etc).
38+
#[derive(Derivative)]
39+
#[derivative(Debug)]
40+
pub struct Connection {
41+
#[derivative(Debug = "ignore")]
42+
pub stream: Box<dyn ReadWrite + Send>,
43+
pub metadata: Option<ConnectionMetadata>,
44+
}
45+
2846
/// IPC front manager interface
2947
///
3048
/// Interface defining the functionality that any IPC front manager has to expose to Parsec for normal
@@ -45,5 +63,5 @@ pub trait Listen {
4563
/// # Panics
4664
///
4765
/// If the listener has not been initialised before, with the `init` method.
48-
fn accept(&self) -> Option<Box<dyn ReadWrite + Send>>;
66+
fn accept(&self) -> Option<Connection>;
4967
}

0 commit comments

Comments
 (0)