mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 17:36:29 +03:00
feat: allow plain domain in dcaccount: scheme
This is similar to old `dcaccount:` with URL, but creates a 9-character username on the client and avoids making an HTTPS request. The scheme is reused to avoid the apps needing to register for the new scheme. `http` support is removed because it was not working already, there is a check that the scheme is `https` when the URL is actually used and the core has no way to make HTTP requests without TLS.
This commit is contained in:
@@ -45,8 +45,8 @@ class ACFactory:
|
|||||||
"""Create a new configured account."""
|
"""Create a new configured account."""
|
||||||
addr, password = self.get_credentials()
|
addr, password = self.get_credentials()
|
||||||
account = self.get_unconfigured_account()
|
account = self.get_unconfigured_account()
|
||||||
params = {"addr": addr, "password": password}
|
domain = os.getenv("CHATMAIL_DOMAIN")
|
||||||
yield account.add_or_update_transport.future(params)
|
yield account.add_transport_from_qr.future(f"dcaccount:{domain}")
|
||||||
|
|
||||||
assert account.is_configured()
|
assert account.is_configured()
|
||||||
return account
|
return account
|
||||||
|
|||||||
@@ -816,7 +816,7 @@ def test_configured_imap_certificate_checks(acfactory):
|
|||||||
alice = acfactory.new_configured_account()
|
alice = acfactory.new_configured_account()
|
||||||
|
|
||||||
# Certificate checks should be configured (not None)
|
# Certificate checks should be configured (not None)
|
||||||
assert "cert_automatic" in alice.get_info().used_account_settings
|
assert "cert_strict" in alice.get_info().used_account_settings
|
||||||
|
|
||||||
# "cert_old_automatic" is the value old Delta Chat core versions used
|
# "cert_old_automatic" is the value old Delta Chat core versions used
|
||||||
# to mean user entered "imap_certificate_checks=0" (Automatic)
|
# to mean user entered "imap_certificate_checks=0" (Automatic)
|
||||||
|
|||||||
53
src/qr.rs
53
src/qr.rs
@@ -9,6 +9,8 @@ pub use dclogin_scheme::LoginOptions;
|
|||||||
pub(crate) use dclogin_scheme::login_param_from_login_qr;
|
pub(crate) use dclogin_scheme::login_param_from_login_qr;
|
||||||
use deltachat_contact_tools::{ContactAddress, addr_normalize, may_be_valid_addr};
|
use deltachat_contact_tools::{ContactAddress, addr_normalize, may_be_valid_addr};
|
||||||
use percent_encoding::{NON_ALPHANUMERIC, percent_decode_str, percent_encode};
|
use percent_encoding::{NON_ALPHANUMERIC, percent_decode_str, percent_encode};
|
||||||
|
use rand::TryRngCore as _;
|
||||||
|
use rand::distr::{Alphanumeric, SampleString};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
@@ -543,21 +545,29 @@ async fn decode_ideltachat(context: &Context, prefix: &str, qr: &str) -> Result<
|
|||||||
.with_context(|| format!("failed to decode {prefix} QR code"))
|
.with_context(|| format!("failed to decode {prefix} QR code"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// scheme: `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3`
|
/// scheme: `DCACCOUNT:example.org`
|
||||||
|
/// or `DCACCOUNT:https://example.org/new`
|
||||||
|
/// or `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3`
|
||||||
fn decode_account(qr: &str) -> Result<Qr> {
|
fn decode_account(qr: &str) -> Result<Qr> {
|
||||||
let payload = qr
|
let payload = qr
|
||||||
.get(DCACCOUNT_SCHEME.len()..)
|
.get(DCACCOUNT_SCHEME.len()..)
|
||||||
.context("Invalid DCACCOUNT payload")?;
|
.context("Invalid DCACCOUNT payload")?;
|
||||||
let url = url::Url::parse(payload).context("Invalid account URL")?;
|
if payload.starts_with("https://") {
|
||||||
if url.scheme() == "http" || url.scheme() == "https" {
|
let url = url::Url::parse(payload).context("Invalid account URL")?;
|
||||||
Ok(Qr::Account {
|
if url.scheme() == "https" {
|
||||||
domain: url
|
Ok(Qr::Account {
|
||||||
.host_str()
|
domain: url
|
||||||
.context("can't extract account setup domain")?
|
.host_str()
|
||||||
.to_string(),
|
.context("can't extract account setup domain")?
|
||||||
})
|
.to_string(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bail!("Bad scheme for account URL: {:?}.", url.scheme());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bail!("Bad scheme for account URL: {:?}.", url.scheme());
|
Ok(Qr::Account {
|
||||||
|
domain: payload.to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -659,15 +669,30 @@ pub(crate) async fn login_param_from_account_qr(
|
|||||||
context: &Context,
|
context: &Context,
|
||||||
qr: &str,
|
qr: &str,
|
||||||
) -> Result<EnteredLoginParam> {
|
) -> Result<EnteredLoginParam> {
|
||||||
let url_str = qr
|
let payload = qr
|
||||||
.get(DCACCOUNT_SCHEME.len()..)
|
.get(DCACCOUNT_SCHEME.len()..)
|
||||||
.context("Invalid DCACCOUNT scheme")?;
|
.context("Invalid DCACCOUNT scheme")?;
|
||||||
|
|
||||||
if !url_str.starts_with(HTTPS_SCHEME) {
|
if !payload.starts_with(HTTPS_SCHEME) {
|
||||||
bail!("DCACCOUNT QR codes must use HTTPS scheme");
|
let rng = &mut rand::rngs::OsRng.unwrap_err();
|
||||||
|
let username = Alphanumeric.sample_string(rng, 9);
|
||||||
|
let addr = username + "@" + payload;
|
||||||
|
let password = Alphanumeric.sample_string(rng, 50);
|
||||||
|
|
||||||
|
let param = EnteredLoginParam {
|
||||||
|
addr,
|
||||||
|
imap: EnteredServerLoginParam {
|
||||||
|
password,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
smtp: Default::default(),
|
||||||
|
certificate_checks: EnteredCertificateChecks::Strict,
|
||||||
|
oauth2: false,
|
||||||
|
};
|
||||||
|
return Ok(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (response_text, response_success) = post_empty(context, url_str).await?;
|
let (response_text, response_success) = post_empty(context, payload).await?;
|
||||||
if response_success {
|
if response_success {
|
||||||
let CreateAccountSuccessResponse { password, email } = serde_json::from_str(&response_text)
|
let CreateAccountSuccessResponse { password, email } = serde_json::from_str(&response_text)
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
|
|||||||
@@ -643,30 +643,20 @@ async fn test_decode_dclogin_advanced_options() -> Result<()> {
|
|||||||
async fn test_decode_account() -> Result<()> {
|
async fn test_decode_account() -> Result<()> {
|
||||||
let ctx = TestContext::new().await;
|
let ctx = TestContext::new().await;
|
||||||
|
|
||||||
let qr = check_qr(
|
for text in [
|
||||||
&ctx.ctx,
|
"DCACCOUNT:example.org",
|
||||||
|
"dcaccount:example.org",
|
||||||
"DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
|
"DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
assert_eq!(
|
|
||||||
qr,
|
|
||||||
Qr::Account {
|
|
||||||
domain: "example.org".to_string()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test it again with lowercased "dcaccount:" uri scheme
|
|
||||||
let qr = check_qr(
|
|
||||||
&ctx.ctx,
|
|
||||||
"dcaccount:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
|
"dcaccount:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
|
||||||
)
|
] {
|
||||||
.await?;
|
let qr = check_qr(&ctx.ctx, text).await?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
qr,
|
qr,
|
||||||
Qr::Account {
|
Qr::Account {
|
||||||
domain: "example.org".to_string()
|
domain: "example.org".to_string()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -734,25 +724,6 @@ async fn test_decode_tg_socks_proxy() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn test_decode_account_bad_scheme() {
|
|
||||||
let ctx = TestContext::new().await;
|
|
||||||
let res = check_qr(
|
|
||||||
&ctx.ctx,
|
|
||||||
"DCACCOUNT:ftp://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(res.is_err());
|
|
||||||
|
|
||||||
// Test it again with lowercased "dcaccount:" uri scheme
|
|
||||||
let res = check_qr(
|
|
||||||
&ctx.ctx,
|
|
||||||
"dcaccount:ftp://example.org/new_email?t=1w_7wDjgjelxeX884x96v3",
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert!(res.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_set_proxy_config_from_qr() -> Result<()> {
|
async fn test_set_proxy_config_from_qr() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
|
|||||||
Reference in New Issue
Block a user