mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +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
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "Orchard.Web\Modules\Orchard.Azure\Orchard.Azure.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "Orchard.Web\Modules\Orchard.Azure\Orchard.Azure.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.SecureSocketsLayer", "Orchard.Web\Modules\Orchard.SecureSocketsLayer\Orchard.SecureSocketsLayer.csproj", "{36B82383-D69E-4897-A24A-648BABDF80EC}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
CodeCoverage|Any CPU = CodeCoverage|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{CBC7993C-57D8-4A6C-992C-19E849DFE71D}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -934,6 +946,7 @@ Global
|
|||||||
{6E444FF1-A47C-4CF6-BB3F-507C8EBD776D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
{6E444FF1-A47C-4CF6-BB3F-507C8EBD776D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||||
{8A9FDB57-342D-49C2-BAFC-D885AAE5CC7C} = {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}
|
{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}
|
{ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||||
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {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}
|
{6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||||
|
Reference in New Issue
Block a user