From f4ec2d5aebfa65532ef4ecc239229d0357396d1e Mon Sep 17 00:00:00 2001 From: ryannewington Date: Thu, 27 Jul 2017 17:07:28 +1000 Subject: [PATCH] Adds support for re-writing user names using regular expressions --- src/Lithnet.Pan.RAProxy.Setup/Product.wxs | 2 +- src/Lithnet.Pan.RAProxy/App.config | 8 +- .../ConfigSections/Config.cs | 17 +++- .../ConfigSections/PanApiEndpoint.cs | 14 ++++ .../RAProxyConfigurationSection.cs | 14 ++++ .../ConfigSections/RadiusServerSection.cs | 5 +- .../UsernameRewriteCollection.cs | 77 +++++++++++++++++++ .../ConfigSections/UsernameRewriteSection.cs | 73 ++++++++++++++++++ .../Lithnet.Pan.RAProxy.csproj | 2 + src/Lithnet.Pan.RAProxy/PanApi/Message.cs | 13 +++- src/Lithnet.Pan.RAProxy/Program.cs | 34 +++++--- 11 files changed, 238 insertions(+), 21 deletions(-) create mode 100644 src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteCollection.cs create mode 100644 src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteSection.cs diff --git a/src/Lithnet.Pan.RAProxy.Setup/Product.wxs b/src/Lithnet.Pan.RAProxy.Setup/Product.wxs index 822cfb9..ab81308 100644 --- a/src/Lithnet.Pan.RAProxy.Setup/Product.wxs +++ b/src/Lithnet.Pan.RAProxy.Setup/Product.wxs @@ -4,7 +4,7 @@ diff --git a/src/Lithnet.Pan.RAProxy/App.config b/src/Lithnet.Pan.RAProxy/App.config index 5a71bd1..e226aa7 100644 --- a/src/Lithnet.Pan.RAProxy/App.config +++ b/src/Lithnet.Pan.RAProxy/App.config @@ -6,10 +6,16 @@ - + + diff --git a/src/Lithnet.Pan.RAProxy/ConfigSections/Config.cs b/src/Lithnet.Pan.RAProxy/ConfigSections/Config.cs index de8082a..d5dd10b 100644 --- a/src/Lithnet.Pan.RAProxy/ConfigSections/Config.cs +++ b/src/Lithnet.Pan.RAProxy/ConfigSections/Config.cs @@ -77,7 +77,7 @@ public static void Failover() public static int BatchSize => Config.section.PanApi.BatchSize; public static int BatchWait => Config.section.PanApi.BatchWait; - + private static Regex UsernameFilterRegex { get @@ -128,5 +128,20 @@ public static string GetSecretForIP(IPAddress address) return null; } + + internal static string MatchReplace(string username) + { + string newUsername = username; + + if (Config.section.UsernameRewrites != null) + { + foreach (UsernameRewriteSection rule in Config.section.UsernameRewrites) + { + newUsername = rule.Rewrite(newUsername); + } + } + + return newUsername; + } } } diff --git a/src/Lithnet.Pan.RAProxy/ConfigSections/PanApiEndpoint.cs b/src/Lithnet.Pan.RAProxy/ConfigSections/PanApiEndpoint.cs index 0725352..83318ba 100644 --- a/src/Lithnet.Pan.RAProxy/ConfigSections/PanApiEndpoint.cs +++ b/src/Lithnet.Pan.RAProxy/ConfigSections/PanApiEndpoint.cs @@ -32,5 +32,19 @@ public string ApiKey this["api-key"] = value; } } + + [ConfigurationProperty("url-encode-key", IsRequired = false, DefaultValue = false)] + public bool UrlEncodeKey + { + get + { + return (bool)this["url-encode-key"]; + } + + set + { + this["url-encode-key"] = value; + } + } } } \ No newline at end of file diff --git a/src/Lithnet.Pan.RAProxy/ConfigSections/RAProxyConfigurationSection.cs b/src/Lithnet.Pan.RAProxy/ConfigSections/RAProxyConfigurationSection.cs index d9e23ab..2d0931f 100644 --- a/src/Lithnet.Pan.RAProxy/ConfigSections/RAProxyConfigurationSection.cs +++ b/src/Lithnet.Pan.RAProxy/ConfigSections/RAProxyConfigurationSection.cs @@ -38,6 +38,20 @@ public string UsernameFilter } } + [ConfigurationProperty("username-rewrites")] + public UsernameRewriteCollection UsernameRewrites + { + get + { + return (UsernameRewriteCollection)this["username-rewrites"]; + } + + set + { + this["username-rewrites"] = value; + } + } + [ConfigurationProperty("radius-servers")] public RadiusServerCollection RadiusServers diff --git a/src/Lithnet.Pan.RAProxy/ConfigSections/RadiusServerSection.cs b/src/Lithnet.Pan.RAProxy/ConfigSections/RadiusServerSection.cs index ccda8e6..8322450 100644 --- a/src/Lithnet.Pan.RAProxy/ConfigSections/RadiusServerSection.cs +++ b/src/Lithnet.Pan.RAProxy/ConfigSections/RadiusServerSection.cs @@ -4,12 +4,11 @@ using System.Text; using System.Threading.Tasks; using System.Configuration; +using System.Net; +using System.Net.Sockets; namespace Lithnet.Pan.RAProxy { - using System.Net; - using System.Net.Sockets; - public class RadiusServerSection : ConfigurationElement { [ConfigurationProperty("host", IsRequired = true)] diff --git a/src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteCollection.cs b/src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteCollection.cs new file mode 100644 index 0000000..ee8afa3 --- /dev/null +++ b/src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteCollection.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Configuration; + +namespace Lithnet.Pan.RAProxy +{ + public class UsernameRewriteCollection : ConfigurationElementCollection + { + public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap; + + protected override ConfigurationElement CreateNewElement() + { + return new UsernameRewriteSection(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((UsernameRewriteSection)element).Key; + } + + public UsernameRewriteSection this[int index] + { + get { return (UsernameRewriteSection)this.BaseGet(index); } + set + { + if (this.BaseGet(index) != null) + { + this.BaseRemoveAt(index); + } + this.BaseAdd(index, value); + } + } + + public new UsernameRewriteSection this[string name] => (UsernameRewriteSection)this.BaseGet(name); + + public int IndexOf(UsernameRewriteSection details) + { + return this.BaseIndexOf(details); + } + + public void Add(UsernameRewriteSection details) + { + this.BaseAdd(details); + } + + protected override void BaseAdd(ConfigurationElement element) + { + this.BaseAdd(element, false); + } + + public void Remove(UsernameRewriteSection details) + { + if (this.BaseIndexOf(details) >= 0) + this.BaseRemove(details.Key); + } + + public void RemoveAt(int index) + { + this.BaseRemoveAt(index); + } + + public void Remove(string name) + { + this.BaseRemove(name); + } + + public void Clear() + { + this.BaseClear(); + } + + protected override string ElementName => "username-rewrite"; + } +} \ No newline at end of file diff --git a/src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteSection.cs b/src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteSection.cs new file mode 100644 index 0000000..28cfd94 --- /dev/null +++ b/src/Lithnet.Pan.RAProxy/ConfigSections/UsernameRewriteSection.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Net; +using System.Net.Sockets; +using System.Text.RegularExpressions; + +namespace Lithnet.Pan.RAProxy +{ + public class UsernameRewriteSection : ConfigurationElement + { + private Regex match; + + private string key; + + internal string Key + { + get + { + if (this.key == null) + { + this.key = Guid.NewGuid().ToString(); + } + + return this.key; + } + } + + [ConfigurationProperty("match", IsRequired = true)] + public string Match + { + get + { + return (string)this["match"]; + } + set + { + this["match"] = value; + } + } + + [ConfigurationProperty("replace", IsRequired = true)] + public string Replace + { + get + { + return (string)this["replace"]; + } + set + { + this["replace"] = value; + } + } + + internal Regex MatchRegex + { + get + { + if (this.match == null) + { + this.match = new Regex(this.Match, RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + return this.match; + } + } + + internal string Rewrite(string username) + { + return this.MatchRegex.Replace(username, this.Replace); + } + } +} \ No newline at end of file diff --git a/src/Lithnet.Pan.RAProxy/Lithnet.Pan.RAProxy.csproj b/src/Lithnet.Pan.RAProxy/Lithnet.Pan.RAProxy.csproj index 748bb9b..ec60533 100644 --- a/src/Lithnet.Pan.RAProxy/Lithnet.Pan.RAProxy.csproj +++ b/src/Lithnet.Pan.RAProxy/Lithnet.Pan.RAProxy.csproj @@ -48,8 +48,10 @@ + + diff --git a/src/Lithnet.Pan.RAProxy/PanApi/Message.cs b/src/Lithnet.Pan.RAProxy/PanApi/Message.cs index f86f1af..bd41343 100644 --- a/src/Lithnet.Pan.RAProxy/PanApi/Message.cs +++ b/src/Lithnet.Pan.RAProxy/PanApi/Message.cs @@ -6,7 +6,6 @@ using System.Collections.Specialized; using System.Web; using System.Diagnostics; -using System.Net.Sockets; using System.Xml; namespace Lithnet.Pan.RAProxy @@ -179,7 +178,7 @@ private string Submit() throw; } - Logging.WriteEntry($"The attempt to send the update to endpoint {ep.ApiUri} failed with a communciations error\n{ex.Message}\n{ex.Source}\nThe service will attempt to fail over to the next endpoint", EventLogEntryType.Warning, Logging.EventIDApiEndpointExceptionWillFailover); + Logging.WriteEntry($"The attempt to send the update to endpoint {ep.ApiUri} failed with a communications error\n{ex}\nThe service will attempt to fail over to the next endpoint", EventLogEntryType.Warning, Logging.EventIDApiEndpointExceptionWillFailover); Config.Failover(); return this.Submit(); } @@ -193,7 +192,15 @@ private string Submit(PanApiEndpoint ep) NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); - queryString["key"] = HttpUtility.UrlEncode(ep.ApiKey); + if (Config.ActiveEndPoint.UrlEncodeKey) + { + queryString["key"] = HttpUtility.UrlEncode(ep.ApiKey); + } + else + { + queryString["key"] = ep.ApiKey; + } + queryString["type"] = this.ApiType; builder.Query = queryString.ToString(); diff --git a/src/Lithnet.Pan.RAProxy/Program.cs b/src/Lithnet.Pan.RAProxy/Program.cs index 7f4fdf8..7707be1 100644 --- a/src/Lithnet.Pan.RAProxy/Program.cs +++ b/src/Lithnet.Pan.RAProxy/Program.cs @@ -104,7 +104,7 @@ private static void StartQueue() try { Logging.WriteDebugEntry($"Incoming accounting request received\n{request}", EventLogEntryType.Information, Logging.EventIDAccountingRequestRecieved); - Trace.WriteLine($"Request queue lenth: {Program.incomingRequests.Count}"); + Trace.WriteLine($"Request queue length: {Program.incomingRequests.Count}"); Program.SendMessage(request); } @@ -114,7 +114,7 @@ private static void StartQueue() } catch (Exception ex) { - Logging.WriteEntry($"An error occured while submitting the user-id update\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); + Logging.WriteEntry($"An error occurred while submitting the user-id update\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); } } } @@ -169,7 +169,7 @@ private static void SendMessage(AccountingRequest request) Entry e = new Entry { - Username = username.ValueAsString, + Username = Config.MatchReplace(username.ValueAsString), IpAddress = framedIP.ValueAsString }; @@ -200,15 +200,15 @@ private static void SendMessage(AccountingRequest request) { Logging.CounterSentPerSecond.Increment(); message.Send(); - Logging.WriteEntry($"UserID API mapping succeeded\nUsername: {username.ValueAsString}\nIP address: {framedIP.ValueAsString}\nType: {type}", EventLogEntryType.Information, Logging.EventIDUserIDUpdateComplete); + Logging.WriteEntry($"UserID API mapping succeeded\nUsername: {e.Username}\nIP address: {framedIP.ValueAsString}\nType: {type}", EventLogEntryType.Information, Logging.EventIDUserIDUpdateComplete); } catch (PanApiException ex) { - Logging.WriteEntry($"The UserID API called failed\nUsername: {username.ValueAsString}\nIP address: {framedIP.ValueAsString}\n{ex.Message}\n{ex.StackTrace}\n{ex.Detail}", EventLogEntryType.Error, Logging.EventIDApiException); + Logging.WriteEntry($"The UserID API called failed\nUsername: {e.Username}\nIP address: {framedIP.ValueAsString}\n{ex}", EventLogEntryType.Error, Logging.EventIDApiException); } catch (Exception ex) { - Logging.WriteEntry($"An error occured while submitting the user-id update\nUsername: {username.ValueAsString}\nIP address: {framedIP.ValueAsString}\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); + Logging.WriteEntry($"An error occurred while submitting the user-id update\nUsername: {e.Username}\nIP address: {framedIP.ValueAsString}\n{ex}", EventLogEntryType.Error, Logging.EventIDMessageSendException); } } @@ -222,7 +222,7 @@ private static void StartBatchQueue() { List batchedRequests = new List(); - // Keep processing until the task is cancelled + // Keep processing until the task is canceled while (!Program.cancellationToken.IsCancellationRequested) { // Batch all messages received @@ -241,7 +241,7 @@ private static void StartBatchQueue() // Debug info Trace.WriteLine($"Incoming accounting request dequeued\n{nextRequest}"); - Trace.WriteLine($"Request queue lenth: {Program.incomingRequests.Count}"); + Trace.WriteLine($"Request queue length: {Program.incomingRequests.Count}"); } else { @@ -259,7 +259,7 @@ private static void StartBatchQueue() } catch (Exception ex) { - Logging.WriteEntry($"An error occured while submitting the user-id update\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); + Logging.WriteEntry($"An error occurred while submitting the user-id update\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); } finally { @@ -308,7 +308,7 @@ private static void SendBatchMessage(List requests) Entry e = new Entry { - Username = request.Attributes.FirstOrDefault(t => t.Type == RadiusAttribute.RadiusAttributeType.UserName)?.ValueAsString, + Username = Config.MatchReplace(request.Attributes.FirstOrDefault(t => t.Type == RadiusAttribute.RadiusAttributeType.UserName)?.ValueAsString), IpAddress = request.Attributes.FirstOrDefault(t => t.Type == RadiusAttribute.RadiusAttributeType.FramedIPAddress)?.ValueAsString }; @@ -320,7 +320,7 @@ private static void SendBatchMessage(List requests) if (message.Payload.Logout.Entries.Remove(e)) { - Trace.WriteLine($"Removed logout entry superceeded by login entry {e.Username}:{e.IpAddress}"); + Trace.WriteLine($"Removed logout entry superseded by login entry {e.Username}:{e.IpAddress}"); } break; @@ -354,6 +354,16 @@ private static void SendBatchMessage(List requests) } Trace.WriteLine($"Sending batch of {sending}"); + if (message.Payload.Login.Entries.Count > 0) + { + Trace.WriteLine($"Logins:\n{string.Join("\n", message.Payload.Login.Entries.Select(t => t.Username))}"); + } + + if (message.Payload.Logout.Entries.Count > 0) + { + Trace.WriteLine($"Logouts:\n{string.Join("\n", message.Payload.Logout.Entries.Select(t => t.Username))}"); + } + message.Send(); Logging.CounterSentPerSecond.IncrementBy(sending); Logging.CounterSentLoginsPerSecond.IncrementBy(message.Payload.Login.Entries.Count); @@ -374,7 +384,7 @@ private static void SendBatchMessage(List requests) } catch (Exception ex) { - Logging.WriteEntry($"An error occured while submitting the user-id update\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); + Logging.WriteEntry($"An error occurred while submitting the user-id update\n{ex.Message}\n{ex.StackTrace}", EventLogEntryType.Error, Logging.EventIDMessageSendException); } } }