Partially implement ClientCertificateStore for Filesystem

This commit is contained in:
Nathan Fisher 2023-06-22 19:04:51 -04:00
parent b957138048
commit e1239fea58
2 changed files with 111 additions and 15 deletions

View file

@ -5,6 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
digest = "0.10" digest = "0.10"
rustls-pemfile = "1.0"
sha2 = "0.10" sha2 = "0.10"
time = "0.3" time = "0.3"
x509-parser = "0.15" x509-parser = "0.15"

View file

@ -1,10 +1,19 @@
use std::io::BufReader;
use crate::prelude::Certificate;
use { use {
super::*, super::*,
crate::{message::Parser as MessageParser, prelude::ParseMailboxError}, crate::{
message::Parser as MessageParser,
prelude::{ClientCertificateStore, ParseMailboxError},
},
rustls_pemfile::{read_one, Item},
std::{ std::{
ffi::OsString, ffi::OsString,
fs::{self, File}, fs::{self, File},
io::{self, BufWriter, Write}, io::{self, BufWriter, Write},
iter,
os::unix::fs::DirBuilderExt, os::unix::fs::DirBuilderExt,
path::{Path, PathBuf}, path::{Path, PathBuf},
}, },
@ -141,11 +150,8 @@ impl MailStore for Filesystem {
} }
fn get_message(&self, user: &str, folder: &str, id: &str) -> Option<Message> { fn get_message(&self, user: &str, folder: &str, id: &str) -> Option<Message> {
self.get_folder(user, folder).and_then(|f| { self.get_folder(user, folder)
f.messages .and_then(|f| f.messages.get(id).cloned())
.get(id)
.cloned()
})
} }
fn add_message( fn add_message(
@ -288,6 +294,77 @@ impl MultiDomain for Filesystem {
} }
} }
impl ClientCertificateStore for Filesystem {
fn get_certificate(&self, user: &Mailuser) -> Option<Certificate> {
let mut path = self.path.clone();
path.push(&user.host.to_string());
path.push(&user.username);
path.push("certificate.pem");
if path.exists() {
let Ok(fd) = File::open(&path) else {
return None;
};
let mut reader = BufReader::new(fd);
let mut certificate = Certificate {
der: vec![],
key: vec![],
};
for item in iter::from_fn(|| read_one(&mut reader).transpose()) {
match item {
Ok(Item::X509Certificate(cert)) => {
if certificate.der.is_empty() {
certificate.der = cert;
} else {
return None;
}
}
Ok(Item::RSAKey(key)) => {
if certificate.key.is_empty() {
certificate.key = key;
} else {
return None;
}
}
Ok(Item::PKCS8Key(key)) => {
if certificate.key.is_empty() {
certificate.key = key;
} else {
return None;
}
}
Ok(Item::ECKey(key)) => {
if certificate.key.is_empty() {
certificate.key = key;
} else {
return None;
}
}
_ => {}
}
}
if certificate.der.is_empty() || certificate.key.is_empty() {
None
} else {
Some(certificate)
}
} else {
None
}
}
fn insert_certificate(
&mut self,
user: &Mailuser,
certificate: Certificate,
) -> Option<Certificate> {
todo!()
}
fn contains_certificate(&self, user: &Mailuser) -> bool {
self.get_certificate(user).is_some()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -299,10 +376,18 @@ mod tests {
#[test] #[test]
fn users() { fn users() {
let users = store().users(); let users = store().users();
assert!(users.iter().any(|u| u.host.to_string() == "mail.gmi.org" && u.username == "jane" )); assert!(users
assert!(users.iter().any(|u| u.host.to_string() == "mail.gmi.org" && u.username == "dick" )); .iter()
assert!(users.iter().any(|u| u.host.to_string() == "misfin.example.org" && u.username == "jane" )); .any(|u| u.host.to_string() == "mail.gmi.org" && u.username == "jane"));
assert!(users.iter().any(|u| u.host.to_string() == "misfin.example.org" && u.username == "dick" )); assert!(users
.iter()
.any(|u| u.host.to_string() == "mail.gmi.org" && u.username == "dick"));
assert!(users
.iter()
.any(|u| u.host.to_string() == "misfin.example.org" && u.username == "jane"));
assert!(users
.iter()
.any(|u| u.host.to_string() == "misfin.example.org" && u.username == "dick"));
assert!(users.len() >= 4 && users.len() < 6); assert!(users.len() >= 4 && users.len() < 6);
} }
@ -336,7 +421,9 @@ mod tests {
#[test] #[test]
fn get_message() { fn get_message() {
let msg = store().get_message("jane@mail.gmi.org", "Inbox", "867017").unwrap(); let msg = store()
.get_message("jane@mail.gmi.org", "Inbox", "867017")
.unwrap();
assert_eq!(msg.from.username, "april"); assert_eq!(msg.from.username, "april");
assert_eq!(msg.from.host.subdomain.unwrap(), "we.wear"); assert_eq!(msg.from.host.subdomain.unwrap(), "we.wear");
} }
@ -352,18 +439,26 @@ mod tests {
title: Some(String::from("Get a look at 'is 'orse")), title: Some(String::from("Get a look at 'is 'orse")),
body: String::from("Gorgeous, innit?"), body: String::from("Gorgeous, innit?"),
}; };
store().add_message("dick@mail.gmi.org", "Inbox", msg).unwrap(); store()
.add_message("dick@mail.gmi.org", "Inbox", msg)
.unwrap();
assert!(PathBuf::from("test/mailstore/mail.gmi.org/dick/Inbox/1687407844.gmi").exists()); assert!(PathBuf::from("test/mailstore/mail.gmi.org/dick/Inbox/1687407844.gmi").exists());
store().move_message("dick@mail.gmi.org", "1687407844", "Inbox", "Lists").unwrap(); store()
.move_message("dick@mail.gmi.org", "1687407844", "Inbox", "Lists")
.unwrap();
assert!(!PathBuf::from("test/mailstore/mail.gmi.org/dick/Inbox/1687407844.gmi").exists()); assert!(!PathBuf::from("test/mailstore/mail.gmi.org/dick/Inbox/1687407844.gmi").exists());
assert!(PathBuf::from("test/mailstore/mail.gmi.org/dick/Lists/1687407844.gmi").exists()); assert!(PathBuf::from("test/mailstore/mail.gmi.org/dick/Lists/1687407844.gmi").exists());
store().delete_message("dick@mail.gmi.org", "Lists", "1687407844").unwrap(); store()
.delete_message("dick@mail.gmi.org", "Lists", "1687407844")
.unwrap();
assert!(!PathBuf::from("test/mailstore/mail.gmi.org/dick/Lists/1687407844.gmi").exists()); assert!(!PathBuf::from("test/mailstore/mail.gmi.org/dick/Lists/1687407844.gmi").exists());
} }
#[test] #[test]
fn add_remove_user() { fn add_remove_user() {
store().add_user("rob@misfin.example.org Rob Zombie").unwrap(); store()
.add_user("rob@misfin.example.org Rob Zombie")
.unwrap();
assert!(store().has_mailuser("rob@misfin.example.org")); assert!(store().has_mailuser("rob@misfin.example.org"));
assert!(store().remove_user("rob@misfin.example.org")); assert!(store().remove_user("rob@misfin.example.org"));
assert!(!store().has_mailuser("rob@misfin.example.org")); assert!(!store().has_mailuser("rob@misfin.example.org"));