use crate::repository::PasswordDirectory; use common::request::Request; use log::{error, info}; use std::io::{Error, Read, Write}; pub const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O']; pub const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K']; pub struct Handler { stream: S, repository: PasswordDirectory, } impl Handler { pub fn new(stream: S, repository: PasswordDirectory) -> Handler { Handler { stream, repository } } fn communicate(&mut self) -> Result<(), Error> { let request = Request::from_stream(&mut self.stream)?; if self.repository.check_auth(&request) { self.stream.write_all(&RESPONSE_OK)?; info!("userid: {}, service: {}, realm: {} - OK", request.userid, request.service, request.realm); } else { self.stream.write_all(&RESPONSE_NO)?; info!("userid: {}, service: {}, realm: {} - FAIL", request.userid, request.service, request.realm); } Ok(()) } pub fn handle_client(&mut self) { if let Err(error) = self.communicate() { error!("{error}"); } } } #[cfg(test)] mod tests { use crate::handler::{Handler, RESPONSE_OK}; use crate::repository::PasswordDirectory; use common::request::Request; use std::io::Cursor; const TEST_PATH: &str = "../test_passwords"; #[test] fn it_handles_a_request() { let repository = PasswordDirectory::new(TEST_PATH); let request = Request::new("bp", "some-rad-password", "ignore-this", "also-ignore-this"); let mut cursor = Cursor::new(Vec::new()); request.write_to_stream(&mut cursor).unwrap(); // Figure out where the response should end up let response_position: usize = cursor.position().try_into().unwrap(); // Rewind and have the handler deal with the request cursor.set_position(0); Handler::new(&mut cursor, repository).handle_client(); // Extract the response, which should come after the // request in the cursor buffer let response = cursor.into_inner().split_off(response_position); assert_eq!(response, RESPONSE_OK); } }