From c1ae11c84b0f355a43ab2303b55db082559a3531 Mon Sep 17 00:00:00 2001 From: mdecimus Date: Fri, 14 Jul 2023 16:38:13 +0200 Subject: [PATCH] Install script fixes, moved SMTP management API to JMAP listener. --- .github/workflows/build.yml | 140 ++++++++++++++++++---------- README.md | 10 ++ crates/cli/src/main.rs | 6 +- crates/cli/src/modules/cli.rs | 2 +- crates/cli/src/modules/queue.rs | 10 +- crates/cli/src/modules/report.rs | 8 +- crates/install/src/main.rs | 17 +++- crates/jmap/src/api/http.rs | 6 ++ crates/main/src/main.rs | 4 +- crates/smtp/src/core/management.rs | 56 +++++++---- install.sh | 6 +- resources/config/directory.toml | 4 +- resources/config/smtp.toml | 8 +- tests/src/smtp/management/queue.rs | 31 +++--- tests/src/smtp/management/report.rs | 20 ++-- 15 files changed, 212 insertions(+), 116 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3aeefb09..222996fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: pull_request: push: tags: - - '*' + - "v*.*.*" jobs: build: @@ -240,67 +240,113 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - docker-amd: - name: Build Docker AMD64 images - if: '!cancelled()' + build_docker: + name: Build Docker image for ${{ matrix.platform }} runs-on: ubuntu-latest + if: '!cancelled()' + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 steps: - - name: Checkout + - + name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + - + name: Checkout uses: actions/checkout@v3 - - - name: Set up QEMU + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + - + name: Set up QEMU uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx + - + name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub + - + name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v3 + - + name: Build and push by digest + id: build + uses: docker/build-push-action@v4 with: context: . - push: true - platforms: linux/amd64 - tags: stalwartlabs/mail-server:latest - cache-from: type=registry,ref=stalwartlabs/mail-server:buildcache - cache-to: type=registry,ref=stalwartlabs/mail-server:buildcache,mode=max - #cache-from: type=gha - #cache-to: type=gha,mode=max - - docker-arm: - name: Build Docker ARM64 images - if: '!cancelled()' + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true + #cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:buildcache-${{ env.PLATFORM_PAIR }} + #cache-to: type=registry,ref=s${{ env.REGISTRY_IMAGE }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max + cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }} + cache-to: type=gha,scope=build-${{ env.PLATFORM_PAIR }} + - + name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + - + name: Upload digest + uses: actions/upload-artifact@v3 + with: + name: digests + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge_docker: + name: Merge and push Docker manifest runs-on: ubuntu-latest + needs: + - build_docker steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx + - + name: Download digests + uses: actions/download-artifact@v3 + with: + name: digests + path: /tmp/digests + - + name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY_IMAGE }} + - + name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - push: true - platforms: linux/arm64 - tags: stalwartlabs/mail-server:latest - cache-from: type=registry,ref=stalwartlabs/mail-server:buildcache - cache-to: type=registry,ref=stalwartlabs/mail-server:buildcache,mode=max - #cache-from: type=gha - #cache-to: type=gha,mode=max + - + name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - + name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} diff --git a/README.md b/README.md index 7f194c6f..f261b2b2 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,16 @@ try each of these servers individually: * [Stalwart IMAP Server](https://github.com/stalwartlabs/imap-server/) * [Stalwart SMTP Server](https://github.com/stalwartlabs/smtp-server/) +## Why choose Stalwart? + +Within the field of mail servers, established names like Postfix, Courier and Dovecot have long been the go-to solutions. However, the landscape of internet messaging is evolving, with a need for more efficient, easy to maintain, reliable, and secure systems. Here's why you might consider making the switch to Stalwart Mail Server: + +- Designed with the latest internet messaging protocols in mind - JMAP and IMAP4rev2, along with the conventional SMTP. +- Leverages the performance and security benefits of the Rust programming language. This statically typed, compiled language is known for its memory safety and concurrency support, reducing the likelihood of typical security issues like buffer overflows. +- Thanks to its native FoundationDB and S3 storage support, it can be scaled across many servers, accommodating millions of users. +- Available as a single, integrated package that includes JMAP, IMAP, and SMTP servers. This means that you don't have to install, configure and maintain multiple servers to get a complete solution. +- Designed to be easy to install and maintain, with a single configuration file and a simple command-line interface. + ## License Licensed under the terms of the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.en.html) as published by diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index a0b50336..17748506 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -50,9 +50,9 @@ async fn main() -> std::io::Result<()> { let is_jmap = args.command.is_jmap(); let credentials = if let Some(credentials) = args.credentials { parse_credentials(&credentials) - } else if is_jmap { + } else { let credentials = rpassword::prompt_password( - "\nEnter JMAP admin credentials or press [ENTER] to use OAuth: ", + "\nEnter administrator credentials or press [ENTER] to use OAuth: ", ) .unwrap(); if !credentials.is_empty() { @@ -60,8 +60,6 @@ async fn main() -> std::io::Result<()> { } else { oauth(&args.url).await } - } else { - parse_credentials(&rpassword::prompt_password("\nEnter SMTP admin credentials: ").unwrap()) }; if is_jmap { diff --git a/crates/cli/src/modules/cli.rs b/crates/cli/src/modules/cli.rs index 9caf340d..efb6ac4e 100644 --- a/crates/cli/src/modules/cli.rs +++ b/crates/cli/src/modules/cli.rs @@ -31,7 +31,7 @@ use serde::Deserialize; pub struct Cli { #[clap(subcommand)] pub command: Commands, - /// JMAP or SMTP server base URL + /// Server base URL #[clap(short, long)] pub url: String, /// Authentication credentials diff --git a/crates/cli/src/modules/queue.rs b/crates/cli/src/modules/queue.rs index df17a586..3daebab5 100644 --- a/crates/cli/src/modules/queue.rs +++ b/crates/cli/src/modules/queue.rs @@ -100,7 +100,7 @@ pub async fn cmd_queue(url: &str, credentials: Credentials, command: QueueComman .collect(), )); for (message, id) in smtp_manage_request::>>( - &build_query(url, "/queue/status?ids=", chunk), + &build_query(url, "/admin/queue/status?ids=", chunk), &credentials, ) .await @@ -172,7 +172,7 @@ pub async fn cmd_queue(url: &str, credentials: Credentials, command: QueueComman } QueueCommands::Status { ids } => { for (message, id) in smtp_manage_request::>>( - &build_query(url, "/queue/status?ids=", &parse_ids(&ids)), + &build_query(url, "/admin/queue/status?ids=", &parse_ids(&ids)), &credentials, ) .await @@ -311,7 +311,7 @@ pub async fn cmd_queue(url: &str, credentials: Credentials, command: QueueComman std::process::exit(1); } - let mut query = form_urlencoded::Serializer::new(format!("{url}/queue/retry?")); + let mut query = form_urlencoded::Serializer::new(format!("{url}/admin/queue/retry?")); if let Some(filter) = &domain { query.append_pair("filter", filter); @@ -365,7 +365,7 @@ pub async fn cmd_queue(url: &str, credentials: Credentials, command: QueueComman std::process::exit(1); } - let mut query = form_urlencoded::Serializer::new(format!("{url}/queue/cancel?")); + let mut query = form_urlencoded::Serializer::new(format!("{url}/admin/queue/cancel?")); if let Some(filter) = &rcpt { query.append_pair("filter", filter); @@ -443,7 +443,7 @@ async fn query_messages( before: &Option, after: &Option, ) -> Vec { - let mut query = form_urlencoded::Serializer::new(format!("{url}/queue/list?")); + let mut query = form_urlencoded::Serializer::new(format!("{url}/admin/queue/list?")); if let Some(sender) = from { query.append_pair("from", sender); diff --git a/crates/cli/src/modules/report.rs b/crates/cli/src/modules/report.rs index 5a45e4e8..42a3fdb1 100644 --- a/crates/cli/src/modules/report.rs +++ b/crates/cli/src/modules/report.rs @@ -50,7 +50,7 @@ pub async fn cmd_report(url: &str, credentials: Credentials, command: ReportComm page_size, } => { let stdout = Term::buffered_stdout(); - let mut query = form_urlencoded::Serializer::new(format!("{url}/report/list?")); + let mut query = form_urlencoded::Serializer::new(format!("{url}/admin/report/list?")); if let Some(domain) = &domain { query.append_pair("domain", domain); @@ -73,7 +73,7 @@ pub async fn cmd_report(url: &str, credentials: Credentials, command: ReportComm .collect(), )); for (report, id) in smtp_manage_request::>>( - &format!("{url}/report/status?ids={}", chunk.join(",")), + &format!("{url}/admin/report/status?ids={}", chunk.join(",")), &credentials, ) .await @@ -110,7 +110,7 @@ pub async fn cmd_report(url: &str, credentials: Credentials, command: ReportComm } ReportCommands::Status { ids } => { for (report, id) in smtp_manage_request::>>( - &format!("{url}/report/status?ids={}", ids.join(",")), + &format!("{url}/admin/report/status?ids={}", ids.join(",")), &credentials, ) .await @@ -164,7 +164,7 @@ pub async fn cmd_report(url: &str, credentials: Credentials, command: ReportComm let mut success_count = 0; let mut failed_list = vec![]; for (success, id) in smtp_manage_request::>( - &format!("{url}/report/cancel?ids={}", ids.join(",")), + &format!("{url}/admin/report/cancel?ids={}", ids.join(",")), &credentials, ) .await diff --git a/crates/install/src/main.rs b/crates/install/src/main.rs index c67921e8..2c71e6ea 100644 --- a/crates/install/src/main.rs +++ b/crates/install/src/main.rs @@ -363,15 +363,20 @@ fn main() -> std::io::Result<()> { // Obtain TLS certificate path let (cert_path, pk_path) = if !args.docker { + #[cfg(not(target_env = "msvc"))] + let cert_base_path = format!("/etc/letsencrypt/live/{}/", hostname); + #[cfg(target_env = "msvc")] + let cert_base_path = format!("C:\\Program Files\\Letsencrypt\\live\\{}\\", hostname); + ( input( &format!("Where is the TLS certificate for '{hostname}' located?"), - &format!("/etc/letsencrypt/live/{hostname}/fullchain.pem"), + &format!("{cert_base_path}fullchain.pem"), file_exists, )?, input( &format!("Where is the TLS private key for '{hostname}' located?"), - &format!("/etc/letsencrypt/live/{hostname}/privkey.pem"), + &format!("{cert_base_path}privkey.pem"), file_exists, )?, ) @@ -827,12 +832,20 @@ impl SelectItem for Blob { impl Component { fn default_base_path(&self) -> &'static str { + #[cfg(not(target_env = "msvc"))] match self { Self::AllInOne => "/opt/stalwart-mail", Self::Jmap => "/opt/stalwart-jmap", Self::Imap => "/opt/stalwart-imap", Self::Smtp => "/opt/stalwart-smtp", } + #[cfg(target_env = "msvc")] + match self { + Self::AllInOne => "C:\\Program Files\\Stalwart Mail", + Self::Jmap => "C:\\Program Files\\Stalwart JMAP", + Self::Imap => "C:\\Program Files\\Stalwart IMAP", + Self::Smtp => "C:\\Program Files\\Stalwart SMTP", + } } fn binary_name(&self) -> &'static str { diff --git a/crates/jmap/src/api/http.rs b/crates/jmap/src/api/http.rs index 340c68d0..73b91d4e 100644 --- a/crates/jmap/src/api/http.rs +++ b/crates/jmap/src/api/http.rs @@ -339,6 +339,12 @@ pub async fn parse_jmap_request( .into_http_response(), }; } + (path_1 @ ("queue" | "report"), path_2, &Method::GET) => { + return jmap + .smtp + .handle_manage_request(req.uri(), req.method(), path_1, path_2) + .await; + } _ => (), } } diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index 12f1d002..ad7f69ea 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -27,7 +27,7 @@ use directory::config::ConfigDirectory; use imap::core::{ImapSessionManager, IMAP}; use jmap::{api::JmapSessionManager, services::IPC_CHANNEL_BUFFER, JMAP}; use managesieve::core::ManageSieveSessionManager; -use smtp::core::{SmtpAdminSessionManager, SmtpSessionManager, SMTP}; +use smtp::core::{SmtpSessionManager, SMTP}; use tokio::sync::mpsc; use utils::{ config::{Config, ServerProtocol}, @@ -79,7 +79,7 @@ async fn main() -> std::io::Result<()> { server.spawn(SmtpSessionManager::new(smtp.clone()), shutdown_rx) } ServerProtocol::Http => { - server.spawn(SmtpAdminSessionManager::new(smtp.clone()), shutdown_rx) + tracing::debug!("Ignoring HTTP server listener, using JMAP port instead."); } ServerProtocol::Jmap => { server.spawn(JmapSessionManager::new(jmap.clone()), shutdown_rx) diff --git a/crates/smtp/src/core/management.rs b/crates/smtp/src/core/management.rs index 220702b9..57900382 100644 --- a/crates/smtp/src/core/management.rs +++ b/crates/smtp/src/core/management.rs @@ -30,7 +30,7 @@ use hyper::{ header::{self, AUTHORIZATION}, server::conn::http1, service::service_fn, - Method, StatusCode, + Method, StatusCode, Uri, }; use mail_parser::{decoders::base64::base64_decode, DateTime}; use mail_send::Credentials; @@ -312,15 +312,33 @@ impl SMTP { let mut path = req.uri().path().split('/'); path.next(); - let (status, response) = match (req.method(), path.next(), path.next()) { - (&Method::GET, Some("queue"), Some("list")) => { + path.next(); // Skip the leading /admin + Ok(self + .handle_manage_request( + req.uri(), + req.method(), + path.next().unwrap_or_default(), + path.next().unwrap_or_default(), + ) + .await) + } + + pub async fn handle_manage_request( + &self, + uri: &Uri, + method: &Method, + path_1: &str, + path_2: &str, + ) -> hyper::Response> { + let (status, response) = match (method, path_1, path_2) { + (&Method::GET, "queue", "list") => { let mut from = None; let mut to = None; let mut before = None; let mut after = None; let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "from" => { @@ -373,11 +391,11 @@ impl SMTP { Some(error) => error.into_bad_request(), } } - (&Method::GET, Some("queue"), Some("status")) => { + (&Method::GET, "queue", "status") => { let mut queue_ids = Vec::new(); let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "id" | "ids" => match value.parse_queue_ids() { @@ -412,13 +430,13 @@ impl SMTP { Some(error) => error.into_bad_request(), } } - (&Method::GET, Some("queue"), Some("retry")) => { + (&Method::GET, "queue", "retry") => { let mut queue_ids = Vec::new(); let mut time = Instant::now(); let mut item = None; let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "id" | "ids" => match value.parse_queue_ids() { @@ -467,12 +485,12 @@ impl SMTP { Some(error) => error.into_bad_request(), } } - (&Method::GET, Some("queue"), Some("cancel")) => { + (&Method::GET, "queue", "cancel") => { let mut queue_ids = Vec::new(); let mut item = None; let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "id" | "ids" => match value.parse_queue_ids() { @@ -511,12 +529,12 @@ impl SMTP { Some(error) => error.into_bad_request(), } } - (&Method::GET, Some("report"), Some("list")) => { + (&Method::GET, "report", "list") => { let mut domain = None; let mut type_ = None; let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "type" => match value.as_ref() { @@ -558,11 +576,11 @@ impl SMTP { Some(error) => error.into_bad_request(), } } - (&Method::GET, Some("report"), Some("status")) => { + (&Method::GET, "report", "status") => { let mut report_ids = Vec::new(); let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "id" | "ids" => match value.parse_report_ids() { @@ -597,11 +615,11 @@ impl SMTP { Some(error) => error.into_bad_request(), } } - (&Method::GET, Some("report"), Some("cancel")) => { + (&Method::GET, "report", "cancel") => { let mut report_ids = Vec::new(); let mut error = None; - if let Some(query) = req.uri().query() { + if let Some(query) = uri.query() { for (key, value) in form_urlencoded::parse(query.as_bytes()) { match key.as_ref() { "id" | "ids" => match value.parse_report_ids() { @@ -640,12 +658,12 @@ impl SMTP { StatusCode::NOT_FOUND, format!( "{{\"error\": \"not-found\", \"details\": \"URL {} does not exist.\"}}", - req.uri().path() + uri.path() ), ), }; - Ok(hyper::Response::builder() + hyper::Response::builder() .status(status) .header(header::CONTENT_TYPE, "application/json; charset=utf-8") .body( @@ -653,7 +671,7 @@ impl SMTP { .map_err(|never| match never {}) .boxed(), ) - .unwrap()) + .unwrap() } async fn send_queue_event( diff --git a/install.sh b/install.sh index b265ce1e..740431af 100644 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh # shellcheck shell=dash -# Stalwart SMTP install script -- based on the rustup installation script. +# Stalwart Mail install script -- based on the rustup installation script. set -e set -u @@ -67,14 +67,14 @@ main() { ensure dscl /Local/Default -create Groups/_stalwart-mail ensure dscl /Local/Default -create Groups/_stalwart-mail Password \* ensure dscl /Local/Default -create Groups/_stalwart-mail PrimaryGroupID $_gid - ensure dscl /Local/Default -create Groups/_stalwart-mail RealName "Stalwart SMTP service" + ensure dscl /Local/Default -create Groups/_stalwart-mail RealName "Stalwart Mail service" ensure dscl /Local/Default -create Groups/_stalwart-mail RecordName _stalwart-mail stalwart-mail ensure dscl /Local/Default -create Users/_stalwart-mail ensure dscl /Local/Default -create Users/_stalwart-mail NFSHomeDirectory /Users/_stalwart-mail ensure dscl /Local/Default -create Users/_stalwart-mail Password \* ensure dscl /Local/Default -create Users/_stalwart-mail PrimaryGroupID $_gid - ensure dscl /Local/Default -create Users/_stalwart-mail RealName "Stalwart SMTP service" + ensure dscl /Local/Default -create Users/_stalwart-mail RealName "Stalwart Mail service" ensure dscl /Local/Default -create Users/_stalwart-mail RecordName _stalwart-mail stalwart-mail ensure dscl /Local/Default -create Users/_stalwart-mail UniqueID $_uid ensure dscl /Local/Default -create Users/_stalwart-mail UserShell /bin/bash diff --git a/resources/config/directory.toml b/resources/config/directory.toml index eaf14470..e6a0470f 100644 --- a/resources/config/directory.toml +++ b/resources/config/directory.toml @@ -64,9 +64,9 @@ connect-timeout = "30s" [directory."ldap".filter] name = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(uid=?))" -email = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=?)(mailAlias=?)))" +email = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=?)(mailAlias=?)(mailList=?)))" verify = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*?*)(mailAlias=*?*)))" -expand = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(sn=?))" +expand = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(mailList=?))" domains = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*@?)(mailAlias=*@?)))" [directory."ldap".object-classes] diff --git a/resources/config/smtp.toml b/resources/config/smtp.toml index eb28009d..80652963 100644 --- a/resources/config/smtp.toml +++ b/resources/config/smtp.toml @@ -74,10 +74,6 @@ relay = [ { if = "authenticated-as", ne = "", then = true }, max-recipients = 25 directory = "__SMTP_DIRECTORY__" -[session.rcpt.cache] -entries = 1000 -ttl = {positive = 10, negative = 5} - [session.rcpt.errors] total = 5 wait = "5s" @@ -218,10 +214,10 @@ mta-sts = 1024 [report] path = "__PATH__/reports" hash = 64 -#submitter = "mx.domain.org" +#submitter = "__HOST__" [report.analysis] -addresses = ["dmarc@*", "abuse@*"] +addresses = ["dmarc@*", "abuse@*", "postmaster@*"] forward = true #store = "__PATH__/incoming" diff --git a/tests/src/smtp/management/queue.rs b/tests/src/smtp/management/queue.rs index 800168ef..6eec9cbe 100644 --- a/tests/src/smtp/management/queue.rs +++ b/tests/src/smtp/management/queue.rs @@ -183,7 +183,7 @@ async fn manage_queue() { ); // Fetch and validate messages - let ids = send_manage_request::>("/queue/list") + let ids = send_manage_request::>("/admin/queue/list") .await .unwrap() .unwrap_data(); @@ -264,15 +264,24 @@ async fn manage_queue() { // Test list search for (query, expected_ids) in [ - ("/queue/list?from=bill1@foobar.net".to_string(), vec!["a"]), - ("/queue/list?to=foobar.org".to_string(), vec!["d", "e", "f"]), ( - "/queue/list?from=bill3@foobar.net&to=rcpt5@example1.com".to_string(), + "/admin/queue/list?from=bill1@foobar.net".to_string(), + vec!["a"], + ), + ( + "/admin/queue/list?to=foobar.org".to_string(), + vec!["d", "e", "f"], + ), + ( + "/admin/queue/list?from=bill3@foobar.net&to=rcpt5@example1.com".to_string(), vec!["c"], ), - (format!("/queue/list?before={test_search}"), vec!["a", "b"]), ( - format!("/queue/list?after={test_search}"), + format!("/admin/queue/list?before={test_search}"), + vec!["a", "b"], + ), + ( + format!("/admin/queue/list?after={test_search}"), vec!["d", "e", "f", "c"], ), ] { @@ -290,7 +299,7 @@ async fn manage_queue() { // Retry delivery assert_eq!( send_manage_request::>(&format!( - "/queue/retry?id={},{}", + "/admin/queue/retry?id={},{}", id_map.get("e").unwrap(), id_map.get("f").unwrap() )) @@ -301,7 +310,7 @@ async fn manage_queue() { ); assert_eq!( send_manage_request::>(&format!( - "/queue/retry?id={}&filter=example1.org&at=2200-01-01T00:00:00Z", + "/admin/queue/retry?id={}&filter=example1.org&at=2200-01-01T00:00:00Z", id_map.get("a").unwrap(), )) .await @@ -365,7 +374,7 @@ async fn manage_queue() { ] { assert_eq!( send_manage_request::>(&format!( - "/queue/cancel?id={}{}{}", + "/admin/queue/cancel?id={}{}{}", id_map.get(id).unwrap(), if !filter.is_empty() { "&filter=" } else { "" }, filter @@ -378,7 +387,7 @@ async fn manage_queue() { ); } assert_eq!( - send_manage_request::>("/queue/list") + send_manage_request::>("/admin/queue/list") .await .unwrap() .unwrap_data() @@ -464,7 +473,7 @@ fn assert_timestamp(timestamp: &DateTime, expected: i64, ctx: &str, message: &Me async fn get_messages(ids: &[QueueId]) -> Vec> { send_manage_request(&format!( - "/queue/status?id={}", + "/admin/queue/status?id={}", ids.iter() .map(|id| id.to_string()) .collect::>() diff --git a/tests/src/smtp/management/report.rs b/tests/src/smtp/management/report.rs index 7e0af0d0..ce9ff924 100644 --- a/tests/src/smtp/management/report.rs +++ b/tests/src/smtp/management/report.rs @@ -141,7 +141,7 @@ async fn manage_reports() { .await; // List reports - let ids = send_manage_request::>("/report/list") + let ids = send_manage_request::>("/admin/report/list") .await .unwrap() .unwrap_data(); @@ -172,12 +172,12 @@ async fn manage_reports() { // Test list search for (query, expected_ids) in [ - ("/report/list?type=dmarc", vec!["a", "b"]), - ("/report/list?type=tls", vec!["c", "d"]), - ("/report/list?domain=foobar.org", vec!["a", "c"]), - ("/report/list?domain=foobar.net", vec!["b", "d"]), - ("/report/list?domain=foobar.org&type=dmarc", vec!["a"]), - ("/report/list?domain=foobar.net&type=tls", vec!["d"]), + ("/admin/report/list?type=dmarc", vec!["a", "b"]), + ("/admin/report/list?type=tls", vec!["c", "d"]), + ("/admin/report/list?domain=foobar.org", vec!["a", "c"]), + ("/admin/report/list?domain=foobar.net", vec!["b", "d"]), + ("/admin/report/list?domain=foobar.org&type=dmarc", vec!["a"]), + ("/admin/report/list?domain=foobar.net&type=tls", vec!["d"]), ] { let expected_ids = HashSet::from_iter(expected_ids.into_iter().map(|s| s.to_string())); let ids = send_manage_request::>(query) @@ -194,7 +194,7 @@ async fn manage_reports() { for id in ["a", "b"] { assert_eq!( send_manage_request::>(&format!( - "/report/cancel?id={}", + "/admin/report/cancel?id={}", id_map.get(id).unwrap(), )) .await @@ -205,7 +205,7 @@ async fn manage_reports() { ); } assert_eq!( - send_manage_request::>("/report/list") + send_manage_request::>("/admin/report/list") .await .unwrap() .unwrap_data() @@ -227,7 +227,7 @@ async fn manage_reports() { } async fn get_reports(ids: &[String]) -> Vec> { - send_manage_request(&format!("/report/status?id={}", ids.join(","))) + send_manage_request(&format!("/admin/report/status?id={}", ids.join(","))) .await .unwrap() .unwrap_data()