Fixed configuration-managed settings remaining active after their configuration plugin was removed.

This commit is contained in:
Peer Schütt 2026-07-28 10:58:28 +02:00
parent df4663fff4
commit b7e0d948c4
5 changed files with 107 additions and 0 deletions

View File

@ -15,6 +15,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
{
this.ConfigSelection = configSelection;
this.PropertyExpression = propertyExpression;
this.SettingName = SettingsManager.ToSettingName(propertyExpression);
}
/// <summary>
@ -26,6 +27,11 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
/// The expression to select the property within the configuration class.
/// </summary>
private Expression<Func<TClass, TValue>> PropertyExpression { get; }
/// <summary>
/// The persisted name of the configuration setting.
/// </summary>
private string SettingName { get; }
/// <summary>
/// Indicates whether the configuration is locked by a configuration plugin.
@ -77,6 +83,25 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
this.LockedByConfigPluginId = pluginId;
this.ManagedMode = ManagedConfigurationMode.LOCKED;
this.EditableDefaultByConfigPluginId = Guid.Empty;
SettingsManagerAccess.ConfigurationData.ManagedLockedConfigurations[this.SettingName] = pluginId;
}
/// <summary>
/// Restores persisted locked configuration metadata after settings were loaded.
/// </summary>
public void RestoreLockedConfiguration()
{
if (this.IsLocked || this.ManagedMode is not null)
return;
if (!SettingsManagerAccess.ConfigurationData.ManagedLockedConfigurations.TryGetValue(this.SettingName, out var pluginId)
|| pluginId == Guid.Empty)
return;
this.IsLocked = true;
this.LockedByConfigPluginId = pluginId;
this.ManagedMode = ManagedConfigurationMode.LOCKED;
this.EditableDefaultByConfigPluginId = Guid.Empty;
}
/// <summary>
@ -85,6 +110,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
/// </summary>
public void ResetLockedConfiguration()
{
SettingsManagerAccess.ConfigurationData.ManagedLockedConfigurations.Remove(this.SettingName);
this.IsLocked = false;
this.LockedByConfigPluginId = Guid.Empty;
if (this.ManagedMode is ManagedConfigurationMode.LOCKED)
@ -98,6 +124,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
/// </summary>
public void UnlockConfiguration()
{
SettingsManagerAccess.ConfigurationData.ManagedLockedConfigurations.Remove(this.SettingName);
this.IsLocked = false;
this.LockedByConfigPluginId = Guid.Empty;
if (this.ManagedMode is ManagedConfigurationMode.LOCKED)
@ -109,6 +136,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
/// </summary>
public void SetEditableDefaultConfiguration(Guid pluginId)
{
SettingsManagerAccess.ConfigurationData.ManagedLockedConfigurations.Remove(this.SettingName);
this.IsLocked = false;
this.LockedByConfigPluginId = Guid.Empty;
this.ManagedMode = ManagedConfigurationMode.EDITABLE_DEFAULT;

View File

@ -63,6 +63,11 @@ public sealed class Data
/// </summary>
public Dictionary<string, ManagedEditableDefaultState> ManagedEditableDefaults { get; set; } = [];
/// <summary>
/// The configuration plugin that owns each locked managed setting.
/// </summary>
public Dictionary<string, Guid> ManagedLockedConfigurations { get; set; } = [];
/// <summary>
/// Cached audit results for assistant plugins.
/// </summary>

View File

@ -37,6 +37,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, TValue> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}
@ -73,6 +74,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, string> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}
@ -114,6 +116,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, TValue> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}
@ -151,6 +154,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, IList<TValue>> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}
@ -186,6 +190,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, ISet<TValue>> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}
@ -220,6 +225,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, IDictionary<string, string>> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}
@ -258,6 +264,7 @@ public static partial class ManagedConfiguration
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, Dictionary<TKey, TValue>> meta)
{
meta.RestoreLockedConfiguration();
configMeta = meta;
return true;
}

View File

@ -369,6 +369,72 @@ public static partial class PluginFactory
// Check enterprise-managed assistant plugin approvals
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.EnterpriseApprovedPlugins, AVAILABLE_PLUGINS))
wasConfigurationChanged = true;
// Compatibility shim: repair config-only values that may predate persisted lock ownership.
// these values can only be set by a config plugin and therefore cause the biggest problem, since the user can not change them himself
if (ManagedConfiguration.TryGet(x => x.App, x => x.ShowIntroduction, out var showIntroductionMeta)
&& showIntroductionMeta.ManagedMode is null
&& !SettingsManagerAccess.ConfigurationData.App.ShowIntroduction)
{
showIntroductionMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.App, x => x.ShowQuickStartGuide, out var showQuickStartGuideMeta)
&& showQuickStartGuideMeta.ManagedMode is null
&& !SettingsManagerAccess.ConfigurationData.App.ShowQuickStartGuide)
{
showQuickStartGuideMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.App, x => x.ShowLastChangelog, out var showLastChangelogMeta)
&& showLastChangelogMeta.ManagedMode is null
&& !SettingsManagerAccess.ConfigurationData.App.ShowLastChangelog)
{
showLastChangelogMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.App, x => x.ShowVision, out var showVisionMeta)
&& showVisionMeta.ManagedMode is null
&& !SettingsManagerAccess.ConfigurationData.App.ShowVision)
{
showVisionMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.App, x => x.AllowUserToAddProvider, out var allowUserToAddProviderMeta)
&& allowUserToAddProviderMeta.ManagedMode is null
&& !SettingsManagerAccess.ConfigurationData.App.AllowUserToAddProvider)
{
allowUserToAddProviderMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.App, x => x.HiddenAssistants, out var hiddenAssistantsMeta)
&& hiddenAssistantsMeta.ManagedMode is null
&& SettingsManagerAccess.ConfigurationData.App.HiddenAssistants.Count > 0)
{
hiddenAssistantsMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.DataSourceSecurity, x => x.TrustedProviderIds, out var trustedProviderIdsMeta)
&& trustedProviderIdsMeta.ManagedMode is null
&& SettingsManagerAccess.ConfigurationData.DataSourceSecurity.TrustedProviderIds.Count > 0)
{
trustedProviderIdsMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (ManagedConfiguration.TryGet(x => x.AssistantPluginAudit, x => x.EnterpriseApprovedPlugins, out var enterpriseApprovedPluginsMeta)
&& enterpriseApprovedPluginsMeta.ManagedMode is null
&& SettingsManagerAccess.ConfigurationData.AssistantPluginAudit.EnterpriseApprovedPlugins.Count > 0)
{
enterpriseApprovedPluginsMeta.ResetLockedConfiguration();
wasConfigurationChanged = true;
}
if (wasConfigurationChanged)
{

View File

@ -1 +1,2 @@
# v26.7.4, build 251 (2026-07-xx xx:xx UTC)
- Fixed configuration-managed settings remaining active after their configuration plugin was removed.