mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 11:44:58 +08:00
SSL module brought into the core distribution
This commit is contained in:
@@ -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:<value> /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<SslSettingsPart>();
|
||||
if (settings == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Urls)) {
|
||||
var comma = false;
|
||||
var urlList = new List<string>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<SslSettingsPart> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<SslSettingsPartRecord> repository) {
|
||||
T = NullLocalizer.Instance;
|
||||
Filters.Add(new ActivatingFilter<SslSettingsPart>("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"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<bool>("CustomEnabled")
|
||||
.Column<bool>("SecureEverything")
|
||||
.Column<string>("Urls", c => c.Unlimited())
|
||||
.Column<string>("SecureHostName")
|
||||
.Column<string>("InsecureHostName")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.SecureSocketsLayer.Models {
|
||||
public class SslSettingsPart : ContentPart<SslSettingsPartRecord> {
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -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
|
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{36B82383-D69E-4897-A24A-648BABDF80EC}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.SecureSocketsLayer</RootNamespace>
|
||||
<AssemblyName>Orchard.SecureSocketsLayer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<MvcBuildViews>false</MvcBuildViews>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<UpgradeBackupLocation />
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
<IISExpressSSLPort />
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Abstractions" />
|
||||
<Reference Include="System.Web.Routing" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Properties\AssemblyInfo.cs" />
|
||||
<Content Include="Module.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Commands\SecureSocketsLayersCommand.cs" />
|
||||
<Compile Include="Drivers\SslSettingsPartDriver.cs" />
|
||||
<Compile Include="Filters\SecureSocketsLayersFilter.cs" />
|
||||
<Compile Include="Handlers\SslSettingsPartHandler.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\SslSettingsPart.cs" />
|
||||
<Compile Include="Models\SslSettingsPartRecord.cs" />
|
||||
<Compile Include="Services\ISecureSocketsLayerService.cs" />
|
||||
<Compile Include="Services\SecuredSocketsLayerService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\Parts\SecureSocketsLayer.Settings.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Placement.info" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target> -->
|
||||
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
|
||||
<PropertyGroup>
|
||||
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
|
||||
</PropertyGroup>
|
||||
<!-- If this is an area child project, uncomment the following line:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
-->
|
||||
<!-- If this is an area parent project, uncomment the following lines:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
|
||||
-->
|
||||
</Target>
|
||||
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
|
||||
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
|
||||
</Target>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>45979</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
@@ -0,0 +1,3 @@
|
||||
<Placement>
|
||||
<Place Parts_SslSettings_Edit="Content:2"/>
|
||||
</Placement>
|
@@ -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")]
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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<SslSettingsPart>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
@model Orchard.SecureSocketsLayer.Models.SslSettingsPart
|
||||
|
||||
<fieldset>
|
||||
<legend>@T("SSL Settings")</legend>
|
||||
<div>
|
||||
@Html.EditorFor(m => m.SecureEverything)
|
||||
<label for="@Html.FieldIdFor(m => m.SecureEverything)" class="forcheckbox">@T("Force SSL on all pages")</label>
|
||||
@Html.ValidationMessage("SecureEverything", "*")
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
@Html.EditorFor(m => m.CustomEnabled)
|
||||
<label for="@Html.FieldIdFor(m => m.CustomEnabled)" class="forcheckbox">@T("Enable SSL on specific pages")</label>
|
||||
@Html.ValidationMessage("CustomEnabled", "*")
|
||||
</div>
|
||||
<div data-controllerid="@Html.FieldIdFor(m => m.CustomEnabled)">
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.Urls)">@T("Urls")</label>
|
||||
@Html.TextAreaFor(m => m.Urls, new { @class = "textMedium", rows = "5" } )
|
||||
@Html.ValidationMessage("Urls", "*")
|
||||
<span class="hint">@T("Provide a list of urls, one per line. Urls can contains wildcard matches using '*', or root identifier like '~/'")</span>
|
||||
<span class="hint">@T("Examples: http://mysite.com/mypage, ~/Profile/Edit, ~/Profile/*")</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.SecureHostName)">@T("Secure Host Name")</label>
|
||||
@Html.TextBoxFor(m => m.SecureHostName, new { @class = "textMedium" })
|
||||
<span class="hint">@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).")</span>
|
||||
</div>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.InsecureHostName)">@T("Insecure Host Name")</label>
|
||||
@Html.TextBoxFor(m => m.InsecureHostName, new { @class = "textMedium" })
|
||||
<span class="hint">@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).")</span>
|
||||
</div>
|
||||
</fieldset>
|
@@ -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}
|
||||
|
Reference in New Issue
Block a user