From f7998bc95c0fcf34de310ce177646f54fbdc22ad Mon Sep 17 00:00:00 2001 From: Bertrand Le Roy Date: Mon, 7 Oct 2013 21:30:20 -0700 Subject: [PATCH 1/4] SSL module brought into the core distribution --- .../Commands/SecureSocketsLayersCommand.cs | 87 +++++++ .../Drivers/SslSettingsPartDriver.cs | 51 ++++ .../Filters/SecureSocketsLayersFilter.cs | 58 +++++ .../Handlers/SslSettingsPartHandler.cs | 27 ++ .../Orchard.SecureSocketsLayer/Migrations.cs | 20 ++ .../Models/SslSettingsPart.cs | 31 +++ .../Models/SslSettingsPartRecord.cs | 11 + .../Orchard.SecureSocketsLayer/Module.txt | 11 + .../Orchard.SecureSocketsLayer.csproj | 141 +++++++++++ .../Orchard.SecureSocketsLayer/Placement.info | 3 + .../Properties/AssemblyInfo.cs | 34 +++ .../Services/ISecureSocketsLayerService.cs | 16 ++ .../Services/SecuredSocketsLayerService.cs | 239 ++++++++++++++++++ .../Parts/SecureSocketsLayer.Settings.cshtml | 36 +++ src/Orchard.sln | 13 + 15 files changed, 778 insertions(+) create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Commands/SecureSocketsLayersCommand.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Filters/SecureSocketsLayersFilter.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Module.txt create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Placement.info create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Properties/AssemblyInfo.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/ISecureSocketsLayerService.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs create mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Views/EditorTemplates/Parts/SecureSocketsLayer.Settings.cshtml diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Commands/SecureSocketsLayersCommand.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Commands/SecureSocketsLayersCommand.cs new file mode 100644 index 000000000..b580b6cb0 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Commands/SecureSocketsLayersCommand.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using Orchard.Commands; +using Orchard.ContentManagement; +using Orchard.SecureSocketsLayer.Models; + +namespace Orchard.SecureSocketsLayer.Commands { + public class SecureSocketsLayersCommand : DefaultOrchardCommandHandler { + private readonly IOrchardServices _services; + + public SecureSocketsLayersCommand(IOrchardServices services) { + _services = services; + } + + [OrchardSwitch] + public bool SecureEverything { get; set; } + [OrchardSwitch] + public bool CustomEnabled { get; set; } + [OrchardSwitch] + public string Urls { get; set; } + [OrchardSwitch] + public string SecureHostName { get; set; } + [OrchardSwitch] + public string InsecureHostName { get; set; } + + [CommandName("site setting set ssl")] + [CommandHelp("site setting set ssl /SecureEverything:true /CustomEnabled:true /Urls: /SecureHostName:domain.com /InsecureHostName:secure.domain.com\r\n" + + "\tSet the 'SSL' site settings. Urls example: /Urls:\"'mysite.com/a','mysite.com/b'\"")] + [OrchardSwitches("SecureEverything,CustomEnabled,Urls,SecureHostName,InsecureHostName")] + public void SetSSLInfo() { + var settings = _services.WorkContext.CurrentSite.As(); + if (settings == null) { + return; + } + + if (!string.IsNullOrWhiteSpace(Urls)) { + var comma = false; + var urlList = new List(); + try { + Urls = Urls.Trim(); + while (Urls.Length != 0) { + var first = Urls[0]; + if (first == ',' && comma) { + Urls = Urls.Substring(1); + comma = false; + } + else if (first == '\'' && !comma) { + int end = Urls.IndexOf('\'', 1); + if (end == -1) { + throw new ArgumentException("Invalid Urls"); + } + urlList.Add(Urls.Substring(1, end - 1)); + Urls = Urls.Substring(end + 1); + comma = true; + } + else { + throw new ArgumentException("Invalid Urls"); + } + } + if (!comma) + throw new ArgumentException("Invalid Urls"); + Urls = string.Join("\r\n", urlList); + } + catch(ArgumentException) { + Context.Output.WriteLine(T("'Urls' site setting invalid")); + return; + } + } + + if (string.IsNullOrWhiteSpace(Urls)) { + Urls = null; + } + + settings.SecureEverything = SecureEverything; + settings.CustomEnabled = CustomEnabled; + settings.Urls = Urls; + settings.SecureHostName = SecureHostName; + settings.InsecureHostName = InsecureHostName; + + Context.Output.WriteLine(T("'Secure Everything' site setting set to '{0}'", SecureEverything)); + Context.Output.WriteLine(T("'Custom Enabled' site setting set to '{0}'", CustomEnabled)); + Context.Output.WriteLine(T("'Urls' site setting set to '{0}'", Urls)); + Context.Output.WriteLine(T("'Secure Host Name' site setting set to '{0}'", SecureHostName)); + Context.Output.WriteLine(T("'Insecure Host Name' site setting set to '{0}'", InsecureHostName)); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs new file mode 100644 index 000000000..27ce4d17b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs @@ -0,0 +1,51 @@ +using Orchard.ContentManagement; +using Orchard.ContentManagement.Drivers; +using Orchard.ContentManagement.Handlers; +using Orchard.Localization; +using Orchard.SecureSocketsLayer.Models; + +namespace Orchard.SecureSocketsLayer.Drivers { + public class SslSettingsPartDriver : ContentPartDriver { + private const string TemplateName = "Parts/SecureSocketsLayer.Settings"; + + public SslSettingsPartDriver() { + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + protected override string Prefix { + get { return "SslSettings"; } + } + + protected override DriverResult Editor(SslSettingsPart part, dynamic shapeHelper) { + return ContentShape("Parts_SslSettings_Edit", + () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: part, Prefix: Prefix)) + .OnGroup("Ssl"); + } + + protected override DriverResult Editor(SslSettingsPart part, IUpdateModel updater, dynamic shapeHelper) { + updater.TryUpdateModel(part, Prefix, null, null); + + return Editor(part, shapeHelper); + } + + protected override void Importing(SslSettingsPart part, ImportContentContext context) { + var elementName = part.PartDefinition.Name; + part.SecureEverything = bool.Parse(context.Attribute(elementName, "SecureEverything") ?? "true"); + part.CustomEnabled = bool.Parse(context.Attribute(elementName, "CustomEnabled") ?? "false"); + part.Urls = context.Attribute(elementName, "Urls") ?? ""; + part.InsecureHostName = context.Attribute(elementName, "InsecureHostName") ?? ""; + part.SecureHostName = context.Attribute(elementName, "SecureHostName") ?? ""; + } + + protected override void Exporting(SslSettingsPart part, ExportContentContext context) { + var el = context.Element(part.PartDefinition.Name); + el.SetAttributeValue("SecureEverything", part.SecureEverything); + el.SetAttributeValue("CustomEnabled", part.CustomEnabled); + el.SetAttributeValue("Urls", part.Urls); + el.SetAttributeValue("InsecureHostName", part.InsecureHostName); + el.SetAttributeValue("SecureHostName", part.SecureHostName); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Filters/SecureSocketsLayersFilter.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Filters/SecureSocketsLayersFilter.cs new file mode 100644 index 000000000..0c976c5e5 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Filters/SecureSocketsLayersFilter.cs @@ -0,0 +1,58 @@ +using System.Collections.Specialized; +using System.Web.Mvc; +using Orchard.Mvc.Filters; +using Orchard.SecureSocketsLayer.Services; + +namespace Orchard.SecureSocketsLayer.Filters { + public class SecureSocketsLayersFilter : FilterProvider, IActionFilter { + private readonly ISecureSocketsLayerService _sslService; + + public SecureSocketsLayersFilter(ISecureSocketsLayerService sslService) { + _sslService = sslService; + } + + public void OnActionExecuted(ActionExecutedContext filterContext) {} + + public void OnActionExecuting(ActionExecutingContext filterContext) { + var user = filterContext.HttpContext.User; + var secure = + (user != null && user.Identity.IsAuthenticated) || + _sslService.ShouldBeSecure(filterContext); + + var request = filterContext.HttpContext.Request; + + // redirect to a secured connection ? + if (secure && !request.IsSecureConnection) { + var secureActionUrl = AppendQueryString( + request.QueryString, + _sslService.SecureActionUrl( + filterContext.ActionDescriptor.ActionName, + filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, + filterContext.RequestContext.RouteData.Values)); + + filterContext.Result = new RedirectResult(secureActionUrl); + return; + } + + // non auth page on a secure canal + // nb: needed as the ReturnUrl for LogOn doesn't force the scheme to http, and reuses the current one + if (!secure && request.IsSecureConnection) { + var insecureActionUrl = AppendQueryString( + request.QueryString, + _sslService.InsecureActionUrl( + filterContext.ActionDescriptor.ActionName, + filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, + filterContext.RequestContext.RouteData.Values)); + + filterContext.Result = new RedirectResult(insecureActionUrl); + } + } + + private static string AppendQueryString(NameValueCollection queryString, string url) { + if (queryString.Count > 0) { + url += '?' + queryString.ToString(); + } + return url; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs new file mode 100644 index 000000000..1e4f81479 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs @@ -0,0 +1,27 @@ +using Orchard.ContentManagement; +using Orchard.Data; +using Orchard.ContentManagement.Handlers; +using Orchard.Localization; +using Orchard.SecureSocketsLayer.Models; + +namespace Orchard.SecureSocketsLayer.Handlers { + public class SslSettingsPartHandler : ContentHandler { + public SslSettingsPartHandler(IRepository repository) { + T = NullLocalizer.Instance; + Filters.Add(new ActivatingFilter("Site")); + Filters.Add(StorageFilter.For(repository)); + } + + public Localizer T { get; set; } + + protected override void GetItemMetadata(GetContentItemMetadataContext context) { + if (context.ContentItem.ContentType != "Site") + return; + base.GetItemMetadata(context); + context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("SSL")) { + Id = "Ssl", + Position = "2" + }); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs new file mode 100644 index 000000000..39c8d54b8 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs @@ -0,0 +1,20 @@ +using Orchard.Data.Migration; + +namespace Orchard.SecureSocketsLayer { + public class Migrations : DataMigrationImpl { + public int Create() { + + SchemaBuilder.CreateTable("SslSettingsPartRecord", + table => table + .ContentPartRecord() + .Column("CustomEnabled") + .Column("SecureEverything") + .Column("Urls", c => c.Unlimited()) + .Column("SecureHostName") + .Column("InsecureHostName") + ); + + return 1; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs new file mode 100644 index 000000000..4602a62b4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs @@ -0,0 +1,31 @@ +using Orchard.ContentManagement; + +namespace Orchard.SecureSocketsLayer.Models { + public class SslSettingsPart : ContentPart { + public string Urls + { + get { return Record.Urls; } + set { Record.Urls = value; } + } + + public bool SecureEverything { + get { return Record.SecureEverything; } + set { Record.SecureEverything = value; } + } + + public bool CustomEnabled { + get { return Record.CustomEnabled; } + set { Record.CustomEnabled = value; } + } + + public string SecureHostName { + get { return Record.SecureHostName; } + set { Record.SecureHostName = value; } + } + + public string InsecureHostName { + get { return Record.InsecureHostName; } + set { Record.InsecureHostName = value; } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs new file mode 100644 index 000000000..5fc97f825 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs @@ -0,0 +1,11 @@ +using Orchard.ContentManagement.Records; + +namespace Orchard.SecureSocketsLayer.Models { + public class SslSettingsPartRecord : ContentPartRecord { + public virtual string Urls { get; set; } + public virtual bool SecureEverything { get; set; } + public virtual bool CustomEnabled { get; set; } + public virtual string SecureHostName { get; set; } + public virtual string InsecureHostName { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Module.txt b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Module.txt new file mode 100644 index 000000000..0bc064e97 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Module.txt @@ -0,0 +1,11 @@ +Name: Secure Sockets Layer +AntiForgery: enabled +Author: The Orchard Team +Website: http://orchardproject.net +Version: 1.7.1 +OrchardVersion: 1.7.1 +Description: This module will ensure SSL is used when accessing specific parts of the website like the dashboard, authentication pages or custom pages. +FeatureName: Secure Sockets Layer +Category: Security +FeatureDescription: Use SSL for specific parts of the website +Dependencies: Orchard.Users diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj new file mode 100644 index 000000000..9ef9ced9b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj @@ -0,0 +1,141 @@ + + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {36B82383-D69E-4897-A24A-648BABDF80EC} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Orchard.SecureSocketsLayer + Orchard.SecureSocketsLayer + v4.0 + false + + + 4.0 + + + false + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + + 3.5 + + + + False + ..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll + + + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard.Framework + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + + + + + + + + + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + $(ProjectDir)\..\Manifests + + + + + + + + + + + + False + True + 45979 + / + + + False + True + http://orchard.codeplex.com + False + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Placement.info b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Placement.info new file mode 100644 index 000000000..d0034ee93 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Placement.info @@ -0,0 +1,3 @@ + + + diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9ed336f14 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Orchard.SecureSocketsLayer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Orchard")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("285775cf-fa49-4030-8a30-7872120d6574")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/ISecureSocketsLayerService.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/ISecureSocketsLayerService.cs new file mode 100644 index 000000000..0ed377816 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/ISecureSocketsLayerService.cs @@ -0,0 +1,16 @@ +using System.Web.Mvc; +using System.Web.Routing; + +namespace Orchard.SecureSocketsLayer.Services { + public interface ISecureSocketsLayerService : IDependency { + bool ShouldBeSecure(string actionName, string controllerName, RouteValueDictionary routeValues); + bool ShouldBeSecure(RequestContext requestContext); + bool ShouldBeSecure(ActionExecutingContext actionContext); + string InsecureActionUrl(string actionName, string controllerName); + string InsecureActionUrl(string actionName, string controllerName, object routeValues); + string InsecureActionUrl(string actionName, string controllerName, RouteValueDictionary routeValues); + string SecureActionUrl(string actionName, string controllerName); + string SecureActionUrl(string actionName, string controllerName, object routeValues); + string SecureActionUrl(string actionName, string controllerName, RouteValueDictionary routeValues); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs new file mode 100644 index 000000000..a3c340329 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs @@ -0,0 +1,239 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Web.Mvc; +using System.Web.Routing; +using Orchard.Caching; +using Orchard.ContentManagement; +using Orchard.SecureSocketsLayer.Models; +using Orchard.UI.Admin; + +namespace Orchard.SecureSocketsLayer.Services { + public class SecureSocketsLayerService : ISecureSocketsLayerService { + private readonly IWorkContextAccessor _workContextAccessor; + private readonly ICacheManager _cacheManager; + + public SecureSocketsLayerService(IWorkContextAccessor workContextAccessor, ICacheManager cacheManager) { + _workContextAccessor = workContextAccessor; + _cacheManager = cacheManager; + } + + public bool ShouldBeSecure(string actionName, string controllerName, RouteValueDictionary routeValues) { + var requestContext = GetRequestContext(actionName, controllerName, routeValues); + return ShouldBeSecure(requestContext, null); + } + + public bool ShouldBeSecure(RequestContext requestContext) { + return ShouldBeSecure(requestContext, null); + } + + public bool ShouldBeSecure(ActionExecutingContext actionContext) { + var requestContext = GetRequestContext( + actionContext.ActionDescriptor.ActionName, + actionContext.ActionDescriptor.ControllerDescriptor.ControllerName, + actionContext.RequestContext.RouteData.Values); + return ShouldBeSecure(requestContext, actionContext); + } + + private bool ShouldBeSecure(RequestContext requestContext, ActionExecutingContext actionContext) { + var controllerName = (string) requestContext.RouteData.Values["controller"]; + if (controllerName == null) return false; + var actionName = (string) requestContext.RouteData.Values["action"]; + if (actionName == null) return false; + + if (actionName.EndsWith("Ssl") || controllerName.EndsWith("Ssl")) { + return true; + } + + var controller = (actionContext != null + ? actionContext.Controller + : ControllerBuilder.Current.GetControllerFactory() + .CreateController(requestContext, controllerName)) as ControllerBase; + if (controller != null) { + var controllerType = controller.GetType(); + if (controllerType.GetCustomAttributes(typeof(RequireHttpsAttribute), false).Any()) { + return true; + } + ActionDescriptor actionDescriptor; + if (actionContext != null) { + actionDescriptor = actionContext.ActionDescriptor; + } + else { + var controllerContext = new ControllerContext(requestContext, controller); + var controllerDescriptor = new ReflectedControllerDescriptor(controllerType); + actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName); + } + if (actionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), false).Any()) { + return true; + } + } + + var settings = GetSettings(); + if (settings == null) return false; + + if (settings.SecureEverything) return true; + + if (controllerName == "Account" && + (actionName == "LogOn" + || actionName == "ChangePassword" + || actionName == "AccessDenied" + || actionName == "Register" + || actionName.StartsWith("ChallengeEmail", StringComparison.OrdinalIgnoreCase))) { + return true; + } + + if (controllerName == "Admin" || AdminFilter.IsApplied(requestContext)) { + return true; + } + + if (!settings.CustomEnabled) return false; + + var urlHelper = new UrlHelper(requestContext); + var url = urlHelper.Action(actionName, controllerName, requestContext.RouteData); + + return IsRequestProtected( + url, requestContext.HttpContext.Request.ApplicationPath, settings); + } + + public string SecureActionUrl(string actionName, string controllerName) { + return SecureActionUrl(actionName, controllerName, new object()); + } + + public string SecureActionUrl(string actionName, string controllerName, object routeValues) { + return SecureActionUrl(actionName, controllerName, new RouteValueDictionary(routeValues)); + } + + public string SecureActionUrl(string actionName, string controllerName, RouteValueDictionary routeValues) { + var requestContext = GetRequestContext(actionName, controllerName, routeValues); + var url = new UrlHelper(requestContext); + var actionUrl = url.Action(actionName, controllerName, routeValues); + if (actionUrl == null) return null; + var currentUri = _workContextAccessor.GetContext().HttpContext.Request.Url; + return currentUri != null && currentUri.Scheme.Equals(Uri.UriSchemeHttps) ? + actionUrl /* action url is relative so will keep current protocol */ : + MakeSecure(actionUrl); + } + + public string InsecureActionUrl(string actionName, string controllerName) { + return InsecureActionUrl(actionName, controllerName, new object()); + } + + public string InsecureActionUrl(string actionName, string controllerName, object routeValues) { + return InsecureActionUrl(actionName, controllerName, new RouteValueDictionary(routeValues)); + } + + public string InsecureActionUrl(string actionName, string controllerName, RouteValueDictionary routeValues) { + var requestContext = GetRequestContext(actionName, controllerName, routeValues); + var url = new UrlHelper(requestContext); + var actionUrl = url.Action(actionName, controllerName, routeValues); + if (actionUrl == null) return null; + var currentUri = _workContextAccessor.GetContext().HttpContext.Request.Url; + return currentUri != null && currentUri.Scheme.Equals(Uri.UriSchemeHttp) ? + actionUrl /* action url is relative so will keep current protocol */ : + MakeInsecure(actionUrl); + } + + private RequestContext GetRequestContext( + string actionName, + string controllerName, + RouteValueDictionary routeValues) { + + var httpContext = _workContextAccessor.GetContext().HttpContext; + var routeData = new RouteData(); + foreach (var routeValue in routeValues) { + routeData.Values[routeValue.Key] = routeValue.Value; + } + routeData.Values["controller"] = controllerName; + routeData.Values["action"] = actionName; + var requestContext = new RequestContext(httpContext, routeData); + return requestContext; + } + + private static bool IsRequestProtected(string path, string appPath, SslSettingsPartRecord settings) { + var match = false; + var sr = new StringReader(settings.Urls ?? ""); + string pattern; + + while (!match && null != (pattern = sr.ReadLine())) { + pattern = pattern.Trim(); + match = IsMatched(pattern, path, appPath); + } + + return match; + } + + private static bool IsMatched(string pattern, string path, string appPath) { + if (pattern.StartsWith("~/")) { + pattern = pattern.Substring(2); + if (appPath == "/") + appPath = ""; + pattern = string.Format("{0}/{1}", appPath, pattern); + } + + if (!pattern.Contains("?")) + pattern = pattern.TrimEnd('/'); + + var requestPath = path; + if (!requestPath.Contains("?")) + requestPath = requestPath.TrimEnd('/'); + + return pattern.EndsWith("*") + ? requestPath.StartsWith(pattern.TrimEnd('*'), StringComparison.OrdinalIgnoreCase) + : string.Equals(requestPath, pattern, StringComparison.OrdinalIgnoreCase); + } + + private SslSettingsPartRecord GetSettings() { + return _cacheManager.Get("SslSettings", ctx => { + var settings = _workContextAccessor.GetContext().CurrentSite.As(); + return new SslSettingsPartRecord { + CustomEnabled = settings.CustomEnabled, + Urls = settings.Urls, + SecureEverything = settings.SecureEverything, + InsecureHostName = settings.InsecureHostName, + SecureHostName = settings.SecureHostName + }; + }); + } + + private string MakeInsecure(string path) { + var settings = GetSettings(); + if (settings == null) return path; + var builder = new UriBuilder { + Scheme = Uri.UriSchemeHttp, + Port = 80 + }; + var insecureHostName = settings.InsecureHostName; + SetHost(insecureHostName, builder); + builder.Path = path; + return builder.Uri.ToString(); + } + + private string MakeSecure(string path) { + var settings = GetSettings(); + if (settings == null) return path; + var builder = new UriBuilder { + Scheme = Uri.UriSchemeHttps, + Port = 443 + }; + var secureHostName = settings.SecureHostName; + SetHost(secureHostName, builder); + builder.Path = path; + return builder.Uri.ToString(); + } + + private static void SetHost(string hostName, UriBuilder builder) { + if (string.IsNullOrWhiteSpace(hostName)) return; + var splitSecuredHostName = hostName.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries); + if (splitSecuredHostName.Length == 2) { + int port; + if (int.TryParse(splitSecuredHostName[1], NumberStyles.Integer, CultureInfo.InvariantCulture, + out port)) { + builder.Port = port; + hostName = splitSecuredHostName[0]; + } + } + builder.Host = hostName; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Views/EditorTemplates/Parts/SecureSocketsLayer.Settings.cshtml b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Views/EditorTemplates/Parts/SecureSocketsLayer.Settings.cshtml new file mode 100644 index 000000000..9433f52e4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Views/EditorTemplates/Parts/SecureSocketsLayer.Settings.cshtml @@ -0,0 +1,36 @@ +@model Orchard.SecureSocketsLayer.Models.SslSettingsPart + +
+ @T("SSL Settings") +
+ @Html.EditorFor(m => m.SecureEverything) + + @Html.ValidationMessage("SecureEverything", "*") +
+
+
+ @Html.EditorFor(m => m.CustomEnabled) + + @Html.ValidationMessage("CustomEnabled", "*") +
+
+
+ + @Html.TextAreaFor(m => m.Urls, new { @class = "textMedium", rows = "5" } ) + @Html.ValidationMessage("Urls", "*") + @T("Provide a list of urls, one per line. Urls can contains wildcard matches using '*', or root identifier like '~/'") + @T("Examples: http://mysite.com/mypage, ~/Profile/Edit, ~/Profile/*") +
+
+
+
+ + @Html.TextBoxFor(m => m.SecureHostName, new { @class = "textMedium" }) + @T("Provide the host name secure traffic should be redirected to (e.g. secure.mydomain.com). Don't include the protocol or anything else than the host name. A port can be specified after a colon if necessary (e.g. secure.127-0-0-1.org.uk:4333). Leave empty to remain on the same host name (not recommended as this can be a security issue).") +
+
+ + @Html.TextBoxFor(m => m.InsecureHostName, new { @class = "textMedium" }) + @T("Provide the host name non-secured traffic should be redirected to (e.g. mydomain.com). Don't include the protocol or anything else than the host name. A port can be specified after a colon if necessary (e.g. dev.127-0-0-1.org.uk:4333). Leave empty to remain on the same host name (not recommended as this can be a security issue).") +
+
\ No newline at end of file diff --git a/src/Orchard.sln b/src/Orchard.sln index 378128adc..c9f77712a 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -158,6 +158,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules.Deprecated", "Modul EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "Orchard.Web\Modules\Orchard.Azure\Orchard.Azure.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.SecureSocketsLayer", "Orchard.Web\Modules\Orchard.SecureSocketsLayer\Orchard.SecureSocketsLayer.csproj", "{36B82383-D69E-4897-A24A-648BABDF80EC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeCoverage|Any CPU = CodeCoverage|Any CPU @@ -876,6 +878,16 @@ Global {CBC7993C-57D8-4A6C-992C-19E849DFE71D}.FxCop|Any CPU.Build.0 = Release|Any CPU {CBC7993C-57D8-4A6C-992C-19E849DFE71D}.Release|Any CPU.ActiveCfg = Release|Any CPU {CBC7993C-57D8-4A6C-992C-19E849DFE71D}.Release|Any CPU.Build.0 = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.Coverage|Any CPU.ActiveCfg = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.Coverage|Any CPU.Build.0 = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.FxCop|Any CPU.Build.0 = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36B82383-D69E-4897-A24A-648BABDF80EC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -934,6 +946,7 @@ Global {6E444FF1-A47C-4CF6-BB3F-507C8EBD776D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {8A9FDB57-342D-49C2-BAFC-D885AAE5CC7C} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {CBC7993C-57D8-4A6C-992C-19E849DFE71D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} + {36B82383-D69E-4897-A24A-648BABDF80EC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} From 5166c16c73594456e0555c296a06a55e8125880a Mon Sep 17 00:00:00 2001 From: Bertrand Le Roy Date: Tue, 8 Oct 2013 21:50:02 -0700 Subject: [PATCH 2/4] Shifting settings. Settings are now saved into the infoset rather than a record. No migration, no record. --- .../Handlers/SslSettingsPartHandler.cs | 3 +- .../Orchard.SecureSocketsLayer/Migrations.cs | 20 ------------- .../Models/SslSettingsPart.cs | 30 ++++++++++++------- .../Models/SslSettingsPartRecord.cs | 11 ------- .../Orchard.SecureSocketsLayer.csproj | 4 +-- ...ervice.cs => SecureSocketsLayerService.cs} | 15 ++-------- .../InfosetStorage/InfosetPart.cs | 4 +-- 7 files changed, 26 insertions(+), 61 deletions(-) delete mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs delete mode 100644 src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs rename src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/{SecuredSocketsLayerService.cs => SecureSocketsLayerService.cs} (93%) diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs index 1e4f81479..8d9e08d07 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Handlers/SslSettingsPartHandler.cs @@ -6,10 +6,9 @@ using Orchard.SecureSocketsLayer.Models; namespace Orchard.SecureSocketsLayer.Handlers { public class SslSettingsPartHandler : ContentHandler { - public SslSettingsPartHandler(IRepository repository) { + public SslSettingsPartHandler() { T = NullLocalizer.Instance; Filters.Add(new ActivatingFilter("Site")); - Filters.Add(StorageFilter.For(repository)); } public Localizer T { get; set; } diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs deleted file mode 100644 index 39c8d54b8..000000000 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Migrations.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Orchard.Data.Migration; - -namespace Orchard.SecureSocketsLayer { - public class Migrations : DataMigrationImpl { - public int Create() { - - SchemaBuilder.CreateTable("SslSettingsPartRecord", - table => table - .ContentPartRecord() - .Column("CustomEnabled") - .Column("SecureEverything") - .Column("Urls", c => c.Unlimited()) - .Column("SecureHostName") - .Column("InsecureHostName") - ); - - return 1; - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs index 4602a62b4..c42cca698 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs @@ -1,31 +1,39 @@ +using System; using Orchard.ContentManagement; +using Orchard.ContentManagement.FieldStorage.InfosetStorage; namespace Orchard.SecureSocketsLayer.Models { - public class SslSettingsPart : ContentPart { + public class SslSettingsPart : ContentPart { public string Urls { - get { return Record.Urls; } - set { Record.Urls = value; } + get { return this.As().Get("Urls"); } + set { this.As().Set("Urls", value); } } public bool SecureEverything { - get { return Record.SecureEverything; } - set { Record.SecureEverything = value; } + get { + var attributeValue = this.As().Get("SecureEverything"); + return !String.IsNullOrWhiteSpace(attributeValue) && Convert.ToBoolean(attributeValue); + } + set { this.As().Set("SecureEverything", value.ToString()); } } public bool CustomEnabled { - get { return Record.CustomEnabled; } - set { Record.CustomEnabled = value; } + get { + var attributeValue = this.As().Get("CustomEnabled"); + return !String.IsNullOrWhiteSpace(attributeValue) && Convert.ToBoolean(attributeValue); + } + set { this.As().Set("CustomEnabled", value.ToString()); } } public string SecureHostName { - get { return Record.SecureHostName; } - set { Record.SecureHostName = value; } + get { return this.As().Get("SecureHostName"); } + set { this.As().Set("SecureHostName", value); } } public string InsecureHostName { - get { return Record.InsecureHostName; } - set { Record.InsecureHostName = value; } + get { return this.As().Get("InsecureHostName"); } + set { this.As().Set("InsecureHostName", value); } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs deleted file mode 100644 index 5fc97f825..000000000 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPartRecord.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Orchard.ContentManagement.Records; - -namespace Orchard.SecureSocketsLayer.Models { - public class SslSettingsPartRecord : ContentPartRecord { - public virtual string Urls { get; set; } - public virtual bool SecureEverything { get; set; } - public virtual bool CustomEnabled { get; set; } - public virtual string SecureHostName { get; set; } - public virtual string InsecureHostName { get; set; } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj index 9ef9ced9b..8224c10a0 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Orchard.SecureSocketsLayer.csproj @@ -82,11 +82,9 @@ - - - + diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs similarity index 93% rename from src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs rename to src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs index a3c340329..331ca36bf 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecuredSocketsLayerService.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs @@ -150,7 +150,7 @@ namespace Orchard.SecureSocketsLayer.Services { return requestContext; } - private static bool IsRequestProtected(string path, string appPath, SslSettingsPartRecord settings) { + private static bool IsRequestProtected(string path, string appPath, SslSettingsPart settings) { var match = false; var sr = new StringReader(settings.Urls ?? ""); string pattern; @@ -183,17 +183,8 @@ namespace Orchard.SecureSocketsLayer.Services { : string.Equals(requestPath, pattern, StringComparison.OrdinalIgnoreCase); } - private SslSettingsPartRecord GetSettings() { - return _cacheManager.Get("SslSettings", ctx => { - var settings = _workContextAccessor.GetContext().CurrentSite.As(); - return new SslSettingsPartRecord { - CustomEnabled = settings.CustomEnabled, - Urls = settings.Urls, - SecureEverything = settings.SecureEverything, - InsecureHostName = settings.InsecureHostName, - SecureHostName = settings.SecureHostName - }; - }); + private SslSettingsPart GetSettings() { + return _cacheManager.Get("SslSettings", ctx => _workContextAccessor.GetContext().CurrentSite.As()); } private string MakeInsecure(string path) { diff --git a/src/Orchard/ContentManagement/FieldStorage/InfosetStorage/InfosetPart.cs b/src/Orchard/ContentManagement/FieldStorage/InfosetStorage/InfosetPart.cs index dc1694b73..f5431eb49 100644 --- a/src/Orchard/ContentManagement/FieldStorage/InfosetStorage/InfosetPart.cs +++ b/src/Orchard/ContentManagement/FieldStorage/InfosetStorage/InfosetPart.cs @@ -67,10 +67,10 @@ namespace Orchard.ContentManagement.FieldStorage.InfosetStorage { partElement.Add(fieldElement); } if (string.IsNullOrEmpty(valueName)) { - fieldElement.Value = value; + fieldElement.Value = value ?? ""; } else { - fieldElement.SetAttributeValue(XmlConvert.EncodeLocalName(valueName), value); + fieldElement.SetAttributeValue(XmlConvert.EncodeLocalName(valueName), value ?? ""); } } } From 7abd20f9ec72b57ebbc90e383cf970af87552dd1 Mon Sep 17 00:00:00 2001 From: Bertrand Le Roy Date: Wed, 9 Oct 2013 11:52:07 -0700 Subject: [PATCH 3/4] Caching done right --- .../Models/SslSettingsPart.cs | 8 ++++++++ .../Services/SecureSocketsLayerService.cs | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs index c42cca698..3a817326a 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs @@ -3,6 +3,14 @@ using Orchard.ContentManagement; using Orchard.ContentManagement.FieldStorage.InfosetStorage; namespace Orchard.SecureSocketsLayer.Models { + internal class SslSettings { + public string Urls { get; set; } + public bool SecureEverything { get; set; } + public bool CustomEnabled { get; set; } + public string SecureHostName { get; set; } + public string InsecureHostName { get; set; } + } + public class SslSettingsPart : ContentPart { public string Urls { diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs index 331ca36bf..53c00cb2d 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs @@ -150,7 +150,7 @@ namespace Orchard.SecureSocketsLayer.Services { return requestContext; } - private static bool IsRequestProtected(string path, string appPath, SslSettingsPart settings) { + private static bool IsRequestProtected(string path, string appPath, SslSettings settings) { var match = false; var sr = new StringReader(settings.Urls ?? ""); string pattern; @@ -183,8 +183,18 @@ namespace Orchard.SecureSocketsLayer.Services { : string.Equals(requestPath, pattern, StringComparison.OrdinalIgnoreCase); } - private SslSettingsPart GetSettings() { - return _cacheManager.Get("SslSettings", ctx => _workContextAccessor.GetContext().CurrentSite.As()); + private SslSettings GetSettings() { + return _cacheManager.Get("SslSettings", + ctx => { + var settingsPart = _workContextAccessor.GetContext().CurrentSite.As(); + return new SslSettings { + Urls = settingsPart.Urls, + CustomEnabled = settingsPart.CustomEnabled, + SecureEverything = settingsPart.SecureEverything, + SecureHostName = settingsPart.SecureHostName, + InsecureHostName = settingsPart.InsecureHostName + }; + }); } private string MakeInsecure(string path) { From 03f6d06f36e74228521ccdc68888a59480fbd085 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 9 Oct 2013 12:13:49 -0700 Subject: [PATCH 4/4] Invalidating SSL settings cache --- .../Drivers/SslSettingsPartDriver.cs | 13 ++++++++++--- .../Models/SslSettingsPart.cs | 2 ++ .../Services/SecureSocketsLayerService.cs | 8 +++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs index 27ce4d17b..323650d77 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Drivers/SslSettingsPartDriver.cs @@ -1,4 +1,5 @@ -using Orchard.ContentManagement; +using Orchard.Caching; +using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; using Orchard.Localization; @@ -6,9 +7,11 @@ using Orchard.SecureSocketsLayer.Models; namespace Orchard.SecureSocketsLayer.Drivers { public class SslSettingsPartDriver : ContentPartDriver { + private readonly ISignals _signals; private const string TemplateName = "Parts/SecureSocketsLayer.Settings"; - public SslSettingsPartDriver() { + public SslSettingsPartDriver(ISignals signals) { + _signals = signals; T = NullLocalizer.Instance; } @@ -25,7 +28,9 @@ namespace Orchard.SecureSocketsLayer.Drivers { } protected override DriverResult Editor(SslSettingsPart part, IUpdateModel updater, dynamic shapeHelper) { - updater.TryUpdateModel(part, Prefix, null, null); + if (updater.TryUpdateModel(part, Prefix, null, null)) { + _signals.Trigger(SslSettingsPart.CacheKey); + } return Editor(part, shapeHelper); } @@ -37,6 +42,8 @@ namespace Orchard.SecureSocketsLayer.Drivers { part.Urls = context.Attribute(elementName, "Urls") ?? ""; part.InsecureHostName = context.Attribute(elementName, "InsecureHostName") ?? ""; part.SecureHostName = context.Attribute(elementName, "SecureHostName") ?? ""; + + _signals.Trigger(SslSettingsPart.CacheKey); } protected override void Exporting(SslSettingsPart part, ExportContentContext context) { diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs index 3a817326a..842610534 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Models/SslSettingsPart.cs @@ -12,6 +12,8 @@ namespace Orchard.SecureSocketsLayer.Models { } public class SslSettingsPart : ContentPart { + public const string CacheKey = "SslSettingsPart"; + public string Urls { get { return this.As().Get("Urls"); } diff --git a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs index 53c00cb2d..0993e6b37 100644 --- a/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs +++ b/src/Orchard.Web/Modules/Orchard.SecureSocketsLayer/Services/SecureSocketsLayerService.cs @@ -13,10 +13,15 @@ namespace Orchard.SecureSocketsLayer.Services { public class SecureSocketsLayerService : ISecureSocketsLayerService { private readonly IWorkContextAccessor _workContextAccessor; private readonly ICacheManager _cacheManager; + private readonly ISignals _signals; - public SecureSocketsLayerService(IWorkContextAccessor workContextAccessor, ICacheManager cacheManager) { + public SecureSocketsLayerService( + IWorkContextAccessor workContextAccessor, + ICacheManager cacheManager, + ISignals signals) { _workContextAccessor = workContextAccessor; _cacheManager = cacheManager; + _signals = signals; } public bool ShouldBeSecure(string actionName, string controllerName, RouteValueDictionary routeValues) { @@ -186,6 +191,7 @@ namespace Orchard.SecureSocketsLayer.Services { private SslSettings GetSettings() { return _cacheManager.Get("SslSettings", ctx => { + ctx.Monitor(_signals.When(SslSettingsPart.CacheKey)); var settingsPart = _workContextAccessor.GetContext().CurrentSite.As(); return new SslSettings { Urls = settingsPart.Urls,