From 1b1b5c047c7c1c687f913de973184149afaea99b Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 26 Feb 2013 16:52:06 -0100 Subject: [PATCH 1/2] U4-1779 - Web.BaseRest supports rest extensions over multiple classes --- .../BaseRest/RestExtensionMethodInfo.cs | 92 ++++++++++--------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/src/Umbraco.Web/BaseRest/RestExtensionMethodInfo.cs b/src/Umbraco.Web/BaseRest/RestExtensionMethodInfo.cs index b1ffcaeb3b..c20ece0c38 100644 --- a/src/Umbraco.Web/BaseRest/RestExtensionMethodInfo.cs +++ b/src/Umbraco.Web/BaseRest/RestExtensionMethodInfo.cs @@ -199,33 +199,34 @@ namespace Umbraco.Web.BaseRest // which has been properly marked with the attribute, and use the attribute // properties to setup a RestExtensionMethodInfo - var extensions = PluginManager.Current.ResolveLegacyRestExtensions(); - var extension = extensions - .SingleOrDefault(type => type.GetCustomAttribute(false).GetAlias() == extensionAlias); + var extensions = PluginManager.Current.ResolveLegacyRestExtensions() + .Where(type => type.GetCustomAttribute(false).GetAlias() == extensionAlias); RestExtensionMethodInfo info = null; - if (extension != null) + foreach (var extension in extensions) // foreach classes with extension alias { var method = extension.GetMethod(methodName); - if (method != null) - { - var attribute = method.GetCustomAttributes(typeof(global::umbraco.presentation.umbracobase.RestExtensionMethod), false).Cast().SingleOrDefault(); - if (attribute != null) - { - info = new RestExtensionMethodInfo(attribute.GetAllowAll(), - attribute.GetAllowGroup(), attribute.GetAllowType(), attribute.GetAllowMember(), - attribute.returnXml, - method); + if (method == null) continue; // not implementing the method = ignore - // looks good, cache - lock (_cache) - { - _cache[cacheKey] = info; - } - } - } - } + var attribute = method.GetCustomAttributes(typeof(global::umbraco.presentation.umbracobase.RestExtensionMethod), false).Cast().SingleOrDefault(); + if (attribute == null) continue; // method has not attribute = ignore + + // got it! + info = new RestExtensionMethodInfo(attribute.GetAllowAll(), + attribute.GetAllowGroup(), attribute.GetAllowType(), attribute.GetAllowMember(), + attribute.returnXml, + method); + + // cache + lock (_cache) + { + _cache[cacheKey] = info; + } + + // got it, no need to look any further + break; + } return info; } @@ -249,34 +250,37 @@ namespace Umbraco.Web.BaseRest // find an extension with that alias, then find a method with that name, // which has been properly marked with the attribute, and use the attribute // properties to setup a RestExtensionMethodInfo + // + // note: the extension may be implemented by more than one class - var extensions = PluginManager.Current.ResolveRestExtensions(); - var extension = extensions - .SingleOrDefault(type => type.GetCustomAttribute(false).Alias == extensionAlias); + var extensions = PluginManager.Current.ResolveRestExtensions() + .Where(type => type.GetCustomAttribute(false).Alias == extensionAlias); RestExtensionMethodInfo info = null; - if (extension != null) - { - var method = extension.GetMethod(methodName); - if (method != null) - { - var attribute = method.GetCustomAttributes(typeof(RestExtensionMethodAttribute), false).Cast().SingleOrDefault(); - if (attribute != null) - { - info = new RestExtensionMethodInfo(attribute.AllowAll, - attribute.AllowGroup, attribute.AllowType, attribute.AllowMember, - attribute.ReturnXml, - method); + foreach (var extension in extensions) // foreach classes with extension alias + { + var method = extension.GetMethod(methodName); + if (method == null) continue; // not implementing the method = ignore - // looks good, cache - lock (_cache) - { - _cache[cacheKey] = info; - } - } - } - } + var attribute = method.GetCustomAttributes(typeof(RestExtensionMethodAttribute), false).Cast().SingleOrDefault(); + if (attribute == null) continue; // method has not attribute = ignore + + // got it! + info = new RestExtensionMethodInfo(attribute.AllowAll, + attribute.AllowGroup, attribute.AllowType, attribute.AllowMember, + attribute.ReturnXml, + method); + + // cache + lock (_cache) + { + _cache[cacheKey] = info; + } + + // got it, no need to look any further + break; + } return info; } From cd8c998ebf1546ae9dfc35f94e255e1e63741aa3 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 27 Feb 2013 20:42:11 +0600 Subject: [PATCH 2/2] Fixes: U4-1788 - ensures internal reserved paths and urls (that cannot be modified) are stored staticly in code, Also fixes that the _reservedPathsCache and _reservedUrlsCache actually works, before we were doing an object.ReferenceEquals comparison which would never have worked and it was also not thread safe. --- .../Configuration/GlobalSettings.cs | 94 ++++++++++++------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index 673a791de1..22e69e0100 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -30,7 +30,9 @@ namespace Umbraco.Core.Configuration // CURRENT UMBRACO VERSION ID private const string CurrentUmbracoVersion = "4.11.5"; - private static string _reservedUrlsCache; + private static readonly object Locker = new object(); + //make this volatile so that we can ensure thread safety with a double check lock + private static volatile string _reservedUrlsCache; private static string _reservedPathsCache; private static StartsWithContainer _reservedList = new StartsWithContainer(); @@ -44,9 +46,14 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoReservedUrls") - ? ConfigurationManager.AppSettings["umbracoReservedUrls"] - : string.Empty; + //ensure the built on (non-changeable) reserved paths are there at all times + const string staticReservedUrls = "~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd,"; + + var urls = ConfigurationManager.AppSettings.ContainsKey("umbracoReservedUrls") + ? ConfigurationManager.AppSettings["umbracoReservedUrls"] + : string.Empty; + + return staticReservedUrls + urls; } } @@ -58,9 +65,20 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoReservedPaths") - ? ConfigurationManager.AppSettings["umbracoReservedPaths"] - : string.Empty; + //ensure the built on (non-changeable) reserved paths are there at all times + var staticReservedPaths = "~/app_plugins/,~/install/,"; + + //always add the umbraco path to the list + if (ConfigurationManager.AppSettings.ContainsKey("umbracoPath")) + { + staticReservedPaths += ConfigurationManager.AppSettings["umbracoPath"].EnsureEndsWith(','); + } + + var paths = ConfigurationManager.AppSettings.ContainsKey("umbracoReservedPaths") + ? ConfigurationManager.AppSettings["umbracoReservedPaths"] + : string.Empty; + + return staticReservedPaths + paths; } } @@ -581,38 +599,42 @@ namespace Umbraco.Core.Configuration /// public static bool IsReservedPathOrUrl(string url) { - // check if GlobalSettings.ReservedPaths and GlobalSettings.ReservedUrls are unchanged - if (!object.ReferenceEquals(_reservedPathsCache, GlobalSettings.ReservedPaths) - || !object.ReferenceEquals(_reservedUrlsCache, GlobalSettings.ReservedUrls)) + if (_reservedUrlsCache == null) { - // store references to strings to determine changes - _reservedPathsCache = GlobalSettings.ReservedPaths; - _reservedUrlsCache = GlobalSettings.ReservedUrls; - - string _root = SystemDirectories.Root.Trim().ToLower(); - - // add URLs and paths to a new list - StartsWithContainer _newReservedList = new StartsWithContainer(); - foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)) + lock (Locker) { - //resolves the url to support tilde chars - string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl).Trim().ToLower(); - if (reservedUrlTrimmed.Length > 0) - _newReservedList.Add(reservedUrlTrimmed); + if (_reservedUrlsCache == null) + { + // store references to strings to determine changes + _reservedPathsCache = GlobalSettings.ReservedPaths; + _reservedUrlsCache = GlobalSettings.ReservedUrls; + + string _root = SystemDirectories.Root.Trim().ToLower(); + + // add URLs and paths to a new list + StartsWithContainer _newReservedList = new StartsWithContainer(); + foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)) + { + //resolves the url to support tilde chars + string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl).Trim().ToLower(); + if (reservedUrlTrimmed.Length > 0) + _newReservedList.Add(reservedUrlTrimmed); + } + + foreach (string reservedPath in _reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)) + { + bool trimEnd = !reservedPath.EndsWith("/"); + //resolves the url to support tilde chars + string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath).Trim().ToLower(); + + if (reservedPathTrimmed.Length > 0) + _newReservedList.Add(reservedPathTrimmed + (reservedPathTrimmed.EndsWith("/") ? "" : "/")); + } + + // use the new list from now on + _reservedList = _newReservedList; + } } - - foreach (string reservedPath in _reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)) - { - bool trimEnd = !reservedPath.EndsWith("/"); - //resolves the url to support tilde chars - string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath).Trim().ToLower(); - - if (reservedPathTrimmed.Length > 0) - _newReservedList.Add(reservedPathTrimmed + (reservedPathTrimmed.EndsWith("/") ? "" : "/")); - } - - // use the new list from now on - _reservedList = _newReservedList; } //The url should be cleaned up before checking: