diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index acfe2486b..8b3915057 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -154,6 +154,7 @@ + diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs index 9e35a845a..601bbd69d 100644 --- a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs +++ b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Security.Cryptography; using System.Web; using System.Web.Mvc; using System.Web.Routing; @@ -26,20 +27,19 @@ using Orchard.Messaging.Events; using Orchard.Messaging.Services; using Orchard.Security; using Orchard.Security.Permissions; +using Orchard.Security.Providers; using Orchard.Tests.Stubs; using Orchard.UI.Notify; using Orchard.Users.Controllers; using Orchard.Users.Handlers; using Orchard.Users.Models; using Orchard.Users.Services; -using Orchard.Users.ViewModels; using Orchard.Settings; using Orchard.Core.Settings.Services; using Orchard.Tests.Messaging; using Orchard.Environment.Configuration; using Orchard.Core.Settings.Models; using Orchard.Core.Settings.Handlers; -using Orchard.Messaging.Models; using System.Collections.Specialized; namespace Orchard.Tests.Modules.Users.Controllers { @@ -74,11 +74,14 @@ namespace Orchard.Tests.Modules.Users.Controllers { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterInstance(new Mock().Object); builder.RegisterInstance(new Mock().Object); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" }); + + builder.RegisterType().As(); + builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled()); _authorizer = new Mock(); builder.RegisterInstance(_authorizer.Object); diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs index 6f6d395cb..ca2f20493 100644 --- a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs +++ b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs @@ -26,6 +26,7 @@ using Orchard.Messaging.Events; using Orchard.Messaging.Services; using Orchard.Security; using Orchard.Security.Permissions; +using Orchard.Security.Providers; using Orchard.Tests.Stubs; using Orchard.UI.Notify; using Orchard.Users.Controllers; @@ -69,7 +70,8 @@ namespace Orchard.Tests.Modules.Users.Controllers { builder.RegisterInstance(new Mock().Object); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" }); + builder.RegisterType().As(); + builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled()); _authorizer = new Mock(); builder.RegisterInstance(_authorizer.Object); diff --git a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs index efa354cc8..34b572dc7 100644 --- a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs +++ b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs @@ -1,5 +1,4 @@ using System; -using System.Web.Security; using System.Xml.Linq; using Autofac; using Moq; @@ -21,6 +20,7 @@ using Orchard.Environment.Extensions; using Orchard.Messaging.Events; using Orchard.Messaging.Services; using Orchard.Security; +using Orchard.Security.Providers; using Orchard.Tests.Stubs; using Orchard.Tests.Utility; using Orchard.Users.Handlers; @@ -96,7 +96,9 @@ namespace Orchard.Tests.Modules.Users.Services { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" }); + + builder.RegisterType().As(); + builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled()); _session = _sessionFactory.OpenSession(); builder.RegisterInstance(new TestSessionLocator(_session)).As(); @@ -121,25 +123,5 @@ namespace Orchard.Tests.Modules.Users.Services { Assert.That(username, Is.EqualTo("foo")); Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow)); } - - [Test] - public void NonceShouldNotBeUsedOnAnotherTenant() { - var user = _membershipService.CreateUser(new CreateUserParams("foo", "66554321", "foo@bar.com", "", "", true)); - var nonce = _userService.CreateNonce(user, new TimeSpan(1, 0, 0)); - - Assert.That(nonce, Is.Not.Empty); - - string username; - DateTime validateByUtc; - - _container.Resolve().Name = "Beta"; - - var result = _userService.DecryptNonce(nonce, out username, out validateByUtc); - - Assert.That(result, Is.False); - Assert.That(username, Is.EqualTo("foo")); - Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow)); - } - } } diff --git a/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs b/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs new file mode 100644 index 000000000..ff2a020be --- /dev/null +++ b/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs @@ -0,0 +1,27 @@ +using System; +using System.Security.Cryptography; +using Orchard.Environment.Configuration; +using Orchard.Utility.Extensions; + +namespace Orchard.Tests.Modules.Users { + public class ShellSettingsUtility { + public static ShellSettings CreateEncryptionEnabled() { + // generate random keys for encryption + var key = new byte[32]; + var iv = new byte[16]; + using ( var random = new RNGCryptoServiceProvider() ) { + random.GetBytes(key); + random.GetBytes(iv); + } + + return new ShellSettings { + Name = "Alpha", + RequestUrlHost = "wiki.example.com", + RequestUrlPrefix = "~/foo", + EncryptionAlgorithm = "AES", + EncryptionKey = key.ToHexString(), + EncryptionIV = iv.ToHexString() + }; + } + } +} diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/ShapeTemplateBindingStrategyTests.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/ShapeTemplateBindingStrategyTests.cs index ec16aa7cb..eb35adff9 100644 --- a/src/Orchard.Tests/DisplayManagement/Descriptors/ShapeTemplateBindingStrategyTests.cs +++ b/src/Orchard.Tests/DisplayManagement/Descriptors/ShapeTemplateBindingStrategyTests.cs @@ -5,12 +5,14 @@ using System.Linq; using Autofac; using Moq; using NUnit.Framework; +using Orchard.Caching; using Orchard.DisplayManagement.Descriptors; using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.VirtualPath; +using Orchard.Tests.Stubs; namespace Orchard.Tests.DisplayManagement.Descriptors { [TestFixture] @@ -21,12 +23,14 @@ namespace Orchard.Tests.DisplayManagement.Descriptors { private TestVirtualPathProvider _testVirtualPathProvider; - protected override void Register(Autofac.ContainerBuilder builder) { - _descriptor = new ShellDescriptor { }; + protected override void Register(ContainerBuilder builder) { + _descriptor = new ShellDescriptor(); _testViewEngine = new TestViewEngine(); _testVirtualPathProvider = new TestVirtualPathProvider(); builder.Register(ctx => _descriptor); + builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterInstance(_testViewEngine).As(); diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/StylesheetBindingStrategyTests.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/StylesheetBindingStrategyTests.cs new file mode 100644 index 000000000..fa10824a2 --- /dev/null +++ b/src/Orchard.Tests/DisplayManagement/Descriptors/StylesheetBindingStrategyTests.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Autofac; +using Moq; +using NUnit.Framework; +using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy; +using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; +using Orchard.Environment.Descriptor.Models; +using Orchard.Environment.Extensions; +using Orchard.Environment.Extensions.Models; +using Orchard.FileSystems.VirtualPath; + +namespace Orchard.Tests.DisplayManagement.Descriptors { + [TestFixture] + public class StylesheetBindingStrategyTests : ContainerTestBase { + private ShellDescriptor _descriptor; + private IList _features; + private TestViewEngine _testViewEngine; + private TestVirtualPathProvider _testVirtualPathProvider; + + + protected override void Register(Autofac.ContainerBuilder builder) { + _descriptor = new ShellDescriptor { }; + _testViewEngine = new TestViewEngine(); + _testVirtualPathProvider = new TestVirtualPathProvider(); + + builder.Register(ctx => _descriptor); + builder.RegisterType().As(); + builder.RegisterInstance(_testViewEngine).As(); + builder.RegisterInstance(_testVirtualPathProvider).As(); + + var extensionManager = new Mock(); + builder.Register(ctx => extensionManager); + builder.Register(ctx => extensionManager.Object); + } + + public class TestViewEngine : Dictionary, IShapeTemplateViewEngine { + public IEnumerable DetectTemplateFileNames(IEnumerable fileNames) { + return fileNames; + } + } + + public class TestVirtualPathProvider : IVirtualPathProvider { + public string Combine(params string[] paths) { + throw new NotImplementedException(); + } + + public string ToAppRelative(string virtualPath) { + throw new NotImplementedException(); + } + + public string MapPath(string virtualPath) { + throw new NotImplementedException(); + } + + public bool FileExists(string virtualPath) { + throw new NotImplementedException(); + } + + public Stream OpenFile(string virtualPath) { + throw new NotImplementedException(); + } + + public StreamWriter CreateText(string virtualPath) { + throw new NotImplementedException(); + } + + public Stream CreateFile(string virtualPath) { + throw new NotImplementedException(); + } + + public DateTime GetFileLastWriteTimeUtc(string virtualPath) { + throw new NotImplementedException(); + } + + public bool DirectoryExists(string virtualPath) { + throw new NotImplementedException(); + } + + public void CreateDirectory(string virtualPath) { + throw new NotImplementedException(); + } + + public string GetDirectoryName(string virtualPath) { + throw new NotImplementedException(); + } + + public IEnumerable ListFiles(string path) { + return new List {"~/Modules/Alpha/Styles/AlphaStyle.css"}; + } + + public IEnumerable ListDirectories(string path) { + throw new NotImplementedException(); + } + } + + protected override void Resolve(ILifetimeScope container) { + _features = new List(); + + container.Resolve>() + .Setup(em => em.AvailableFeatures()) + .Returns(_features); + } + + void AddFeature(string name, params string[] dependencies) { + var featureDescriptor = new FeatureDescriptor { + Id = name, + Dependencies = dependencies, + Extension = new ExtensionDescriptor { + Id = name, + Location = "~/Modules" + } + }; + featureDescriptor.Extension.Features = new[] { featureDescriptor }; + + _features.Add(featureDescriptor); + } + + void AddEnabledFeature(string name, params string[] dependencies) { + AddFeature(name, dependencies); + _descriptor.Features = _descriptor.Features.Concat(new[] { new ShellFeature { Name = name } }); + } + + [Test] + public void TemplateResolutionWorks() { + AddEnabledFeature("Alpha"); + + _testViewEngine.Add("~/Modules/Alpha/Styles/AlphaShape.css", null); + var strategy = _container.Resolve(); + + IList alterationBuilders = new List(); + var builder = new ShapeTableBuilder(alterationBuilders,null); + strategy.Discover(builder); + var alterations = alterationBuilders.Select(alterationBuilder=>alterationBuilder.Build()); + + Assert.That(alterations.Any(alteration => alteration.ShapeType == "Style")); + } + + } +} diff --git a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs index e50dd1567..6f1d02806 100644 --- a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs @@ -58,7 +58,7 @@ namespace Orchard.Tests.Environment.Configuration { _appDataFolder.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SqlCe\r\nDataConnectionString: something else"); IShellSettingsManager loader = new ShellSettingsManager(_appDataFolder, new Mock().Object); - var foo = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux" }; + var foo = new ShellSettings {Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux"}; Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1)); loader.SaveSettings(foo); @@ -69,5 +69,19 @@ namespace Orchard.Tests.Environment.Configuration { Assert.That(text, Is.StringContaining("Bar")); Assert.That(text, Is.StringContaining("Quux")); } + + [Test] + public void EncryptionSettingsAreStoredAndReadable() { + IShellSettingsManager loader = new ShellSettingsManager(_appDataFolder, new Mock().Object); + var foo = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux", EncryptionAlgorithm = "AES", EncryptionKey = "ABCDEFG", EncryptionIV= "HIJKL" }; + loader.SaveSettings(foo); + Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1)); + + var settings = loader.LoadSettings().First(); + + Assert.That(settings.EncryptionAlgorithm, Is.EqualTo("AES")); + Assert.That(settings.EncryptionKey, Is.EqualTo("ABCDEFG")); + Assert.That(settings.EncryptionIV, Is.EqualTo("HIJKL")); + } } } diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 5815c2241..0fcaf7102 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -212,6 +212,7 @@ + @@ -244,6 +245,7 @@ + diff --git a/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs b/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs new file mode 100644 index 000000000..37a9fbc45 --- /dev/null +++ b/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs @@ -0,0 +1,53 @@ +using System.Security.Cryptography; +using System.Text; +using Autofac; +using NUnit.Framework; +using Orchard.Environment.Configuration; +using Orchard.Security; +using Orchard.Security.Providers; +using Orchard.Utility.Extensions; + +namespace Orchard.Tests.Security { + [TestFixture] + public class DefaultEncryptionServiceTests { + private IContainer container; + + [SetUp] + public void Init() { + + var key = new byte[32]; + var iv = new byte[16]; + using ( var random = new RNGCryptoServiceProvider() ) { + random.GetBytes(key); + random.GetBytes(iv); + } + + var shellSettings = new ShellSettings { + Name = "Foo", + DataProvider = "Bar", + DataConnectionString = "Quux", + EncryptionAlgorithm = "AES", + EncryptionKey = key.ToHexString(), + EncryptionIV = iv.ToHexString() + }; + + var builder = new ContainerBuilder(); + builder.RegisterInstance(shellSettings); + builder.RegisterType().As(); + container = builder.Build(); + } + + [Test] + public void CanEncodeAndDecodeData() { + var encryptionService = container.Resolve(); + + var secretData = Encoding.Unicode.GetBytes("this is secret data"); + var encrypted = encryptionService.Encode(secretData); + var decrypted = encryptionService.Decode(encrypted); + + Assert.That(encrypted, Is.Not.EqualTo(decrypted)); + Assert.That(decrypted, Is.EqualTo(secretData)); + } + + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Shapes/CoreShapes.cs b/src/Orchard.Web/Core/Shapes/CoreShapes.cs index 72efafe06..f292be49b 100644 --- a/src/Orchard.Web/Core/Shapes/CoreShapes.cs +++ b/src/Orchard.Web/Core/Shapes/CoreShapes.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; @@ -8,6 +9,8 @@ using System.Web.Mvc; using System.Web.Mvc.Html; using Orchard.DisplayManagement; using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy; +using Orchard.Mvc; using Orchard.Settings; using Orchard.UI; using Orchard.UI.Resources; @@ -19,11 +22,20 @@ using Orchard.Utility.Extensions; namespace Orchard.Core.Shapes { public class CoreShapes : IShapeTableProvider { private readonly IWorkContextAccessor _workContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; - public CoreShapes(IWorkContextAccessor workContextAccessor) { + public CoreShapes(IWorkContextAccessor workContextAccessor, IHttpContextAccessor httpContextAccessor) { // needed to get CurrentSite. // note that injecting ISiteService here causes a stack overflow in AutoFac! _workContextAccessor = workContextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + // not injected the usual way because this component is a 'static' dependency and RM is per-request + private IResourceManager ResourceManager { + get { + return _workContextAccessor.GetContext(_httpContextAccessor.Current()).Resolve(); + } } public void Discover(ShapeTableBuilder builder) { @@ -82,6 +94,26 @@ namespace Orchard.Core.Shapes { list.ItemClasses = new List(); list.ItemAttributes = new Dictionary(); }); + + builder.Describe("Style") + .OnDisplaying(displaying => { + var resource = displaying.Shape; + string url = resource.Url; + string fileName = StylesheetBindingStrategy.GetAlternateShapeNameFromFileName(url); + if (!string.IsNullOrEmpty(fileName)) { + resource.Metadata.Alternates.Add("Style__" + fileName); + } + }); + + builder.Describe("Resource") + .OnDisplaying(displaying => { + var resource = displaying.Shape; + string url = resource.Url; + string fileName = StylesheetBindingStrategy.GetAlternateShapeNameFromFileName(url); + if (!string.IsNullOrEmpty(fileName)) { + resource.Metadata.Alternates.Add("Resource__" + fileName); + } + }); } @@ -159,51 +191,58 @@ namespace Orchard.Core.Shapes { #endregion [Shape] - public void HeadScripts(HtmlHelper Html, IResourceManager ResourceManager) { - WriteResources(Html, _workContextAccessor.GetContext(Html.ViewContext).CurrentSite, - ResourceManager, "script", ResourceLocation.Head, null); - WriteLiteralScripts(Html, ResourceManager.GetRegisteredHeadScripts()); + public void HeadScripts(dynamic Display, TextWriter Output) { + WriteResources(Display, Output, "script", ResourceLocation.Head, null); + WriteLiteralScripts(Output, ResourceManager.GetRegisteredHeadScripts()); } [Shape] - public void FootScripts(HtmlHelper Html, IResourceManager ResourceManager) { - WriteResources(Html, _workContextAccessor.GetContext(Html.ViewContext).CurrentSite, - ResourceManager, "script", null, ResourceLocation.Head); - WriteLiteralScripts(Html, ResourceManager.GetRegisteredFootScripts()); + public void FootScripts(dynamic Display, TextWriter Output) { + WriteResources(Display, Output, "script", null, ResourceLocation.Head); + WriteLiteralScripts(Output, ResourceManager.GetRegisteredFootScripts()); } [Shape] - public void Metas(HtmlHelper Html, IResourceManager ResourceManager) { + public void Metas(TextWriter Output) { foreach (var meta in ResourceManager.GetRegisteredMetas()) { - Html.ViewContext.Writer.WriteLine(meta.GetTag()); + Output.WriteLine(meta.GetTag()); } } [Shape] - public void HeadLinks(HtmlHelper Html, IResourceManager ResourceManager) { + public void HeadLinks(TextWriter Output) { foreach (var link in ResourceManager.GetRegisteredLinks()) { - Html.ViewContext.Writer.WriteLine(link.GetTag()); + Output.WriteLine(link.GetTag()); } } [Shape] - public void StylesheetLinks(HtmlHelper Html, IResourceManager ResourceManager) { - WriteResources(Html, _workContextAccessor.GetContext(Html.ViewContext).CurrentSite, - ResourceManager, "stylesheet", null, null); + public void StylesheetLinks(dynamic Display, TextWriter Output) { + WriteResources(Display, Output, "stylesheet", null, null); } - private static void WriteLiteralScripts(HtmlHelper html, IEnumerable scripts) { + [Shape] + public void Style(TextWriter Output, ResourceDefinition Resource, string Url, string Condition) { + UI.Resources.ResourceManager.WriteResource(Output, Resource, Url, Condition); + } + + [Shape] + public void Resource(TextWriter Output, ResourceDefinition Resource, string Url, string Condition) { + UI.Resources.ResourceManager.WriteResource(Output, Resource, Url, Condition); + } + + private static void WriteLiteralScripts(TextWriter output, IEnumerable scripts) { if (scripts == null) { return; } - var writer = html.ViewContext.Writer; foreach (string script in scripts) { - writer.WriteLine(script); + output.WriteLine(script); } } - private static void WriteResources(HtmlHelper html, ISite site, IResourceManager rm, string resourceType, ResourceLocation? includeLocation, ResourceLocation? excludeLocation) { + private void WriteResources(dynamic Display, TextWriter Output, string resourceType, ResourceLocation? includeLocation, ResourceLocation? excludeLocation) { bool debugMode; + var site = _workContextAccessor.GetContext(_httpContextAccessor.Current()).CurrentSite; switch (site.ResourceDebugMode) { case ResourceDebugMode.Enabled: debugMode = true; @@ -213,26 +252,29 @@ namespace Orchard.Core.Shapes { break; default: Debug.Assert(site.ResourceDebugMode == ResourceDebugMode.FromAppSetting, "Unknown ResourceDebugMode value."); - debugMode = html.ViewContext.HttpContext.IsDebuggingEnabled; + debugMode = _httpContextAccessor.Current().IsDebuggingEnabled; break; } var defaultSettings = new RequireSettings { DebugMode = debugMode, Culture = CultureInfo.CurrentUICulture.Name, }; - var requiredResources = rm.BuildRequiredResources(resourceType); - var appPath = html.ViewContext.HttpContext.Request.ApplicationPath; + var requiredResources = ResourceManager.BuildRequiredResources(resourceType); + var appPath = _httpContextAccessor.Current().Request.ApplicationPath; foreach (var context in requiredResources.Where(r => (includeLocation.HasValue ? r.Settings.Location == includeLocation.Value : true) && (excludeLocation.HasValue ? r.Settings.Location != excludeLocation.Value : true))) { + + var path = context.GetResourceUrl(defaultSettings, appPath); var condition = context.Settings.Condition; - if (!string.IsNullOrEmpty(condition)) { - html.ViewContext.Writer.WriteLine(""); + else { + result = Display.Resource(Url: path, Condition: condition, Resource: context.Resource); } + Output.Write(result); } } diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs index d00d99d7b..6fdb834a1 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs @@ -6,10 +6,8 @@ using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Models; -using Orchard.ContentTypes.Extensions; using Orchard.ContentTypes.ViewModels; using Orchard.Core.Contents.Extensions; -using Orchard.Core.Contents.Settings; using Orchard.Localization; namespace Orchard.ContentTypes.Services { diff --git a/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs index dda95b909..c7a2e1e20 100644 --- a/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs @@ -1,14 +1,27 @@ -using JetBrains.Annotations; +using System; +using System.Text; +using JetBrains.Annotations; using Orchard.Email.Models; using Orchard.Data; using Orchard.ContentManagement.Handlers; +using Orchard.Security; namespace Orchard.Email.Handlers { [UsedImplicitly] public class SmtpSettingsPartHandler : ContentHandler { - public SmtpSettingsPartHandler(IRepository repository) { + private readonly IEncryptionService _encryptionService; + + public SmtpSettingsPartHandler(IRepository repository, IEncryptionService encryptionService) { + _encryptionService = encryptionService; Filters.Add(new ActivatingFilter("Site")); Filters.Add(StorageFilter.For(repository)); + + OnLoaded(LazyLoadHandlers); + } + + void LazyLoadHandlers(LoadContentContext context, SmtpSettingsPart part) { + part.PasswordField.Getter(() => String.IsNullOrWhiteSpace(part.Record.Password) ? String.Empty : Encoding.UTF8.GetString(_encryptionService.Decode(Convert.FromBase64String(part.Record.Password)))); + part.PasswordField.Setter(value => part.Record.Password = String.IsNullOrWhiteSpace(value) ? String.Empty : Convert.ToBase64String(_encryptionService.Encode(Encoding.UTF8.GetBytes(value)))); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs index 964d022df..e371459b4 100644 --- a/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs @@ -1,14 +1,13 @@ -using System.Text; -using System.Web.Security; -using Orchard.ContentManagement; +using Orchard.ContentManagement; using System; +using Orchard.ContentManagement.Utilities; namespace Orchard.Email.Models { public class SmtpSettingsPart : ContentPart { - public bool IsValid() { - return !String.IsNullOrWhiteSpace(Record.Host) - && Record.Port > 0 - && !String.IsNullOrWhiteSpace(Record.Address); + private readonly ComputedField _password = new ComputedField(); + + public ComputedField PasswordField { + get { return _password; } } public string Address { @@ -42,8 +41,14 @@ namespace Orchard.Email.Models { } public string Password { - get { return String.IsNullOrWhiteSpace(Record.Password) ? String.Empty : Encoding.UTF8.GetString(MachineKey.Decode(Record.Password, MachineKeyProtection.All)); ; } - set { Record.Password = String.IsNullOrWhiteSpace(value) ? String.Empty : MachineKey.Encode(Encoding.UTF8.GetBytes(value), MachineKeyProtection.All); } + get { return _password.Value; } + set { _password.Value = value; } + } + + public bool IsValid() { + return !String.IsNullOrWhiteSpace(Record.Host) + && Record.Port > 0 + && !String.IsNullOrWhiteSpace(Record.Address); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index 4c68417fc..2d26c4bbb 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Security.Cryptography; using System.Web; using Orchard.ContentManagement; using Orchard.ContentManagement.MetaData; @@ -29,6 +30,7 @@ using Orchard.Settings; using Orchard.Environment.State; using Orchard.Data.Migration; using Orchard.Themes.Services; +using Orchard.Utility.Extensions; using Orchard.Widgets.Models; using Orchard.Widgets; @@ -118,6 +120,21 @@ namespace Orchard.Setup.Services { shellSettings.DataTablePrefix = context.DatabaseTablePrefix; } + #region Encryption Settings + + // generate random keys for encryption + var key = new byte[32]; + var iv = new byte[16]; + using ( var random = new RNGCryptoServiceProvider() ) { + random.GetBytes(key); + random.GetBytes(iv); + } + + shellSettings.EncryptionAlgorithm = "AES"; + shellSettings.EncryptionKey = key.ToHexString(); + shellSettings.EncryptionIV = iv.ToHexString(); + #endregion + var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) }; diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs index a1ed2f3fc..712936452 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs @@ -25,13 +25,15 @@ namespace Orchard.Users.Services { private readonly IClock _clock; private readonly IMessageManager _messageManager; private readonly ShellSettings _shellSettings; + private readonly IEncryptionService _encryptionService; - public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager, ShellSettings shellSettings) { + public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager, ShellSettings shellSettings, IEncryptionService encryptionService) { _contentManager = contentManager; _membershipService = membershipService; _clock = clock; _messageManager = messageManager; _shellSettings = shellSettings; + _encryptionService = encryptionService; Logger = NullLogger.Instance; } @@ -66,24 +68,22 @@ namespace Orchard.Users.Services { } public string CreateNonce(IUser user, TimeSpan delay) { - // the tenant's name is added to the token to prevent cross-tenant requests - var challengeToken = new XElement("n", new XAttribute("s", _shellSettings.Name), new XAttribute("un", user.UserName), new XAttribute("utc", _clock.UtcNow.ToUniversalTime().Add(delay).ToString(CultureInfo.InvariantCulture))).ToString(); - var data = Encoding.Unicode.GetBytes(challengeToken); - return MachineKey.Encode(data, MachineKeyProtection.All); + var challengeToken = new XElement("n", new XAttribute("un", user.UserName), new XAttribute("utc", _clock.UtcNow.ToUniversalTime().Add(delay).ToString(CultureInfo.InvariantCulture))).ToString(); + var data = Encoding.UTF8.GetBytes(challengeToken); + return Convert.ToBase64String(_encryptionService.Encode(data)); } - public bool DecryptNonce(string challengeToken, out string username, out DateTime validateByUtc) { + public bool DecryptNonce(string nonce, out string username, out DateTime validateByUtc) { username = null; validateByUtc = _clock.UtcNow; try { - var data = MachineKey.Decode(challengeToken, MachineKeyProtection.All); - var xml = Encoding.Unicode.GetString(data); + var data = _encryptionService.Decode(Convert.FromBase64String(nonce)); + var xml = Encoding.UTF8.GetString(data); var element = XElement.Parse(xml); - var tenant = element.Attribute("s").Value; username = element.Attribute("un").Value; validateByUtc = DateTime.Parse(element.Attribute("utc").Value, CultureInfo.InvariantCulture); - return String.Equals(_shellSettings.Name, tenant, StringComparison.Ordinal) && _clock.UtcNow <= validateByUtc; + return _clock.UtcNow <= validateByUtc; } catch { return false; diff --git a/src/Orchard/ContentManagement/Utilities/ComputedField.cs b/src/Orchard/ContentManagement/Utilities/ComputedField.cs new file mode 100644 index 000000000..3bbd33b7a --- /dev/null +++ b/src/Orchard/ContentManagement/Utilities/ComputedField.cs @@ -0,0 +1,29 @@ +using System; + +namespace Orchard.ContentManagement.Utilities { + public class ComputedField { + private Func _getter; + private Action _setter; + + public T Value { + get { return GetValue(); } + set { SetValue(value); } + } + + public void Getter(Func loader) { + _getter = loader; + } + + public void Setter(Action setter) { + _setter = setter; + } + + private T GetValue() { + return _getter(); + } + + private void SetValue(T value) { + _setter(value); + } + } +} diff --git a/src/Orchard/Data/SessionFactoryHolder.cs b/src/Orchard/Data/SessionFactoryHolder.cs index f7ba7389a..bc39d6e2f 100644 --- a/src/Orchard/Data/SessionFactoryHolder.cs +++ b/src/Orchard/Data/SessionFactoryHolder.cs @@ -1,9 +1,5 @@ -using System; -using System.IO; -using System.Xml.Serialization; -using NHibernate; +using NHibernate; using NHibernate.Cfg; -using Orchard.Data; using Orchard.Data.Providers; using Orchard.Environment; using Orchard.Environment.Configuration; diff --git a/src/Orchard/DisplayManagement/Descriptors/ResourceBindingStrategy/StylesheetBindingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ResourceBindingStrategy/StylesheetBindingStrategy.cs new file mode 100644 index 000000000..910364b13 --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ResourceBindingStrategy/StylesheetBindingStrategy.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Orchard.Environment.Descriptor.Models; +using Orchard.Environment.Extensions; +using Orchard.Environment.Extensions.Models; +using Orchard.FileSystems.VirtualPath; +using Orchard.UI.Resources; + +namespace Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy { + // discovers .css files and turns them into Style__ shapes. + public class StylesheetBindingStrategy : IShapeTableProvider { + private readonly IExtensionManager _extensionManager; + private readonly ShellDescriptor _shellDescriptor; + private readonly IVirtualPathProvider _virtualPathProvider; + private static readonly Regex _safeName = new Regex(@"[/:?#\[\]@!&'()*+,;=\s\""<>\.\-_]+", RegexOptions.Compiled); + + public StylesheetBindingStrategy(IExtensionManager extensionManager, ShellDescriptor shellDescriptor, IVirtualPathProvider virtualPathProvider) { + _extensionManager = extensionManager; + _shellDescriptor = shellDescriptor; + _virtualPathProvider = virtualPathProvider; + } + + private static string SafeName(string name) { + if (string.IsNullOrWhiteSpace(name)) + return String.Empty; + return _safeName.Replace(name, String.Empty); + } + + public static string GetAlternateShapeNameFromFileName(string fileName) { + if (fileName == null) { + throw new ArgumentNullException("fileName"); + } + string shapeName; + if (Uri.IsWellFormedUriString(fileName, UriKind.Absolute)) { + var uri = new Uri(fileName); + shapeName = uri.Authority + "$" + uri.AbsolutePath + "$" + uri.Query; + } + else { + shapeName = Path.GetFileNameWithoutExtension(fileName); + } + return SafeName(shapeName); + } + + private static IEnumerable Once(IEnumerable featureDescriptors) { + var once = new ConcurrentDictionary(); + return featureDescriptors.Select(fd => fd.Extension).Where(ed => once.TryAdd(ed.Id, null)).ToList(); + } + + public void Discover(ShapeTableBuilder builder) { + var availableFeatures = _extensionManager.AvailableFeatures(); + var activeFeatures = availableFeatures.Where(FeatureIsEnabled); + var activeExtensions = Once(activeFeatures); + + var hits = activeExtensions.SelectMany(extensionDescriptor => { + var basePath = Path.Combine(extensionDescriptor.Location, extensionDescriptor.Id).Replace(Path.DirectorySeparatorChar, '/'); + var virtualPath = Path.Combine(basePath, "Styles").Replace(Path.DirectorySeparatorChar, '/'); + var shapes = _virtualPathProvider.ListFiles(virtualPath) + .Select(Path.GetFileName) + .Where(fileName => string.Equals(Path.GetExtension(fileName), ".css", System.StringComparison.OrdinalIgnoreCase)) + .Select(cssFileName => new { + fileName = Path.GetFileNameWithoutExtension(cssFileName), + fileVirtualPath = Path.Combine(virtualPath, cssFileName).Replace(Path.DirectorySeparatorChar, '/'), + shapeType = "Style__" + GetAlternateShapeNameFromFileName(cssFileName), + extensionDescriptor + }); + return shapes; + }); + + foreach (var iter in hits) { + var hit = iter; + var featureDescriptors = hit.extensionDescriptor.Features.Where(fd => fd.Id == hit.extensionDescriptor.Id); + foreach (var featureDescriptor in featureDescriptors) { + builder.Describe(iter.shapeType) + .From(new Feature {Descriptor = featureDescriptor}) + .BoundAs( + hit.fileVirtualPath, + shapeDescriptor => displayContext => { + var shape = ((dynamic) displayContext.Value); + var output = displayContext.ViewContext.Writer; + ResourceDefinition resource = shape.Resource; + string condition = shape.Condition; + ResourceManager.WriteResource(output, resource, hit.fileVirtualPath, condition); + return null; + }); + } + } + } + + private bool FeatureIsEnabled(FeatureDescriptor fd) { + return (DefaultExtensionTypes.IsTheme(fd.Extension.ExtensionType) && (fd.Id == "TheAdmin" || fd.Id == "SafeMode")) || + _shellDescriptor.Features.Any(sf => sf.Name == fd.Id); + } + } +} diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/ShapeTemplateBindingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/ShapeTemplateBindingStrategy.cs index 921ffc803..f038ed61e 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/ShapeTemplateBindingStrategy.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/ShapeTemplateBindingStrategy.cs @@ -1,10 +1,12 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Html; +using Orchard.Caching; using Orchard.DisplayManagement.Implementation; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.Extensions; @@ -15,6 +17,8 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy { public class ShapeTemplateBindingStrategy : IShapeTableProvider { private readonly ShellDescriptor _shellDescriptor; private readonly IExtensionManager _extensionManager; + private readonly ICacheManager _cacheManager; + private readonly IVirtualPathMonitor _virtualPathMonitor; private readonly IVirtualPathProvider _virtualPathProvider; private readonly IEnumerable _harvesters; private readonly IEnumerable _shapeTemplateViewEngines; @@ -24,11 +28,15 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy { IEnumerable harvesters, ShellDescriptor shellDescriptor, IExtensionManager extensionManager, + ICacheManager cacheManager, + IVirtualPathMonitor virtualPathMonitor, IVirtualPathProvider virtualPathProvider, IEnumerable shapeTemplateViewEngines) { _harvesters = harvesters; _shellDescriptor = shellDescriptor; _extensionManager = extensionManager; + _cacheManager = cacheManager; + _virtualPathMonitor = virtualPathMonitor; _virtualPathProvider = virtualPathProvider; _shapeTemplateViewEngines = shapeTemplateViewEngines; } @@ -49,13 +57,21 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy { var pathContexts = harvesterInfos.SelectMany(harvesterInfo => harvesterInfo.subPaths.Select(subPath => { var basePath = Path.Combine(extensionDescriptor.Location, extensionDescriptor.Id).Replace(Path.DirectorySeparatorChar, '/'); var virtualPath = Path.Combine(basePath, subPath).Replace(Path.DirectorySeparatorChar, '/'); - var fileNames = _virtualPathProvider.ListFiles(virtualPath).Select(Path.GetFileName); + var fileNames = _cacheManager.Get(virtualPath, ctx => { + ctx.Monitor(_virtualPathMonitor.WhenPathChanges(virtualPath)); + return _virtualPathProvider.ListFiles(virtualPath).Select(Path.GetFileName); + }); return new { harvesterInfo.harvester, basePath, subPath, virtualPath, fileNames }; })); var fileContexts = pathContexts.SelectMany(pathContext => _shapeTemplateViewEngines.SelectMany(ve => { var fileNames = ve.DetectTemplateFileNames(pathContext.fileNames); - return fileNames.Select(fileName => new { fileName = Path.GetFileNameWithoutExtension(fileName), fileVirtualPath = Path.Combine(pathContext.virtualPath, fileName).Replace(Path.DirectorySeparatorChar, '/'), pathContext }); + return fileNames.Select( + fileName => new { + fileName = Path.GetFileNameWithoutExtension(fileName), + fileVirtualPath = Path.Combine(pathContext.virtualPath, fileName).Replace(Path.DirectorySeparatorChar, '/'), + pathContext + }); })); var shapeContexts = fileContexts.SelectMany(fileContext => { diff --git a/src/Orchard/Environment/Configuration/ShellSettings.cs b/src/Orchard/Environment/Configuration/ShellSettings.cs index c6717938f..65795783f 100644 --- a/src/Orchard/Environment/Configuration/ShellSettings.cs +++ b/src/Orchard/Environment/Configuration/ShellSettings.cs @@ -16,6 +16,9 @@ DataTablePrefix = settings.DataTablePrefix; RequestUrlHost = settings.RequestUrlHost; RequestUrlPrefix = settings.RequestUrlPrefix; + EncryptionAlgorithm = settings.EncryptionAlgorithm; + EncryptionKey = settings.EncryptionKey; + EncryptionIV = settings.EncryptionIV; State = settings.State; } @@ -28,6 +31,10 @@ public string RequestUrlHost { get; set; } public string RequestUrlPrefix { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionKey { get; set; } + public string EncryptionIV { get; set; } + public TenantState State { get; set; } } } diff --git a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs index 9e819b36e..6e6d24044 100644 --- a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs +++ b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs @@ -84,6 +84,15 @@ namespace Orchard.Environment.Configuration { case "RequestUrlPrefix": shellSettings.RequestUrlPrefix = settingFields[1]; break; + case "EncryptionAlgorithm": + shellSettings.EncryptionAlgorithm = settingFields[1]; + break; + case "EncryptionKey": + shellSettings.EncryptionKey = settingFields[1]; + break; + case "EncryptionIV": + shellSettings.EncryptionIV = settingFields[1]; + break; } } } @@ -94,14 +103,18 @@ namespace Orchard.Environment.Configuration { if (settings == null) return ""; - return string.Format("Name: {0}\r\nDataProvider: {1}\r\nDataConnectionString: {2}\r\nDataPrefix: {3}\r\nRequestUrlHost: {4}\r\nRequestUrlPrefix: {5}\r\nState: {6}\r\n", + return string.Format("Name: {0}\r\nDataProvider: {1}\r\nDataConnectionString: {2}\r\nDataPrefix: {3}\r\nRequestUrlHost: {4}\r\nRequestUrlPrefix: {5}\r\nState: {6}\r\nEncryptionAlgorithm: {7}\r\nEncryptionKey: {8}\r\nEncryptionIV: {9}\r\n", settings.Name, settings.DataProvider, settings.DataConnectionString ?? "null", settings.DataTablePrefix ?? "null", settings.RequestUrlHost ?? "null", settings.RequestUrlPrefix ?? "null", - settings.State != null ? settings.State.ToString() : String.Empty); + settings.State != null ? settings.State.ToString() : String.Empty, + settings.EncryptionAlgorithm ?? "null", + settings.EncryptionKey ?? "null", + settings.EncryptionIV ?? "null" + ); } } } diff --git a/src/Orchard/FileSystems/VirtualPath/DefaultVirtualPathMonitor.cs b/src/Orchard/FileSystems/VirtualPath/DefaultVirtualPathMonitor.cs index 12fa9ec42..486cc4153 100644 --- a/src/Orchard/FileSystems/VirtualPath/DefaultVirtualPathMonitor.cs +++ b/src/Orchard/FileSystems/VirtualPath/DefaultVirtualPathMonitor.cs @@ -11,16 +11,21 @@ namespace Orchard.FileSystems.VirtualPath { private readonly string _prefix = Guid.NewGuid().ToString("n"); private readonly IDictionary> _tokens = new Dictionary>(); private readonly IClock _clock; + private readonly IVirtualPathProvider _virtualPathProvider; - public DefaultVirtualPathMonitor(IClock clock) { + public DefaultVirtualPathMonitor(IClock clock, IVirtualPathProvider virtualPathProvider) { _clock = clock; + _virtualPathProvider = virtualPathProvider; _thunk = new Thunk(this); } public IVolatileToken WhenPathChanges(string virtualPath) { // Fix this to monitor first existing parent directory. - var token = BindToken(virtualPath); - BindSignal(virtualPath); + IVolatileToken token = new Token(virtualPath); + if (_virtualPathProvider.DirectoryExists(virtualPath)) { + token = BindToken(virtualPath); + BindSignal(virtualPath); + } return token; } @@ -108,6 +113,5 @@ namespace Orchard.FileSystems.VirtualPath { provider.Signal(key, value, reason); } } - } } \ No newline at end of file diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 40d72d553..a8350ed09 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -142,6 +142,8 @@ + + @@ -170,7 +172,9 @@ + + diff --git a/src/Orchard/Security/IEncryptionService.cs b/src/Orchard/Security/IEncryptionService.cs new file mode 100644 index 000000000..e2d0471ce --- /dev/null +++ b/src/Orchard/Security/IEncryptionService.cs @@ -0,0 +1,20 @@ +namespace Orchard.Security { + /// + /// Provides encryption services adapted to securing tenant level information + /// + public interface IEncryptionService : ISingletonDependency { + /// + /// Decodes data that has been encrypted. + /// + /// The encrypted data to decrypt. + /// A Byte[] array that represents the decrypted data. + byte[] Decode(byte[] encodedData); + + /// + /// Encrypts data. + /// + /// The data to encrypt. + /// The encrypted value. + byte[] Encode(byte[] data); + } +} diff --git a/src/Orchard/Security/Providers/DefaultEncryptionService.cs b/src/Orchard/Security/Providers/DefaultEncryptionService.cs new file mode 100644 index 000000000..9620f4157 --- /dev/null +++ b/src/Orchard/Security/Providers/DefaultEncryptionService.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using Orchard.Environment.Configuration; +using Orchard.Utility.Extensions; + +namespace Orchard.Security.Providers { + public class DefaultEncryptionService : IEncryptionService { + private readonly ShellSettings _shellSettings; + private const int SaltSize = 16; + + public DefaultEncryptionService(ShellSettings shellSettings ) { + _shellSettings = shellSettings; + } + + public byte[] Decode(byte[] encodedData) { + using ( var ms = new MemoryStream() ) { + using (var algorithm = CreateAlgorithm()) { + using ( var cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write) ) { + cs.Write(encodedData, 0, encodedData.Length); + cs.FlushFinalBlock(); + } + + // remove the salt part + return ms.ToArray().Skip(SaltSize).ToArray(); + } + } + } + + public byte[] Encode(byte[] data) { + var salt = new byte[SaltSize]; + + // generate a random salt to happend to encoded data + using ( var random = new RNGCryptoServiceProvider() ) { + random.GetBytes(salt); + } + using ( var ms = new MemoryStream() ) { + using (var algorithm = CreateAlgorithm()) { + using ( var cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write) ) { + // append the salt to the data and encrypt + var salted = salt.Concat(data).ToArray(); + cs.Write(salted, 0, salted.Length); + cs.FlushFinalBlock(); + } + + return ms.ToArray(); + } + } + } + + private SymmetricAlgorithm CreateAlgorithm() { + var encryptionAlgorithm = SymmetricAlgorithm.Create(_shellSettings.EncryptionAlgorithm); + + encryptionAlgorithm.Key = _shellSettings.EncryptionKey.ToByteArray(); + encryptionAlgorithm.IV = _shellSettings.EncryptionIV.ToByteArray(); + + return encryptionAlgorithm; + + } + } +} diff --git a/src/Orchard/UI/Resources/ResourceFilter.cs b/src/Orchard/UI/Resources/ResourceFilter.cs index 990184f1b..fa291fe08 100644 --- a/src/Orchard/UI/Resources/ResourceFilter.cs +++ b/src/Orchard/UI/Resources/ResourceFilter.cs @@ -4,20 +4,16 @@ using Orchard.Mvc.Filters; namespace Orchard.UI.Resources { public class ResourceFilter : FilterProvider, IResultFilter { - private readonly IResourceManager _resourceManager; private readonly IWorkContextAccessor _workContextAccessor; private readonly dynamic _shapeFactory; public ResourceFilter( - IResourceManager resourceManager, IWorkContextAccessor workContextAccessor, IShapeFactory shapeFactory) { - _resourceManager = resourceManager; _workContextAccessor = workContextAccessor; _shapeFactory = shapeFactory; } - public void OnResultExecuting(ResultExecutingContext filterContext) { // should only run on a full view rendering result if (!(filterContext.Result is ViewResult)) @@ -26,11 +22,11 @@ namespace Orchard.UI.Resources { var ctx = _workContextAccessor.GetContext(); var head = ctx.Layout.Head; var tail = ctx.Layout.Tail; - head.Add(_shapeFactory.Metas().ResourceManager(_resourceManager)); - head.Add(_shapeFactory.HeadLinks().ResourceManager(_resourceManager)); - head.Add(_shapeFactory.StylesheetLinks().ResourceManager(_resourceManager)); - head.Add(_shapeFactory.HeadScripts().ResourceManager(_resourceManager)); - tail.Add(_shapeFactory.FootScripts().ResourceManager(_resourceManager)); + head.Add(_shapeFactory.Metas()); + head.Add(_shapeFactory.HeadLinks()); + head.Add(_shapeFactory.StylesheetLinks()); + head.Add(_shapeFactory.HeadScripts()); + tail.Add(_shapeFactory.FootScripts()); } public void OnResultExecuted(ResultExecutedContext filterContext) { diff --git a/src/Orchard/UI/Resources/ResourceManager.cs b/src/Orchard/UI/Resources/ResourceManager.cs index e5f36502b..429332006 100644 --- a/src/Orchard/UI/Resources/ResourceManager.cs +++ b/src/Orchard/UI/Resources/ResourceManager.cs @@ -3,8 +3,10 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; +using System.IO; using System.Linq; using System.Web; +using System.Web.Mvc; using Autofac.Features.Metadata; using Orchard.Environment.Extensions.Models; @@ -40,6 +42,30 @@ namespace Orchard.UI.Resources { return resourcePath; } + private static TagBuilder GetTagBuilder(ResourceDefinition resource, string url) { + var tagBuilder = new TagBuilder(resource.TagName); + tagBuilder.MergeAttributes(resource.TagBuilder.Attributes); + if (!String.IsNullOrEmpty(resource.FilePathAttributeName)) { + if (!String.IsNullOrEmpty(url)) { + if (VirtualPathUtility.IsAppRelative(url)) { + url = VirtualPathUtility.ToAbsolute(url); + } + tagBuilder.MergeAttribute(resource.FilePathAttributeName, url, true); + } + } + return tagBuilder; + } + + public static void WriteResource(TextWriter writer, ResourceDefinition resource, string url, string condition) { + if (!string.IsNullOrEmpty(condition)) { + writer.WriteLine(""); + } + } + public ResourceManager(IEnumerable> resourceProviders) { _providers = resourceProviders; } diff --git a/src/Orchard/UI/Resources/ResourceRequiredContext.cs b/src/Orchard/UI/Resources/ResourceRequiredContext.cs index 1cbe31f31..8475613ad 100644 --- a/src/Orchard/UI/Resources/ResourceRequiredContext.cs +++ b/src/Orchard/UI/Resources/ResourceRequiredContext.cs @@ -6,11 +6,15 @@ namespace Orchard.UI.Resources { public ResourceDefinition Resource { get; set; } public RequireSettings Settings { get; set; } + public string GetResourceUrl(RequireSettings baseSettings, string appPath) { + return Resource.ResolveUrl(baseSettings == null ? Settings : baseSettings.Combine(Settings), appPath); + } + public TagBuilder GetTagBuilder(RequireSettings baseSettings, string appPath) { var tagBuilder = new TagBuilder(Resource.TagName); tagBuilder.MergeAttributes(Resource.TagBuilder.Attributes); if (!String.IsNullOrEmpty(Resource.FilePathAttributeName)) { - var resolvedUrl = Resource.ResolveUrl(baseSettings == null ? Settings : baseSettings.Combine(Settings), appPath); + var resolvedUrl = GetResourceUrl(baseSettings, appPath); if (!String.IsNullOrEmpty(resolvedUrl)) { tagBuilder.MergeAttribute(Resource.FilePathAttributeName, resolvedUrl, true); } diff --git a/src/Orchard/Utility/Extensions/StringExtensions.cs b/src/Orchard/Utility/Extensions/StringExtensions.cs index bc1225b1e..3a11a1c65 100644 --- a/src/Orchard/Utility/Extensions/StringExtensions.cs +++ b/src/Orchard/Utility/Extensions/StringExtensions.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Text.RegularExpressions; using Orchard.Localization; @@ -55,5 +56,16 @@ namespace Orchard.Utility.Extensions { ? "" : Regex.Replace(text, @"(\r?\n)", replacement, RegexOptions.Singleline); } + + public static string ToHexString(this byte[] bytes) { + return BitConverter.ToString(bytes).Replace("-", ""); + } + + public static byte[] ToByteArray(this string hex) { + return Enumerable.Range(0, hex.Length). + Where(x => 0 == x % 2). + Select(x => Convert.ToByte(hex.Substring(x, 2), 16)). + ToArray(); + } } } \ No newline at end of file