diff --git a/src/Umbraco.Core/Security/PasswordSecurity.cs b/src/Umbraco.Core/Security/PasswordSecurity.cs index b38e125a44..cedceecd3d 100644 --- a/src/Umbraco.Core/Security/PasswordSecurity.cs +++ b/src/Umbraco.Core/Security/PasswordSecurity.cs @@ -150,6 +150,10 @@ namespace Umbraco.Core.Security public bool VerifyPassword(string password, string dbPassword) { if (string.IsNullOrWhiteSpace(dbPassword)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(dbPassword)); + + if (dbPassword.StartsWith(Constants.Security.EmptyPasswordPrefix)) + return false; + var storedHashedPass = ParseStoredHashPassword(dbPassword, out var salt); var hashed = HashPassword(password, salt); return storedHashedPass == hashed; diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 0dccca8a64..f29a11cd27 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -1120,77 +1120,6 @@ namespace Umbraco.Core.Services.Implement #region Membership - /// - /// A helper method that will create a basic/generic member for use with a generic membership provider - /// - /// - internal static IMember CreateGenericMembershipProviderMember(string name, string email, string username, string password) - { - var identity = int.MaxValue; - - var memType = new MemberType(-1); - var propGroup = new PropertyGroup(MemberType.SupportsPublishingConst) - { - Name = "Membership", - Id = --identity - }; - propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext, Constants.Conventions.Member.Comments) - { - Name = Constants.Conventions.Member.CommentsLabel, - SortOrder = 0, - Id = --identity, - Key = identity.ToGuid() - }); - propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, Constants.Conventions.Member.IsApproved) - { - Name = Constants.Conventions.Member.IsApprovedLabel, - SortOrder = 3, - Id = --identity, - Key = identity.ToGuid() - }); - propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, Constants.Conventions.Member.IsLockedOut) - { - Name = Constants.Conventions.Member.IsLockedOutLabel, - SortOrder = 4, - Id = --identity, - Key = identity.ToGuid() - }); - propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Date, Constants.Conventions.Member.LastLockoutDate) - { - Name = Constants.Conventions.Member.LastLockoutDateLabel, - SortOrder = 5, - Id = --identity, - Key = identity.ToGuid() - }); - propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Date, Constants.Conventions.Member.LastLoginDate) - { - Name = Constants.Conventions.Member.LastLoginDateLabel, - SortOrder = 6, - Id = --identity, - Key = identity.ToGuid() - }); - propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Date, Constants.Conventions.Member.LastPasswordChangeDate) - { - Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, - SortOrder = 7, - Id = --identity, - Key = identity.ToGuid() - }); - - memType.PropertyGroups.Add(propGroup); - - // should we "create member"? - var member = new Member(name, email, username, password, memType); - - //we've assigned ids to the property types and groups but we also need to assign fake ids to the properties themselves. - foreach (var property in member.Properties) - { - property.Id = --identity; - } - - return member; - } - /// /// Exports a member. /// diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js index 2dd319142b..0512abe579 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js @@ -80,11 +80,7 @@ })); unsubscribe.push($scope.$on("formSubmitting", function () { - //if there was a previously generated password displaying, clear it - if (vm.changing && vm.passwordValues) { - vm.passwordValues.generatedPassword = null; - } - else if (!vm.changing) { + if (!vm.changing) { //we are not changing, so the model needs to be null vm.passwordValues = null; } @@ -105,8 +101,6 @@ function doChange() { resetModel(); vm.changing = true; - //if there was a previously generated password displaying, clear it - vm.passwordValues.generatedPassword = null; vm.passwordValues.confirm = null; }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index a03a71febe..825b4d1d44 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -41,7 +41,7 @@ if (!model) { return null; } - var trimmed = _.omit(model, ["confirm", "generatedPassword"]); + var trimmed = _.omit(model, ["confirm"]); //ensure that the pass value is null if all child properties are null var allNull = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js index 5e19235dce..060ef77a5d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js @@ -141,11 +141,6 @@ angular.module("umbraco") //reset old data clearPasswordFields(); - //if the password has been reset, then update our model - if (data.value) { - $scope.changePasswordModel.value.generatedPassword = data.value; - } - formHelper.resetForm({ scope: $scope }); $scope.changePasswordButtonState = "success"; diff --git a/src/Umbraco.Web/Editors/Binders/MemberBinder.cs b/src/Umbraco.Web/Editors/Binders/MemberBinder.cs index 8cc34896ba..33e37adb2b 100644 --- a/src/Umbraco.Web/Editors/Binders/MemberBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/MemberBinder.cs @@ -2,17 +2,12 @@ using System.Collections.Generic; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using System.Linq; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; -using Umbraco.Web.Security; namespace Umbraco.Web.Editors.Binders { diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 18450f41ae..fe490e8699 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -8,7 +8,6 @@ using System.Text; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs index f837253b4b..5b3aa3d7ee 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs @@ -5,7 +5,6 @@ using System.Net; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -21,11 +20,13 @@ namespace Umbraco.Web.Editors.Filters internal class MemberSaveModelValidator : ContentModelValidator> { private readonly IMemberTypeService _memberTypeService; + private readonly IMemberService _memberService; - public MemberSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService) + public MemberSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService, IMemberService memberService) : base(logger, umbracoContextAccessor) { _memberTypeService = memberTypeService; + _memberService = memberService; } /// @@ -55,7 +56,7 @@ namespace Umbraco.Web.Editors.Filters //default provider! var membershipProvider = MembershipProviderExtensions.GetMembersMembershipProvider(); - var validEmail = ValidateUniqueEmail(model, membershipProvider); + var validEmail = ValidateUniqueEmail(model); if (validEmail == false) { modelState.AddPropertyError( @@ -63,7 +64,7 @@ namespace Umbraco.Web.Editors.Filters $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email"); } - var validLogin = ValidateUniqueLogin(model, membershipProvider); + var validLogin = ValidateUniqueLogin(model); if (validLogin == false) { modelState.AddPropertyError( @@ -121,13 +122,11 @@ namespace Umbraco.Web.Editors.Filters return ValidateProperties(propertiesToValidate, model.PersistedContent.Properties.ToList(), actionContext); } - internal bool ValidateUniqueLogin(MemberSave model, MembershipProvider membershipProvider) + internal bool ValidateUniqueLogin(MemberSave model) { if (model == null) throw new ArgumentNullException(nameof(model)); - if (membershipProvider == null) throw new ArgumentNullException(nameof(membershipProvider)); - - int totalRecs; - var existingByName = membershipProvider.FindUsersByName(model.Username.Trim(), 0, int.MaxValue, out totalRecs); + + var existingByName = _memberService.GetByUsername(model.Username.Trim()); switch (model.Action) { case ContentSaveAction.Save: @@ -136,8 +135,7 @@ namespace Umbraco.Web.Editors.Filters if (model.PersistedContent.Username.InvariantEquals(model.Username.Trim()) == false) { //they are changing their login name - if (existingByName.Cast().Select(x => x.UserName) - .Any(x => x == model.Username.Trim())) + if (existingByName != null && existingByName.Username == model.Username.Trim()) { //the user cannot use this login return false; @@ -146,8 +144,7 @@ namespace Umbraco.Web.Editors.Filters break; case ContentSaveAction.SaveNew: //check if the user's login already exists - if (existingByName.Cast().Select(x => x.UserName) - .Any(x => x == model.Username.Trim())) + if (existingByName != null && existingByName.Username == model.Username.Trim()) { //the user cannot use this login return false; @@ -161,18 +158,11 @@ namespace Umbraco.Web.Editors.Filters return true; } - internal bool ValidateUniqueEmail(MemberSave model, MembershipProvider membershipProvider) + internal bool ValidateUniqueEmail(MemberSave model) { if (model == null) throw new ArgumentNullException(nameof(model)); - if (membershipProvider == null) throw new ArgumentNullException(nameof(membershipProvider)); - if (membershipProvider.RequiresUniqueEmail == false) - { - return true; - } - - int totalRecs; - var existingByEmail = membershipProvider.FindUsersByEmail(model.Email.Trim(), 0, int.MaxValue, out totalRecs); + var existingByEmail = _memberService.GetByEmail(model.Email.Trim()); switch (model.Action) { case ContentSaveAction.Save: @@ -180,8 +170,7 @@ namespace Umbraco.Web.Editors.Filters if (model.PersistedContent.Email.InvariantEquals(model.Email.Trim()) == false) { //they are changing their email - if (existingByEmail.Cast().Select(x => x.Email) - .Any(x => x.InvariantEquals(model.Email.Trim()))) + if (existingByEmail != null && existingByEmail.Email.InvariantEquals(model.Email.Trim())) { //the user cannot use this email return false; @@ -190,8 +179,7 @@ namespace Umbraco.Web.Editors.Filters break; case ContentSaveAction.SaveNew: //check if the user's email already exists - if (existingByEmail.Cast().Select(x => x.Email) - .Any(x => x.InvariantEquals(model.Email.Trim()))) + if (existingByEmail != null && existingByEmail.Email.InvariantEquals(model.Email.Trim())) { //the user cannot use this email return false; diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs index 04b0112d56..14b48e8219 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs @@ -15,22 +15,24 @@ namespace Umbraco.Web.Editors.Filters private readonly ILogger _logger; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMemberTypeService _memberTypeService; + private readonly IMemberService _memberService; public MemberSaveValidationAttribute() - : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MemberTypeService) + : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MemberTypeService, Current.Services.MemberService) { } - public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService) + public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService, IMemberService memberService) { _logger = logger; _umbracoContextAccessor = umbracoContextAccessor; _memberTypeService = memberTypeService; + _memberService = memberService; } public override void OnActionExecuting(HttpActionContext actionContext) { var model = (MemberSave)actionContext.ActionArguments["contentItem"]; - var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor, _memberTypeService); + var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor, _memberTypeService, _memberService); //now do each validation step if (contentItemValidator.ValidateExistingContent(model, actionContext)) if (contentItemValidator.ValidateProperties(model, model, actionContext)) diff --git a/src/Umbraco.Web/Editors/MemberGroupController.cs b/src/Umbraco.Web/Editors/MemberGroupController.cs index 89041fb512..d06c45aad6 100644 --- a/src/Umbraco.Web/Editors/MemberGroupController.cs +++ b/src/Umbraco.Web/Editors/MemberGroupController.cs @@ -3,14 +3,10 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; -using System.Web.Security; -using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; -using Umbraco.Web.Security; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index 9600b96f92..fd34eaf300 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; @@ -12,11 +11,9 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; -using Umbraco.Web.Security; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs index c16a743dd7..6b4227557b 100644 --- a/src/Umbraco.Web/Editors/PasswordChanger.cs +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -1,9 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; -using System.Web.Security; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Identity; @@ -111,62 +109,6 @@ namespace Umbraco.Web.Editors return passwordSecurity.HashPasswordForStorage(passwordModel.NewPassword); } - /// - /// Changes password for a member/user given the membership provider and the password change model - /// - /// The username of the user having their password changed - /// - /// - /// - public Attempt ChangePasswordWithMembershipProvider( - string username, - ChangingPasswordModel passwordModel, - MembershipProvider membershipProvider) - { - var umbracoBaseProvider = membershipProvider as MembershipProviderBase; - - // YES! It is completely insane how many options you have to take into account based on the membership provider. yikes! - - if (passwordModel == null) throw new ArgumentNullException(nameof(passwordModel)); - if (membershipProvider == null) throw new ArgumentNullException(nameof(membershipProvider)); - var userId = -1; - - - //we're not resetting it so we need to try to change it. - - if (passwordModel.NewPassword.IsNullOrWhiteSpace()) - { - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Cannot set an empty password", new[] { "value" }) }); - } - - if (membershipProvider.EnablePasswordRetrieval) - { - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Membership providers using encrypted passwords and password retrieval are not supported", new[] { "value" }) }); - } - - //without being able to retrieve the original password - if (passwordModel.OldPassword.IsNullOrWhiteSpace()) - { - //if password retrieval is not enabled but there is no old password we cannot continue - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "oldPassword" }) }); - } - - //if an old password is supplied try to change it - - try - { - var result = membershipProvider.ChangePassword(username, passwordModel.OldPassword, passwordModel.NewPassword); - - return result == false - ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "oldPassword" }) }) - : Attempt.Succeed(new PasswordChangedModel()); - } - catch (Exception ex) - { - _logger.Warn(ex, "Could not change member password"); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] { "value" }) }); - } - - } + } } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 98f958c844..9bdade968c 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Web; -using System.Web.Security; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Composing; @@ -14,7 +13,6 @@ using Umbraco.Core.Models.Identity; using Umbraco.Core.Services; using Umbraco.Web.Install.Models; using Umbraco.Web.Security; -using System.Web; namespace Umbraco.Web.Install.InstallSteps { diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs index 435d349aa3..9b17ac9d70 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -32,43 +31,11 @@ namespace Umbraco.Web.Models.Mapping public void DefineMaps(UmbracoMapper mapper) { - mapper.Define((source, context) => new MemberDisplay(), Map); - mapper.Define((source, context) => MemberService.CreateGenericMembershipProviderMember(source.UserName, source.Email, source.UserName, ""), Map); mapper.Define((source, context) => new MemberDisplay(), Map); mapper.Define((source, context) => new MemberBasic(), Map); - mapper.Define((source, context) => new MemberBasic(), Map); mapper.Define((source, context) => new MemberGroupDisplay(), Map); mapper.Define((source, context) => new ContentPropertyCollectionDto(), Map); - } - - private void Map(MembershipUser source, MemberDisplay target, MapperContext context) - { - //first convert to IMember - var member = context.Map(source); - //then convert to MemberDisplay - context.Map(member); - } - - // TODO: SD: I can't remember why this mapping is here? - // Umbraco.Code.MapAll -Properties -CreatorId -Level -Name -CultureInfos -ParentId - // Umbraco.Code.MapAll -Path -SortOrder -DeleteDate -WriterId -VersionId -PasswordQuestion - // Umbraco.Code.MapAll -RawPasswordAnswerValue -FailedPasswordAttempts - private void Map(MembershipUser source, IMember target, MapperContext context) - { - target.Comments = source.Comment; - target.CreateDate = source.CreationDate; - target.Email = source.Email; - target.Id = int.MaxValue; - target.IsApproved = source.IsApproved; - target.IsLockedOut = source.IsLockedOut; - target.Key = source.ProviderUserKey.TryConvertTo().Result; - target.LastLockoutDate = source.LastLockoutDate; - target.LastLoginDate = source.LastLoginDate; - target.LastPasswordChangeDate = source.LastPasswordChangedDate; - target.RawPasswordValue = source.CreationDate > DateTime.MinValue ? Guid.NewGuid().ToString("N") : ""; - target.UpdateDate = source.LastActivityDate; - target.Username = source.UserName; - } + } // Umbraco.Code.MapAll -Properties -Errors -Edited -Updater -Alias -IsChildOfListView // Umbraco.Code.MapAll -Trashed -IsContainer -VariesByCulture @@ -118,23 +85,6 @@ namespace Umbraco.Web.Models.Mapping target.Username = source.Username; } - //TODO: SD: I can't remember why this mapping is here? - // Umbraco.Code.MapAll -Udi -Properties -ParentId -Path -SortOrder -Edited -Updater - // Umbraco.Code.MapAll -Trashed -Alias -ContentTypeId -ContentTypeAlias -VariesByCulture - private void Map(MembershipUser source, MemberBasic target, MapperContext context) - { - target.CreateDate = source.CreationDate; - target.Email = source.Email; - target.Icon = Constants.Icons.Member; - target.Id = int.MaxValue; - target.Key = source.ProviderUserKey.TryConvertTo().Result; - target.Name = source.UserName; - target.Owner = new UserProfile { Name = "Admin", UserId = -1 }; - target.State = ContentSavedState.Draft; - target.UpdateDate = source.LastActivityDate; - target.Username = source.UserName; -} - // Umbraco.Code.MapAll -Icon -Trashed -ParentId -Alias private void Map(IMemberGroup source, MemberGroupDisplay target, MapperContext context) { diff --git a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs index 67694d3ea8..1a52816d14 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Composing; @@ -29,15 +28,17 @@ namespace Umbraco.Web.Models.Mapping private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILocalizedTextService _localizedTextService; private readonly IMemberTypeService _memberTypeService; - private readonly IUserService _userService; + private readonly IMemberService _memberService; + private readonly IMemberGroupService _memberGroupService; - public MemberTabsAndPropertiesMapper(ICultureDictionary cultureDictionary, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IUserService userService, IMemberTypeService memberTypeService) + public MemberTabsAndPropertiesMapper(ICultureDictionary cultureDictionary, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IMemberTypeService memberTypeService, IMemberService memberService, IMemberGroupService memberGroupService) : base(cultureDictionary, localizedTextService) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + _memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService)); } /// @@ -127,12 +128,10 @@ namespace Umbraco.Web.Models.Mapping { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password", Label = _localizedTextService.Localize("password"), - // NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists - // only when creating a new member and we want to have a generated password pre-filled. + Value = new Dictionary { - // TODO: why ignoreCase, what are we doing here?! - {"generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null)}, + // TODO: why ignoreCase, what are we doing here?! {"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)}, }, // TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor @@ -231,12 +230,13 @@ namespace Umbraco.Web.Models.Mapping return prop; } - internal static IDictionary GetMemberGroupValue(string username) + internal IDictionary GetMemberGroupValue(string username) { - var userRoles = username.IsNullOrWhiteSpace() ? null : Roles.GetRolesForUser(username); + var userRoles = username.IsNullOrWhiteSpace() ? null : _memberService.GetAllRoles(username); // create a dictionary of all roles (except internal roles) + "false" - var result = Roles.GetAllRoles().Distinct() + var result = _memberGroupService.GetAll() + .Select(x => x.Name) // if a role starts with __umbracoRole we won't show it as it's an internal role used for public access .Where(x => x.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false) .OrderBy(x => x, StringComparer.OrdinalIgnoreCase) diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 91fc903ba8..30076ee2b0 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Globalization; using System.IO; -using System.Web.Security; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; diff --git a/src/Umbraco.Web/Security/BackOfficeUserManager.cs b/src/Umbraco.Web/Security/BackOfficeUserManager.cs index 4476edbd10..cca08318f4 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserManager.cs @@ -1,9 +1,7 @@ using System; -using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using System.Web; -using System.Web.Security; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security.DataProtection; diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index da6e846020..c9a507494c 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Web.Editors; using Umbraco.Web.Security.Providers; +using System.ComponentModel.DataAnnotations; namespace Umbraco.Web.Security { @@ -643,7 +644,7 @@ namespace Umbraco.Web.Security public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) { var passwordChanger = new PasswordChanger(_logger); - return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider); + return ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider); } /// @@ -732,5 +733,63 @@ namespace Umbraco.Web.Security return sb.ToString(); } + /// + /// Changes password for a member/user given the membership provider and the password change model + /// + /// The username of the user having their password changed + /// + /// + /// + private Attempt ChangePasswordWithMembershipProvider( + string username, + ChangingPasswordModel passwordModel, + MembershipProvider membershipProvider) + { + var umbracoBaseProvider = membershipProvider as MembershipProviderBase; + + // YES! It is completely insane how many options you have to take into account based on the membership provider. yikes! + + if (passwordModel == null) throw new ArgumentNullException(nameof(passwordModel)); + if (membershipProvider == null) throw new ArgumentNullException(nameof(membershipProvider)); + var userId = -1; + + + //we're not resetting it so we need to try to change it. + + if (passwordModel.NewPassword.IsNullOrWhiteSpace()) + { + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Cannot set an empty password", new[] { "value" }) }); + } + + if (membershipProvider.EnablePasswordRetrieval) + { + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Membership providers using encrypted passwords and password retrieval are not supported", new[] { "value" }) }); + } + + //without being able to retrieve the original password + if (passwordModel.OldPassword.IsNullOrWhiteSpace()) + { + //if password retrieval is not enabled but there is no old password we cannot continue + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "oldPassword" }) }); + } + + //if an old password is supplied try to change it + + try + { + var result = membershipProvider.ChangePassword(username, passwordModel.OldPassword, passwordModel.NewPassword); + + return result == false + ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "oldPassword" }) }) + : Attempt.Succeed(new PasswordChangedModel()); + } + catch (Exception ex) + { + _logger.Warn(ex, "Could not change member password"); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] { "value" }) }); + } + + } + } } diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 7c97873a2c..84909d5fda 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -6,7 +6,6 @@ using System.Net.Http; using System.Net.Http.Formatting; using System.Web.Http; using System.Web.Http.ModelBinding; -using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -37,11 +36,9 @@ namespace Umbraco.Web.Trees public MemberTreeController(UmbracoTreeSearcher treeSearcher) { _treeSearcher = treeSearcher; - _provider = MembershipProviderExtensions.GetMembersMembershipProvider(); } private readonly UmbracoTreeSearcher _treeSearcher; - private readonly MembershipProvider _provider; /// /// Gets an individual tree node