Improved enterprise plugin rollouts and protected deployed plugins against changes (#938)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-08-28 14:06:24 +02:00
1 parent a4c35fc2f2
commit 3940ad7f29
11 files changed
+232 -54

No files matched your search

@@ -176,13 +176,22 @@ public sealed partial class PluginInstallService
return TB("The plugin has no local directory.");
//
// We decide by the plugin path, not by what a plugin declares about itself. Both
// DEPLOYED_USING_CONFIG_SERVER and the Assistant Builder metadata are self-declared: a
// locally placed plugin could claim to be deployed by an organization, or simply omit the
// builder metadata, and would then be impossible to remove through the user interface, which
// is exactly the situation this deletion is meant to resolve.
// Nothing an organization rolled out belongs to the user, so none of it may be removed here.
// The plugin path is the primary criterion and covers every plugin type: a deployed
// configuration, a test configuration staged for it, and every plugin an organization ships
// alongside them in a subdirectory.
//
if (PluginFactory.IsEnterpriseConfigurationPath(plugin.LocalPath))
if (PluginFactory.IsOrganizationConfigurationPath(plugin.LocalPath))
return TB("Plugins deployed by your organization cannot be deleted.");
//
// Organizations also roll plugins out past these directories, e.g. through their MDM
// solution. DEPLOYED_USING_CONFIG_SERVER is the only marker such a plugin has, so we honor
// it here just like sharing, replacing, and revising already do. A plugin cannot acquire the
// flag by accident: an archive declaring it is refused on import, which leaves deliberate
// manual placement as the only way in, and the file system as the way back out.
//
if (plugin.IsManagedByConfigServer)
return TB("Plugins deployed by your organization cannot be deleted.");
if (!PluginFactory.IsInsidePluginsRoot(plugin.LocalPath) || PluginFactory.IsPluginsRoot(plugin.LocalPath))