Skip to content

Commit e3754e6

Browse files
committed
Adjust config merge
When the override config has an appSettings/add element that the base config does not have, the base config will copy over the override config element. Before this commit, the override appSettings/add element would be ignored if the base config did not also have the same element.
1 parent 87872a4 commit e3754e6

File tree

3 files changed

+125
-80
lines changed

3 files changed

+125
-80
lines changed

IPBanCore/Core/IPBan/IPBanConfig.cs

+106-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,12 @@ static IPBanConfig()
171171
private IPBanConfig(XmlDocument doc, IDnsLookup dns = null, IDnsServerList dnsList = null, IHttpRequestMaker httpRequestMaker = null)
172172
{
173173
// deserialize with XmlDocument for fine grained control
174-
foreach (XmlNode node in doc.SelectNodes("/configuration/appSettings/add"))
174+
var appSettingsNodes = doc.SelectNodes("/configuration/appSettings/add");
175+
if (appSettingsNodes is null || appSettingsNodes.Count == 0)
176+
{
177+
throw new InvalidDataException("Configuration is missing or has empty /configuration/appSettings element. This element name is case sensitive. Please check your config.");
178+
}
179+
foreach (XmlNode node in appSettingsNodes)
175180
{
176181
appSettings[node.Attributes["key"].Value] = node.Attributes["value"].Value;
177182
}
@@ -761,6 +766,106 @@ public static string ChangeConfigAppSetting(string config, string key, string ne
761766
return doc.OuterXml;
762767
}
763768

769+
/// <summary>
770+
/// Merge two configurations
771+
/// </summary>
772+
/// <param name="xmlBase">Base configuration</param>
773+
/// <param name="xmlOverride">Override configuration</param>
774+
/// <returns>Merged configuration</returns>
775+
/// <exception cref="ArgumentException">Base xml is null or white space</exception>
776+
public static XmlDocument MergeXml(string xmlBase, string xmlOverride)
777+
{
778+
if (string.IsNullOrWhiteSpace(xmlBase))
779+
{
780+
throw new ArgumentException("Cannot merge null base xml");
781+
}
782+
783+
XmlDocument docBase = new();
784+
docBase.LoadXml(xmlBase);
785+
786+
if (string.IsNullOrWhiteSpace(xmlOverride))
787+
{
788+
return docBase;
789+
}
790+
791+
XmlDocument docOverride = new();
792+
docOverride.LoadXml(xmlOverride);
793+
794+
XmlNode logFilesOverride = docOverride.SelectSingleNode("/configuration/LogFilesToParse/LogFiles");
795+
XmlNode logFilesBase = docBase.SelectSingleNode("/configuration/LogFilesToParse/LogFiles") ?? logFilesOverride;
796+
if (logFilesBase is not null &&
797+
logFilesOverride is not null &&
798+
logFilesBase != logFilesOverride)
799+
{
800+
foreach (XmlNode overrideNode in logFilesOverride)
801+
{
802+
if (overrideNode.NodeType == XmlNodeType.Element)
803+
{
804+
logFilesBase.AppendChild(docBase.ImportNode(overrideNode, true));
805+
}
806+
}
807+
}
808+
809+
XmlNode expressionsBlockOverride = docOverride.SelectSingleNode("/configuration/ExpressionsToBlock/Groups");
810+
XmlNode expressionsBlockBase = docBase.SelectSingleNode("/configuration/ExpressionsToBlock/Groups") ?? expressionsBlockOverride;
811+
if (expressionsBlockBase is not null &&
812+
expressionsBlockOverride is not null &&
813+
expressionsBlockBase != expressionsBlockOverride)
814+
{
815+
foreach (XmlNode overrideNode in expressionsBlockOverride)
816+
{
817+
if (overrideNode.NodeType == XmlNodeType.Element)
818+
{
819+
expressionsBlockBase.AppendChild(docBase.ImportNode(overrideNode, true));
820+
}
821+
}
822+
}
823+
824+
XmlNode expressionsNotifyOverride = docOverride.SelectSingleNode("/configuration/ExpressionsToNotify/Groups");
825+
XmlNode expressionsNotifyBase = docBase.SelectSingleNode("/configuration/ExpressionsToNotify/Groups") ?? expressionsNotifyOverride;
826+
if (expressionsNotifyBase is not null &&
827+
expressionsNotifyOverride is not null &&
828+
expressionsNotifyBase != expressionsNotifyOverride)
829+
{
830+
foreach (XmlNode overrideNode in expressionsNotifyOverride)
831+
{
832+
if (overrideNode.NodeType == XmlNodeType.Element)
833+
{
834+
expressionsNotifyBase.AppendChild(docBase.ImportNode(overrideNode, true));
835+
}
836+
}
837+
}
838+
839+
XmlNode appSettingsOverride = docOverride.SelectSingleNode("/configuration/appSettings");
840+
XmlNode appSettingsBase = docBase.SelectSingleNode("/configuration/appSettings") ?? appSettingsOverride;
841+
if (appSettingsBase is not null &&
842+
appSettingsOverride is not null &&
843+
appSettingsBase != appSettingsOverride)
844+
{
845+
foreach (XmlNode overrideNode in appSettingsOverride)
846+
{
847+
if (overrideNode.NodeType == XmlNodeType.Element)
848+
{
849+
string xpath = $"/configuration/appSettings/add[@key='{overrideNode.Attributes["key"].Value}']";
850+
XmlNode existing = appSettingsBase.SelectSingleNode(xpath);
851+
if (existing is null)
852+
{
853+
// create a new node
854+
appSettingsBase.AppendChild(docBase.ImportNode(overrideNode, true));
855+
}
856+
else
857+
{
858+
// replace existing node
859+
string overrideValue = overrideNode.Attributes["value"]?.Value ?? string.Empty;
860+
existing.Attributes["value"].Value = overrideValue;
861+
}
862+
}
863+
}
864+
}
865+
866+
return docBase;
867+
}
868+
764869
/// <inheritdoc />
765870
public bool IsWhitelisted(string entry) => whitelistFilter.IsFiltered(entry);
766871

IPBanCore/Core/IPBan/IPBanService_Private.cs

+1-79
Original file line numberDiff line numberDiff line change
@@ -56,84 +56,6 @@ private void RunTask(Action action)
5656
}
5757
}
5858

59-
private static XmlDocument MergeXml(string xmlBase, string xmlOverride)
60-
{
61-
if (string.IsNullOrWhiteSpace(xmlBase))
62-
{
63-
throw new ArgumentException("Cannot merge null base xml");
64-
}
65-
66-
XmlDocument docBase = new();
67-
docBase.LoadXml(xmlBase);
68-
69-
if (string.IsNullOrWhiteSpace(xmlOverride))
70-
{
71-
return docBase;
72-
}
73-
74-
XmlDocument docOverride = new();
75-
docOverride.LoadXml(xmlOverride);
76-
77-
XmlNode logFilesBase = docBase.SelectSingleNode("/configuration/LogFilesToParse/LogFiles");
78-
XmlNode logFilesOverride = docOverride.SelectSingleNode("/configuration/LogFilesToParse/LogFiles");
79-
if (logFilesBase is not null && logFilesOverride is not null)
80-
{
81-
foreach (XmlNode overrideNode in logFilesOverride)
82-
{
83-
if (overrideNode.NodeType == XmlNodeType.Element)
84-
{
85-
logFilesBase.AppendChild(docBase.ImportNode(overrideNode, true));
86-
}
87-
}
88-
}
89-
90-
XmlNode expressionsBlockBase = docBase.SelectSingleNode("/configuration/ExpressionsToBlock/Groups");
91-
XmlNode expressionsBlockOverride = docOverride.SelectSingleNode("/configuration/ExpressionsToBlock/Groups");
92-
if (expressionsBlockBase is not null && expressionsBlockOverride is not null)
93-
{
94-
foreach (XmlNode overrideNode in expressionsBlockOverride)
95-
{
96-
if (overrideNode.NodeType == XmlNodeType.Element)
97-
{
98-
expressionsBlockBase.AppendChild(docBase.ImportNode(overrideNode, true));
99-
}
100-
}
101-
}
102-
103-
XmlNode expressionsNotifyBase = docBase.SelectSingleNode("/configuration/ExpressionsToNotify/Groups");
104-
XmlNode expressionsNotifyOverride = docOverride.SelectSingleNode("/configuration/ExpressionsToNotify/Groups");
105-
if (expressionsNotifyBase is not null && expressionsNotifyOverride is not null)
106-
{
107-
foreach (XmlNode overrideNode in expressionsNotifyOverride)
108-
{
109-
if (overrideNode.NodeType == XmlNodeType.Element)
110-
{
111-
expressionsNotifyBase.AppendChild(docBase.ImportNode(overrideNode, true));
112-
}
113-
}
114-
}
115-
116-
XmlNode appSettingsBase = docBase.SelectSingleNode("/configuration/appSettings");
117-
XmlNode appSettingsOverride = docOverride.SelectSingleNode("/configuration/appSettings");
118-
if (appSettingsBase is not null && appSettingsOverride is not null)
119-
{
120-
foreach (XmlNode overrideNode in appSettingsOverride)
121-
{
122-
if (overrideNode.NodeType == XmlNodeType.Element)
123-
{
124-
string xpath = "/configuration/appSettings/add[@key='" + overrideNode.Attributes["key"].Value + "']";
125-
XmlNode existing = appSettingsBase.SelectSingleNode(xpath);
126-
if (existing != null)
127-
{
128-
existing.Attributes["value"].Value = overrideNode.Attributes["value"].Value;
129-
}
130-
}
131-
}
132-
}
133-
134-
return docBase;
135-
}
136-
13759
internal async Task UpdateConfiguration()
13860
{
13961
try
@@ -147,7 +69,7 @@ internal async Task UpdateConfiguration()
14769
// merge override xml
14870
string baseXml = configChange ?? Config?.Xml;
14971
string overrideXml = configChangeOverride;
150-
XmlDocument finalXml = MergeXml(baseXml, overrideXml);
72+
XmlDocument finalXml = IPBanConfig.MergeXml(baseXml, overrideXml);
15173
IPBanConfig oldConfig = Config;
15274
IPBanConfig newConfig = IPBanConfig.LoadFromXml(finalXml, DnsLookup, DnsList, RequestMaker);
15375
bool configChanged = oldConfig is null || oldConfig.Xml != newConfig.Xml;

IPBanTests/IPBanConfigTests.cs

+18
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3333
using System.Runtime.InteropServices;
3434
using System.Text.RegularExpressions;
3535
using System.Threading.Tasks;
36+
using System.Xml;
3637

3738
namespace DigitalRuby.IPBanTests
3839
{
@@ -234,6 +235,23 @@ public async Task TestDefaultConfig()
234235
}
235236
}
236237

238+
/// <summary>
239+
/// Test config merge
240+
/// </summary>
241+
[Test]
242+
public void TestConfigMerge()
243+
{
244+
string config1 = "<?xml version=\"1.0\"?><configuration><appSettings><add key='Whitelist' value='1.1.1.1' /></appSettings></configuration>";
245+
string config2 = "<?xml version=\"1.0\"?><configuration><appSettings><add key='Whitelist' value='2.2.2.2' /><add key='Blacklist' value='3.3.3.3' /></appSettings></configuration>";
246+
XmlDocument doc = IPBanConfig.MergeXml(config1, config2);
247+
var nodes = doc["configuration"]["appSettings"].ChildNodes;
248+
Assert.AreEqual(2, nodes.Count);
249+
string value1 = nodes[0].Attributes["value"].Value;
250+
string value2 = nodes[1].Attributes["value"].Value;
251+
Assert.AreEqual("2.2.2.2", value1);
252+
Assert.AreEqual("3.3.3.3", value2);
253+
}
254+
237255
/// <summary>
238256
/// Test that we can parse a blacklist or whitelist with comments
239257
/// </summary>

0 commit comments

Comments
 (0)