Create user now generates a password

This commit is contained in:
Shannon
2017-06-27 15:30:32 +10:00
parent f309e3587b
commit 8e21bc3c74
5 changed files with 104 additions and 13 deletions

View File

@@ -90,6 +90,7 @@ namespace Umbraco.Core.Security
base.InitUserManager(manager, membershipProvider, options.DataProtectionProvider);
}
}
/// <summary>
@@ -152,15 +153,8 @@ namespace Umbraco.Core.Security
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = membershipProvider.MinRequiredPasswordLength,
RequireNonLetterOrDigit = membershipProvider.MinRequiredNonAlphanumericCharacters > 0,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
//TODO: Do we support the old regex match thing that membership providers used?
};
var provider = MembershipProviderExtensions.GetUsersMembershipProvider();
manager.PasswordValidator = new MembershipProviderPasswordValidator(provider);
//use a custom hasher based on our membership provider
manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider);
@@ -238,5 +232,39 @@ namespace Umbraco.Core.Security
/// Gets/sets the default back office user password checker
/// </summary>
public IBackOfficeUserPasswordChecker BackOfficeUserPasswordChecker { get; set; }
/// <summary>
/// Helper method to generate a password for a user based on the current password validator
/// </summary>
/// <returns></returns>
public string GeneratePassword()
{
var passwordValidator = PasswordValidator as PasswordValidator;
if (passwordValidator != null)
{
var password = Membership.GeneratePassword(
passwordValidator.RequiredLength,
passwordValidator.RequireNonLetterOrDigit ? 2 : 0);
var random = new Random();
var passwordChars = password.ToCharArray();
if (passwordValidator.RequireDigit && passwordChars.ContainsAny(Enumerable.Range(48, 58).Select(x => (char)x)))
password += Convert.ToChar(random.Next(48, 58)); // 0-9
if (passwordValidator.RequireLowercase && passwordChars.ContainsAny(Enumerable.Range(97, 123).Select(x => (char)x)))
password += Convert.ToChar(random.Next(97, 123)); // a-z
if (passwordValidator.RequireUppercase && passwordChars.ContainsAny(Enumerable.Range(65, 91).Select(x => (char)x)))
password += Convert.ToChar(random.Next(65, 91)); // A-Z
if (passwordValidator.RequireNonLetterOrDigit && passwordChars.ContainsAny(Enumerable.Range(33, 48).Select(x => (char)x)))
password += Convert.ToChar(random.Next(33, 48)); // symbols !"#$%&'()*+,-./
return password;
}
throw new NotSupportedException("Cannot generate a password since the type of the password validator (" + PasswordValidator.GetType() + ") is not known");
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Threading.Tasks;
using System.Web.Security;
using Microsoft.AspNet.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// Ensure that both the normal password validator rules are processed along with the underlying memberhsip provider rules
/// </summary>
public class MembershipProviderPasswordValidator : PasswordValidator
{
public MembershipProvider Provider { get; private set; }
public MembershipProviderPasswordValidator(MembershipProvider provider)
{
Provider = provider;
RequiredLength = Provider.MinRequiredPasswordLength;
RequireNonLetterOrDigit = Provider.MinRequiredNonAlphanumericCharacters > 0;
RequireDigit = false;
RequireLowercase = false;
RequireUppercase = false;
}
public override async Task<IdentityResult> ValidateAsync(string item)
{
var result = await base.ValidateAsync(item);
if (result.Succeeded == false)
return result;
var providerValidate = MembershipProviderBase.IsPasswordValid(item, Provider.MinRequiredNonAlphanumericCharacters, Provider.PasswordStrengthRegularExpression, Provider.MinRequiredPasswordLength);
if (providerValidate.Success == false)
{
return IdentityResult.Failed("Could not set password, password rules violated: " + providerValidate.Result);
}
return IdentityResult.Success;
}
}
}

View File

@@ -654,6 +654,7 @@
<Compile Include="Security\IBackOfficeUserPasswordChecker.cs" />
<Compile Include="Security\MembershipPasswordHasher.cs" />
<Compile Include="Security\EmailService.cs" />
<Compile Include="Security\MembershipProviderPasswordValidator.cs" />
<Compile Include="Security\OwinExtensions.cs" />
<Compile Include="SemVersionExtensions.cs" />
<Compile Include="Serialization\NoTypeConverterJsonConverter.cs" />

View File

@@ -234,8 +234,10 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
emptyContent = new Member(contentType);
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(Membership.MinRequiredPasswordLength, Membership.MinRequiredNonAlphanumericCharacters);
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters);
return Mapper.Map<IMember, MemberDisplay>(emptyContent);
case MembershipScenario.CustomProviderWithUmbracoLink:
//TODO: Support editing custom properties for members with a custom membership provider here.

View File

@@ -10,6 +10,7 @@ using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using System.Web.WebPages;
using AutoMapper;
using ClientDependency.Core;
@@ -203,6 +204,7 @@ namespace Umbraco.Web.Editors
string orderBy = "username",
Direction orderDirection = Direction.Ascending,
[FromUri]string[] userGroups = null,
//TODO: Add User state filtering
string filter = "")
{
long pageIndex = pageNumber - 1;
@@ -246,18 +248,36 @@ namespace Umbraco.Web.Editors
//we want to create the user with the UserManager, this ensures the 'empty' (special) password
//format is applied without us having to duplicate that logic
var created = await UserManager.CreateAsync(new BackOfficeIdentityUser
var identityUser = new BackOfficeIdentityUser
{
Email = userSave.Email,
Name = userSave.Name,
UserName = userSave.Email
});
};
var created = await UserManager.CreateAsync(identityUser);
if (created.Succeeded == false)
{
throw new HttpResponseException(
Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors)));
}
//we need to generate a password, however we can only do that if the user manager has a password validator that
//we can read values from
var passwordValidator = UserManager.PasswordValidator as PasswordValidator;
var resetPassword = string.Empty;
if (passwordValidator != null)
{
var password = UserManager.GeneratePassword();
var result = await UserManager.AddPasswordAsync(identityUser.Id, password);
if (result.Succeeded == false)
{
throw new HttpResponseException(
Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors)));
}
resetPassword = password;
}
//now re-look the user back up which will now exist
var user = Services.UserService.GetByEmail(userSave.Email);
@@ -269,7 +289,9 @@ namespace Umbraco.Web.Editors
Services.UserService.Save(user);
return Mapper.Map<UserDisplay>(user);
var display = Mapper.Map<UserDisplay>(user);
display.ResetPasswordValue = resetPassword;
return display;
}
/// <summary>