Adding IEncryptionServices

Implements symetric encoding/decoding services based on a per-tenant key generated randomly during setup in the ShellSettings.
Replaces MachineKey.Encode/Decode usages.
Adding ComputedField to wrap get/set calls from parts, making the Smtp password encrypted in the db automatically.

--HG--
branch : dev
This commit is contained in:
Sebastien Ros
2010-12-03 16:14:17 -08:00
parent 7b4025b8cb
commit fadcc4ef6e
20 changed files with 315 additions and 53 deletions

View File

@@ -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 {

View File

@@ -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<SmtpSettingsPartRecord> repository) {
private readonly IEncryptionService _encryptionService;
public SmtpSettingsPartHandler(IRepository<SmtpSettingsPartRecord> repository, IEncryptionService encryptionService) {
_encryptionService = encryptionService;
Filters.Add(new ActivatingFilter<SmtpSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
OnLoaded<SmtpSettingsPart>(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))));
}
}
}

View File

@@ -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<SmtpSettingsPartRecord> {
public bool IsValid() {
return !String.IsNullOrWhiteSpace(Record.Host)
&& Record.Port > 0
&& !String.IsNullOrWhiteSpace(Record.Address);
private readonly ComputedField<string> _password = new ComputedField<string>();
public ComputedField<string> 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);
}
}
}

View File

@@ -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 })
};

View File

@@ -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;