Browse Source

Fixing concurrency issues in preset retrieval.

- Also enhanced security.


Former-commit-id: d977f2eb89343065cf95418bc2d43dd5e32ccbbc
pull/17/head
James South 12 years ago
parent
commit
9ffa02cef1
  1. 81
      src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs
  2. 21
      src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs
  3. 10
      src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs
  4. BIN
      src/Images/falahill_design__160p.jpg
  5. 5
      src/TestWebsites/NET45/Test_Website_NET45/Views/Home/External.cshtml
  6. 16
      src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
  7. 27
      src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/security.config

81
src/ImageProcessor.Web/NET45/Config/ImageProcessorConfig.cs

@ -12,6 +12,7 @@ namespace ImageProcessor.Web.Config
{
#region Using
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -37,14 +38,14 @@ namespace ImageProcessor.Web.Config
/// A collection of the <see cref="T:ImageProcessor.Web.Config.ImageProcessingSection.SettingElementCollection"/> elements
/// for available plugins.
/// </summary>
private static readonly Dictionary<string, Dictionary<string, string>> PluginSettings =
new Dictionary<string, Dictionary<string, string>>();
private static readonly ConcurrentDictionary<string, Dictionary<string, string>> PluginSettings =
new ConcurrentDictionary<string, Dictionary<string, string>>();
/// <summary>
/// A collection of the processing presets defined in the configuration.
/// for available plugins.
/// </summary>
private static readonly Dictionary<string, string> PresetSettings = new Dictionary<string, string>();
private static readonly ConcurrentDictionary<string, string> PresetSettings = new ConcurrentDictionary<string, string>();
/// <summary>
/// The processing configuration section from the current application configuration.
@ -185,33 +186,26 @@ namespace ImageProcessor.Web.Config
#region Methods
/// <summary>
/// Returns the collection of the processing presets defined in the configuration.
/// Returns the processing instructions matching the preset defined in the configuration.
/// </summary>
/// <param name="name">
/// The name of the plugin to get the settings for.
/// </param>
/// <returns>
/// The <see cref="T:Systems.Collections.Generic.Dictionary{string, string}"/> containing the processing presets defined in the configuration.
/// The <see cref="T:Systems.String"/> the processing instructions.
/// </returns>
public string GetPresetSettings(string name)
{
if (!PresetSettings.ContainsKey(name))
{
var presetElement =
GetImageProcessingSection().Presets
.Cast<ImageProcessingSection.PresetElement>()
.FirstOrDefault(x => x.Name == name);
if (presetElement != null)
{
PresetSettings[presetElement.Name] = presetElement.Value;
}
}
string preset;
PresetSettings.TryGetValue(name, out preset);
return preset;
return PresetSettings.GetOrAdd(
name,
n =>
{
ImageProcessingSection.PresetElement presetElement = GetImageProcessingSection()
.Presets
.Cast<ImageProcessingSection.PresetElement>()
.FirstOrDefault(x => x.Name == n);
return presetElement != null ? presetElement.Value : null;
});
}
/// <summary>
@ -225,31 +219,30 @@ namespace ImageProcessor.Web.Config
/// </returns>
public Dictionary<string, string> GetPluginSettings(string name)
{
if (!PluginSettings.ContainsKey(name))
{
var pluginElement =
GetImageProcessingSection().Plugins
.Cast<ImageProcessingSection.PluginElement>()
.FirstOrDefault(x => x.Name == name);
Dictionary<string, string> settings;
if (pluginElement != null)
return PluginSettings.GetOrAdd(
name,
n =>
{
settings = pluginElement.Settings
.Cast<ImageProcessingSection.SettingElement>()
.ToDictionary(setting => setting.Key, setting => setting.Value);
}
else
{
settings = new Dictionary<string, string>();
}
ImageProcessingSection.PluginElement pluginElement = GetImageProcessingSection()
.Plugins
.Cast<ImageProcessingSection.PluginElement>()
.FirstOrDefault(x => x.Name == n);
PluginSettings.Add(name, settings);
return settings;
}
Dictionary<string, string> settings;
if (pluginElement != null)
{
settings = pluginElement.Settings
.Cast<ImageProcessingSection.SettingElement>()
.ToDictionary(setting => setting.Key, setting => setting.Value);
}
else
{
settings = new Dictionary<string, string>();
}
return PluginSettings[name];
return settings;
});
}
/// <summary>

21
src/ImageProcessor.Web/NET45/Helpers/RemoteFile.cs

@ -15,7 +15,6 @@ namespace ImageProcessor.Web.Helpers
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Text;
@ -354,8 +353,24 @@ namespace ImageProcessor.Web.Helpers
string upper = this.url.Host.ToUpperInvariant();
// Check for root or subdomain.
bool validUrl = RemoteFileWhiteList.Any(item =>
upper.StartsWith(item.Host.ToUpperInvariant()) || upper.EndsWith(item.Host.ToUpperInvariant()));
bool validUrl = false;
foreach (Uri uri in RemoteFileWhiteList)
{
if (!uri.IsAbsoluteUri)
{
Uri rebaseUri = new Uri("http://" + uri.ToString().TrimStart(new[] { '.', '/' }));
validUrl = upper.StartsWith(rebaseUri.Host.ToUpperInvariant()) || upper.EndsWith(rebaseUri.Host.ToUpperInvariant());
}
else
{
validUrl = upper.StartsWith(uri.Host.ToUpperInvariant()) || upper.EndsWith(uri.Host.ToUpperInvariant());
}
if (validUrl)
{
break;
}
}
if (!validUrl)
{

10
src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs

@ -261,7 +261,6 @@ namespace ImageProcessor.Web.HttpModules
bool isRemote = request.Path.EndsWith(remotePrefix, StringComparison.OrdinalIgnoreCase);
string requestPath = string.Empty;
string queryString = string.Empty;
bool validExtensionLessUrl = false;
string urlParameters = "";
string extensionLessExtension = "";
@ -346,14 +345,12 @@ namespace ImageProcessor.Web.HttpModules
// in the UrlAuthorizationModule by creating a generic identity.
string virtualCachedPath = cache.GetVirtualCachedPath();
IPrincipal user = context.User
?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
// Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal?
PermissionSet permission = new PermissionSet(PermissionState.None);
permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted));
bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet);
bool isAllowed = true;
// Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value
@ -493,6 +490,11 @@ namespace ImageProcessor.Web.HttpModules
throw new HttpException(403, "Access denied");
}
}
else if (isRemote)
{
// Just repoint to the external url.
HttpContext.Current.Response.Redirect(requestPath);
}
}
/// <summary>

BIN
src/Images/falahill_design__160p.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

5
src/TestWebsites/NET45/Test_Website_NET45/Views/Home/External.cshtml

@ -11,4 +11,9 @@
<img src="/remote.axd?http://maps.googleapis.com/maps/api/staticmap?center=Albany,+NY&zoom=13&scale=false&size=800x500&maptype=roadmap&sensor=false&format=png&visual_refresh=true?width=400" />
</div>
</div>
<div class="row">
<div class="col-s-6">
<img src="/remote.axd?http://images.hanselminutes.com/images/255.jpg?width=200" />
</div>
</div>
</section>

16
src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml

@ -11,6 +11,8 @@
<h3>Foreign language test.</h3>
<img src="/images/udendørs.jpg?width=300" />
<img src="/images/udendørs.jpg?preset=demo&filter=comic" />
<h3>Strange name</h3>
<img src="/images/falahill_design__160p.jpg?preset=demo" />
</div>
<div class="col-s-6">
<h2>Cropped </h2>
@ -201,18 +203,6 @@
</article>
<article>
<h1>Color Profiles</h1>
@* <section>
<div class="row">
<div class="col-s-6">
<h2>CMYK original jpg</h2>
<img src="/images/cmyk.jpg?" width="400" />
</div>
<div class="col-s-6">
<h2>sRGB original jpg</h2>
<img src="/images/srgb.jpg?" width="400" />
</div>
</div>
</section>*@
<section>
<div class="row">
<div class="col-s-6">
@ -233,4 +223,4 @@
<img src="/Images/header_1.jpg?width=750&crop=0-48-750-220" />
</div>
</section>
</article>
</article>

27
src/TestWebsites/NET45/Test_Website_NET45/config/imageprocessor/security.config

@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<security allowRemoteDownloads="true" timeout="300000" maxBytes="524288" remotePrefix="/remote.axd">
<whiteList>
<add url="http://images.mymovies.net"/>
<add url="http://maps.googleapis.com" extensionLess="true" imageFormat=".png"/>
<add url="https://fbcdn-profile-"/>
<add url="http://fbcdn-profile-"/>
<add url="https://profile."/>
<add url="http://profile."/>
<add url="https://pbs.twimg.com"/>
<add url="http://pbs.twimg.com"/>
<add url="http://placekitten.com"/>
</whiteList>
</security>
<security allowRemoteDownloads="true" timeout="300000" maxBytes="524288" remotePrefix="/remote.axd">
<whiteList>
<add url="http://images.mymovies.net"/>
<add url="http://maps.googleapis.com" extensionLess="true" imageFormat=".png"/>
<add url="https://fbcdn-profile-"/>
<add url="http://fbcdn-profile-"/>
<add url="https://profile."/>
<add url="http://profile."/>
<add url="https://pbs.twimg.com"/>
<add url="http://pbs.twimg.com"/>
<add url="http://placekitten.com"/>
<add url="http://hanselminutes.com/"/>
</whiteList>
</security>

Loading…
Cancel
Save