diff --git a/MultiFactor.Radius.Adapter/Configuration/ClientConfiguration.cs b/MultiFactor.Radius.Adapter/Configuration/ClientConfiguration.cs
index 5648469..b87eb0d 100644
--- a/MultiFactor.Radius.Adapter/Configuration/ClientConfiguration.cs
+++ b/MultiFactor.Radius.Adapter/Configuration/ClientConfiguration.cs
@@ -2,6 +2,7 @@
//Please see licence at
//https://github.com/MultifactorLab/MultiFactor.Radius.Adapter/blob/master/LICENSE.md
+using MultiFactor.Radius.Adapter.Configuration.Features.PrivacyModeFeature;
using MultiFactor.Radius.Adapter.Server;
using System;
using System.Collections.Generic;
@@ -51,7 +52,7 @@ public ClientConfiguration()
///
public bool BypassSecondFactorWhenApiUnreachable { get; set; }
- public PrivacyMode PrivacyMode { get; set; }
+ public PrivacyModeDescriptor PrivacyMode { get; set; }
#region ActiveDirectory Authentication settings
diff --git a/MultiFactor.Radius.Adapter/Configuration/PrivacyMode.cs b/MultiFactor.Radius.Adapter/Configuration/Features/PrivacyModeFeature/PrivacyMode.cs
similarity index 71%
rename from MultiFactor.Radius.Adapter/Configuration/PrivacyMode.cs
rename to MultiFactor.Radius.Adapter/Configuration/Features/PrivacyModeFeature/PrivacyMode.cs
index d4aa52a..872619f 100644
--- a/MultiFactor.Radius.Adapter/Configuration/PrivacyMode.cs
+++ b/MultiFactor.Radius.Adapter/Configuration/Features/PrivacyModeFeature/PrivacyMode.cs
@@ -12,6 +12,11 @@ public enum PrivacyMode
///
/// Disable all but identity
///
- Full
+ Full,
+
+ ///
+ /// Disable all but identity and specified fields.
+ ///
+ Partial
}
}
\ No newline at end of file
diff --git a/MultiFactor.Radius.Adapter/Configuration/Features/PrivacyModeFeature/PrivacyModeDescriptor.cs b/MultiFactor.Radius.Adapter/Configuration/Features/PrivacyModeFeature/PrivacyModeDescriptor.cs
new file mode 100644
index 0000000..0c04ff6
--- /dev/null
+++ b/MultiFactor.Radius.Adapter/Configuration/Features/PrivacyModeFeature/PrivacyModeDescriptor.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Linq;
+
+namespace MultiFactor.Radius.Adapter.Configuration.Features.PrivacyModeFeature
+{
+ public class PrivacyModeDescriptor
+ {
+ private readonly string[] _fields;
+ public PrivacyMode Mode { get; }
+
+ public bool HasField(string field)
+ {
+ if (string.IsNullOrWhiteSpace(field))
+ {
+ return false;
+ }
+
+ return _fields.Any(x => x.Equals(field, StringComparison.OrdinalIgnoreCase));
+ }
+
+ private PrivacyModeDescriptor(PrivacyMode mode, params string[] fields)
+ {
+ Mode = mode;
+ _fields = fields ?? throw new ArgumentNullException(nameof(fields));
+ }
+
+ public static PrivacyModeDescriptor Create(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value)) return new PrivacyModeDescriptor(PrivacyMode.None);
+
+ var mode = GetMode(value);
+ if (mode != PrivacyMode.Partial) return new PrivacyModeDescriptor(mode);
+
+ var fields = GetFields(value);
+ return new PrivacyModeDescriptor(mode, fields);
+ }
+
+ private static PrivacyMode GetMode(string value)
+ {
+ var index = value.IndexOf(':');
+ if (index == -1)
+ {
+ if (!Enum.TryParse(value, true, out var parsed1)) throw new Exception("Unexpected privacy-mode value");
+ return parsed1;
+ }
+
+ var sub = value.Substring(0, index);
+ if (!Enum.TryParse(sub, true, out var parsed2)) throw new Exception("Unexpected privacy-mode value");
+
+ return parsed2;
+ }
+
+ private static string[] GetFields(string value)
+ {
+ var index = value.IndexOf(':');
+ if (index == -1 || value.Length <= index + 1)
+ {
+ return Array.Empty();
+ }
+
+ var sub = value.Substring(index + 1);
+ return sub.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
+ }
+ }
+}
diff --git a/MultiFactor.Radius.Adapter/Configuration/ServiceConfiguration.cs b/MultiFactor.Radius.Adapter/Configuration/ServiceConfiguration.cs
index dd02d5b..4fa5a2a 100644
--- a/MultiFactor.Radius.Adapter/Configuration/ServiceConfiguration.cs
+++ b/MultiFactor.Radius.Adapter/Configuration/ServiceConfiguration.cs
@@ -14,6 +14,8 @@
using System.IO;
using NetTools;
using System.Text.RegularExpressions;
+using MultiFactor.Radius.Adapter.Configuration.Features.PrivacyModeFeature;
+using MultiFactor.Radius.Adapter.Services.MultiFactorApi;
namespace MultiFactor.Radius.Adapter.Configuration
{
@@ -288,7 +290,6 @@ public static ClientConfiguration Load(string name, IRadiusDictionary dictionary
var radiusPapEncodingSetting = appSettings.Settings["radius-pap-encoding"]?.Value;
var firstFactorAuthenticationSourceSettings = appSettings.Settings["first-factor-authentication-source"]?.Value;
var bypassSecondFactorWhenApiUnreachableSetting = appSettings.Settings["bypass-second-factor-when-api-unreachable"]?.Value;
- var privacyModeSetting = appSettings.Settings["privacy-mode"]?.Value;
var multiFactorApiKeySetting = appSettings.Settings["multifactor-nas-identifier"]?.Value;
var multiFactorApiSecretSetting = appSettings.Settings["multifactor-shared-secret"]?.Value;
@@ -346,13 +347,13 @@ public static ClientConfiguration Load(string name, IRadiusDictionary dictionary
}
}
- if (!string.IsNullOrEmpty(privacyModeSetting))
+ try
{
- if (!Enum.TryParse(privacyModeSetting, true, out var privacyMode))
- {
- throw new Exception("Configuration error: Can't parse 'privacy-mode' value. Must be one of: Full, None");
- }
- configuration.PrivacyMode = privacyMode;
+ configuration.PrivacyMode = PrivacyModeDescriptor.Create(appSettings.Settings[Literals.Configuration.PrivacyMode]?.Value);
+ }
+ catch
+ {
+ throw new Exception($"Configuration error: Can't parse '{Literals.Configuration.PrivacyMode}' value. Must be one of: Full, None, Partial:Field1,Field2");
}
switch (configuration.FirstFactorAuthenticationSource)
diff --git a/MultiFactor.Radius.Adapter/MultiFactor.Radius.Adapter.csproj b/MultiFactor.Radius.Adapter/MultiFactor.Radius.Adapter.csproj
index 450167f..838f808 100644
--- a/MultiFactor.Radius.Adapter/MultiFactor.Radius.Adapter.csproj
+++ b/MultiFactor.Radius.Adapter/MultiFactor.Radius.Adapter.csproj
@@ -137,7 +137,8 @@
-
+
+
diff --git a/MultiFactor.Radius.Adapter/Services/MultiFactorApi/Literals.cs b/MultiFactor.Radius.Adapter/Services/MultiFactorApi/Literals.cs
index 1c5d80e..6e29b67 100644
--- a/MultiFactor.Radius.Adapter/Services/MultiFactorApi/Literals.cs
+++ b/MultiFactor.Radius.Adapter/Services/MultiFactorApi/Literals.cs
@@ -8,5 +8,10 @@ public static class RadiusCode
public const string Denied = "Denied";
public const string AwaitingAuthentication = "AwaitingAuthentication";
}
+
+ public static class Configuration
+ {
+ public const string PrivacyMode = "privacy-mode";
+ }
}
}
diff --git a/MultiFactor.Radius.Adapter/Services/MultiFactorApi/MultiFactorApiClient.cs b/MultiFactor.Radius.Adapter/Services/MultiFactorApi/MultiFactorApiClient.cs
index a304c37..e52eb3e 100644
--- a/MultiFactor.Radius.Adapter/Services/MultiFactorApi/MultiFactorApiClient.cs
+++ b/MultiFactor.Radius.Adapter/Services/MultiFactorApi/MultiFactorApiClient.cs
@@ -10,7 +10,6 @@
using Newtonsoft.Json;
using Serilog;
using System;
-using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Text;
@@ -61,7 +60,7 @@ public async Task CreateSecondFactorRequest(PendingRequest request,
}
//remove user information for privacy
- switch (clientConfig.PrivacyMode)
+ switch (clientConfig.PrivacyMode.Mode)
{
case PrivacyMode.Full:
displayName = null;
@@ -70,6 +69,31 @@ public async Task CreateSecondFactorRequest(PendingRequest request,
callingStationId = "";
calledStationId = null;
break;
+
+ case PrivacyMode.Partial:
+ if (!clientConfig.PrivacyMode.HasField("Name"))
+ {
+ displayName = null;
+ }
+
+ if (!clientConfig.PrivacyMode.HasField("Email"))
+ {
+ email = null;
+ }
+
+ if (!clientConfig.PrivacyMode.HasField("Phone"))
+ {
+ userPhone = null;
+ }
+
+ if (!clientConfig.PrivacyMode.HasField("RemoteHost"))
+ {
+ callingStationId = "";
+ }
+
+ calledStationId = null;
+
+ break;
}
//try to get authenticated client to bypass second factor if configured