Files
Umbraco-CMS/src/Umbraco.Tests/PartialTrust/PartialTrustHelper.cs
shannon@ShandemVaio 62d6db935d Integrating v5 TypeFinder. Imported a few classes from v5 regarding utility classes and partial trust
unit test helper classes. Changed calls from old TypeFinder to new TypeFinder2. Created benchmark unit
tests to test speed differences between them.
Fixes: 30946 - IOHelper with HttpContext issue
Fixes: 30945 - AppCodeFileExtensions issue with UmbracoSettings
2012-07-26 21:12:54 +06:00

293 lines
10 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Xml;
using System.Xml.Linq;
using NUnit.Framework;
using Umbraco.Core;
namespace Umbraco.Tests.PartialTrust
{
public static class PartialTrustHelper<T>
where T : class, new()
{
public const string PartialTrustAppDomainName = "Partial Trust AppDomain";
public static void RunInPartial(MethodInfo methodInfo)
{
AppDomain partiallyTrustedDomain = CreatePartialTrustDomain();
try
{
RunInPartial(methodInfo, partiallyTrustedDomain);
}
finally
{
AppDomain.Unload(partiallyTrustedDomain);
}
}
/// <summary>
/// Runs the provided <paramref name="methodInfo"/> in the <paramref name="partiallyTrustedDomain"/>.
/// If <paramref name="marshaller"/> is provided, it will be used, otherwise a new one will be created.
/// </summary>
/// <param name="methodInfo">The method info.</param>
/// <param name="partiallyTrustedDomain">The partially trusted domain.</param>
/// <param name="marshaller">The marshaller.</param>
public static void RunInPartial(MethodInfo methodInfo, AppDomain partiallyTrustedDomain, PartialTrustMethodRunner<T> marshaller = null)
{
// Ensure no mistakes creep in and this code is actually running on the fully trusted AppDomain
Assert.That(AppDomain.CurrentDomain.FriendlyName, Is.Not.StringStarting(PartialTrustAppDomainName));
if (marshaller == null) marshaller = GenerateMarshaller(partiallyTrustedDomain);
try
{
marshaller.Run(methodInfo);
}
catch (PartialTrustTestException ex)
{
throw ex;
}
catch (TargetInvocationException ex)
{
// If this gets raised, it could be because the method failed, or an assertion was called (either pass or failure)
if (ex.InnerException != null)
{
if (ex.InnerException is SuccessException)
/* Do nothing but return - it's NUnit's way of exiting out of a method when Assert.Pass() is called */
return;
Assert.Fail(ex.InnerException.ToString());
}
Assert.Fail("Failed but InnerException was null; " + ex.Message);
}
catch (Exception ex)
{
Assert.Fail(ex.ToString());
}
}
/// <summary>
/// Generates a marshaller proxy of <see cref="PartialTrustMethodRunner{TFixture}"/> inside the provided <paramref name="partiallyTrustedDomain"/>.
/// </summary>
/// <param name="partiallyTrustedDomain">The partially trusted domain.</param>
/// <returns></returns>
public static PartialTrustMethodRunner<T> GenerateMarshaller(AppDomain partiallyTrustedDomain)
{
var marshallerType = typeof(PartialTrustMethodRunner<T>);
var marshaller =
partiallyTrustedDomain.CreateInstanceAndUnwrap(marshallerType.Assembly.FullName, marshallerType.FullName) as
PartialTrustMethodRunner<T>;
return marshaller;
}
/// <summary>
/// Creates the an <see cref="AppDomain"/> with a partial trust <see cref="PermissionSet"/>.
/// </summary>
/// <returns></returns>
public static AppDomain CreatePartialTrustDomain()
{
var setup = new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
var permissions = MediumTrustConfigHelper.GetMediumTrustPermissionSet();
return AppDomain.CreateDomain("Partial Trust AppDomain: " + DateTime.Now.Ticks, null, setup, permissions);
}
public static void CheckRunNUnitTestInPartialTrust()
{
var findMethodInfo = FindCurrentTestMethodInfo();
try
{
RunInPartial(findMethodInfo);
}
catch (SuccessException assertPass)
{
/* This means the test passed with a direct call to Pass, so do nothing */
throw;
}
}
public static MethodInfo FindCurrentTestMethodInfo()
{
var nameOfTest = TestContext.CurrentContext.Test.Name;
var findMethodInfo = typeof(T).GetMethod(nameOfTest);
return findMethodInfo;
}
public static void CheckRunNUnitTestInPartialTrust(AppDomain withAppDomain, PartialTrustMethodRunner<T> marshaller)
{
var findMethodInfo = FindCurrentTestMethodInfo();
try
{
RunInPartial(findMethodInfo, withAppDomain, marshaller);
}
catch (SuccessException assertPass)
{
/* This means the test passed with a direct call to Pass, so do nothing */
throw;
}
}
[Serializable]
public class PartialTrustMethodRunner<TFixture> : MarshalByRefObject
where TFixture : class, new()
{
protected TFixture Instance { get; set; }
public Type TestClass { get { return typeof(TFixture); } }
public void Run(MethodInfo methodToRun)
{
// Verify that we are definitely running in an appdomain that we made ourselves
Assert.That(AppDomain.CurrentDomain.FriendlyName, Is.StringStarting(PartialTrustAppDomainName));
try
{
// Support having the marshaller run multiple methods on the same instance by storing the instance locally
if (Instance == null) Instance = new TFixture();
// Run the method on the instance we've either created or cached locally
methodToRun.Invoke(Instance, null);
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null && (ex.InnerException is SecurityException || ex.InnerException is MemberAccessException))
{
var inner = ex.InnerException;
throw new PartialTrustTestException("Test {0} fails in a partial trust environment, due to: {1}".InvariantFormat(methodToRun.Name, inner.Message), ex.InnerException);
}
throw;
}
}
}
public static class MediumTrustConfigHelper
{
/// <summary>
/// Gets the medium trust permission set from the default config path.
/// </summary>
/// <returns></returns>
public static NamedPermissionSet GetMediumTrustPermissionSet()
{
return GetMediumTrustPermissionSet(GetMediumTrustConfigPath());
}
/// <summary>
/// Gets the medium trust permission set.
/// </summary>
/// <param name="pathToConfigFile">The path to the config file.</param>
/// <returns></returns>
public static NamedPermissionSet GetMediumTrustPermissionSet(string pathToConfigFile)
{
// Load the config file trusting that it exists.
var xDocument = XDocument.Load(pathToConfigFile);
// Get all of the SecurityClass elements which we'll use later to look
// up a type strongname given a key
var securityClasses = xDocument.Descendants("SecurityClass").Select(
x => new
{
Name = (string)x.Attribute("Name"),
Type = (string)x.Attribute("Description")
});
// Get the first PermissionSet element where the Name attribute is "ASP.Net"
var namedSet = xDocument.Descendants("PermissionSet").Where(x => (string)x.Attribute("Name") == "ASP.Net").FirstOrDefault();
// If we didn't find it, that's a fail
Assert.NotNull(namedSet);
// Create a new SecurityElement class to mimic what is represented in Xml
var secElement = new SecurityElement("PermissionSet");
secElement.AddAttribute("Name", "ASP.Net");
secElement.AddAttribute("class", "NamedPermissionSet");
secElement.AddAttribute("version", "1");
// For each child of the ASP.Net PermissionSet, create a child SecurityElement representing the IPermission
foreach (var xElement in namedSet.Elements())
{
var child = new SecurityElement("IPermission");
// Check if we need to do any string replacement on the Xml values first
ProcessIPermissionAttributeValue(xElement);
// Get the attributes of the IPermission from Xml and put them onto our child SecurityElement
foreach (var xAttribute in xElement.Attributes())
{
var attribName = xAttribute.Name.LocalName;
var value = xAttribute.Value;
try
{
if (attribName == "class")
// This is the type key. Get the full type name from the SecurityClasses list we grabbed earlier
value = securityClasses.Where(x => x.Name == value).Select(x => x.Type).Single();
}
catch (Exception ex)
{
throw new XmlException("Could not find the fully-qualified type name for " + value, ex);
}
child.AddAttribute(attribName, value);
}
secElement.AddChild(child);
}
// Create a new NamedPermissionSet, pass in the SecurityElement class representing the Xml
var permissionSet = new NamedPermissionSet("ASP.Net");
permissionSet.FromXml(secElement);
return permissionSet;
}
/// <summary>
/// Processes the custom attribute values of IPermission config elements to replicate what ASP.Net performs
/// when scanning the permission set. Primarily just replaces $AppDir$ on a FileIOPermission with the current
/// directory.
/// </summary>
/// <param name="element">The element.</param>
public static void ProcessIPermissionAttributeValue(XElement element)
{
var classKey = (string)element.Attribute("class");
if (classKey == null) return;
// These attributes are on every IPermission, we can ignore them later
var ignoredAttributes = new[] { "class", "version" };
// Get all the attribute on this IPermission that are "custom"
var customAttributes = element.Attributes().Where(x => !ignoredAttributes.Contains(x.Name.LocalName));
foreach (var customAttribute in customAttributes)
{
switch (classKey)
{
case "FileIOPermission":
switch (customAttribute.Name.LocalName)
{
case "Read":
case "Write":
case "Append":
case "PathDiscovery":
// Replace an $AppDir$ token with the current directory to mimic setting it to the IIS root
customAttribute.Value = customAttribute.Value.Replace("$AppDir$", Environment.CurrentDirectory);
break;
}
break;
}
}
}
/// <summary>
/// Gets the medium trust config path for the current runtime environment.
/// </summary>
/// <returns></returns>
public static string GetMediumTrustConfigPath()
{
var readFromDirectory = Path.GetDirectoryName(RuntimeEnvironment.SystemConfigurationFile);
var autoPath = Path.Combine(readFromDirectory, "web_mediumtrust.config");
return autoPath;
}
}
}
}