Each tenant's page carries its legacy protocols switch (LP-9 to LP-18)
A card above the tenant form: whether legacy mail protocols are on or off
for the organization, a one-click "Turn legacy protocols back on", and
"Turn off legacy protocols…", which opens -- before anything can change --
the impact panel with the tenant's own people (LP-15), the statement at
tenant scope and the typed confirmation (LP-16, LP-17). The statement reads
"everyone in {organization}" and names no ports and no firewall: a
tenant's switch closes nothing, other tenants share the ports (LP-13).
It reads and turns inbuxa:TenantProtocolPolicy, with the domain
permissions the server checks for it. It shows on the read-only tenant page
too: a tenant administrator reads its tenant without changing it (MT-12)
and may still turn the switch. Turning it back on while the server has
legacy protocols off is refused by the server, and the refusal is shown.
The banner (LP-18) falls back to the tenant's switch where the server's
can't be read -- inside a tenant -- and reads "off for your
organization".
The impact panel, statement and confirmation move to parts.tsx, shared by
Hardening and the tenant card; the statement takes its scope.
Checked by hand against a local server with a tenant, two of its users and
the admin signed in over IMAP: the tenant card's panel lists the tenant's
two users and not the admin; the statement reads "everyone in Example Co"
with no ports or firewall; turning it off makes the tenant's user get "NO
[ALERT] Your organization allows only INBUXA webmail and JMAP apps" over
IMAP; one click turns it back on; Hardening's panel lists all three. Not
checked by hand: the banner as a tenant administrator sees it.
This commit is contained in:
@@ -15,24 +15,43 @@ import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ShieldCheck } from 'lucide-react';
|
||||
import { useAccountStore } from '@/stores/accountStore';
|
||||
import { fetchProtocolPolicy } from './protocolPolicy';
|
||||
import { fetchProtocolPolicy, fetchTenantPolicy } from './protocolPolicy';
|
||||
|
||||
export const LEGACY_PROTOCOLS_VIEW = 'CustomComponent/LegacyProtocols';
|
||||
|
||||
export function LegacyProtocolsBanner() {
|
||||
const { t } = useTranslation();
|
||||
const canGet = useAccountStore((s) => s.hasObjectPermission('sysNetworkListener', 'Get'));
|
||||
const [off, setOff] = useState(false);
|
||||
const canGetServer = useAccountStore((s) => s.hasObjectPermission('sysNetworkListener', 'Get'));
|
||||
const canGetTenant = useAccountStore((s) => s.hasObjectPermission('sysDomain', 'Get'));
|
||||
const [off, setOff] = useState<null | 'server' | 'tenant'>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canGet) return;
|
||||
if (!canGetServer && !canGetTenant) return;
|
||||
const controller = new AbortController();
|
||||
fetchProtocolPolicy(controller.signal)
|
||||
.then((policy) => setOff(policy.legacyProtocols === 'disabled'))
|
||||
// A banner is not worth an error: an older server simply has no switch.
|
||||
.catch(() => setOff(false));
|
||||
const signal = controller.signal;
|
||||
(async () => {
|
||||
// The server's switch first. Inside a tenant it can't be read, and the
|
||||
// tenant's own is the one to report (LP-18 at tenant scope).
|
||||
try {
|
||||
if (canGetServer) {
|
||||
const policy = await fetchProtocolPolicy(signal);
|
||||
if (!signal.aborted) setOff(policy.legacyProtocols === 'disabled' ? 'server' : null);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// Fall through to the tenant's.
|
||||
}
|
||||
try {
|
||||
if (canGetTenant) {
|
||||
const policy = await fetchTenantPolicy(null, signal);
|
||||
if (!signal.aborted) setOff(policy.legacyProtocols === 'disabled' ? 'tenant' : null);
|
||||
}
|
||||
} catch {
|
||||
// A banner is not worth an error: an older server simply has no switch.
|
||||
}
|
||||
})();
|
||||
return () => controller.abort();
|
||||
}, [canGet]);
|
||||
}, [canGetServer, canGetTenant]);
|
||||
|
||||
if (!off) return null;
|
||||
|
||||
@@ -42,11 +61,18 @@ export function LegacyProtocolsBanner() {
|
||||
<span>
|
||||
{t('legacyProtocols.bannerLead', 'Legacy mail protocols are')}{' '}
|
||||
<strong>{t('legacyProtocols.bannerOff', 'off')}</strong>{' '}
|
||||
{t('legacyProtocols.bannerTail', 'on this server. Only INBUXA webmail and JMAP apps can sign in.')}
|
||||
{off === 'server'
|
||||
? t('legacyProtocols.bannerTail', 'on this server. Only INBUXA webmail and JMAP apps can sign in.')
|
||||
: t(
|
||||
'legacyProtocols.bannerTailTenant',
|
||||
'for your organization. Only INBUXA webmail and JMAP apps can sign in.',
|
||||
)}
|
||||
</span>
|
||||
<Link to={`/Settings/${LEGACY_PROTOCOLS_VIEW}`} className="font-medium text-primary hover:underline">
|
||||
{t('legacyProtocols.review', 'Review')}
|
||||
</Link>
|
||||
{off === 'server' && (
|
||||
<Link to={`/Settings/${LEGACY_PROTOCOLS_VIEW}`} className="font-medium text-primary hover:underline">
|
||||
{t('legacyProtocols.review', 'Review')}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user