diff --git a/DevProxy.Abstractions/Plugins/BasePlugin.cs b/DevProxy.Abstractions/Plugins/BasePlugin.cs
index c440be55..d093f9da 100644
--- a/DevProxy.Abstractions/Plugins/BasePlugin.cs
+++ b/DevProxy.Abstractions/Plugins/BasePlugin.cs
@@ -114,6 +114,30 @@ public override async Task InitializeAsync(InitArgs e, CancellationToken cancell
}
}
+ ///
+ /// Evaluates the array property.
+ /// If the property exists, the value is used;
+ /// otherwise, the default is applied.
+ /// If the property is null, it is interpreted as an empty array ([]).
+ /// Note: This is necessary because .NET configuration binding cannot differentiate between an empty array,
+ /// a null value, or a missing property in appsettings.json.
+ /// See at
+ ///
+ ///
+ /// The array property name
+ /// The configured list of string values
+ /// The default list of string values
+ /// Returns the result list of string values
+ protected virtual IEnumerable? GetConfigurationValue(string key, IEnumerable? configuredList,
+ IEnumerable? defaultList = default)
+ {
+ ArgumentNullException.ThrowIfNull(key, nameof(key));
+
+ var keyExists = ConfigurationSection.GetChildren().Any(f => string.Equals(key, f.Key, StringComparison.Ordinal));
+ configuredList = configuredList?.Where(static p => !string.IsNullOrEmpty(p));
+ return keyExists ? configuredList ?? [] : defaultList;
+ }
+
private async Task<(bool IsValid, IEnumerable ValidationErrors)> ValidatePluginConfigAsync(CancellationToken cancellationToken)
{
if (!ProxyConfiguration.ValidateSchemas)
diff --git a/DevProxy.Plugins/Extensions/StringExtensions.cs b/DevProxy.Plugins/Extensions/StringExtensions.cs
index 780b83b4..3f0c713c 100644
--- a/DevProxy.Plugins/Extensions/StringExtensions.cs
+++ b/DevProxy.Plugins/Extensions/StringExtensions.cs
@@ -67,11 +67,9 @@ internal static string ToCamelCase(this string str)
{
if (string.IsNullOrEmpty(str))
{
-
return str;
}
-
return char.ToLowerInvariant(str[0]) + str[1..];
}
diff --git a/DevProxy.Plugins/Reporting/GraphMinimalPermissionsGuidancePlugin.cs b/DevProxy.Plugins/Reporting/GraphMinimalPermissionsGuidancePlugin.cs
index dc11842a..ca88be18 100644
--- a/DevProxy.Plugins/Reporting/GraphMinimalPermissionsGuidancePlugin.cs
+++ b/DevProxy.Plugins/Reporting/GraphMinimalPermissionsGuidancePlugin.cs
@@ -61,20 +61,7 @@ public override async Task InitializeAsync(InitArgs e, CancellationToken cancell
_graphUtils = ActivatorUtilities.CreateInstance(e.ServiceProvider);
- // we need to do it this way because .NET doesn't distinguish between
- // an empty array and a null value and we want to be able to tell
- // if the user hasn't specified a value and we should use the default
- // set or if they have specified an empty array and we shouldn't exclude
- // any permissions
- if (Configuration.PermissionsToExclude is null)
- {
- Configuration.PermissionsToExclude = ["profile", "openid", "offline_access", "email"];
- }
- else
- {
- // remove empty strings
- Configuration.PermissionsToExclude = Configuration.PermissionsToExclude.Where(p => !string.IsNullOrEmpty(p));
- }
+ InitializePermissionsToExclude();
}
public override async Task AfterRecordingStopAsync(RecordingArgs e, CancellationToken cancellationToken)
@@ -220,6 +207,15 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
Logger.LogTrace("Left {Name}", nameof(AfterRecordingStopAsync));
}
+ private void InitializePermissionsToExclude()
+ {
+ var key = nameof(GraphMinimalPermissionsGuidancePluginConfiguration.PermissionsToExclude)
+ .ToCamelCase();
+
+ string[] defaultPermissionsToExclude = ["profile", "openid", "offline_access", "email"];
+ Configuration.PermissionsToExclude = GetConfigurationValue(key, Configuration.PermissionsToExclude, defaultPermissionsToExclude);
+ }
+
private async Task EvaluateMinimalScopesAsync(
IEnumerable<(string method, string url)> endpoints,
IEnumerable permissionsFromAccessToken,