Files
Stalwart/crates/jmap/src/api/management/mod.rs

155 lines
5.6 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
pub mod dkim;
pub mod domain;
#[cfg(feature = "enterprise")]
pub mod enterprise;
pub mod log;
pub mod principal;
pub mod queue;
pub mod reload;
pub mod report;
pub mod settings;
pub mod sieve;
pub mod stores;
use std::{borrow::Cow, sync::Arc};
use hyper::Method;
use jmap_proto::error::request::RequestError;
use serde::Serialize;
use super::{http::ToHttpResponse, HttpRequest, HttpResponse, JsonResponse};
use crate::{auth::AccessToken, JMAP};
#[cfg(feature = "enterprise")]
use se_common::EnterpriseCore;
#[derive(Serialize)]
#[serde(tag = "error")]
pub enum ManagementApiError {
FieldAlreadyExists {
field: Cow<'static, str>,
value: Cow<'static, str>,
},
FieldMissing {
field: Cow<'static, str>,
},
NotFound {
item: Cow<'static, str>,
},
Unsupported {
details: Cow<'static, str>,
},
AssertFailed,
Other {
details: Cow<'static, str>,
},
UnsupportedDirectoryOperation {
class: Cow<'static, str>,
},
}
impl JMAP {
pub async fn handle_api_manage_request(
&self,
req: &HttpRequest,
body: Option<Vec<u8>>,
access_token: Arc<AccessToken>,
) -> HttpResponse {
let path = req.uri().path().split('/').skip(2).collect::<Vec<_>>();
let is_superuser = access_token.is_super_user();
match path.first().copied().unwrap_or_default() {
"queue" if is_superuser => self.handle_manage_queue(req, path).await,
"settings" if is_superuser => self.handle_manage_settings(req, path, body).await,
"reports" if is_superuser => self.handle_manage_reports(req, path).await,
"principal" if is_superuser => self.handle_manage_principal(req, path, body).await,
"domain" if is_superuser => self.handle_manage_domain(req, path).await,
"store" if is_superuser => self.handle_manage_store(req, path).await,
"reload" if is_superuser => self.handle_manage_reload(req, path).await,
"dkim" if is_superuser => self.handle_manage_dkim(req, path, body).await,
"update" if is_superuser => self.handle_manage_update(req, path).await,
"logs" if is_superuser && req.method() == Method::GET => {
self.handle_view_logs(req).await
}
"sieve" if is_superuser => self.handle_run_sieve(req, path, body).await,
"restart" if is_superuser && req.method() == Method::GET => {
ManagementApiError::Unsupported {
details: "Restart is not yet supported".into(),
}
.into_http_response()
}
"oauth" => self.handle_oauth_api_request(access_token, body).await,
"account" => match (path.get(1).copied().unwrap_or_default(), req.method()) {
("crypto", &Method::POST) => self.handle_crypto_post(access_token, body).await,
("crypto", &Method::GET) => self.handle_crypto_get(access_token).await,
("auth", &Method::GET) => self.handle_account_auth_get(access_token).await,
("auth", &Method::POST) => {
self.handle_account_auth_post(req, access_token, body).await
}
_ => RequestError::not_found().into_http_response(),
},
// SPDX-SnippetBegin
// SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
"pro" if is_superuser => {
// WARNING: TAMPERING WITH THIS FUNCTION IS STRICTLY PROHIBITED
// Any attempt to modify, bypass, or disable this license validation mechanism
// constitutes a severe violation of the Stalwart Enterprise License Agreement.
// Such actions may result in immediate termination of your license, legal action,
// and substantial financial penalties. Stalwart Labs Ltd. actively monitors for
// unauthorized modifications and will pursue all available legal remedies against
// violators to the fullest extent of the law, including but not limited to claims
// for copyright infringement, breach of contract, and fraud.
if self.core.is_enterprise_edition() {
self.handle_enterprise_api_request(req, path, body).await
} else {
ManagementApiError::Unsupported {
details: "This feature is only available in the Enterprise version".into(),
}
.into_http_response()
}
}
// SPDX-SnippetEnd
_ => RequestError::not_found().into_http_response(),
}
}
}
impl ToHttpResponse for ManagementApiError {
fn into_http_response(self) -> super::HttpResponse {
JsonResponse::new(self).into_http_response()
}
}
impl From<Cow<'static, str>> for ManagementApiError {
fn from(details: Cow<'static, str>) -> Self {
ManagementApiError::Other { details }
}
}
impl From<String> for ManagementApiError {
fn from(details: String) -> Self {
ManagementApiError::Other {
details: details.into(),
}
}
}
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())
}