Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2f41bce26 | ||
|
|
cd99037ca4 |
@@ -403,6 +403,23 @@ impl Server {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether legacy protocols are off for this account: the stricter of the
|
||||
/// server's switch and its tenant's. What the JMAP session tells the
|
||||
/// account's apps (legacy-protocols spec, Interfaces), so the webmail can
|
||||
/// say why a mail app won't connect (LP-19).
|
||||
pub async fn legacy_protocols_off_for_account(
|
||||
&self,
|
||||
access_token: &AccessToken,
|
||||
) -> trc::Result<bool> {
|
||||
if self.protocol_policy().await?.legacy_protocols.is_disabled() {
|
||||
return Ok(true);
|
||||
}
|
||||
match access_token.tenant_id() {
|
||||
Some(tenant_id) => self.tenant_legacy_protocols_off(tenant_id).await,
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a tenant has turned legacy protocols off for itself (LP-10).
|
||||
pub async fn tenant_legacy_protocols_off(&self, tenant_id: u32) -> trc::Result<bool> {
|
||||
Ok(
|
||||
|
||||
@@ -142,6 +142,11 @@ pub struct InbuxaAccountCapabilities {
|
||||
/// The logo that applies to the principal (MT-22): a URL or a data URL.
|
||||
#[serde(rename(serialize = "logo"))]
|
||||
pub logo: Option<String>,
|
||||
/// Whether legacy mail protocols are `enabled` or `disabled` for the
|
||||
/// principal: the stricter of the server's switch and its tenant's
|
||||
/// (legacy-protocols spec, Interfaces; LP-19).
|
||||
#[serde(rename(serialize = "legacyProtocols"))]
|
||||
pub legacy_protocols: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
|
||||
@@ -66,9 +66,18 @@ impl SessionHandler for Server {
|
||||
Capability::Inbuxa,
|
||||
Capabilities::Empty(EmptyCapabilities::default()),
|
||||
);
|
||||
// inbuxa: legacy-protocols, Interfaces: whichever switch is stricter
|
||||
let legacy_protocols = if self.legacy_protocols_off_for_account(access_token).await? {
|
||||
"disabled"
|
||||
} else {
|
||||
"enabled"
|
||||
};
|
||||
account.account_capabilities.append(
|
||||
Capability::Inbuxa,
|
||||
Capabilities::Inbuxa(InbuxaAccountCapabilities { logo }),
|
||||
Capabilities::Inbuxa(InbuxaAccountCapabilities {
|
||||
logo,
|
||||
legacy_protocols,
|
||||
}),
|
||||
);
|
||||
// inbuxa: Fastmail's Masked Email API, for accounts that may hold masks
|
||||
if access_token.has_permission(Permission::SysMaskedEmailGet) {
|
||||
|
||||
@@ -68,6 +68,12 @@ Each has an ID, and tests name the IDs they check.
|
||||
In `accountCapabilities`, the signed-in principal's own account carries
|
||||
`urn:inbuxa:jmap` with `logo`: the logo that applies to it (multi-tenancy
|
||||
MT-22), a string (URL or data URL) or `null`. Added 2026-09-18.
|
||||
|
||||
It also carries `legacyProtocols`: `enabled` or `disabled`, whether IMAP,
|
||||
POP3, ManageSieve and SMTP submission are off for the principal -- the
|
||||
stricter of the server's switch and its tenant's (legacy-protocols spec,
|
||||
Interfaces). A front end uses it to say why a mail app can't connect
|
||||
(LP-19). Added 2026-09-21.
|
||||
- **C-2.** Each front end states the contract versions it supports and checks
|
||||
`contract` after signing in. Outside its range it stops, with a message
|
||||
naming both versions. For ihasmail-inbuxa this replaces public ihasmail's
|
||||
|
||||
@@ -29,7 +29,8 @@ address or made-up, right password or wrong -- in the organization's words,
|
||||
leaves every other domain alone, and stops client configuration offering
|
||||
legacy servers for those domains. It reaches only its own tenant's switch,
|
||||
and can't turn it back on while the server has legacy protocols off
|
||||
(acceptance tests 6 to 10, 14).
|
||||
(acceptance tests 6 to 10, 14). Throughout, the JMAP session tells each
|
||||
account which way its switches point (test 13).
|
||||
|
||||
Passwords are generated into files under target/e2e and never printed.
|
||||
Everything is removed afterwards unless KEEP=1.
|
||||
@@ -289,6 +290,9 @@ def tenant_checks(admin, admin_pw, account):
|
||||
tset = lambda value: one(ta, tadmin_pw, "inbuxa:TenantProtocolPolicy/set",
|
||||
{"accountId": tacct, "update": {t: {"legacyProtocols": value}}})
|
||||
|
||||
check(session_flag(tu, user_pw) == "enabled",
|
||||
"the session says enabled for the tenant's user while both switches are on (test 13)")
|
||||
|
||||
# Before: the tenant's user signs in, and its domain is offered IMAP.
|
||||
check(imap_login(PORTS["imap"], tu, user_pw).startswith("OK"),
|
||||
"a tenant's user signs in over IMAP with the tenant's switch on")
|
||||
@@ -313,6 +317,11 @@ def tenant_checks(admin, admin_pw, account):
|
||||
'value = "disabled"'),
|
||||
"and it is an event, scope tenant (LP-14, test 14)")
|
||||
|
||||
check(session_flag(tu, user_pw) == "disabled",
|
||||
"the session says disabled for the tenant's user once its tenant turns it off (test 13)")
|
||||
check(session_flag(admin, admin_pw) == "enabled",
|
||||
"and still enabled for an account outside the tenant (test 13)")
|
||||
|
||||
# Refused on the tenant's domain, every way in the same words (tests 6-8).
|
||||
imap_no = ("NO [ALERT] Your organization allows only INBUXA webmail and JMAP apps. "
|
||||
"This mail app can't sign in.")
|
||||
@@ -343,6 +352,8 @@ def tenant_checks(admin, admin_pw, account):
|
||||
# Server off means off for everyone: the tenant can't turn it back on (test 9).
|
||||
one(admin, admin_pw, "inbuxa:ProtocolPolicy/set",
|
||||
{"accountId": account, "update": {"singleton": {"legacyProtocols": "disabled"}}})
|
||||
check(session_flag(admin, admin_pw) == "disabled",
|
||||
"with the server off, the session says disabled for everyone (test 13)")
|
||||
res = tset("enabled")
|
||||
refused = (res[1].get("notUpdated") or {}).get(t) or {}
|
||||
check(refused.get("type") == "forbidden"
|
||||
@@ -357,6 +368,14 @@ def tenant_checks(admin, admin_pw, account):
|
||||
check(t in (res[1].get("updated") or {}), "with the server on, the tenant turns them back on")
|
||||
check(imap_login(PORTS["imap"], tu, user_pw).startswith("OK"),
|
||||
"and its user signs in over IMAP again")
|
||||
check(session_flag(tu, user_pw) == "enabled", "and its session says enabled again (test 13)")
|
||||
|
||||
|
||||
def session_flag(user, password):
|
||||
"""legacyProtocols from the account's urn:inbuxa:jmap capability."""
|
||||
sess = session(user, password)
|
||||
acct = sess["primaryAccounts"].get(INBUXA) or list(sess["accounts"])[0]
|
||||
return sess["accounts"][acct]["accountCapabilities"].get(INBUXA, {}).get("legacyProtocols")
|
||||
|
||||
|
||||
def events_matching(name, *parts):
|
||||
|
||||
Reference in New Issue
Block a user