Browse Source

Fixing concurrency issues in preset retrieval.

- Also enhanced security.


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

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

@ -15,7 +15,6 @@ namespace ImageProcessor.Web.Helpers
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Security; using System.Security;
using System.Text; using System.Text;
@ -354,8 +353,24 @@ namespace ImageProcessor.Web.Helpers
string upper = this.url.Host.ToUpperInvariant(); string upper = this.url.Host.ToUpperInvariant();
// Check for root or subdomain. // Check for root or subdomain.
bool validUrl = RemoteFileWhiteList.Any(item => bool validUrl = false;
upper.StartsWith(item.Host.ToUpperInvariant()) || upper.EndsWith(item.Host.ToUpperInvariant())); 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) 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); bool isRemote = request.Path.EndsWith(remotePrefix, StringComparison.OrdinalIgnoreCase);
string requestPath = string.Empty; string requestPath = string.Empty;
string queryString = string.Empty; string queryString = string.Empty;
bool validExtensionLessUrl = false; bool validExtensionLessUrl = false;
string urlParameters = ""; string urlParameters = "";
string extensionLessExtension = ""; string extensionLessExtension = "";
@ -346,14 +345,12 @@ namespace ImageProcessor.Web.HttpModules
// in the UrlAuthorizationModule by creating a generic identity. // in the UrlAuthorizationModule by creating a generic identity.
string virtualCachedPath = cache.GetVirtualCachedPath(); string virtualCachedPath = cache.GetVirtualCachedPath();
IPrincipal user = context.User IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
// Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal? // Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal?
PermissionSet permission = new PermissionSet(PermissionState.None); PermissionSet permission = new PermissionSet(PermissionState.None);
permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted)); permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted));
bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet); bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet);
bool isAllowed = true; bool isAllowed = true;
// Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value // 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"); throw new HttpException(403, "Access denied");
} }
} }
else if (isRemote)
{
// Just repoint to the external url.
HttpContext.Current.Response.Redirect(requestPath);
}
} }
/// <summary> /// <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" /> <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> </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> </section>

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

@ -11,6 +11,8 @@
<h3>Foreign language test.</h3> <h3>Foreign language test.</h3>
<img src="/images/udendørs.jpg?width=300" /> <img src="/images/udendørs.jpg?width=300" />
<img src="/images/udendørs.jpg?preset=demo&filter=comic" /> <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>
<div class="col-s-6"> <div class="col-s-6">
<h2>Cropped </h2> <h2>Cropped </h2>
@ -201,18 +203,6 @@
</article> </article>
<article> <article>
<h1>Color Profiles</h1> <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> <section>
<div class="row"> <div class="row">
<div class="col-s-6"> <div class="col-s-6">
@ -233,4 +223,4 @@
<img src="/Images/header_1.jpg?width=750&crop=0-48-750-220" /> <img src="/Images/header_1.jpg?width=750&crop=0-48-750-220" />
</div> </div>
</section> </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" ?> <?xml version="1.0" encoding="utf-8" ?>
<security allowRemoteDownloads="true" timeout="300000" maxBytes="524288" remotePrefix="/remote.axd"> <security allowRemoteDownloads="true" timeout="300000" maxBytes="524288" remotePrefix="/remote.axd">
<whiteList> <whiteList>
<add url="http://images.mymovies.net"/> <add url="http://images.mymovies.net"/>
<add url="http://maps.googleapis.com" extensionLess="true" imageFormat=".png"/> <add url="http://maps.googleapis.com" extensionLess="true" imageFormat=".png"/>
<add url="https://fbcdn-profile-"/> <add url="https://fbcdn-profile-"/>
<add url="http://fbcdn-profile-"/> <add url="http://fbcdn-profile-"/>
<add url="https://profile."/> <add url="https://profile."/>
<add url="http://profile."/> <add url="http://profile."/>
<add url="https://pbs.twimg.com"/> <add url="https://pbs.twimg.com"/>
<add url="http://pbs.twimg.com"/> <add url="http://pbs.twimg.com"/>
<add url="http://placekitten.com"/> <add url="http://placekitten.com"/>
</whiteList> <add url="http://hanselminutes.com/"/>
</security> </whiteList>
</security>

Loading…
Cancel
Save