Adding password storage and verification. Clear and hashed supported. Hashed is default, uses SHA1 with unique salt per user.

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4040935
This commit is contained in:
loudej
2009-11-17 05:52:23 +00:00
parent 0cc64ebe1c
commit e225650203
10 changed files with 195 additions and 10 deletions

View File

@@ -13,14 +13,17 @@ using Orchard.Users.ViewModels;
namespace Orchard.Users.Controllers {
public class AdminController : Controller, IModelUpdater {
private readonly IMembershipService _membershipService;
private readonly IModelManager _modelManager;
private readonly IRepository<UserRecord> _userRepository;
private readonly INotifier _notifier;
public AdminController(
IMembershipService membershipService,
IModelManager modelManager,
IRepository<UserRecord> userRepository,
INotifier notifier) {
_membershipService = membershipService;
_modelManager = modelManager;
_userRepository = userRepository;
_notifier = notifier;
@@ -49,12 +52,17 @@ namespace Orchard.Users.Controllers {
[HttpPost]
public ActionResult Create(UserCreateViewModel model) {
if (model.Password != model.ConfirmPassword) {
ModelState.AddModelError("ConfirmPassword", T("Password confirmation must match").ToString());
}
if (ModelState.IsValid == false) {
return View(model);
}
var user = _modelManager.New("user");
user.As<UserModel>().Record = new UserRecord { UserName = model.UserName, Email = model.Email };
_modelManager.Create(user);
var user = _membershipService.CreateUser(new CreateUserParams(
model.UserName,
model.Password,
model.Email,
null, null, true));
return RedirectToAction("edit", new { user.Id });
}

View File

@@ -1,8 +1,13 @@
using System.Web.Security;
using Orchard.Models.Records;
namespace Orchard.Users.Models {
public class UserRecord : ModelPartRecord {
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
public virtual string Password { get; set; }
public virtual MembershipPasswordFormat PasswordFormat { get; set; }
public virtual string PasswordSalt { get; set; }
}
}

View File

@@ -77,6 +77,7 @@
<Content Include="Package.txt" />
<Content Include="Views\Admin\Edit.aspx" />
<Content Include="Views\Admin\Create.aspx" />
<Content Include="Views\Admin\EditorTemplates\inputPasswordLarge.ascx" />
<Content Include="Views\Admin\EditorTemplates\UserEditViewModel.ascx" />
<Content Include="Views\Admin\EditorTemplates\inputTextLarge.ascx" />
<Content Include="Views\Admin\EditorTemplates\UserCreateViewModel.ascx" />

View File

@@ -1,4 +1,8 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web.Security;
using Orchard.Data;
using Orchard.Logging;
using Orchard.Models;
@@ -26,11 +30,14 @@ namespace Orchard.Users.Services {
public IUser CreateUser(CreateUserParams createUserParams) {
Logger.Information("CreateUser {0} {1}", createUserParams.Username, createUserParams.Email);
var user = _modelManager.New("user");
user.As<UserModel>().Record = new UserRecord {
var record = new UserRecord {
UserName = createUserParams.Username,
Email = createUserParams.Email
};
SetPassword(record, createUserParams.Password);
var user = _modelManager.New("user");
user.As<UserModel>().Record = record;
_modelManager.Create(user);
return user.As<IUser>();
}
@@ -44,7 +51,104 @@ namespace Orchard.Users.Services {
}
public IUser ValidateUser(string username, string password) {
return GetUser(username);
var userRecord = _userRepository.Get(x => x.UserName == username);
if (userRecord == null || ValidatePassword(userRecord, password) == false)
return null;
return _modelManager.Get(userRecord.Id).As<IUser>();
}
public void SetPassword(IUser user, string password) {
if (!user.Is<UserModel>())
throw new InvalidCastException();
var userRecord = user.As<UserModel>().Record;
SetPassword(userRecord, password);
}
void SetPassword(UserRecord record, string password) {
switch (GetSettings().PasswordFormat) {
case MembershipPasswordFormat.Clear:
SetPasswordClear(record, password);
break;
case MembershipPasswordFormat.Hashed:
SetPasswordHashed(record, password);
break;
case MembershipPasswordFormat.Encrypted:
SetPasswordEncrypted(record, password);
break;
default:
throw new ApplicationException("Unexpected password format value");
}
}
private bool ValidatePassword(UserRecord record, string password) {
// Note - the password format stored with the record is used
// otherwise changing the password format on the site would invalidate
// all logins
switch (record.PasswordFormat) {
case MembershipPasswordFormat.Clear:
return ValidatePasswordClear(record, password);
case MembershipPasswordFormat.Hashed:
return ValidatePasswordHashed(record, password);
case MembershipPasswordFormat.Encrypted:
return ValidatePasswordEncrypted(record, password);
default:
throw new ApplicationException("Unexpected password format value");
}
}
private static void SetPasswordClear(UserRecord record, string password) {
record.PasswordFormat = MembershipPasswordFormat.Clear;
record.Password = password;
record.PasswordSalt = null;
}
private static bool ValidatePasswordClear(UserRecord record, string password) {
return record.Password == password;
}
private static void SetPasswordHashed(UserRecord record, string password) {
var saltBytes = new byte[0x10];
var random = new RNGCryptoServiceProvider();
random.GetBytes(saltBytes);
var passwordBytes = Encoding.Unicode.GetBytes(password);
var combinedBytes = saltBytes.Concat(passwordBytes).ToArray();
var hashAlgorithm = HashAlgorithm.Create("SHA1");
var hashBytes = hashAlgorithm.ComputeHash(combinedBytes);
record.PasswordFormat = MembershipPasswordFormat.Hashed;
record.Password = Convert.ToBase64String(hashBytes);
record.PasswordSalt = Convert.ToBase64String(saltBytes);
}
private static bool ValidatePasswordHashed(UserRecord record, string password) {
var saltBytes = Convert.FromBase64String(record.PasswordSalt);
var passwordBytes = Encoding.Unicode.GetBytes(password);
var combinedBytes = saltBytes.Concat(passwordBytes).ToArray();
var hashAlgorithm = HashAlgorithm.Create("SHA1");
var hashBytes = hashAlgorithm.ComputeHash(combinedBytes);
return record.Password == Convert.ToBase64String(hashBytes);
}
private static void SetPasswordEncrypted(UserRecord record, string password) {
throw new NotImplementedException();
}
private static bool ValidatePasswordEncrypted(UserRecord record, string password) {
throw new NotImplementedException();
}
}
}

View File

@@ -6,7 +6,13 @@ namespace Orchard.Users.ViewModels {
[Required]
public string UserName { get; set; }
[Required]
[Required, DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required, DataType(DataType.Password)]
public string Password { get; set; }
[Required, DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
}
}

View File

@@ -4,4 +4,6 @@
<ol>
<%=Html.EditorFor(m=>m.UserName, "inputTextLarge") %>
<%=Html.EditorFor(m=>m.Email, "inputTextLarge") %>
<%=Html.EditorFor(m=>m.Password, "inputPasswordLarge") %>
<%=Html.EditorFor(m=>m.ConfirmPassword, "inputPasswordLarge") %>
</ol>

View File

@@ -0,0 +1,6 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<li>
<%=Html.LabelForModel() %>
<%=Html.Password("",Model,new{@class="inputText inputTextLarge"}) %>
<%=Html.ValidationMessage("","*")%>
</li>