Masked email: Fastmail's Masked Email API, MaskedEmail/get and /set (ME-1, ME-7a, ME-16)

Advertised as https://www.fastmail.com/dev/maskedemail in the session and on
every account that may hold masks. Masks created through it start pending
unless the create sets a state; pending can't be set again once left; state
and the other mutable fields map onto the same records the x: API uses.
This commit is contained in:
2026-09-18 16:29:47 -07:00
parent 7080028437
commit d04aafd3d7
16 changed files with 626 additions and 5 deletions
+12 -1
View File
@@ -69,6 +69,8 @@ impl JmapAuthorization for AccessToken {
GetRequestMethod::PushSubscription(_) => Permission::JmapPushSubscriptionGet,
GetRequestMethod::Sieve(_) => Permission::JmapSieveScriptGet,
GetRequestMethod::VacationResponse(_) => Permission::JmapVacationResponseGet,
// inbuxa: Fastmail's MaskedEmail (ME-18)
GetRequestMethod::MaskedEmail(_) => Permission::SysMaskedEmailGet,
GetRequestMethod::Principal(_) => Permission::JmapPrincipalGet,
GetRequestMethod::Quota(_) => Permission::JmapQuotaGet,
GetRequestMethod::Blob(_) => Permission::JmapBlobGet,
@@ -141,6 +143,14 @@ impl JmapAuthorization for AccessToken {
Permission::JmapSieveScriptUpdate,
Permission::JmapSieveScriptDestroy,
),
// inbuxa: Fastmail's MaskedEmail (ME-18)
SetRequestMethod::MaskedEmail(s) => validate_set(
s,
self,
Permission::SysMaskedEmailCreate,
Permission::SysMaskedEmailUpdate,
Permission::SysMaskedEmailDestroy,
),
SetRequestMethod::VacationResponse(s) => validate_set(
s,
self,
@@ -247,7 +257,8 @@ impl JmapAuthorization for AccessToken {
| MethodObject::PushSubscription
| MethodObject::SearchSnippet
| MethodObject::VacationResponse
| MethodObject::SieveScript => Permission::JmapEmailChanges,
| MethodObject::SieveScript
| MethodObject::MaskedEmail => Permission::JmapEmailChanges,
// inbuxa: x:MaskedEmail/changes reads what /get reads
MethodObject::Registry(object_type) => object_type.get_permission(),
},
+17
View File
@@ -162,6 +162,9 @@ impl RequestHandler for Server {
SetResponseMethod::VacationResponse(set_response) => {
set_response.update_created_ids(&mut response);
}
SetResponseMethod::MaskedEmail(set_response) => {
set_response.update_created_ids(&mut response);
}
SetResponseMethod::AddressBook(set_response) => {
set_response.update_created_ids(&mut response);
}
@@ -296,6 +299,13 @@ impl RequestHandler for Server {
self.vacation_response_get(*req).await?.into()
}
// inbuxa: Fastmail's MaskedEmail/get
GetRequestMethod::MaskedEmail(mut req) => {
resolve_account_id(&mut req.account_id, method_name.obj, access_token)?;
crate::inbuxa::fastmail::get(self, access_token, *req)
.await?
.into()
}
GetRequestMethod::Principal(req) => {
self.principal_get(*req, access_token).await?.into()
}
@@ -516,6 +526,13 @@ impl RequestHandler for Server {
self.vacation_response_set(*req, access_token).await?.into()
}
// inbuxa: Fastmail's MaskedEmail/set
SetRequestMethod::MaskedEmail(mut req) => {
resolve_account_id(&mut req.account_id, method_name.obj, access_token)?;
crate::inbuxa::fastmail::set(self, access_token, *req)
.await?
.into()
}
SetRequestMethod::AddressBook(mut req) => {
resolve_account_id(&mut req.account_id, method_name.obj, access_token)?;
access_token.assert_has_access(req.account_id, Collection::AddressBook)?;
+16 -1
View File
@@ -68,6 +68,20 @@ impl SessionHandler for Server {
Capability::Inbuxa,
Capabilities::Inbuxa(InbuxaAccountCapabilities { logo }),
);
// inbuxa: Fastmail's Masked Email API, for accounts that may hold masks
if access_token.has_permission(Permission::SysMaskedEmailGet) {
session.capabilities.append(
Capability::FastmailMaskedEmail,
Capabilities::Empty(EmptyCapabilities::default()),
);
account.account_capabilities.append(
Capability::FastmailMaskedEmail,
Capabilities::Empty(EmptyCapabilities::default()),
);
session
.primary_accounts
.append(Capability::FastmailMaskedEmail, account_id);
}
session.accounts.append(account_id, account);
// Add secondary accounts
@@ -139,7 +153,8 @@ impl AccountCapabilities for AccessToken {
Capability::Core
| Capability::PrincipalsOwner
| Capability::WebPushVapid
| Capability::Inbuxa => {
| Capability::Inbuxa
| Capability::FastmailMaskedEmail => {
return false;
}
};