Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7
# Conflicts: # src/Umbraco.Core/Security/BackOfficeSignInManager.cs # src/Umbraco.Core/Security/BackOfficeUserManager.cs # src/Umbraco.Web/Editors/AuthenticationController.cs # src/Umbraco.Web/Security/MembershipHelper.cs # src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
This commit is contained in:
106
src/Umbraco.Core/Auditing/IdentityAuditEventArgs.cs
Normal file
106
src/Umbraco.Core/Auditing/IdentityAuditEventArgs.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Core.Auditing
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used by events raised from hthe BackofficeUserManager
|
||||
/// </summary>
|
||||
public class IdentityAuditEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The action that got triggered from the audit event
|
||||
/// </summary>
|
||||
public AuditEvent Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current date/time in UTC format
|
||||
/// </summary>
|
||||
public DateTime DateTimeUtc { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source IP address of the user performing the action
|
||||
/// </summary>
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user affected by the event raised
|
||||
/// </summary>
|
||||
public int AffectedUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a user is perfoming an action on a different user, then this will be set. Otherwise it will be -1
|
||||
/// </summary>
|
||||
public int PerformingUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional comment about the action being logged
|
||||
/// </summary>
|
||||
public string Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This property is always empty except in the LoginFailed event for an unknown user trying to login
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the properties on the event being raised, all parameters are optional except for the action being performed
|
||||
/// </summary>
|
||||
/// <param name="action">An action based on the AuditEvent enum</param>
|
||||
/// <param name="ipAddress">The client's IP address. This is usually automatically set but could be overridden if necessary</param>
|
||||
/// <param name="performingUser">The Id of the user performing the action (if different from the user affected by the action)</param>
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress = "", int performingUser = -1)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
Action = action;
|
||||
|
||||
IpAddress = string.IsNullOrWhiteSpace(ipAddress)
|
||||
? GetCurrentRequestIpAddress()
|
||||
: ipAddress;
|
||||
|
||||
PerformingUser = performingUser == -1
|
||||
? GetCurrentRequestBackofficeUserId()
|
||||
: performingUser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current request IP address for logging if there is one
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string GetCurrentRequestIpAddress()
|
||||
{
|
||||
var httpContext = HttpContext.Current == null ? (HttpContextBase)null : new HttpContextWrapper(HttpContext.Current);
|
||||
return httpContext.GetCurrentRequestIpAddress();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current logged in backoffice user's Id logging if there is one
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected int GetCurrentRequestBackofficeUserId()
|
||||
{
|
||||
var userId = -1;
|
||||
var backOfficeIdentity = Thread.CurrentPrincipal.GetUmbracoIdentity();
|
||||
if (backOfficeIdentity != null)
|
||||
int.TryParse(backOfficeIdentity.Id.ToString(), out userId);
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
|
||||
public enum AuditEvent
|
||||
{
|
||||
AccountLocked,
|
||||
AccountUnlocked,
|
||||
ForgotPasswordRequested,
|
||||
ForgotPasswordChangedSuccess,
|
||||
LoginFailed,
|
||||
LoginRequiresVerification,
|
||||
LoginSucces,
|
||||
LogoutSuccess,
|
||||
PasswordChanged,
|
||||
PasswordReset,
|
||||
ResetAccessFailedCount
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace Umbraco.Core.Security
|
||||
public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
|
||||
{
|
||||
var result = await PasswordSignInAsyncImpl(userName, password, isPersistent, shouldLockout);
|
||||
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
@@ -122,15 +122,34 @@ namespace Umbraco.Core.Security
|
||||
return await SignInOrTwoFactor(user, isPersistent);
|
||||
}
|
||||
|
||||
var requestContext = _request.Context;
|
||||
|
||||
if (user.HasIdentity && shouldLockout)
|
||||
{
|
||||
// If lockout is requested, increment access failed count which might lock out the user
|
||||
await UserManager.AccessFailedAsync(user.Id);
|
||||
if (await UserManager.IsLockedOutAsync(user.Id))
|
||||
{
|
||||
//at this point we've just locked the user out after too many failed login attempts
|
||||
|
||||
if (requestContext != null)
|
||||
{
|
||||
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
|
||||
if (backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseAccountLockedEvent(user.Id);
|
||||
}
|
||||
|
||||
return SignInStatus.LockedOut;
|
||||
}
|
||||
}
|
||||
|
||||
if (requestContext != null)
|
||||
{
|
||||
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
|
||||
if (backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseInvalidLoginAttemptEvent(userName);
|
||||
}
|
||||
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
|
||||
@@ -183,7 +202,7 @@ namespace Umbraco.Core.Security
|
||||
AllowRefresh = true,
|
||||
IssuedUtc = nowUtc,
|
||||
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -198,7 +217,9 @@ namespace Umbraco.Core.Security
|
||||
|
||||
//track the last login date
|
||||
user.LastLoginDateUtc = DateTime.UtcNow;
|
||||
user.AccessFailedCount = 0;
|
||||
if (user.AccessFailedCount > 0)
|
||||
//we have successfully logged in, reset the AccessFailedCount
|
||||
user.AccessFailedCount = 0;
|
||||
await UserManager.UpdateAsync(user);
|
||||
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration.Provider;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -7,9 +8,11 @@ using System.Web.Security;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.Owin;
|
||||
using Microsoft.Owin.Security.DataProtection;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
@@ -43,7 +46,7 @@ namespace Umbraco.Core.Security
|
||||
IContentSection contentSectionConfig)
|
||||
: base(store)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException("options"); ;
|
||||
if (options == null) throw new ArgumentNullException("options");
|
||||
InitUserManager(this, membershipProvider, contentSectionConfig, options);
|
||||
}
|
||||
|
||||
@@ -403,7 +406,10 @@ namespace Umbraco.Core.Security
|
||||
|
||||
public override Task<IdentityResult> ChangePasswordAsync(int userId, string currentPassword, string newPassword)
|
||||
{
|
||||
return base.ChangePasswordAsync(userId, currentPassword, newPassword);
|
||||
var result = base.ChangePasswordAsync(userId, currentPassword, newPassword);
|
||||
if (result.Result.Succeeded)
|
||||
RaisePasswordChangedEvent(userId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -484,5 +490,241 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override Task<IdentityResult> SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd)
|
||||
{
|
||||
var result = base.SetLockoutEndDateAsync(userId, lockoutEnd);
|
||||
|
||||
// The way we unlock is by setting the lockoutEnd date to the current datetime
|
||||
if (result.Result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
|
||||
RaiseAccountLockedEvent(userId);
|
||||
else
|
||||
RaiseAccountUnlockedEvent(userId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override async Task<IdentityResult> ResetAccessFailedCountAsync(int userId)
|
||||
{
|
||||
var user = ApplicationContext.Current.Services.UserService.GetUserById(userId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ProviderException(string.Format("No user with the id {0} found", userId));
|
||||
}
|
||||
|
||||
if (user.FailedPasswordAttempts > 0)
|
||||
{
|
||||
user.FailedPasswordAttempts = 0;
|
||||
ApplicationContext.Current.Services.UserService.Save(user);
|
||||
RaiseResetAccessFailedCountEvent(userId);
|
||||
}
|
||||
|
||||
return await Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears a lock so that the membership user can be validated.
|
||||
/// </summary>
|
||||
/// <param name="memberService">The IMemberService to user for unlocking</param>
|
||||
/// <param name="username">The membership user to clear the lock status for.</param>
|
||||
/// <returns>
|
||||
/// true if the membership user was successfully unlocked; otherwise, false.
|
||||
/// </returns>
|
||||
public bool UnlockUser<TEntity>(IMembershipMemberService<TEntity> memberService, string username) where TEntity : class, IMembershipUser
|
||||
{
|
||||
var member = memberService.GetByUsername(username);
|
||||
|
||||
if (member == null)
|
||||
{
|
||||
throw new ProviderException(string.Format("No member with the username '{0}' found", username));
|
||||
}
|
||||
|
||||
// Non need to update
|
||||
if (member.IsLockedOut == false) return true;
|
||||
|
||||
member.IsLockedOut = false;
|
||||
member.FailedPasswordAttempts = 0;
|
||||
|
||||
memberService.Save(member);
|
||||
|
||||
RaiseAccountUnlockedEvent(member.Id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Task<IdentityResult> AccessFailedAsync(int userId)
|
||||
{
|
||||
var result = base.AccessFailedAsync(userId);
|
||||
|
||||
//Slightly confusing: this will return a Success if we successfully update the AccessFailed count
|
||||
if (result.Result.Succeeded)
|
||||
RaiseLoginFailedEvent(userId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void RaiseAccountLockedEvent(int userId)
|
||||
{
|
||||
OnAccountLocked(new IdentityAuditEventArgs(AuditEvent.AccountLocked)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseAccountUnlockedEvent(int userId)
|
||||
{
|
||||
OnAccountUnlocked(new IdentityAuditEventArgs(AuditEvent.AccountUnlocked)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseForgotPasswordRequestedEvent(int userId)
|
||||
{
|
||||
OnForgotPasswordRequested(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordRequested)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseForgotPasswordChangedSuccessEvent(int userId)
|
||||
{
|
||||
OnForgotPasswordChangedSuccess(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordChangedSuccess)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
public void RaiseLoginFailedEvent(int userId)
|
||||
{
|
||||
OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
public void RaiseInvalidLoginAttemptEvent(string username)
|
||||
{
|
||||
OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed)
|
||||
{
|
||||
Username = username,
|
||||
Comment = string.Format("Attempted login for username '{0}' failed", username)
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseLoginRequiresVerificationEvent(int userId)
|
||||
{
|
||||
OnLoginRequiresVerification(new IdentityAuditEventArgs(AuditEvent.LoginRequiresVerification)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseLoginSuccessEvent(int userId)
|
||||
{
|
||||
OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseLogoutSuccessEvent(int userId)
|
||||
{
|
||||
OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaisePasswordChangedEvent(int userId)
|
||||
{
|
||||
OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaisePasswordResetEvent(int userId)
|
||||
{
|
||||
OnPasswordReset(new IdentityAuditEventArgs(AuditEvent.PasswordReset)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
internal void RaiseResetAccessFailedCountEvent(int userId)
|
||||
{
|
||||
OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount)
|
||||
{
|
||||
AffectedUser = userId
|
||||
});
|
||||
}
|
||||
|
||||
public static event EventHandler AccountLocked;
|
||||
public static event EventHandler AccountUnlocked;
|
||||
public static event EventHandler ForgotPasswordRequested;
|
||||
public static event EventHandler ForgotPasswordChangedSuccess;
|
||||
public static event EventHandler LoginFailed;
|
||||
public static event EventHandler LoginRequiresVerification;
|
||||
public static event EventHandler LoginSuccess;
|
||||
public static event EventHandler LogoutSuccess;
|
||||
public static event EventHandler PasswordChanged;
|
||||
public static event EventHandler PasswordReset;
|
||||
public static event EventHandler ResetAccessFailedCount;
|
||||
|
||||
protected virtual void OnAccountLocked(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (AccountLocked != null) AccountLocked(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnAccountUnlocked(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (AccountUnlocked != null) AccountUnlocked(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnForgotPasswordRequested(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (ForgotPasswordRequested != null) ForgotPasswordRequested(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnForgotPasswordChangedSuccess(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (ForgotPasswordChangedSuccess != null) ForgotPasswordChangedSuccess(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnLoginFailed(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (LoginFailed != null) LoginFailed(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnLoginRequiresVerification(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (LoginRequiresVerification != null) LoginRequiresVerification(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnLoginSuccess(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (LoginSuccess != null) LoginSuccess(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnLogoutSuccess(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (LogoutSuccess != null) LogoutSuccess(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnPasswordChanged(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (PasswordChanged != null) PasswordChanged(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnPasswordReset(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (PasswordReset != null) PasswordReset(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnResetAccessFailedCount(IdentityAuditEventArgs e)
|
||||
{
|
||||
if (ResetAccessFailedCount != null) ResetAccessFailedCount(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
<Compile Include="Attempt{T}.cs" />
|
||||
<Compile Include="Auditing\Audit.cs" />
|
||||
<Compile Include="Auditing\AuditTypes.cs" />
|
||||
<Compile Include="Auditing\IdentityAuditEventArgs.cs" />
|
||||
<Compile Include="BindingRedirects.cs" />
|
||||
<Compile Include="ByteArrayExtensions.cs" />
|
||||
<Compile Include="CacheHelper.cs" />
|
||||
|
||||
@@ -56,12 +56,12 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
[WebApi.UmbracoAuthorize(requireApproval: false)]
|
||||
public IDictionary<string, object> GetMembershipProviderConfig()
|
||||
{
|
||||
//TODO: Check if the current PasswordValidator is an IMembershipProviderPasswordValidator, if
|
||||
{
|
||||
//TODO: Check if the current PasswordValidator is an IMembershipProviderPasswordValidator, if
|
||||
//it's not than we should return some generic defaults
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider();
|
||||
return provider.GetConfiguration(Services.UserService);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a valid token is specified for an invited user and if so logs the user in and returns the user object
|
||||
@@ -76,13 +76,13 @@ namespace Umbraco.Web.Editors
|
||||
public async Task<UserDisplay> PostVerifyInvite([FromUri]int id, [FromUri]string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
|
||||
var decoded = token.FromUrlBase64();
|
||||
if (decoded.IsNullOrWhiteSpace())
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
|
||||
var identityUser = await UserManager.FindByIdAsync(id);
|
||||
var identityUser = await UserManager.FindByIdAsync(id);
|
||||
if (identityUser == null)
|
||||
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
|
||||
@@ -95,15 +95,15 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
Request.TryGetOwinContext().Result.Authentication.SignOut(
|
||||
Core.Constants.Security.BackOfficeAuthenticationType,
|
||||
Core.Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
Core.Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
|
||||
await SignInManager.SignInAsync(identityUser, false, false);
|
||||
|
||||
var user = ApplicationContext.Services.UserService.GetUserById(id);
|
||||
var user = ApplicationContext.Services.UserService.GetUserById(id);
|
||||
|
||||
return Mapper.Map<UserDisplay>(user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[WebApi.UmbracoAuthorize]
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
public async Task<HttpResponseMessage> PostUnLinkLogin(UnLinkLoginModel unlinkLoginModel)
|
||||
@@ -136,7 +136,7 @@ namespace Umbraco.Web.Editors
|
||||
if (attempt == ValidateRequestAttempt.Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Umbraco.Web.Editors
|
||||
/// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we need to re-set the csrf cookies.
|
||||
/// </remarks>
|
||||
[WebApi.UmbracoAuthorize]
|
||||
[SetAngularAntiForgeryTokens]
|
||||
[SetAngularAntiForgeryTokens]
|
||||
[CheckIfUserTicketDataIsStale]
|
||||
public UserDetail GetCurrentUser()
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// When a user is invited they are not approved but we need to resolve the partially logged on (non approved)
|
||||
/// user.
|
||||
@@ -175,7 +175,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <remarks>
|
||||
/// We cannot user GetCurrentUser since that requires they are approved, this is the same as GetCurrentUser but doesn't require them to be approved
|
||||
/// </remarks>
|
||||
[WebApi.UmbracoAuthorize(requireApproval:false)]
|
||||
[WebApi.UmbracoAuthorize(requireApproval: false)]
|
||||
[SetAngularAntiForgeryTokens]
|
||||
public UserDetail GetCurrentInvitedUser()
|
||||
{
|
||||
@@ -185,7 +185,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
//if they are approved, than they are no longer invited and we can return an error
|
||||
throw new HttpResponseException(Request.CreateUserNoAccessResponse());
|
||||
}
|
||||
}
|
||||
|
||||
var result = Mapper.Map<UserDetail>(user);
|
||||
var httpContextAttempt = TryGetHttpContext();
|
||||
@@ -197,11 +197,11 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//TODO: This should be on the CurrentUserController?
|
||||
[WebApi.UmbracoAuthorize]
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
public async Task<Dictionary<string, string>> GetCurrentUserLinkedLogins()
|
||||
public async Task<Dictionary<string, string>> GetCurrentUserLinkedLogins()
|
||||
{
|
||||
var identityUser = await UserManager.FindByIdAsync(UmbracoContext.Security.GetUserId());
|
||||
return identityUser.Logins.ToDictionary(x => x.LoginProvider, x => x.ProviderKey);
|
||||
@@ -215,18 +215,22 @@ namespace Umbraco.Web.Editors
|
||||
public async Task<HttpResponseMessage> PostLogin(LoginModel loginModel)
|
||||
{
|
||||
var http = EnsureHttpContext();
|
||||
|
||||
//Sign the user in with username/password, this also gives a chance for developers to
|
||||
|
||||
//Sign the user in with username/password, this also gives a chance for developers to
|
||||
//custom verify the credentials and auto-link user accounts with a custom IBackOfficePasswordChecker
|
||||
var result = await SignInManager.PasswordSignInAsync(
|
||||
loginModel.Username, loginModel.Password, isPersistent: true, shouldLockout: true);
|
||||
|
||||
loginModel.Username, loginModel.Password, isPersistent: true, shouldLockout: true);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
|
||||
|
||||
//get the user
|
||||
var user = Services.UserService.GetByUsername(loginModel.Username);
|
||||
|
||||
if (UserManager != null)
|
||||
UserManager.RaiseLoginSuccessEvent(user.Id);
|
||||
|
||||
return SetPrincipalAndReturnUserDetail(user);
|
||||
case SignInStatus.RequiresVerification:
|
||||
|
||||
@@ -235,10 +239,10 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
throw new HttpResponseException(
|
||||
Request.CreateErrorResponse(
|
||||
HttpStatusCode.BadRequest,
|
||||
HttpStatusCode.BadRequest,
|
||||
"UserManager does not implement " + typeof(IUmbracoBackOfficeTwoFactorOptions)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var twofactorView = twofactorOptions.GetTwoFactorView(
|
||||
TryGetOwinContext().Result,
|
||||
UmbracoContext,
|
||||
@@ -252,24 +256,27 @@ namespace Umbraco.Web.Editors
|
||||
typeof(IUmbracoBackOfficeTwoFactorOptions) + ".GetTwoFactorView returned an empty string"));
|
||||
}
|
||||
|
||||
var attemptedUser = Services.UserService.GetByUsername(loginModel.Username);
|
||||
|
||||
//create a with information to display a custom two factor send code view
|
||||
var attemptedUser = Services.UserService.GetByUsername(loginModel.Username);
|
||||
|
||||
//create a with information to display a custom two factor send code view
|
||||
var verifyResponse = Request.CreateResponse(HttpStatusCode.PaymentRequired, new
|
||||
{
|
||||
twoFactorView = twofactorView,
|
||||
userId = attemptedUser.Id
|
||||
});
|
||||
|
||||
if (UserManager != null)
|
||||
UserManager.RaiseLoginRequiresVerificationEvent(attemptedUser.Id);
|
||||
|
||||
return verifyResponse;
|
||||
|
||||
case SignInStatus.LockedOut:
|
||||
case SignInStatus.Failure:
|
||||
default:
|
||||
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
|
||||
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
|
||||
// by our angular helper because it thinks that we need to re-perform the request once we are
|
||||
// authorized and we don't want to return a 403 because angular will show a warning msg indicating
|
||||
// that the user doesn't have access to perform this function, we just want to return a normal invalid msg.
|
||||
// authorized and we don't want to return a 403 because angular will show a warning msg indicating
|
||||
// that the user doesn't have access to perform this function, we just want to return a normal invalid msg.
|
||||
throw new HttpResponseException(HttpStatusCode.BadRequest);
|
||||
}
|
||||
}
|
||||
@@ -297,16 +304,19 @@ namespace Umbraco.Web.Editors
|
||||
var code = await UserManager.GeneratePasswordResetTokenAsync(identityUser.Id);
|
||||
var callbackUrl = ConstructCallbackUrl(identityUser.Id, code);
|
||||
|
||||
var message = Services.TextService.Localize("resetPasswordEmailCopyFormat",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
var message = Services.TextService.Localize("resetPasswordEmailCopyFormat",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
UserExtensions.GetUserCulture(identityUser.Culture, Services.TextService),
|
||||
new[] {identityUser.UserName, callbackUrl});
|
||||
new[] { identityUser.UserName, callbackUrl });
|
||||
|
||||
await UserManager.SendEmailAsync(identityUser.Id,
|
||||
Services.TextService.Localize("login/resetPasswordEmailCopySubject",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
Services.TextService.Localize("login/resetPasswordEmailCopySubject",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
UserExtensions.GetUserCulture(identityUser.Culture, Services.TextService)),
|
||||
message);
|
||||
|
||||
if (UserManager != null)
|
||||
UserManager.RaiseForgotPasswordRequestedEvent(user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,21 +376,27 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: true, rememberBrowser: false);
|
||||
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: true, rememberBrowser: false);
|
||||
|
||||
var user = Services.UserService.GetByUsername(userName);
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
//get the user
|
||||
var user = Services.UserService.GetByUsername(userName);
|
||||
if (UserManager != null)
|
||||
UserManager.RaiseLoginSuccessEvent(user.Id);
|
||||
|
||||
return SetPrincipalAndReturnUserDetail(user);
|
||||
case SignInStatus.LockedOut:
|
||||
return Request.CreateValidationErrorResponse("User is locked out");
|
||||
if (UserManager != null)
|
||||
UserManager.RaiseAccountLockedEvent(user.Id);
|
||||
|
||||
return Request.CreateValidationErrorResponse("User is locked out");
|
||||
case SignInStatus.Failure:
|
||||
default:
|
||||
return Request.CreateValidationErrorResponse("Invalid code");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a set password request. Validates the request and sets a new password.
|
||||
/// </summary>
|
||||
@@ -396,16 +412,16 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
Logger.Info<AuthenticationController>(
|
||||
"User {0} is currently locked out, unlocking and resetting AccessFailedCount",
|
||||
() => model.UserId);
|
||||
() => model.UserId);
|
||||
|
||||
//var user = await UserManager.FindByIdAsync(model.UserId);
|
||||
var unlockResult = await UserManager.SetLockoutEndDateAsync(model.UserId, DateTimeOffset.Now);
|
||||
if(unlockResult.Succeeded == false)
|
||||
var unlockResult = await UserManager.SetLockoutEndDateAsync(model.UserId, DateTimeOffset.Now);
|
||||
if (unlockResult.Succeeded == false)
|
||||
{
|
||||
Logger.Warn<AuthenticationController>("Could not unlock for user {0} - error {1}",
|
||||
() => model.UserId, () => unlockResult.Errors.First());
|
||||
}
|
||||
|
||||
() => model.UserId, () => unlockResult.Errors.First());
|
||||
}
|
||||
|
||||
var resetAccessFailedCountResult = await UserManager.ResetAccessFailedCountAsync(model.UserId);
|
||||
if (resetAccessFailedCountResult.Succeeded == false)
|
||||
{
|
||||
@@ -414,6 +430,8 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
|
||||
if (UserManager != null)
|
||||
UserManager.RaiseForgotPasswordChangedSuccessEvent(model.UserId);
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
return Request.CreateValidationErrorResponse(
|
||||
@@ -437,10 +455,16 @@ namespace Umbraco.Web.Editors
|
||||
() => User.Identity == null ? "UNKNOWN" : User.Identity.Name,
|
||||
() => TryGetOwinContext().Result.Request.RemoteIpAddress);
|
||||
|
||||
if (UserManager != null)
|
||||
{
|
||||
var userId = -1;
|
||||
int.TryParse(User.Identity.GetUserId(), out userId);
|
||||
UserManager.RaiseLogoutSuccessEvent(userId);
|
||||
}
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is used when the user is auth'd successfully and we need to return an OK with user details along with setting the current Principal in the request
|
||||
/// </summary>
|
||||
@@ -468,7 +492,7 @@ namespace Umbraco.Web.Editors
|
||||
// Get an mvc helper to get the url
|
||||
var http = EnsureHttpContext();
|
||||
var urlHelper = new UrlHelper(http.Request.RequestContext);
|
||||
var action = urlHelper.Action("ValidatePasswordResetCode", "BackOffice",
|
||||
var action = urlHelper.Action("ValidatePasswordResetCode", "BackOffice",
|
||||
new
|
||||
{
|
||||
area = GlobalSettings.UmbracoMvcArea,
|
||||
@@ -480,19 +504,19 @@ namespace Umbraco.Web.Editors
|
||||
var applicationUri = new Uri(ApplicationContext.UmbracoApplicationUrl);
|
||||
var callbackUri = new Uri(applicationUri, action);
|
||||
return callbackUri.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private HttpContextBase EnsureHttpContext()
|
||||
{
|
||||
var attempt = this.TryGetHttpContext();
|
||||
if (attempt.Success == false)
|
||||
throw new InvalidOperationException("This method requires that an HttpContext be active");
|
||||
return attempt.Result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void AddModelErrors(IdentityResult result, string prefix = "")
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Web.Editors
|
||||
/// </returns>
|
||||
public async Task<ModelWithNotifications<string>> PostChangePassword(ChangingPasswordModel data)
|
||||
{
|
||||
var passwordChanger = new PasswordChanger(Logger, Services.UserService);
|
||||
var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);
|
||||
var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, Security.CurrentUser, data, UserManager);
|
||||
|
||||
if (passwordChangeResult.Success)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using System.Web.Security;
|
||||
using Microsoft.AspNet.Identity;
|
||||
@@ -22,11 +23,13 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserService _userService;
|
||||
private readonly HttpContextBase _httpContext;
|
||||
|
||||
public PasswordChanger(ILogger logger, IUserService userService)
|
||||
public PasswordChanger(ILogger logger, IUserService userService, HttpContextBase httpContext)
|
||||
{
|
||||
_logger = logger;
|
||||
_userService = userService;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -148,6 +151,20 @@ namespace Umbraco.Web.Editors
|
||||
if (passwordModel == null) throw new ArgumentNullException("passwordModel");
|
||||
if (membershipProvider == null) throw new ArgumentNullException("membershipProvider");
|
||||
|
||||
BackOfficeUserManager<BackOfficeIdentityUser> backofficeUserManager = null;
|
||||
var userId = -1;
|
||||
|
||||
if (membershipProvider.IsUmbracoUsersProvider())
|
||||
{
|
||||
backofficeUserManager = _httpContext.GetOwinContext().GetBackOfficeUserManager();
|
||||
if (backofficeUserManager != null)
|
||||
{
|
||||
var profile = _userService.GetProfileByUserName(username);
|
||||
if (profile != null)
|
||||
int.TryParse(profile.Id.ToString(), out userId);
|
||||
}
|
||||
}
|
||||
|
||||
//Are we resetting the password??
|
||||
if (passwordModel.Reset.HasValue && passwordModel.Reset.Value)
|
||||
{
|
||||
@@ -167,6 +184,9 @@ namespace Umbraco.Web.Editors
|
||||
username,
|
||||
membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null);
|
||||
|
||||
if (membershipProvider.IsUmbracoUsersProvider() && backofficeUserManager != null && userId >= 0)
|
||||
backofficeUserManager.RaisePasswordResetEvent(userId);
|
||||
|
||||
//return the generated pword
|
||||
return Attempt.Succeed(new PasswordChangedModel { ResetPassword = newPass });
|
||||
}
|
||||
@@ -217,6 +237,10 @@ namespace Umbraco.Web.Editors
|
||||
try
|
||||
{
|
||||
var result = membershipProvider.ChangePassword(username, passwordModel.OldPassword, passwordModel.NewPassword);
|
||||
|
||||
if (result && backofficeUserManager != null && userId >= 0)
|
||||
backofficeUserManager.RaisePasswordChangedEvent(userId);
|
||||
|
||||
return result == false
|
||||
? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "oldPassword" }) })
|
||||
: Attempt.Succeed(new PasswordChangedModel());
|
||||
@@ -266,6 +290,5 @@ namespace Umbraco.Web.Editors
|
||||
return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex2.Message + " (see log for full details)", new[] { "value" }) });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -557,7 +557,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
if (userSave.ChangePassword != null)
|
||||
{
|
||||
var passwordChanger = new PasswordChanger(Logger, Services.UserService);
|
||||
var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);
|
||||
|
||||
var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager);
|
||||
if (passwordChangeResult.Success)
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Web.Security
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MembershipHelper(ApplicationContext applicationContext, HttpContextBase httpContext)
|
||||
: this(applicationContext, httpContext, MPE.GetMembersMembershipProvider(), Roles.Enabled ? Roles.Provider : new MembersRoleProvider(applicationContext.Services.MemberService))
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor specifying an UmbracoContext")]
|
||||
@@ -54,11 +54,11 @@ namespace Umbraco.Web.Security
|
||||
_httpContext = httpContext;
|
||||
_membershipProvider = membershipProvider;
|
||||
_roleProvider = roleProvider;
|
||||
}
|
||||
}
|
||||
|
||||
public MembershipHelper(UmbracoContext umbracoContext)
|
||||
: this(umbracoContext, MPE.GetMembersMembershipProvider(), Roles.Enabled ? Roles.Provider: new MembersRoleProvider(umbracoContext.Application.Services.MemberService))
|
||||
{
|
||||
: this(umbracoContext, MPE.GetMembersMembershipProvider(), Roles.Enabled ? Roles.Provider : new MembersRoleProvider(umbracoContext.Application.Services.MemberService))
|
||||
{
|
||||
}
|
||||
|
||||
public MembershipHelper(UmbracoContext umbracoContext, MembershipProvider membershipProvider, RoleProvider roleProvider)
|
||||
@@ -117,11 +117,11 @@ namespace Umbraco.Web.Security
|
||||
/// </remarks>
|
||||
private bool HasAccess(string path, RoleProvider roleProvider)
|
||||
{
|
||||
return _umbracoContext.PublishedContentRequest == null
|
||||
? _applicationContext.Services.PublicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser)
|
||||
return _umbracoContext.PublishedContentRequest == null
|
||||
? _applicationContext.Services.PublicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser)
|
||||
: _applicationContext.Services.PublicAccessService.HasAccess(path, CurrentUserName, _umbracoContext.PublishedContentRequest.GetRolesForLogin);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current membership provider is the Umbraco built-in one.
|
||||
/// </summary>
|
||||
@@ -253,7 +253,7 @@ namespace Umbraco.Web.Security
|
||||
{
|
||||
//Set member online
|
||||
provider.GetUser(model.Username, true);
|
||||
|
||||
|
||||
//Log them in
|
||||
FormsAuthentication.SetAuthCookie(membershipUser.UserName, model.CreatePersistentLoginCookie);
|
||||
}
|
||||
@@ -280,7 +280,7 @@ namespace Umbraco.Web.Security
|
||||
if (member == null)
|
||||
{
|
||||
//this should not happen
|
||||
LogHelper.Warn<MembershipHelper>("The member validated but then no member was returned with the username " + username);
|
||||
LogHelper.Warn<MembershipHelper>("The member validated but then no member was returned with the username " + username);
|
||||
return false;
|
||||
}
|
||||
//Log them in
|
||||
@@ -389,7 +389,7 @@ namespace Umbraco.Web.Security
|
||||
var result = GetCurrentMember();
|
||||
return result == null ? -1 : result.Id;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Model Creation methods for member data editing on the front-end
|
||||
@@ -408,7 +408,7 @@ namespace Umbraco.Web.Security
|
||||
var provider = _membershipProvider;
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider())
|
||||
{
|
||||
{
|
||||
var membershipUser = provider.GetCurrentUserOnline();
|
||||
var member = GetCurrentPersistedMember();
|
||||
//this shouldn't happen but will if the member is deleted in the back office while the member is trying
|
||||
@@ -496,7 +496,7 @@ namespace Umbraco.Web.Security
|
||||
if (propValue != null && propValue.Value != null)
|
||||
{
|
||||
value = propValue.Value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var viewProperty = new UmbracoProperty
|
||||
@@ -636,7 +636,7 @@ namespace Umbraco.Web.Security
|
||||
|
||||
// Allow by default
|
||||
var allowAction = true;
|
||||
|
||||
|
||||
if (IsLoggedIn() == false)
|
||||
{
|
||||
// If not logged on, not allowed
|
||||
@@ -671,7 +671,7 @@ namespace Umbraco.Web.Security
|
||||
var member = provider.GetCurrentUser();
|
||||
username = member.UserName;
|
||||
}
|
||||
|
||||
|
||||
// If groups defined, check member is of one of those groups
|
||||
var allowGroupsList = allowGroups as IList<string> ?? allowGroups.ToList();
|
||||
if (allowAction && allowGroupsList.Any(allowGroup => allowGroup != string.Empty))
|
||||
@@ -681,7 +681,7 @@ namespace Umbraco.Web.Security
|
||||
allowAction = allowGroupsList.Select(s => s.ToLowerInvariant()).Intersect(groups.Select(myGroup => myGroup.ToLowerInvariant())).Any();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return allowAction;
|
||||
@@ -701,6 +701,7 @@ namespace Umbraco.Web.Security
|
||||
{
|
||||
throw new InvalidOperationException("Could not find provider with name " + membershipProviderName);
|
||||
}
|
||||
|
||||
return ChangePassword(username, passwordModel, provider);
|
||||
}
|
||||
|
||||
@@ -713,10 +714,10 @@ namespace Umbraco.Web.Security
|
||||
/// <returns></returns>
|
||||
public virtual Attempt<PasswordChangedModel> ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider)
|
||||
{
|
||||
var passwordChanger = new PasswordChanger(_applicationContext.ProfilingLogger.Logger, _applicationContext.Services.UserService);
|
||||
return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider);
|
||||
var passwordChanger = new PasswordChanger(_applicationContext.ProfilingLogger.Logger, _applicationContext.Services.UserService, UmbracoContext.Current.HttpContext);
|
||||
return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates a membership user with all of it's writable properties
|
||||
/// </summary>
|
||||
@@ -775,7 +776,7 @@ namespace Umbraco.Web.Security
|
||||
|
||||
return Attempt<MembershipUser>.Fail(member);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently logged in IMember object - this should never be exposed to the front-end since it's returning a business logic entity!
|
||||
/// </summary>
|
||||
@@ -799,7 +800,7 @@ namespace Umbraco.Web.Security
|
||||
|
||||
private string GetCacheKey(string key, params object[] additional)
|
||||
{
|
||||
var sb = new StringBuilder(string.Format("{0}-{1}", typeof (MembershipHelper).Name, key));
|
||||
var sb = new StringBuilder(string.Format("{0}-{1}", typeof(MembershipHelper).Name, key));
|
||||
foreach (var s in additional)
|
||||
{
|
||||
sb.Append("-");
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Specialized;
|
||||
using System.Configuration.Provider;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
@@ -12,6 +13,7 @@ using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
|
||||
namespace Umbraco.Web.Security.Providers
|
||||
{
|
||||
@@ -449,22 +451,15 @@ namespace Umbraco.Web.Security.Providers
|
||||
/// </returns>
|
||||
public override bool UnlockUser(string username)
|
||||
{
|
||||
var member = MemberService.GetByUsername(username);
|
||||
var userManager = GetBackofficeUserManager();
|
||||
return userManager != null && userManager.UnlockUser(MemberService, username);
|
||||
}
|
||||
|
||||
if (member == null)
|
||||
{
|
||||
throw new ProviderException(string.Format("No member with the username '{0}' found", username));
|
||||
}
|
||||
|
||||
// Non need to update
|
||||
if (member.IsLockedOut == false) return true;
|
||||
|
||||
member.IsLockedOut = false;
|
||||
member.FailedPasswordAttempts = 0;
|
||||
|
||||
MemberService.Save(member);
|
||||
|
||||
return true;
|
||||
internal BackOfficeUserManager<BackOfficeIdentityUser> GetBackofficeUserManager()
|
||||
{
|
||||
return HttpContext.Current == null
|
||||
? null
|
||||
: HttpContext.Current.GetOwinContext().GetBackOfficeUserManager();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -547,6 +542,7 @@ namespace Umbraco.Web.Security.Providers
|
||||
}
|
||||
|
||||
var authenticated = CheckPassword(password, member.RawPasswordValue);
|
||||
var backofficeUserManager = GetBackofficeUserManager();
|
||||
|
||||
if (authenticated == false)
|
||||
{
|
||||
@@ -566,6 +562,9 @@ namespace Umbraco.Web.Security.Providers
|
||||
"Login attempt failed for username {0} from IP address {1}, the user is now locked out, max invalid password attempts exceeded",
|
||||
username,
|
||||
GetCurrentRequestIpAddress()));
|
||||
|
||||
if(backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseAccountLockedEvent(member.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -578,7 +577,14 @@ namespace Umbraco.Web.Security.Providers
|
||||
}
|
||||
else
|
||||
{
|
||||
member.FailedPasswordAttempts = 0;
|
||||
if (member.FailedPasswordAttempts > 0)
|
||||
{
|
||||
//we have successfully logged in, reset the AccessFailedCount
|
||||
member.FailedPasswordAttempts = 0;
|
||||
if (backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseResetAccessFailedCountEvent(member.Id);
|
||||
}
|
||||
|
||||
member.LastLoginDate = DateTime.Now;
|
||||
|
||||
LogHelper.Info<UmbracoMembershipProviderBase>(
|
||||
@@ -586,6 +592,9 @@ namespace Umbraco.Web.Security.Providers
|
||||
"Login attempt succeeded for username {0} from IP address {1}",
|
||||
username,
|
||||
GetCurrentRequestIpAddress()));
|
||||
|
||||
if (backofficeUserManager != null)
|
||||
backofficeUserManager.RaiseLoginSuccessEvent(member.Id);
|
||||
}
|
||||
|
||||
//don't raise events for this! It just sets the member dates, if we do raise events this will
|
||||
@@ -600,10 +609,5 @@ namespace Umbraco.Web.Security.Providers
|
||||
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user