Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Adding Web.config schema validation to the Configuration Manager #4707

Merged
merged 2 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DNN Platform/Library/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@
[assembly: InternalsVisibleTo("DotNetNuke.Modules.Journal")] // Once Globals is refactored to Dependency Injection we should be able to remove this
[assembly: InternalsVisibleTo("DotNetNuke.Modules.RazorHost")] // Once Globals is refactored to Dependency Injection we should be able to remove this
[assembly: InternalsVisibleTo("DotNetNuke.Website")] // Once Globals is refactored to Dependency Injection we should be able to remove this
[assembly: InternalsVisibleTo("Dnn.PersonaBar.ConfigConsole.Tests")] // Once Globals is refactored to Dependency Injection we should be able to remove this
27 changes: 27 additions & 0 deletions DNN_Platform.sln
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dnn.GoogleTagManagerConnect
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "Build\Build.csproj", "{22E535CC-385B-44BB-998D-A3C0A144F9E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dnn.PersonaBar.ConfigConsole.Tests", "Dnn.AdminExperience\Tests\Dnn.PersonaBar.ConfigConsole.Tests\Dnn.PersonaBar.ConfigConsole.Tests.csproj", "{12583A7E-7BEF-4F79-9CEA-3736D28C3241}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Cloud_Debug|Any CPU = Cloud_Debug|Any CPU
Expand Down Expand Up @@ -2007,6 +2009,30 @@ Global
{22E535CC-385B-44BB-998D-A3C0A144F9E1}.Release-Net45|Any CPU.Build.0 = Release|Any CPU
{22E535CC-385B-44BB-998D-A3C0A144F9E1}.Release-Net45|x86.ActiveCfg = Release|Any CPU
{22E535CC-385B-44BB-998D-A3C0A144F9E1}.Release-Net45|x86.Build.0 = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Debug|Any CPU.Build.0 = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Debug|x86.ActiveCfg = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Debug|x86.Build.0 = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Release|Any CPU.ActiveCfg = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Release|Any CPU.Build.0 = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Release|x86.ActiveCfg = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Cloud_Release|x86.Build.0 = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug|x86.ActiveCfg = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug|x86.Build.0 = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug-Net45|Any CPU.ActiveCfg = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug-Net45|Any CPU.Build.0 = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug-Net45|x86.ActiveCfg = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Debug-Net45|x86.Build.0 = Debug|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release|Any CPU.Build.0 = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release|x86.ActiveCfg = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release|x86.Build.0 = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release-Net45|Any CPU.ActiveCfg = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release-Net45|Any CPU.Build.0 = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release-Net45|x86.ActiveCfg = Release|Any CPU
{12583A7E-7BEF-4F79-9CEA-3736D28C3241}.Release-Net45|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -2117,6 +2143,7 @@ Global
{AE7E021E-7C7B-4003-9BD6-5A04C781C277} = {8BA6DD11-0CD5-4556-8853-282B3C1C3A06}
{7B43D266-D6E1-4496-A9A1-487AFEE0D9C0} = {9A986091-4238-4804-8C86-6A9A005F72C2}
{22E535CC-385B-44BB-998D-A3C0A144F9E1} = {29273BE6-1AA8-4970-98A0-41BFFEEDA67B}
{12583A7E-7BEF-4F79-9CEA-3736D28C3241} = {8EB4193C-A708-4DA0-9F2A-F5B7599F6F8F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46B6A641-57EB-4B19-B199-23E6FC2AB40B}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ namespace Dnn.PersonaBar.ConfigConsole.Components
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Xml;
using System.Xml.Schema;

using DotNetNuke.Application;
using DotNetNuke.Common;
Expand All @@ -18,6 +20,11 @@ namespace Dnn.PersonaBar.ConfigConsole.Components

public class ConfigConsoleController
{
/// <summary>
/// Name of the Web configuration file.
/// </summary>
internal const string WebConfig = "Web.config";

private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ConfigConsoleController));
private const string CONFIG_EXT = ".config";
private const string ROBOTS_EXT = "robots.txt"; // in multi-portal instances, there may be multiple robots.txt files (e.g., site1.com.robots.txt, site2.com.robots.txt, etc.)
Expand Down Expand Up @@ -73,6 +80,31 @@ public void UpdateConfigFile(string fileName, string fileContent)
}
}

/// <summary>
/// Validates a config file against a well known schema.
/// </summary>
/// <param name="fileName">The config file name.</param>
/// <param name="fileContent">The contents of the config file.</param>
/// <returns>A list of validation errors.</returns>
public IEnumerable<string> ValidateConfigFile(string fileName, string fileContent)
{
this.ValidateFilePath(fileName);

if (!fileName.EndsWith(CONFIG_EXT, StringComparison.InvariantCultureIgnoreCase))
{
return new string[0];
}

if (fileName.EndsWith(WebConfig, StringComparison.InvariantCultureIgnoreCase))
{
var configDoc = new XmlDocument { XmlResolver = null };
configDoc.LoadXml(fileContent);
return ValidateSchema(configDoc, "Schemas/DotNetConfig.xsd");
}

return new string[0];
}

public void MergeConfigFile(string fileContent)
{
if (this.IsValidXmlMergDocument(fileContent))
Expand All @@ -85,6 +117,39 @@ public void MergeConfigFile(string fileContent)
}
}

private static IEnumerable<string> ValidateSchema(XmlDocument configDoc, string schemaRelPath)
{
var errors = new List<string>();

configDoc.Schemas.Add(LoadSchema(schemaRelPath));
configDoc.Validate((_, e) => errors.Add(e.Message));

return errors;
}

private static XmlSchema LoadSchema(string schemaRelPath)
{
var xsd = LoadResource(schemaRelPath);

using (var reader = new StringReader(xsd))
{
return XmlSchema.Read(reader, (_, e) => { });
}
}

private static string LoadResource(string relativePath)
{
var segments = relativePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
var relativeName = string.Join(".", segments);
var name = $"Dnn.PersonaBar.Extensions.Components.ConfigConsole.{relativeName}";

using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}

private bool IsValidXmlMergDocument(string mergeDocText)
{
if (string.IsNullOrEmpty(mergeDocText.Trim()))
Expand Down
Loading