diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 3c88c07edf..17cea2976c 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -80,7 +80,41 @@ namespace Umbraco.Core.Security return false; } + + /// + /// This will return the current back office identity if the IPrincipal is the correct type + /// + /// + /// + internal static UmbracoBackOfficeIdentity GetUmbracoIdentity(this IPrincipal user) + { + //If it's already a UmbracoBackOfficeIdentity + var backOfficeIdentity = user.Identity as UmbracoBackOfficeIdentity; + if (backOfficeIdentity != null) return backOfficeIdentity; + //Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that + var claimsPrincipal = user as ClaimsPrincipal; + if (claimsPrincipal != null) + { + backOfficeIdentity = claimsPrincipal.Identities.OfType().FirstOrDefault(); + if (backOfficeIdentity != null) return backOfficeIdentity; + } + + //Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session + var claimsIdentity = user.Identity as ClaimsIdentity; + if (claimsIdentity != null && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType)) + { + try + { + return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity); + } + catch (InvalidOperationException) + { + } + } + + return null; + } /// /// This will return the current back office identity. @@ -100,31 +134,8 @@ namespace Umbraco.Core.Security if (http.User == null) return null; //there's no user at all so no identity //If it's already a UmbracoBackOfficeIdentity - var backOfficeIdentity = http.User.Identity as UmbracoBackOfficeIdentity; - if (backOfficeIdentity != null) return backOfficeIdentity; - - //Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that - var claimsPrincipal = http.User as ClaimsPrincipal; - if (claimsPrincipal != null) - { - backOfficeIdentity = claimsPrincipal.Identities.OfType().FirstOrDefault(); - if (backOfficeIdentity != null) return backOfficeIdentity; - } - - //Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session - var claimsIdentity = http.User.Identity as ClaimsIdentity; - if (claimsIdentity != null && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType)) - { - try - { - return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity); - } - catch (InvalidOperationException ex) - { - //This will occur if the required claim types are missing which would mean something strange is going on - LogHelper.Error(typeof(AuthenticationExtensions), "The current identity cannot be converted to " + typeof(UmbracoBackOfficeIdentity), ex); - } - } + var backOfficeIdentity = GetUmbracoIdentity(http.User); + if (backOfficeIdentity != null) return backOfficeIdentity; if (authenticateRequestIfNotFound == false) return null; diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index d8bbc1dced..efee6f4893 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -521,9 +521,9 @@ namespace Umbraco.Core.Security public override string ResetPassword(string username, string answer) { - var auth = new HttpContextWrapper(HttpContext.Current).GetUmbracoAuthTicket(); - var userIsAdmin = ApplicationContext.Current.Services.UserService.GetByUsername(auth.Name).IsAdmin(); - if (userIsAdmin == false && EnablePasswordReset == false) + var canReset = this.CanResetPassword(ApplicationContext.Current.Services.UserService); + + if (canReset == false) { throw new NotSupportedException("Password reset is not supported"); } diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs index bdd3174960..ebd42bd781 100644 --- a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -9,12 +9,44 @@ using System.Web; using System.Web.Hosting; using System.Web.Security; using Umbraco.Core.Configuration; +using Umbraco.Core.Models; using Umbraco.Core.Security; +using Umbraco.Core.Services; namespace Umbraco.Core.Security { public static class MembershipProviderExtensions { + /// + /// Extension method to check if a password can be reset based on a given provider and the current request (logged in user) + /// + /// + /// + /// + internal static bool CanResetPassword(this MembershipProvider provider, IUserService userService) + { + if (provider == null) throw new ArgumentNullException("provider"); + if (userService == null) throw new ArgumentNullException("userService"); + + var canReset = provider.EnablePasswordReset; + + //we need to check for the special case in which a user is an admin - in which acse they can reset the password even if EnablePasswordReset == false + if (provider.EnablePasswordReset == false) + { + var identity = Thread.CurrentPrincipal.GetUmbracoIdentity(); + if (identity != null) + { + var user = userService.GetByUsername(identity.Username); + var userIsAdmin = user.IsAdmin(); + if (userIsAdmin) + { + canReset = true; + } + } + } + return canReset; + } + internal static MembershipUserCollection FindUsersByName(this MembershipProvider provider, string usernameToMatch) { int totalRecords = 0; diff --git a/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs b/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs index f5f2834fba..75540408b8 100644 --- a/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/controls/PasswordChanger.ascx.cs @@ -24,10 +24,12 @@ namespace Umbraco.Web.UI.Umbraco.Controls //reset the flag always IsChangingPasswordField.Value = "false"; - var auth = new HttpContextWrapper(HttpContext.Current).GetUmbracoAuthTicket(); - ResetPlaceHolder.Visible = ApplicationContext.Current.Services.UserService.GetByUsername(auth.Name).IsAdmin(); + var canReset = Provider.CanResetPassword(ApplicationContext.Current.Services.UserService); + + ResetPlaceHolder.Visible = canReset; this.DataBind(); } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index 5c56106063..aacad99fb7 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web.Editors public IDictionary GetMembershipProviderConfig() { var provider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); - return provider.GetConfiguration(); + return provider.GetConfiguration(Services.UserService); } /// diff --git a/src/Umbraco.Web/MembershipProviderExtensions.cs b/src/Umbraco.Web/MembershipProviderExtensions.cs index 49d8abca7f..2a32384732 100644 --- a/src/Umbraco.Web/MembershipProviderExtensions.cs +++ b/src/Umbraco.Web/MembershipProviderExtensions.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Web; using System.Web.Security; using Umbraco.Core.Models; using Umbraco.Core.Security; +using Umbraco.Core.Services; namespace Umbraco.Web { @@ -11,16 +13,19 @@ namespace Umbraco.Web /// Returns the configuration of the membership provider used to configure change password editors /// /// + /// /// public static IDictionary GetConfiguration( - this MembershipProvider membershipProvider) + this MembershipProvider membershipProvider, IUserService userService) { var baseProvider = membershipProvider as MembershipProviderBase; - + + var canReset = membershipProvider.CanResetPassword(userService); + return new Dictionary { {"minPasswordLength", membershipProvider.MinRequiredPasswordLength}, - {"enableReset", UmbracoContext.Current.Security.CurrentUser.IsAdmin()}, + {"enableReset", canReset}, {"enablePasswordRetrieval", membershipProvider.EnablePasswordRetrieval}, {"requiresQuestionAnswer", membershipProvider.RequiresQuestionAndAnswer}, {"allowManuallyChangingPassword", baseProvider != null && baseProvider.AllowManuallyChangingPassword} diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index 3982302906..e0eb318866 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -81,7 +81,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.IsContainer, expression => expression.Ignore()) .ForMember(display => display.TreeNodeUrl, expression => expression.Ignore()) .ForMember(display => display.HasPublishedVersion, expression => expression.Ignore()) - .AfterMap((member, display) => MapGenericCustomProperties(applicationContext.Services.MemberService, member, display, applicationContext.Services.TextService)); + .AfterMap((member, display) => MapGenericCustomProperties(applicationContext.Services.MemberService, applicationContext.Services.UserService, member, display, applicationContext.Services.TextService)); //FROM IMember TO MemberBasic config.CreateMap() @@ -139,13 +139,14 @@ namespace Umbraco.Web.Models.Mapping /// Maps the generic tab with custom properties for content /// /// + /// /// /// /// /// /// If this is a new entity and there is an approved field then we'll set it to true by default. /// - private static void MapGenericCustomProperties(IMemberService memberService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) + private static void MapGenericCustomProperties(IMemberService memberService, IUserService userService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) { var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); @@ -189,7 +190,7 @@ namespace Umbraco.Web.Models.Mapping //TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor View = "changepassword", //initialize the dictionary with the configuration from the default membership provider - Config = new Dictionary(membersProvider.GetConfiguration()) + Config = new Dictionary(membersProvider.GetConfiguration(userService)) { //the password change toggle will only be displayed if there is already a password assigned. {"hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false} diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index c82d8411ad..605b5137d8 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -663,8 +663,8 @@ namespace Umbraco.Web.Security //Are we resetting the password?? if (passwordModel.Reset.HasValue && passwordModel.Reset.Value) { - var userIsAdmin = UmbracoContext.Current.Security.CurrentUser.IsAdmin(); - if (userIsAdmin == false && membershipProvider.EnablePasswordReset == false) + var canReset = membershipProvider.CanResetPassword(_applicationContext.Services.UserService); + if (canReset == false) { return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset is not enabled", new[] { "resetPassword" }) }); }