Skip to content

Commit

Permalink
v0.7.2
Browse files Browse the repository at this point in the history
  • Loading branch information
mdecimus committed Apr 17, 2024
1 parent 929d844 commit 3cc3b72
Show file tree
Hide file tree
Showing 13 changed files with 59 additions and 21 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).


## [0.7.2] - 2024-04-17

To upgrade replace the `stalwart-mail` binary and then upgrade to the latest web-admin version.

## Added
- Support for DNS-01 and HTTP-01 ACME challenges (#226)
- Configurable external resources (#355)

### Changed

### Fixed
- Startup failure when Elasticsearch is down/starting up (#334)
- URL decode path elements in REST API.

## [0.7.1] - 2024-04-12

To upgrade replace the `stalwart-mail` binary.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Key features:
- Self-service portal for password reset and encryption-at-rest key management.
- **Secure and robust**:
- Encryption at rest with **S/MIME** or **OpenPGP**.
- Automatic TLS certificate provisioning with [ACME](https://datatracker.ietf.org/doc/html/rfc8555).
- Automatic TLS certificate provisioning with [ACME](https://datatracker.ietf.org/doc/html/rfc8555) using `TLS-ALPN-01`, `DNS-01` or `HTTP-01` challenges.
- OAuth 2.0 [authorization code](https://www.rfc-editor.org/rfc/rfc8628) and [device authorization](https://www.rfc-editor.org/rfc/rfc8628) flows.
- Automated blocking of hosts that cause multiple authentication errors (aka **fail2ban**).
- Access Control Lists (ACLs).
Expand Down
4 changes: 2 additions & 2 deletions crates/common/src/config/server/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ fn build_dns_updater(config: &mut Config, acme_id: &str) -> Option<DnsUpdater> {
match config.value_require(("acme", acme_id, "provider"))? {
"rfc2136-tsig" => {
let algorithm: TsigAlgorithm = config
.value_require(("acme", acme_id, "algorithm"))?
.value_require(("acme", acme_id, "tsig-algorithm"))?
.parse()
.map_err(|_| {
config.new_parse_error(("acme", acme_id, "algorithm"), "Invalid algorithm")
config.new_parse_error(("acme", acme_id, "tsig-algorithm"), "Invalid algorithm")
})
.ok()?;
let key = STANDARD
Expand Down
4 changes: 3 additions & 1 deletion crates/jmap/src/api/management/dkim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ use crate::{
JMAP,
};

use super::decode_path_element;

#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)]
pub enum Algorithm {
Rsa,
Expand Down Expand Up @@ -76,7 +78,7 @@ impl JMAP {

async fn handle_get_public_key(&self, path: Vec<&str>) -> HttpResponse {
let signature_id = match path.get(1) {
Some(signature_id) => *signature_id,
Some(signature_id) => decode_path_element(signature_id),
None => {
return RequestError::not_found().into_http_response();
}
Expand Down
13 changes: 9 additions & 4 deletions crates/jmap/src/api/management/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ use crate::{
JMAP,
};

use super::decode_path_element;

#[derive(Debug, Serialize, Deserialize)]
struct DnsRecord {
#[serde(rename = "type")]
Expand Down Expand Up @@ -82,7 +84,8 @@ impl JMAP {
}
(Some(domain), &Method::GET) => {
// Obtain DNS records
match self.build_dns_records(domain).await {
let domain = decode_path_element(domain);
match self.build_dns_records(domain.as_ref()).await {
Ok(records) => JsonResponse::new(json!({
"data": records,
}))
Expand All @@ -92,7 +95,8 @@ impl JMAP {
}
(Some(domain), &Method::POST) => {
// Create domain
match self.core.storage.data.create_domain(domain).await {
let domain = decode_path_element(domain);
match self.core.storage.data.create_domain(domain.as_ref()).await {
Ok(_) => {
// Set default domain name if missing
if matches!(
Expand All @@ -103,7 +107,7 @@ impl JMAP {
.core
.storage
.config
.set([("lookup.default.domain", *domain)])
.set([("lookup.default.domain", domain.as_ref())])
.await
{
tracing::error!("Failed to set default domain name: {}", err);
Expand All @@ -120,7 +124,8 @@ impl JMAP {
}
(Some(domain), &Method::DELETE) => {
// Delete domain
match self.core.storage.data.delete_domain(domain).await {
let domain = decode_path_element(domain);
match self.core.storage.data.delete_domain(domain.as_ref()).await {
Ok(_) => JsonResponse::new(json!({
"data": (),
}))
Expand Down
9 changes: 9 additions & 0 deletions crates/jmap/src/api/management/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,12 @@ impl From<String> for ManagementApiError {
}
}
}

pub fn decode_path_element(item: &str) -> Cow<'_, str> {
// Bit hackish but avoids an extra dependency
form_urlencoded::parse(item.as_bytes())
.into_iter()
.next()
.map(|(k, _)| k)
.unwrap_or_else(|| item.into())
}
5 changes: 3 additions & 2 deletions crates/jmap/src/api/management/principal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use crate::{
JMAP,
};

use super::ManagementApiError;
use super::{decode_path_element, ManagementApiError};

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct PrincipalResponse {
Expand Down Expand Up @@ -151,7 +151,8 @@ impl JMAP {
}
(Some(name), method) => {
// Fetch, update or delete principal
let account_id = match self.core.storage.data.get_account_id(name).await {
let name = decode_path_element(name);
let account_id = match self.core.storage.data.get_account_id(name.as_ref()).await {
Ok(Some(account_id)) => account_id,
Ok(None) => {
return RequestError::blank(
Expand Down
8 changes: 5 additions & 3 deletions crates/jmap/src/api/management/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ use crate::{
JMAP,
};

use super::decode_path_element;

#[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct Message {
pub id: QueueId,
Expand Down Expand Up @@ -122,7 +124,7 @@ impl JMAP {

match (
path.get(1).copied().unwrap_or_default(),
path.get(2).copied(),
path.get(2).copied().map(decode_path_element),
req.method(),
) {
("messages", None, &Method::GET) => {
Expand Down Expand Up @@ -439,7 +441,7 @@ impl JMAP {
}
("reports", Some(report_id), &Method::GET) => {
let mut result = None;
if let Some(report_id) = parse_queued_report_id(report_id) {
if let Some(report_id) = parse_queued_report_id(report_id.as_ref()) {
match report_id {
QueueClass::DmarcReportHeader(event) => {
let mut rua = Vec::new();
Expand Down Expand Up @@ -475,7 +477,7 @@ impl JMAP {
}
}
("reports", Some(report_id), &Method::DELETE) => {
if let Some(report_id) = parse_queued_report_id(report_id) {
if let Some(report_id) = parse_queued_report_id(report_id.as_ref()) {
match report_id {
QueueClass::DmarcReportHeader(event) => {
self.smtp.delete_dmarc_report(event).await;
Expand Down
8 changes: 5 additions & 3 deletions crates/jmap/src/api/management/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ use crate::{
JMAP,
};

use super::decode_path_element;

enum ReportType {
Dmarc,
Tls,
Expand All @@ -50,7 +52,7 @@ impl JMAP {
pub async fn handle_manage_reports(&self, req: &HttpRequest, path: Vec<&str>) -> HttpResponse {
match (
path.get(1).copied().unwrap_or_default(),
path.get(2).copied(),
path.get(2).copied().map(decode_path_element),
req.method(),
) {
(class @ ("dmarc" | "tls" | "arf"), None, &Method::GET) => {
Expand Down Expand Up @@ -159,7 +161,7 @@ impl JMAP {
}
}
(class @ ("dmarc" | "tls" | "arf"), Some(report_id), &Method::GET) => {
if let Some(report_id) = parse_incoming_report_id(class, report_id) {
if let Some(report_id) = parse_incoming_report_id(class, report_id.as_ref()) {
match &report_id {
ReportClass::Tls { .. } => match self
.core
Expand Down Expand Up @@ -215,7 +217,7 @@ impl JMAP {
}
}
(class @ ("dmarc" | "tls" | "arf"), Some(report_id), &Method::DELETE) => {
if let Some(report_id) = parse_incoming_report_id(class, report_id) {
if let Some(report_id) = parse_incoming_report_id(class, report_id.as_ref()) {
let mut batch = BatchBuilder::new();
batch.clear(ValueClass::Report(report_id));
let result = self.core.storage.data.write(batch.build()).await.is_ok();
Expand Down
6 changes: 4 additions & 2 deletions crates/jmap/src/api/management/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
JMAP,
};

use super::ManagementApiError;
use super::{decode_path_element, ManagementApiError};

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type")]
Expand Down Expand Up @@ -269,7 +269,9 @@ impl JMAP {
}
}
(Some(prefix), &Method::DELETE) if !prefix.is_empty() => {
match self.core.storage.config.clear(prefix).await {
let prefix = decode_path_element(prefix);

match self.core.storage.config.clear(prefix.as_ref()).await {
Ok(_) => JsonResponse::new(json!({
"data": (),
}))
Expand Down
2 changes: 1 addition & 1 deletion crates/utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ chrono = "0.4"
rand = "0.8.5"
webpki-roots = { version = "0.26"}
ring = { version = "0.17" }
base64 = "0.21"
base64 = "0.22"
serde_json = "1.0"
rcgen = "0.13"
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
Expand Down
2 changes: 1 addition & 1 deletion tests/resources/acme/docker-compose-pebble.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version: '3'
services:
pebble:
image: letsencrypt/pebble:latest
command: pebble -config /test/config/pebble-config.json -strict -dnsserver 8.8.8.8:53 #-dnsserver 10.30.50.3:8053
command: pebble -config /test/config/pebble-config.json -strict -dnsserver 10.30.50.3:8053 #-dnsserver 8.8.8.8:53
ports:
- 14000:14000 # HTTPS ACME API
- 15000:15000 # HTTPS Management API
Expand Down

0 comments on commit 3cc3b72

Please sign in to comment.