Files
webmail/lib/admin/extension-cleanup.ts
T
Linus Rath 25408b2ac2 fix: refuse a policy save from a page opened before someone else saved #1066
The admin pages save the whole policy from the draft they loaded. A
save from a page opened before another tab's or another admin's save,
or before a deleted plugin was dropped from the policy, used to put the
older policy back. GET /api/admin/policy now sends the policy version as
an ETag; the policy, organisation, plugins and themes pages send it back
as If-Match, and a save based on an older version answers 409 and keeps
the draft on the page. A PUT without If-Match replaces the policy as
before. Deleting a plugin or theme changes only its id in the stored
policy, and a policy import compares against what it replaces.

Test mocks of the config manager gain getPolicyVersion and updatePolicy,
which the route and the extension cleanup now call.
2026-10-31 22:57:57 +01:00

30 lines
1.2 KiB
TypeScript

import { configManager } from './config-manager';
import { deletePluginConfig } from './plugin-config';
/**
* Drop a deleted plugin or theme from the policy lists that refer to it by
* id. Left behind, a later upload that reuses the id would start out forced
* on for everyone without the admin asking for it.
*/
export async function forgetExtensionInPolicy(kind: 'plugin' | 'theme', id: string): Promise<void> {
await configManager.ensureLoaded();
const key = kind === 'plugin' ? 'forceEnabledPlugins' : 'forceEnabledThemes';
// Against the stored policy, so a policy save landing at the same time
// keeps its changes.
await configManager.updatePolicy((policy) => {
const list = policy[key] ?? [];
if (!list.includes(id)) return undefined;
return { ...policy, [key]: list.filter((x) => x !== id) };
});
}
/**
* Everything the server keeps about an admin plugin besides its registry
* entry: the forced flag and its config (which can hold secrets). A plugin
* installed later under the same id starts clean.
*/
export async function cleanUpDeletedPlugin(id: string): Promise<void> {
await forgetExtensionInPolicy('plugin', id);
await deletePluginConfig(id);
}