Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/core-cannot-use-system-web

# Conflicts:
#	src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs
#	src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
#	src/Umbraco.Web/Security/AppBuilderExtensions.cs
#	src/Umbraco.Web/Security/BackOfficeUserManager.cs
#	src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
This commit is contained in:
Bjarke Berg
2019-12-04 11:50:12 +01:00
57 changed files with 436 additions and 1240 deletions

View File

@@ -43,7 +43,6 @@ namespace Umbraco.Web.Security
IGlobalSettings globalSettings,
// TODO: This could probably be optional?
IPasswordConfiguration passwordConfiguration,
IPasswordGenerator passwordGenerator,
IIpResolver ipResolver)
{
if (services == null) throw new ArgumentNullException(nameof(services));
@@ -59,7 +58,6 @@ namespace Umbraco.Web.Security
contentSettings,
globalSettings,
passwordConfiguration,
passwordGenerator,
ipResolver));
app.SetBackOfficeUserManagerType<BackOfficeUserManager, BackOfficeIdentityUser>();
@@ -85,7 +83,6 @@ namespace Umbraco.Web.Security
BackOfficeUserStore customUserStore,
// TODO: This could probably be optional?
IPasswordConfiguration passwordConfiguration,
IPasswordGenerator passwordGenerator,
IIpResolver ipResolver)
{
if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState));
@@ -98,7 +95,6 @@ namespace Umbraco.Web.Security
customUserStore,
contentSettings,
passwordConfiguration,
passwordGenerator,
ipResolver));
app.SetBackOfficeUserManagerType<BackOfficeUserManager, BackOfficeIdentityUser>();

View File

@@ -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;
@@ -30,9 +28,8 @@ namespace Umbraco.Web.Security
IdentityFactoryOptions<BackOfficeUserManager> options,
IContentSection contentSectionConfig,
IPasswordConfiguration passwordConfiguration,
IPasswordGenerator passwordGenerator,
IIpResolver ipResolver)
: base(store, passwordConfiguration, passwordGenerator, ipResolver)
: base(store, passwordConfiguration, ipResolver)
{
if (options == null) throw new ArgumentNullException("options");
InitUserManager(this, passwordConfiguration, options.DataProtectionProvider, contentSectionConfig);
@@ -60,7 +57,6 @@ namespace Umbraco.Web.Security
IContentSection contentSectionConfig,
IGlobalSettings globalSettings,
IPasswordConfiguration passwordConfiguration,
IPasswordGenerator passwordGenerator,
IIpResolver ipResolver)
{
if (options == null) throw new ArgumentNullException("options");
@@ -68,7 +64,7 @@ namespace Umbraco.Web.Security
if (externalLoginService == null) throw new ArgumentNullException("externalLoginService");
var store = new BackOfficeUserStore(userService, entityService, externalLoginService, globalSettings, mapper);
var manager = new BackOfficeUserManager(store, options, contentSectionConfig, passwordConfiguration, passwordGenerator, ipResolver);
var manager = new BackOfficeUserManager(store, options, contentSectionConfig, passwordConfiguration, ipResolver);
return manager;
}
@@ -85,10 +81,9 @@ namespace Umbraco.Web.Security
BackOfficeUserStore customUserStore,
IContentSection contentSectionConfig,
IPasswordConfiguration passwordConfiguration,
IPasswordGenerator passwordGenerator,
IIpResolver ipResolver)
{
var manager = new BackOfficeUserManager(customUserStore, options, contentSectionConfig, passwordConfiguration, passwordGenerator, ipResolver);
var manager = new BackOfficeUserManager(customUserStore, options, contentSectionConfig, passwordConfiguration, ipResolver);
return manager;
}
#endregion
@@ -102,14 +97,14 @@ namespace Umbraco.Web.Security
public class BackOfficeUserManager<T> : UserManager<T, int>
where T : BackOfficeIdentityUser
{
private PasswordGenerator _passwordGenerator;
public BackOfficeUserManager(IUserStore<T, int> store,
IPasswordConfiguration passwordConfiguration,
IPasswordGenerator passwordGenerator,
IIpResolver ipResolver)
: base(store)
{
PasswordConfiguration = passwordConfiguration;
PasswordGenerator = passwordGenerator;
IpResolver = ipResolver;
}
@@ -150,24 +145,6 @@ namespace Umbraco.Web.Security
return userIdentity;
}
///// <summary>
///// Initializes the user manager with the correct options
///// </summary>
///// <param name="manager"></param>
///// <param name="passwordConfig"></param>
///// <param name="contentSectionConfig"></param>
///// <param name="options"></param>
///// <returns></returns>
//protected void InitUserManager(
// BackOfficeUserManager manager,
// IPasswordConfiguration passwordConfig,
// IContentSection contentSectionConfig,
// IdentityFactoryOptions<BackOfficeUserManager> options)
//{
// //NOTE: This method is mostly here for backwards compat
// base.InitUserManager(manager, passwordConfig, options.DataProtectionProvider, contentSectionConfig);
//}
/// <summary>
/// Initializes the user manager with the correct options
/// </summary>
@@ -265,7 +242,6 @@ namespace Umbraco.Web.Security
/// </summary>
public IBackOfficeUserPasswordChecker BackOfficeUserPasswordChecker { get; set; }
public IPasswordConfiguration PasswordConfiguration { get; }
public IPasswordGenerator PasswordGenerator { get; }
public IIpResolver IpResolver { get; }
/// <summary>
@@ -274,7 +250,8 @@ namespace Umbraco.Web.Security
/// <returns></returns>
public string GeneratePassword()
{
var password = PasswordGenerator.GeneratePassword(PasswordConfiguration);
if (_passwordGenerator == null) _passwordGenerator = new PasswordGenerator(PasswordConfiguration);
var password = _passwordGenerator.GeneratePassword();
return password;
}
@@ -348,8 +325,6 @@ namespace Umbraco.Web.Security
public override Task<IdentityResult> ResetPasswordAsync(int userId, string token, string newPassword)
{
var result = base.ResetPasswordAsync(userId, token, newPassword);
if (result.Result.Succeeded)
RaisePasswordResetEvent(userId);
return result;
}
@@ -585,12 +560,6 @@ namespace Umbraco.Web.Security
OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
}
// TODO: I don't think this is required anymore since from 7.7 we no longer display the reset password checkbox since that didn't make sense.
internal void RaisePasswordResetEvent(int userId)
{
OnPasswordReset(new IdentityAuditEventArgs(AuditEvent.PasswordReset, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
}
internal void RaiseResetAccessFailedCountEvent(int userId)
{
OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));

View File

@@ -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
{
@@ -28,7 +29,6 @@ namespace Umbraco.Web.Security
private readonly RoleProvider _roleProvider;
private readonly IMemberService _memberService;
private readonly IMemberTypeService _memberTypeService;
private readonly IUserService _userService;
private readonly IPublicAccessService _publicAccessService;
private readonly AppCaches _appCaches;
private readonly ILogger _logger;
@@ -43,7 +43,6 @@ namespace Umbraco.Web.Security
RoleProvider roleProvider,
IMemberService memberService,
IMemberTypeService memberTypeService,
IUserService userService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
ILogger logger
@@ -53,7 +52,6 @@ namespace Umbraco.Web.Security
MemberCache = memberCache;
_memberService = memberService;
_memberTypeService = memberTypeService;
_userService = userService;
_publicAccessService = publicAccessService;
_appCaches = appCaches;
_logger = logger;
@@ -645,8 +643,8 @@ namespace Umbraco.Web.Security
/// <returns></returns>
public virtual Attempt<PasswordChangedModel> ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider)
{
var passwordChanger = new PasswordChanger(_logger, _userService, HttpContext);
return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider);
var passwordChanger = new PasswordChanger(_logger);
return ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider);
}
/// <summary>
@@ -735,5 +733,63 @@ namespace Umbraco.Web.Security
return sb.ToString();
}
/// <summary>
/// Changes password for a member/user given the membership provider and the password change model
/// </summary>
/// <param name="username">The username of the user having their password changed</param>
/// <param name="passwordModel"></param>
/// <param name="membershipProvider"></param>
/// <returns></returns>
private Attempt<PasswordChangedModel> 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<PasswordChanger>(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" }) });
}
}
}
}

View File

@@ -52,16 +52,6 @@ namespace Umbraco.Web.Security
get { return false; }
}
/// <summary>
/// Providers can override this setting, by default this is false which means that the provider will
/// authenticate the username + password when ChangePassword is called. This property exists purely for
/// backwards compatibility.
/// </summary>
public virtual bool AllowManuallyChangingPassword
{
get { return false; }
}
/// <summary>
/// Returns the raw password value for a given user
/// </summary>
@@ -307,24 +297,10 @@ namespace Umbraco.Web.Security
/// <returns>
/// true if the password was updated successfully; otherwise, false.
/// </returns>
/// <remarks>
/// Checks to ensure the AllowManuallyChangingPassword rule is adhered to
/// </remarks>
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
string rawPasswordValue = string.Empty;
if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false)
{
//we need to lookup the member since this could be a brand new member without a password set
var rawPassword = GetRawPassword(username);
rawPasswordValue = rawPassword.Success ? rawPassword.Result : string.Empty;
if (rawPassword.Success == false || rawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix) == false)
{
//If the old password is empty and AllowManuallyChangingPassword is false, than this provider cannot just arbitrarily change the password
throw new NotSupportedException("This provider does not support manually changing the password");
}
}
var args = new ValidatePasswordEventArgs(username, newPassword, false);
OnValidatingPassword(args);
@@ -339,14 +315,13 @@ namespace Umbraco.Web.Security
// * the member is new and doesn't have a password set
// * during installation to set the admin password
var installing = Current.RuntimeState.Level == RuntimeLevel.Install;
if (AllowManuallyChangingPassword == false
&& (rawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix)
|| (installing && oldPassword == "default")))
if (rawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix)
|| (installing && oldPassword == "default"))
{
return PerformChangePassword(username, oldPassword, newPassword);
}
if (AllowManuallyChangingPassword == false)
if (!oldPassword.IsNullOrWhiteSpace())
{
if (ValidateUser(username, oldPassword) == false) return false;
}
@@ -385,7 +360,7 @@ namespace Umbraco.Web.Security
throw new NotSupportedException("Updating the password Question and Answer is not available if requiresQuestionAndAnswer is not set in web.config");
}
if (AllowManuallyChangingPassword == false)
if (!password.IsNullOrWhiteSpace())
{
if (ValidateUser(username, password) == false)
{

View File

@@ -23,40 +23,6 @@ namespace Umbraco.Web.Security
return membershipMember;
}
/// <summary>
/// Extension method to check if a password can be reset based on a given provider and the current request (logged in user)
/// </summary>
/// <param name="provider"></param>
/// <param name="userService"></param>
/// <returns></returns>
/// <remarks>
/// An Admin can always reset the password
/// </remarks>
internal static bool CanResetPassword(this MembershipProvider provider, IUserService userService)
{
if (provider == null) throw new ArgumentNullException("provider");
var canReset = provider.EnablePasswordReset;
if (userService == null) return canReset;
//we need to check for the special case in which a user is an admin - in which case they can reset the password even if EnablePasswordReset == false
if (provider.EnablePasswordReset == false)
{
var identity = Thread.CurrentPrincipal.GetUmbracoIdentity();
if (identity != null)
{
var user = userService.GetUserById(identity.Id.TryConvertTo<int>().Result);
if (user == null) throw new InvalidOperationException("No user with username " + identity.Username + " found");
var userIsAdmin = user.IsAdmin();
if (userIsAdmin)
{
canReset = true;
}
}
}
return canReset;
}
/// <summary>
/// Method to get the Umbraco Members membership provider based on its alias

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Web.Security.Providers
/// <summary>
/// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS)
/// </summary>
public class MembersMembershipProvider : UmbracoMembershipProvider<IMembershipMemberService, IMember>, IUmbracoMemberTypeMembershipProvider
public class MembersMembershipProvider : UmbracoMembershipProvider<IMembershipMemberService, IMember>
{
public MembersMembershipProvider()
: this(Current.Services.MemberService, Current.Services.MemberTypeService, Current.UmbracoVersion, Current.HostingEnvironment, Current.IpResolver)

View File

@@ -44,16 +44,6 @@ namespace Umbraco.Web.Security.Providers
protected abstract MembershipUser ConvertToMembershipUser(TEntity entity);
private bool _allowManuallyChangingPassword = false;
/// <summary>
/// For backwards compatibility, this provider supports this option by default it is false
/// </summary>
public override bool AllowManuallyChangingPassword
{
get { return _allowManuallyChangingPassword; }
}
/// <summary>
/// Initializes the provider.
/// </summary>
@@ -72,8 +62,6 @@ namespace Umbraco.Web.Security.Providers
// Initialize base provider class
base.Initialize(name, config);
_allowManuallyChangingPassword = config.GetValue("allowManuallyChangingPassword", false);
}
/// <summary>
@@ -531,7 +519,7 @@ namespace Umbraco.Web.Security.Providers
};
}
var authenticated = PasswordSecurity.CheckPassword(password, member.RawPasswordValue);
var authenticated = PasswordSecurity.VerifyPassword(password, member.RawPasswordValue);
if (authenticated == false)
{