Partially implement ClientCertificateStore
for Filesystem
This commit is contained in:
parent
b957138048
commit
e1239fea58
2 changed files with 111 additions and 15 deletions
|
@ -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"
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
Loading…
Add table
Reference in a new issue