From 24ad9e15c681cc25236d7e45e06e1abf65f041f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:08:29 +0000 Subject: [PATCH 01/10] Initial plan From 122558723af9a278b7e7cf33f6dc6696e1802c62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:11:14 +0000 Subject: [PATCH 02/10] Add policy to override SettingsFolder Location Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- Source/NETworkManager.Settings/PolicyInfo.cs | 3 ++ .../NETworkManager.Settings/PolicyManager.cs | 1 + .../SettingsManager.cs | 5 ++ .../config.json.example | 3 +- Website/docs/settings/settings.md | 4 ++ Website/docs/system-wide-policies.md | 49 ++++++++++++++++++- 6 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Source/NETworkManager.Settings/PolicyInfo.cs b/Source/NETworkManager.Settings/PolicyInfo.cs index 7b9b64a6a0..0a7f512ba3 100644 --- a/Source/NETworkManager.Settings/PolicyInfo.cs +++ b/Source/NETworkManager.Settings/PolicyInfo.cs @@ -10,4 +10,7 @@ public class PolicyInfo { [JsonPropertyName("Update_CheckForUpdatesAtStartup")] public bool? Update_CheckForUpdatesAtStartup { get; set; } + + [JsonPropertyName("SettingsFolderLocation")] + public string SettingsFolderLocation { get; set; } } diff --git a/Source/NETworkManager.Settings/PolicyManager.cs b/Source/NETworkManager.Settings/PolicyManager.cs index 7dd762c8de..fc946620bc 100644 --- a/Source/NETworkManager.Settings/PolicyManager.cs +++ b/Source/NETworkManager.Settings/PolicyManager.cs @@ -83,6 +83,7 @@ public static void Load() // Log enabled settings Log.Info($"System-wide policy - Update_CheckForUpdatesAtStartup: {Current.Update_CheckForUpdatesAtStartup?.ToString() ?? "Not set"}"); + Log.Info($"System-wide policy - SettingsFolderLocation: {Current.SettingsFolderLocation ?? "Not set"}"); } } catch (Exception ex) diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 29a140218e..83241bde51 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -77,6 +77,11 @@ public static class SettingsManager /// Path to the settings folder. public static string GetSettingsFolderLocation() { + // Policy override takes precedence + if (!string.IsNullOrWhiteSpace(PolicyManager.Current?.SettingsFolderLocation)) + return PolicyManager.Current.SettingsFolderLocation; + + // Fall back to existing logic return ConfigurationManager.Current.IsPortable ? Path.Combine(AssemblyManager.Current.Location, SettingsFolderName) : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), diff --git a/Source/NETworkManager.Settings/config.json.example b/Source/NETworkManager.Settings/config.json.example index 8ca2bf05b3..0a47ffa700 100644 --- a/Source/NETworkManager.Settings/config.json.example +++ b/Source/NETworkManager.Settings/config.json.example @@ -1,3 +1,4 @@ { - "Update_CheckForUpdatesAtStartup": false + "Update_CheckForUpdatesAtStartup": false, + "SettingsFolderLocation": "C:\\CustomPath\\NETworkManager\\Settings" } \ No newline at end of file diff --git a/Website/docs/settings/settings.md b/Website/docs/settings/settings.md index 32342b3d6e..f4404603c2 100644 --- a/Website/docs/settings/settings.md +++ b/Website/docs/settings/settings.md @@ -17,6 +17,10 @@ Folder where the application settings are stored. | Setup / Archiv | `%UserProfile%\Documents\NETworkManager\Settings` | | Portable | `\Settings` | +**Policy Override**: `SettingsFolderLocation` + +This setting can be overridden by a [system-wide policy](../system-wide-policies.md). When a policy is configured, it takes precedence over the default location, allowing administrators to specify a custom settings folder path for all users. + :::note **Recommendation** diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index d8ef644453..56e85d6b5d 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -36,7 +36,8 @@ The `config.json` file uses a simple JSON structure to define policy values. An ```json { - "Update_CheckForUpdatesAtStartup": false + "Update_CheckForUpdatesAtStartup": false, + "SettingsFolderLocation": "C:\\ProgramData\\NETworkManager\\Settings" } ``` @@ -97,6 +98,52 @@ Ensure the `config.json` file has appropriate permissions so that regular users - Verify the value is a boolean (`true` or `false`), not a string (`"true"` or `"false"`) - Check that there are no syntax errors in the JSON file +## Available Policies + +### Update_CheckForUpdatesAtStartup + +Control whether the application checks for updates at startup. + +**Type**: `Boolean` (true/false) + +**Default**: User setting applies + +**Example**: +```json +{ + "Update_CheckForUpdatesAtStartup": false +} +``` + +See [Check for updates at startup](./settings/update.md#check-for-updates-at-startup) for more details. + +### SettingsFolderLocation + +Override the default location where application settings are stored. + +**Type**: `String` (absolute path) + +**Default**: User Documents folder or portable location + +**Example**: +```json +{ + "SettingsFolderLocation": "C:\\ProgramData\\NETworkManager\\Settings" +} +``` + +See [Location](./settings/settings.md#location) for more details. + +:::warning + +When configuring a custom settings folder location, ensure: +- The path uses double backslashes (`\\`) in JSON format +- The application has read/write permissions to the specified directory +- The directory exists or the application has permissions to create it +- All users have appropriate access to the folder if using a shared location + +::: + ## Future Policies Additional policy options will be added in future releases to provide more granular control over application behavior. If you have specific requirements for system-wide policies in your organization, please submit a feature request via the [GitHub issue tracker](https://github.com/BornToBeRoot/NETworkManager/issues/new/choose). From bf0c78e8df2ec8d14ab38a4e760ef18d194b3419 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:13:00 +0000 Subject: [PATCH 03/10] Add validation for policy-provided SettingsFolderLocation Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- Source/NETworkManager.Settings/PolicyInfo.cs | 2 +- .../SettingsManager.cs | 24 ++++++++++++++++++- Website/docs/system-wide-policies.md | 4 ++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Source/NETworkManager.Settings/PolicyInfo.cs b/Source/NETworkManager.Settings/PolicyInfo.cs index 0a7f512ba3..2f17ee8abf 100644 --- a/Source/NETworkManager.Settings/PolicyInfo.cs +++ b/Source/NETworkManager.Settings/PolicyInfo.cs @@ -12,5 +12,5 @@ public class PolicyInfo public bool? Update_CheckForUpdatesAtStartup { get; set; } [JsonPropertyName("SettingsFolderLocation")] - public string SettingsFolderLocation { get; set; } + public string? SettingsFolderLocation { get; set; } } diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 83241bde51..1343bf2c81 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -79,7 +79,29 @@ public static string GetSettingsFolderLocation() { // Policy override takes precedence if (!string.IsNullOrWhiteSpace(PolicyManager.Current?.SettingsFolderLocation)) - return PolicyManager.Current.SettingsFolderLocation; + { + var policyPath = PolicyManager.Current.SettingsFolderLocation; + + // Validate that the policy-provided path is rooted (absolute) + if (!Path.IsPathRooted(policyPath)) + { + Log.Error($"Policy-provided SettingsFolderLocation is not an absolute path: {policyPath}. Falling back to default location."); + } + else + { + // Validate that the path doesn't contain invalid characters + try + { + // This will throw if the path contains invalid characters + _ = Path.GetFullPath(policyPath); + return policyPath; + } + catch (Exception ex) + { + Log.Error($"Policy-provided SettingsFolderLocation contains invalid characters: {policyPath}. Falling back to default location.", ex); + } + } + } // Fall back to existing logic return ConfigurationManager.Current.IsPortable diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index 56e85d6b5d..af3427efbe 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -137,11 +137,15 @@ See [Location](./settings/settings.md#location) for more details. :::warning When configuring a custom settings folder location, ensure: +- The path **must be an absolute path** (e.g., `C:\\Path\\To\\Folder`, not `.\\RelativePath`) - The path uses double backslashes (`\\`) in JSON format +- The path does not contain invalid characters for the file system - The application has read/write permissions to the specified directory - The directory exists or the application has permissions to create it - All users have appropriate access to the folder if using a shared location +If the policy-provided path is invalid (not absolute or contains invalid characters), the application will log an error and fall back to the default location. + ::: ## Future Policies From 65e973245c5279bf8b68830adc75afb0a060df30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:13:45 +0000 Subject: [PATCH 04/10] Improve exception handling and documentation structure Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- Source/NETworkManager.Settings/SettingsManager.cs | 8 ++++++-- Website/docs/system-wide-policies.md | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 1343bf2c81..f220b24587 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -92,14 +92,18 @@ public static string GetSettingsFolderLocation() // Validate that the path doesn't contain invalid characters try { - // This will throw if the path contains invalid characters + // This will throw ArgumentException or NotSupportedException if the path contains invalid characters _ = Path.GetFullPath(policyPath); return policyPath; } - catch (Exception ex) + catch (ArgumentException ex) { Log.Error($"Policy-provided SettingsFolderLocation contains invalid characters: {policyPath}. Falling back to default location.", ex); } + catch (NotSupportedException ex) + { + Log.Error($"Policy-provided SettingsFolderLocation format is not supported: {policyPath}. Falling back to default location.", ex); + } } } diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index af3427efbe..c9126a5d51 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -32,7 +32,7 @@ The `config.json` file uses a simple JSON structure to define policy values. An ``` -**Example:** +**Example with available policies:** ```json { @@ -41,6 +41,8 @@ The `config.json` file uses a simple JSON structure to define policy values. An } ``` +For a complete list of available policies, see the [Available Policies](#available-policies) section below. + Property names generally follow the pattern `Section_SettingName` (see each setting's documentation). Ensure values use the correct JSON type (boolean, string, number, etc.). :::note From 114f86ce82bdeac838d65bfb6e846f19af996768 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:14:31 +0000 Subject: [PATCH 05/10] Add SecurityException handling and fix documentation formatting Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- Source/NETworkManager.Settings/SettingsManager.cs | 6 +++++- Website/docs/system-wide-policies.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index f220b24587..6a8c399a9a 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -92,7 +92,7 @@ public static string GetSettingsFolderLocation() // Validate that the path doesn't contain invalid characters try { - // This will throw ArgumentException or NotSupportedException if the path contains invalid characters + // This will throw ArgumentException, NotSupportedException, or SecurityException if the path is invalid _ = Path.GetFullPath(policyPath); return policyPath; } @@ -104,6 +104,10 @@ public static string GetSettingsFolderLocation() { Log.Error($"Policy-provided SettingsFolderLocation format is not supported: {policyPath}. Falling back to default location.", ex); } + catch (System.Security.SecurityException ex) + { + Log.Error($"Insufficient permissions to access policy-provided SettingsFolderLocation: {policyPath}. Falling back to default location.", ex); + } } } diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index c9126a5d51..be39de8f05 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -32,7 +32,7 @@ The `config.json` file uses a simple JSON structure to define policy values. An ``` -**Example with available policies:** +**Example:** ```json { From 92530c7e71484a91f8580b9bb9171a230ad95ae7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:15:20 +0000 Subject: [PATCH 06/10] Fix using statement and clarify path format documentation Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- Source/NETworkManager.Settings/SettingsManager.cs | 3 ++- Website/docs/system-wide-policies.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 6a8c399a9a..343eac7ec2 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; +using System.Security; using System.Text.Json; using System.Text.Json.Serialization; using System.Xml.Serialization; @@ -104,7 +105,7 @@ public static string GetSettingsFolderLocation() { Log.Error($"Policy-provided SettingsFolderLocation format is not supported: {policyPath}. Falling back to default location.", ex); } - catch (System.Security.SecurityException ex) + catch (SecurityException ex) { Log.Error($"Insufficient permissions to access policy-provided SettingsFolderLocation: {policyPath}. Falling back to default location.", ex); } diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index be39de8f05..87aa09f229 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -139,8 +139,8 @@ See [Location](./settings/settings.md#location) for more details. :::warning When configuring a custom settings folder location, ensure: -- The path **must be an absolute path** (e.g., `C:\\Path\\To\\Folder`, not `.\\RelativePath`) -- The path uses double backslashes (`\\`) in JSON format +- The path **must be an absolute path** (e.g., `C:\\Path\\To\\Folder` or `C:/Path/To/Folder`, not `.\\RelativePath`) +- For Windows paths, use either double backslashes (`\\`) or forward slashes (`/`) in JSON format (both `C:\\Path\\Folder` and `C:/Path/Folder` are valid) - The path does not contain invalid characters for the file system - The application has read/write permissions to the specified directory - The directory exists or the application has permissions to create it From 41633bf1066accc33231f47e72e12b78f692d218 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:16:04 +0000 Subject: [PATCH 07/10] Use consistent example path in documentation Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- Website/docs/system-wide-policies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Website/docs/system-wide-policies.md b/Website/docs/system-wide-policies.md index 87aa09f229..c8c2861010 100644 --- a/Website/docs/system-wide-policies.md +++ b/Website/docs/system-wide-policies.md @@ -37,7 +37,7 @@ The `config.json` file uses a simple JSON structure to define policy values. An ```json { "Update_CheckForUpdatesAtStartup": false, - "SettingsFolderLocation": "C:\\ProgramData\\NETworkManager\\Settings" + "SettingsFolderLocation": "C:\\CustomPath\\NETworkManager\\Settings" } ``` From a58b7e091e6cbb75f4e2c31a87937b16c44388f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:35:06 +0000 Subject: [PATCH 08/10] Address review feedback: validate directory, update docs, add admin indicator Co-authored-by: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> --- .../SettingsManager.cs | 13 ++++- .../ViewModels/SettingsSettingsViewModel.cs | 5 ++ .../Views/SettingsSettingsView.xaml | 15 ++++++ Website/docs/settings/settings.md | 20 +++++++- Website/docs/system-wide-policies.md | 50 ------------------- 5 files changed, 49 insertions(+), 54 deletions(-) diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 343eac7ec2..76961df19b 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -94,8 +94,17 @@ public static string GetSettingsFolderLocation() try { // This will throw ArgumentException, NotSupportedException, or SecurityException if the path is invalid - _ = Path.GetFullPath(policyPath); - return policyPath; + var fullPath = Path.GetFullPath(policyPath); + + // Check if the path is a directory (not a file) + if (File.Exists(fullPath)) + { + Log.Error($"Policy-provided SettingsFolderLocation is a file, not a directory: {policyPath}. Falling back to default location."); + } + else + { + return fullPath; + } } catch (ArgumentException ex) { diff --git a/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs b/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs index 330f98e21e..aa41762eb5 100644 --- a/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs +++ b/Source/NETworkManager/ViewModels/SettingsSettingsViewModel.cs @@ -31,6 +31,11 @@ public string Location } } + /// + /// Gets whether the settings folder location is managed by system-wide policy. + /// + public bool IsLocationManagedByPolicy => !string.IsNullOrWhiteSpace(PolicyManager.Current?.SettingsFolderLocation); + private bool _isDailyBackupEnabled; public bool IsDailyBackupEnabled diff --git a/Source/NETworkManager/Views/SettingsSettingsView.xaml b/Source/NETworkManager/Views/SettingsSettingsView.xaml index b51f7fe9e6..769491d08d 100644 --- a/Source/NETworkManager/Views/SettingsSettingsView.xaml +++ b/Source/NETworkManager/Views/SettingsSettingsView.xaml @@ -7,12 +7,27 @@ xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" xmlns:viewModels="clr-namespace:NETworkManager.ViewModels" xmlns:localization="clr-namespace:NETworkManager.Localization.Resources;assembly=NETworkManager.Localization" + xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters" mc:Ignorable="d" Loaded="UserControl_Loaded" d:DataContext="{d:DesignInstance viewModels:SettingsSettingsViewModel}"> + + + + + + + + + + + + + + +