jcoffey-dev is traveling from Thursday 1 October through Sunday 4 October. Issues and pull requests are welcome, and will get an answer after that. Thanks for your patience.
The legacy-protocols switch was all or nothing. An operator can now stop
POP3 and keep IMAP: each of IMAP, POP3 and ManageSieve has its own
switch, server-wide on inbuxa:ProtocolPolicy and per tenant on
inbuxa:TenantProtocolPolicy (properties imap, pop3, manageSieve).
legacyProtocols stays as the kill-all: setting it sets all three, and it
reads "disabled" exactly when all three are off. A policy stored before
this has only legacyProtocols and reads as all three at that value, so
existing servers and tenants carry over unchanged. In one /set, a
protocol named beside legacyProtocols overrides it.
SMTP submission keeps no switch of its own: sign-in over it is refused
only when all three are off, as the single switch did (LP-6), so
turning one protocol off never stops a mail app sending. For a tenant,
the server's switches and the tenant's count together.
Server-wide, a change closes the listeners of whatever is now off and
puts back the saved listeners of whatever is on again, both in one
change if asked; listeners of a protocol still off stay saved. Sign-in,
autoconfig, autodiscover, PACC (now prepared once per combination) and
the suggested DNS records all follow each protocol separately. A tenant
may turn a protocol on only while the server has it on (LP-9), and the
refusal names which. The JMAP session adds legacyAllowed, the protocols
still allowed for the account; legacyProtocols there keeps its meaning
for older webmail builds. Events name the switches ("pop3 disabled"),
and audit before/after reads every switch even from an older policy.
Tested: unit tests for the switches, the old-policy reading, the
server/tenant combination, the tenant refusal and listener refusal; and
tests/e2e/legacy_protocols.py against a running server, all 100 checks,
including new ones: POP3 alone off closes only its port and refuses
only its sign-in while IMAP and sending go on; only POP3 stops being
advertised; one change closes IMAP and reopens POP3; a tenant turns
POP3 off for itself, and can't turn IMAP on while the server has it off.
The impact panel's data. Every successful sign-in over IMAP, POP3,
ManageSieve or SMTP AUTH records, per account and per protocol, one
timestamp -- nothing else: no address, no IP, no client. It is written at
most once an hour per account and protocol, so a mail app polling every
minute costs a read per sign-in and a write an hour. A record that can't be
written is logged and the sign-in goes ahead.
Both switches serve it as a read-only property, recentLegacyUse, as
wouldClose serves the confirmation: a list of {accountId, name, protocol,
lastUsedAt} for sign-ins in the last 30 days, most recent first.
inbuxa:ProtocolPolicy lists every account; inbuxa:TenantProtocolPolicy
lists only its tenant's own (MT-1). Accounts since deleted are left out. It
is computed only when the property is asked for.
The recording sits where the tenant check already runs once the account is
known, which becomes admit_legacy_session: refuse if the account's tenant
has legacy protocols off, otherwise record. A refused sign-in is never
recorded.
The spec leaves the interface to the implementation; a property on each
switch keeps the panel's data behind the same permission as the switch
itself, with no new object.
Unit tests hold the 30-day window to acceptance test 11 (three days ago
listed, forty not), the hourly throttle and the keys. The e2e proves on a
running server that the admin's IMAP and submission sign-ins are listed
with their time, that a second sign-in within the hour isn't written again,
and that a tenant's list holds its own user and nobody outside the tenant.
All 70 checks pass.
The switch is now reachable. /get and /set on a server-level singleton,
wired through jmap-proto the way inbuxa:AiLimits is: object, method names,
request and response variants, reference resolution and evaluation.
/set does not write the policy. It hands what was asked to
Server::set_protocol_policy, which applies the locks, moves the listener
objects and opens or closes their sockets, and reports what happened. So
the method cannot drift from what the switch actually does.
Two properties exist for the screen rather than the server. lockedProtocols
serves LP-21's locked set, so the selector renders SMTP and JMAP locked
from what the server says instead of a list the front end carries -- and
unlocking later needs no admin release. wouldClose answers LP-16: exactly
which listeners turning the switch on would close, by name and port, before
anything happens. It is computed against a hypothetical disabled policy, so
it reads the same whichever way the switch is set, and the registry is only
asked when the property was requested.
savedListeners, changedAt, changedBy and both of those are the server's to
say; a client that sets one gets invalidProperties naming it. closeSubmission
is different: locked, not immutable, so it is overruled rather than refused
and the response hands back what was really stored (false). JMAP already has
the place for that, the value beside an updated id.
Permissions reuse SysNetworkListenerGet and SysNetworkListenerUpdate rather
than adding to a schema-generated enum -- the same choice AiLimits made with
the classifier's. It also reads right: this takes listeners away and puts
them back, so whoever may edit a listener may turn the switch.
changedBy stores the account id, not the name, which survives a rename.
Still no screen, no sign-in refusal (LP-6) and no event (LP-8).