User invite flow review (#3000)
This commit is contained in:
committed by
Sebastiaan Janssen
parent
8fad718347
commit
63a2a155d1
@@ -236,7 +236,10 @@ namespace Umbraco.Core.Security
|
||||
|
||||
if (dataProtectionProvider != null)
|
||||
{
|
||||
manager.UserTokenProvider = new DataProtectorTokenProvider<T, int>(dataProtectionProvider.Create("ASP.NET Identity"));
|
||||
manager.UserTokenProvider = new DataProtectorTokenProvider<T, int>(dataProtectionProvider.Create("ASP.NET Identity"))
|
||||
{
|
||||
TokenLifespan = TimeSpan.FromDays(3)
|
||||
};
|
||||
}
|
||||
|
||||
manager.UserLockoutEnabledByDefault = true;
|
||||
@@ -748,6 +751,7 @@ namespace Umbraco.Core.Security
|
||||
var httpContext = HttpContext.Current == null ? (HttpContextBase)null : new HttpContextWrapper(HttpContext.Current);
|
||||
return httpContext.GetCurrentRequestIpAddress();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -630,7 +630,9 @@ namespace Umbraco.Core.Security
|
||||
|| identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
|
||||
//if the LastLoginDate is being set to MinValue, don't convert it ToLocalTime
|
||||
var dt = identityUser.LastLoginDateUtc == DateTime.MinValue ? DateTime.MinValue : identityUser.LastLoginDateUtc.Value.ToLocalTime();
|
||||
user.LastLoginDate = dt;
|
||||
}
|
||||
if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc")
|
||||
|| (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
function init() {
|
||||
// Check if it is a new user
|
||||
var inviteVal = $location.search().invite;
|
||||
//1 = enter password, 2 = password set, 3 = invalid token
|
||||
if (inviteVal && (inviteVal === "1" || inviteVal === "2")) {
|
||||
|
||||
$q.all([
|
||||
@@ -58,6 +59,8 @@
|
||||
$scope.inviteStep = Number(inviteVal);
|
||||
|
||||
});
|
||||
} else if (inviteVal && inviteVal === "3") {
|
||||
$scope.inviteStep = Number(inviteVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,10 +99,18 @@
|
||||
</umb-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div ng-show="invitedUser == null && inviteStep === 3" ng-if="inviteStep === 3" class="umb-login-container">
|
||||
<div class="form">
|
||||
<h1 style="margin-bottom: 10px; text-align: left;">Hi there</h1>
|
||||
<p style="line-height: 1.6; margin-bottom: 25px;">
|
||||
<localize key="user_userinviteExpiredMessage">Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it.</localize>
|
||||
</p>
|
||||
|
||||
<div ng-show="invitedUser == null" class="umb-login-container">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="invitedUser == null && !inviteStep" class="umb-login-container">
|
||||
|
||||
<div class="form">
|
||||
<h1>{{greeting}}</h1>
|
||||
|
||||
@@ -1865,7 +1865,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="goToProfile">Go to user profile</key>
|
||||
<key alias="groupsHelp">Add groups to assign access and permissions</key>
|
||||
<key alias="inviteAnotherUser">Invite another user</key>
|
||||
<key alias="inviteUserHelp">Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco.</key>
|
||||
<key alias="inviteUserHelp">Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours.</key>
|
||||
<key alias="language">Language</key>
|
||||
<key alias="languageHelp">Set the language you will see in menus and dialogs</key>
|
||||
<key alias="lastLockoutDate">Last lockout date</key>
|
||||
@@ -1929,6 +1929,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="userInvited">has been invited</key>
|
||||
<key alias="userInvitedSuccessHelp">An invitation has been sent to the new user with details about how to log in to Umbraco.</key>
|
||||
<key alias="userinviteWelcomeMessage">Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password and add a picture for your avatar.</key>
|
||||
<key alias="userinviteExpiredMessage">Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it.</key>
|
||||
<key alias="userinviteAvatarMessage">Upload a picture to make it easy for other users to recognize you.</key>
|
||||
<key alias="writer">Writer</key>
|
||||
<key alias="translator">Translator</key>
|
||||
|
||||
@@ -1858,7 +1858,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="goToProfile">Go to user profile</key>
|
||||
<key alias="groupsHelp">Add groups to assign access and permissions</key>
|
||||
<key alias="inviteAnotherUser">Invite another user</key>
|
||||
<key alias="inviteUserHelp">Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco.</key>
|
||||
<key alias="inviteUserHelp">Invite new users to give them access to Umbraco. An invite email will be sent to the user with information on how to log in to Umbraco. Invites last for 72 hours.</key>
|
||||
<key alias="language">Language</key>
|
||||
<key alias="languageHelp">Set the language you will see in menus and dialogs</key>
|
||||
<key alias="lastLockoutDate">Last lockout date</key>
|
||||
@@ -1922,6 +1922,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="userInvited">has been invited</key>
|
||||
<key alias="userInvitedSuccessHelp">An invitation has been sent to the new user with details about how to log in to Umbraco.</key>
|
||||
<key alias="userinviteWelcomeMessage">Hello there and welcome to Umbraco! In just 1 minute you’ll be good to go, we just need you to setup a password and add a picture for your avatar.</key>
|
||||
<key alias="userinviteExpiredMessage">Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it.</key>
|
||||
<key alias="userinviteAvatarMessage">Upload a picture to make it easy for other users to recognize you.</key>
|
||||
<key alias="writer">Writer</key>
|
||||
<key alias="translator">Translator</key>
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace Umbraco.Web.Editors
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
|
||||
|
||||
//get the user
|
||||
var user = Services.UserService.GetByUsername(loginModel.Username);
|
||||
UserManager.RaiseLoginSuccessEvent(user.Id);
|
||||
@@ -425,6 +425,34 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
|
||||
//They've successfully set their password, we can now update their user account to be confirmed
|
||||
//if user was only invited, then they have not been approved
|
||||
//but a successful forgot password flow (e.g. if their token had expired and they did a forgot password instead of request new invite)
|
||||
//means we have verified their email
|
||||
if (!UserManager.IsEmailConfirmed(model.UserId))
|
||||
{
|
||||
await UserManager.ConfirmEmailAsync(model.UserId, model.ResetCode);
|
||||
}
|
||||
|
||||
//if the user is invited, enable their account on forgot password
|
||||
var identityUser = await UserManager.FindByIdAsync(model.UserId);
|
||||
//invited is not approved, never logged in, invited date present
|
||||
/*
|
||||
if (LastLoginDate == default && IsApproved == false && InvitedDate != null)
|
||||
return UserState.Invited;
|
||||
*/
|
||||
if (identityUser != null && !identityUser.IsApproved)
|
||||
{
|
||||
var user = Services.UserService.GetByUsername(identityUser.UserName);
|
||||
//also check InvitedDate and never logged in, otherwise this would allow a disabled user to reactivate their account with a forgot password
|
||||
if (user.LastLoginDate == default && user.InvitedDate != null)
|
||||
{
|
||||
user.IsApproved = true;
|
||||
user.InvitedDate = null;
|
||||
Services.UserService.Save(user);
|
||||
}
|
||||
}
|
||||
|
||||
UserManager.RaiseForgotPasswordChangedSuccessEvent(model.UserId);
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
@@ -524,4 +552,4 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,16 @@ namespace Umbraco.Web.Editors
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> VerifyInvite(string invite)
|
||||
{
|
||||
//if you are hitting VerifyInvite, you're already signed in as a different user, and the token is invalid
|
||||
//you'll exit on one of the return RedirectToAction("Default") but you're still logged in so you just get
|
||||
//dumped at the default admin view with no detail
|
||||
if(Security.IsAuthenticated())
|
||||
{
|
||||
AuthenticationManager.SignOut(
|
||||
Core.Constants.Security.BackOfficeAuthenticationType,
|
||||
Core.Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
}
|
||||
|
||||
if (invite == null)
|
||||
{
|
||||
Logger.Warn<BackOfficeController>("VerifyUser endpoint reached with invalid token: NULL");
|
||||
@@ -119,16 +129,15 @@ namespace Umbraco.Web.Editors
|
||||
if (result.Succeeded == false)
|
||||
{
|
||||
Logger.Warn<BackOfficeController>("Could not verify email, Error: " + string.Join(",", result.Errors) + ", Token: " + invite);
|
||||
return RedirectToAction("Default");
|
||||
return new RedirectResult(Url.Action("Default") + "#/login/false?invite=3");
|
||||
}
|
||||
|
||||
//sign the user in
|
||||
|
||||
AuthenticationManager.SignOut(
|
||||
Core.Constants.Security.BackOfficeAuthenticationType,
|
||||
Core.Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
|
||||
DateTime? previousLastLoginDate = identityUser.LastLoginDateUtc;
|
||||
await SignInManager.SignInAsync(identityUser, false, false);
|
||||
//reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to SignInManager would be a breaking change
|
||||
identityUser.LastLoginDateUtc = previousLastLoginDate;
|
||||
await UserManager.UpdateAsync(identityUser);
|
||||
|
||||
return new RedirectResult(Url.Action("Default") + "#/login/false?invite=1");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user