2020-04-23 16:03:12 +01:00
using System ;
2018-06-29 19:52:40 +02:00
using System.Linq ;
using System.Net ;
using System.Net.Http ;
using System.Collections.Generic ;
2020-05-20 00:20:25 +02:00
using System.Net.Mail ;
2018-06-29 19:52:40 +02:00
using System.Security.Principal ;
using System.Threading.Tasks ;
using System.Web ;
using System.Web.Http ;
using System.Web.Mvc ;
2020-03-16 13:53:03 +00:00
using Microsoft.AspNetCore.Identity ;
2018-06-29 19:52:40 +02:00
using Umbraco.Core ;
2018-07-06 17:36:33 +02:00
using Umbraco.Core.Cache ;
2018-06-29 19:52:40 +02:00
using Umbraco.Core.Models ;
using Umbraco.Core.Services ;
using Umbraco.Web.Models ;
using Umbraco.Web.Models.ContentEditing ;
using Umbraco.Web.Mvc ;
using Umbraco.Web.Security ;
using Umbraco.Web.WebApi ;
using Umbraco.Web.WebApi.Filters ;
using Umbraco.Core.Configuration ;
using Umbraco.Core.Logging ;
2018-07-06 17:36:33 +02:00
using Umbraco.Core.Persistence ;
2018-06-29 19:52:40 +02:00
using IUser = Umbraco . Core . Models . Membership . IUser ;
2019-11-25 21:20:00 +11:00
using Umbraco.Core.Mapping ;
2020-01-21 17:03:46 -08:00
using Umbraco.Core.Configuration.UmbracoSettings ;
2020-04-03 11:03:06 +11:00
using Umbraco.Core.Hosting ;
2020-05-18 13:00:32 +01:00
using Umbraco.Extensions ;
2020-02-14 13:04:49 +01:00
using Umbraco.Web.Routing ;
2018-06-29 19:52:40 +02:00
namespace Umbraco.Web.Editors
{
/// <summary>
/// The API controller used for editing content
/// </summary>
[PluginController("UmbracoApi")]
[ValidationFilter]
[AngularJsonOnlyConfiguration]
[IsBackOffice]
public class AuthenticationController : UmbracoApiController
{
2020-05-18 12:06:26 +01:00
private BackOfficeOwinUserManager _userManager ;
2020-04-04 12:59:29 +01:00
private BackOfficeSignInManager _signInManager ;
2019-11-25 21:20:00 +11:00
private readonly IUserPasswordConfiguration _passwordConfiguration ;
2020-04-03 11:03:06 +11:00
private readonly IHostingEnvironment _hostingEnvironment ;
2020-02-10 11:23:23 +01:00
private readonly IRuntimeState _runtimeState ;
2020-03-12 14:36:25 +01:00
private readonly ISecuritySettings _securitySettings ;
2020-05-07 09:34:16 +02:00
private readonly IRequestAccessor _requestAccessor ;
2020-05-20 11:42:23 +02:00
private readonly IEmailSender _emailSender ;
2018-06-29 19:52:40 +02:00
2020-02-14 13:04:49 +01:00
public AuthenticationController (
IUserPasswordConfiguration passwordConfiguration ,
IGlobalSettings globalSettings ,
2020-04-03 11:03:06 +11:00
IHostingEnvironment hostingEnvironment ,
2020-02-14 13:04:49 +01:00
IUmbracoContextAccessor umbracoContextAccessor ,
ISqlContext sqlContext ,
ServiceContext services ,
AppCaches appCaches ,
IProfilingLogger logger ,
IRuntimeState runtimeState ,
UmbracoMapper umbracoMapper ,
2020-03-12 14:36:25 +01:00
ISecuritySettings securitySettings ,
2020-05-07 09:34:16 +02:00
IPublishedUrlProvider publishedUrlProvider ,
2020-05-20 12:43:13 +02:00
IRequestAccessor requestAccessor ,
2020-05-20 11:42:23 +02:00
IEmailSender emailSender )
2020-03-03 11:59:17 +01:00
: base ( globalSettings , umbracoContextAccessor , sqlContext , services , appCaches , logger , runtimeState , umbracoMapper , publishedUrlProvider )
2019-01-31 15:09:31 +11:00
{
2019-11-25 23:10:54 +11:00
_passwordConfiguration = passwordConfiguration ? ? throw new ArgumentNullException ( nameof ( passwordConfiguration ) ) ;
2020-04-03 11:03:06 +11:00
_hostingEnvironment = hostingEnvironment ? ? throw new ArgumentNullException ( nameof ( hostingEnvironment ) ) ;
2020-02-10 11:23:23 +01:00
_runtimeState = runtimeState ? ? throw new ArgumentNullException ( nameof ( runtimeState ) ) ;
2020-03-12 14:36:25 +01:00
_securitySettings = securitySettings ? ? throw new ArgumentNullException ( nameof ( securitySettings ) ) ;
2020-05-07 09:34:16 +02:00
_requestAccessor = requestAccessor ? ? throw new ArgumentNullException ( nameof ( securitySettings ) ) ;
2020-05-20 11:42:23 +02:00
_emailSender = emailSender ;
2019-01-31 15:09:31 +11:00
}
2018-11-28 16:19:50 +01:00
2020-05-18 12:06:26 +01:00
protected BackOfficeOwinUserManager UserManager = > _userManager
? ? ( _userManager = TryGetOwinContext ( ) . Result . GetBackOfficeUserManager ( ) ) ;
2018-07-06 17:36:33 +02:00
2020-04-04 12:59:29 +01:00
protected BackOfficeSignInManager SignInManager = > _signInManager
? ? ( _signInManager = TryGetOwinContext ( ) . Result . GetBackOfficeSignInManager ( ) ) ;
2018-07-06 17:36:33 +02:00
2018-06-29 19:52:40 +02:00
/// <summary>
/// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog
/// </summary>
/// <returns></returns>
[WebApi.UmbracoAuthorize(requireApproval: false)]
2019-12-03 15:28:55 +11:00
public IDictionary < string , object > GetPasswordConfig ( int userId )
2018-06-29 19:52:40 +02:00
{
2019-12-03 15:28:55 +11:00
return _passwordConfiguration . GetConfiguration ( userId ! = UmbracoContext . Security . CurrentUser . Id ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Checks if a valid token is specified for an invited user and if so logs the user in and returns the user object
/// </summary>
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns></returns>
/// <remarks>
/// This will also update the security stamp for the user so it can only be used once
/// </remarks>
[ValidateAngularAntiForgeryToken]
public async Task < UserDisplay > PostVerifyInvite ( [ FromUri ] int id , [ FromUri ] string token )
{
if ( string . IsNullOrWhiteSpace ( token ) )
throw new HttpResponseException ( Request . CreateResponse ( HttpStatusCode . NotFound ) ) ;
var decoded = token . FromUrlBase64 ( ) ;
if ( decoded . IsNullOrWhiteSpace ( ) )
throw new HttpResponseException ( Request . CreateResponse ( HttpStatusCode . NotFound ) ) ;
2020-03-16 13:53:03 +00:00
var identityUser = await UserManager . FindByIdAsync ( id . ToString ( ) ) ;
2018-06-29 19:52:40 +02:00
if ( identityUser = = null )
throw new HttpResponseException ( Request . CreateResponse ( HttpStatusCode . NotFound ) ) ;
2020-03-16 13:53:03 +00:00
var result = await UserManager . ConfirmEmailAsync ( identityUser , decoded ) ;
2018-06-29 19:52:40 +02:00
if ( result . Succeeded = = false )
{
2020-05-04 18:49:02 +01:00
throw new HttpResponseException ( Request . CreateNotificationValidationErrorResponse ( result . Errors . ToErrorMessage ( ) ) ) ;
2018-06-29 19:52:40 +02:00
}
Request . TryGetOwinContext ( ) . Result . Authentication . SignOut (
Core . Constants . Security . BackOfficeAuthenticationType ,
Core . Constants . Security . BackOfficeExternalAuthenticationType ) ;
await SignInManager . SignInAsync ( identityUser , false , false ) ;
var user = Services . UserService . GetUserById ( id ) ;
return Mapper . Map < UserDisplay > ( user ) ;
}
[WebApi.UmbracoAuthorize]
[ValidateAngularAntiForgeryToken]
public async Task < HttpResponseMessage > PostUnLinkLogin ( UnLinkLoginModel unlinkLoginModel )
{
2020-03-16 13:53:03 +00:00
var user = await UserManager . FindByIdAsync ( User . Identity . GetUserId ( ) ) ;
2020-04-30 15:48:32 +01:00
if ( user = = null ) throw new InvalidOperationException ( "Could not find user" ) ;
2020-03-16 13:53:03 +00:00
2018-06-29 19:52:40 +02:00
var result = await UserManager . RemoveLoginAsync (
2020-03-16 13:53:03 +00:00
user ,
unlinkLoginModel . LoginProvider ,
unlinkLoginModel . ProviderKey ) ;
2018-06-29 19:52:40 +02:00
if ( result . Succeeded )
{
await SignInManager . SignInAsync ( user , isPersistent : true , rememberBrowser : false ) ;
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
else
{
AddModelErrors ( result ) ;
return Request . CreateValidationErrorResponse ( ModelState ) ;
}
}
/// <summary>
/// Returns the currently logged in Umbraco user
/// </summary>
/// <returns></returns>
/// <remarks>
/// We have the attribute [SetAngularAntiForgeryTokens] applied because this method is called initially to determine if the user
/// is valid before the login screen is displayed. The Auth cookie can be persisted for up to a day but the csrf cookies are only session
/// 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]
[CheckIfUserTicketDataIsStale]
public UserDetail GetCurrentUser ( )
{
var user = UmbracoContext . Security . CurrentUser ;
var result = Mapper . Map < UserDetail > ( user ) ;
var httpContextAttempt = TryGetHttpContext ( ) ;
if ( httpContextAttempt . Success )
{
//set their remaining seconds
result . SecondsUntilTimeout = httpContextAttempt . Result . GetRemainingAuthSeconds ( ) ;
}
return result ;
}
/// <summary>
/// When a user is invited they are not approved but we need to resolve the partially logged on (non approved)
/// user.
/// </summary>
/// <returns></returns>
/// <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)]
[SetAngularAntiForgeryTokens]
public UserDetail GetCurrentInvitedUser ( )
{
var user = UmbracoContext . Security . CurrentUser ;
if ( user . IsApproved )
{
2019-01-26 10:52:19 -05:00
// if they are approved, than they are no longer invited and we can return an error
2018-06-29 19:52:40 +02:00
throw new HttpResponseException ( Request . CreateUserNoAccessResponse ( ) ) ;
}
var result = Mapper . Map < UserDetail > ( user ) ;
var httpContextAttempt = TryGetHttpContext ( ) ;
if ( httpContextAttempt . Success )
{
2019-01-26 10:52:19 -05:00
// set their remaining seconds
2018-06-29 19:52:40 +02:00
result . SecondsUntilTimeout = httpContextAttempt . Result . GetRemainingAuthSeconds ( ) ;
}
return result ;
}
2019-01-26 10:52:19 -05:00
// TODO: This should be on the CurrentUserController?
2018-06-29 19:52:40 +02:00
[WebApi.UmbracoAuthorize]
[ValidateAngularAntiForgeryToken]
public async Task < Dictionary < string , string > > GetCurrentUserLinkedLogins ( )
{
2020-03-16 13:53:03 +00:00
var identityUser = await UserManager . FindByIdAsync ( UmbracoContext . Security . GetUserId ( ) . ResultOr ( 0 ) . ToString ( ) ) ;
2018-06-29 19:52:40 +02:00
return identityUser . Logins . ToDictionary ( x = > x . LoginProvider , x = > x . ProviderKey ) ;
}
/// <summary>
/// Processes a password reset request. Looks for a match on the provided email address
/// and if found sends an email with a link to reset it
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
public async Task < HttpResponseMessage > PostRequestPasswordReset ( RequestPasswordResetModel model )
{
// If this feature is switched off in configuration the UI will be amended to not make the request to reset password available.
// So this is just a server-side secondary check.
2020-03-12 14:36:25 +01:00
if ( _securitySettings . AllowPasswordReset = = false )
2018-06-29 19:52:40 +02:00
{
throw new HttpResponseException ( HttpStatusCode . BadRequest ) ;
}
2020-03-16 13:53:03 +00:00
var identityUser = await UserManager . FindByEmailAsync ( model . Email ) ;
2018-06-29 19:52:40 +02:00
if ( identityUser ! = null )
{
var user = Services . UserService . GetByEmail ( model . Email ) ;
if ( user ! = null )
{
2020-03-16 13:53:03 +00:00
var code = await UserManager . GeneratePasswordResetTokenAsync ( identityUser ) ;
2018-06-29 19:52:40 +02:00
var callbackUrl = ConstructCallbackUrl ( identityUser . Id , code ) ;
var message = Services . TextService . Localize ( "resetPasswordEmailCopyFormat" ,
2019-01-26 10:52:19 -05:00
// Ensure the culture of the found user is used for the email!
2019-11-14 16:59:43 +11:00
UmbracoUserExtensions . GetUserCulture ( identityUser . Culture , Services . TextService , GlobalSettings ) ,
2018-06-29 19:52:40 +02:00
new [ ] { identityUser . UserName , callbackUrl } ) ;
2020-05-20 00:20:25 +02:00
var subject = Services . TextService . Localize ( "login/resetPasswordEmailCopySubject" ,
// Ensure the culture of the found user is used for the email!
UmbracoUserExtensions . GetUserCulture ( identityUser . Culture , Services . TextService , GlobalSettings ) ) ;
var mailMessage = new MailMessage ( )
{
Subject = subject ,
Body = message ,
2020-05-20 11:42:23 +02:00
IsBodyHtml = true ,
To = { user . Email }
2020-05-20 00:20:25 +02:00
} ;
2020-05-20 11:42:23 +02:00
await _emailSender . SendAsync ( mailMessage ) ;
2018-06-29 19:52:40 +02:00
2020-05-21 16:33:24 +10:00
UserManager . RaiseForgotPasswordRequestedEvent ( User , user . Id ) ;
2018-06-29 19:52:40 +02:00
}
}
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
/// <summary>
2019-01-26 10:52:19 -05:00
/// Used to retrieve the 2FA providers for code submission
2018-06-29 19:52:40 +02:00
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
public async Task < IEnumerable < string > > Get2FAProviders ( )
{
var userId = await SignInManager . GetVerifiedUserIdAsync ( ) ;
2020-04-28 12:20:04 +01:00
if ( string . IsNullOrWhiteSpace ( userId ) )
2018-06-29 19:52:40 +02:00
{
Logger . Warn < AuthenticationController > ( "Get2FAProviders :: No verified user found, returning 404" ) ;
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
2020-03-16 13:53:03 +00:00
2020-04-28 12:20:04 +01:00
var user = await UserManager . FindByIdAsync ( userId ) ;
2020-03-16 13:53:03 +00:00
var userFactors = await UserManager . GetValidTwoFactorProvidersAsync ( user ) ;
2018-06-29 19:52:40 +02:00
return userFactors ;
}
[SetAngularAntiForgeryTokens]
public async Task < IHttpActionResult > PostSend2FACode ( [ FromBody ] string provider )
{
if ( provider . IsNullOrWhiteSpace ( ) )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
var userId = await SignInManager . GetVerifiedUserIdAsync ( ) ;
2020-04-28 12:20:04 +01:00
if ( string . IsNullOrWhiteSpace ( userId ) )
2018-06-29 19:52:40 +02:00
{
Logger . Warn < AuthenticationController > ( "Get2FAProviders :: No verified user found, returning 404" ) ;
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
// Generate the token and send it
if ( await SignInManager . SendTwoFactorCodeAsync ( provider ) = = false )
{
return BadRequest ( "Invalid code" ) ;
}
return Ok ( ) ;
}
[SetAngularAntiForgeryTokens]
public async Task < HttpResponseMessage > PostVerify2FACode ( Verify2FACodeModel model )
{
if ( ModelState . IsValid = = false )
{
return Request . CreateValidationErrorResponse ( ModelState ) ;
}
var userName = await SignInManager . GetVerifiedUserNameAsync ( ) ;
if ( userName = = null )
{
Logger . Warn < AuthenticationController > ( "Get2FAProviders :: No verified user found, returning 404" ) ;
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
var result = await SignInManager . TwoFactorSignInAsync ( model . Provider , model . Code , isPersistent : true , rememberBrowser : false ) ;
var owinContext = TryGetOwinContext ( ) . Result ;
var user = Services . UserService . GetByUsername ( userName ) ;
2020-03-16 13:53:03 +00:00
if ( result . Succeeded )
2018-06-29 19:52:40 +02:00
{
2020-05-21 16:33:24 +10:00
UserManager . RaiseLoginSuccessEvent ( User , user . Id ) ;
2020-03-16 13:53:03 +00:00
return SetPrincipalAndReturnUserDetail ( user , owinContext . Request . User ) ;
2018-06-29 19:52:40 +02:00
}
2020-03-16 13:53:03 +00:00
if ( result . IsLockedOut )
{
2020-05-21 16:33:24 +10:00
UserManager . RaiseAccountLockedEvent ( User , user . Id ) ;
2020-03-16 13:53:03 +00:00
return Request . CreateValidationErrorResponse ( "User is locked out" ) ;
}
return Request . CreateValidationErrorResponse ( "Invalid code" ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Processes a set password request. Validates the request and sets a new password.
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
public async Task < HttpResponseMessage > PostSetPassword ( SetPasswordModel model )
{
2020-03-16 13:53:03 +00:00
var identityUser = await UserManager . FindByIdAsync ( model . UserId . ToString ( ) ) ;
var result = await UserManager . ResetPasswordAsync ( identityUser , model . ResetCode , model . Password ) ;
2018-06-29 19:52:40 +02:00
if ( result . Succeeded )
{
2020-03-16 13:53:03 +00:00
var lockedOut = await UserManager . IsLockedOutAsync ( identityUser ) ;
2018-06-29 19:52:40 +02:00
if ( lockedOut )
{
2018-08-14 22:36:47 +01:00
Logger . Info < AuthenticationController > ( "User {UserId} is currently locked out, unlocking and resetting AccessFailedCount" , model . UserId ) ;
2018-06-29 19:52:40 +02:00
2019-01-26 10:52:19 -05:00
//// var user = await UserManager.FindByIdAsync(model.UserId);
2020-03-16 13:53:03 +00:00
var unlockResult = await UserManager . SetLockoutEndDateAsync ( identityUser , DateTimeOffset . Now ) ;
2018-06-29 19:52:40 +02:00
if ( unlockResult . Succeeded = = false )
{
2020-05-04 18:49:02 +01:00
Logger . Warn < AuthenticationController > ( "Could not unlock for user {UserId} - error {UnlockError}" , model . UserId , unlockResult . Errors . First ( ) . Description ) ;
2018-06-29 19:52:40 +02:00
}
2020-03-16 13:53:03 +00:00
var resetAccessFailedCountResult = await UserManager . ResetAccessFailedCountAsync ( identityUser ) ;
2018-06-29 19:52:40 +02:00
if ( resetAccessFailedCountResult . Succeeded = = false )
{
2020-05-04 18:49:02 +01:00
Logger . Warn < AuthenticationController > ( "Could not reset access failed count {UserId} - error {UnlockError}" , model . UserId , unlockResult . Errors . First ( ) . Description ) ;
2018-06-29 19:52:40 +02:00
}
}
2019-01-26 10:52:19 -05:00
// 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
2020-03-16 13:53:03 +00:00
if ( ! await UserManager . IsEmailConfirmedAsync ( identityUser ) )
2018-10-01 14:32:46 +02:00
{
2020-03-16 13:53:03 +00:00
await UserManager . ConfirmEmailAsync ( identityUser , model . ResetCode ) ;
2018-10-01 14:32:46 +02:00
}
2019-01-26 10:52:19 -05:00
// invited is not approved, never logged in, invited date present
2018-10-01 14:32:46 +02:00
/ *
if ( LastLoginDate = = default & & IsApproved = = false & & InvitedDate ! = null )
return UserState . Invited ;
* /
2018-11-27 13:46:43 +01:00
if ( identityUser ! = null & & ! identityUser . IsApproved )
2018-10-01 14:32:46 +02:00
{
var user = Services . UserService . GetByUsername ( identityUser . UserName ) ;
2019-01-26 10:52:19 -05:00
// also check InvitedDate and never logged in, otherwise this would allow a disabled user to reactivate their account with a forgot password
2018-10-01 14:32:46 +02:00
if ( user . LastLoginDate = = default & & user . InvitedDate ! = null )
{
user . IsApproved = true ;
user . InvitedDate = null ;
Services . UserService . Save ( user ) ;
}
}
2020-05-21 16:33:24 +10:00
UserManager . RaiseForgotPasswordChangedSuccessEvent ( User , model . UserId ) ;
2018-06-29 19:52:40 +02:00
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
return Request . CreateValidationErrorResponse (
2020-03-16 13:53:03 +00:00
result . Errors . Any ( ) ? result . Errors . First ( ) . Description : "Set password failed" ) ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Logs the current user out
/// </summary>
/// <returns></returns>
[ClearAngularAntiForgeryToken]
[ValidateAngularAntiForgeryToken]
public HttpResponseMessage PostLogout ( )
{
var owinContext = Request . TryGetOwinContext ( ) . Result ;
owinContext . Authentication . SignOut (
Core . Constants . Security . BackOfficeAuthenticationType ,
Core . Constants . Security . BackOfficeExternalAuthenticationType ) ;
2018-08-14 22:36:47 +01:00
Logger . Info < AuthenticationController > ( "User {UserName} from IP address {RemoteIpAddress} has logged out" , User . Identity = = null ? "UNKNOWN" : User . Identity . Name , owinContext . Request . RemoteIpAddress ) ;
2018-06-29 19:52:40 +02:00
if ( UserManager ! = null )
{
2019-05-02 14:56:12 +02:00
int . TryParse ( User . Identity . GetUserId ( ) , out var userId ) ;
2020-05-21 16:33:24 +10:00
UserManager . RaiseLogoutSuccessEvent ( User , userId ) ;
2018-06-29 19:52:40 +02:00
}
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>
/// <param name="user"></param>
/// <param name="principal"></param>
/// <returns></returns>
private HttpResponseMessage SetPrincipalAndReturnUserDetail ( IUser user , IPrincipal principal )
{
if ( user = = null ) throw new ArgumentNullException ( "user" ) ;
if ( principal = = null ) throw new ArgumentNullException ( nameof ( principal ) ) ;
var userDetail = Mapper . Map < UserDetail > ( user ) ;
2019-01-26 10:52:19 -05:00
// update the userDetail and set their remaining seconds
2018-06-29 19:52:40 +02:00
userDetail . SecondsUntilTimeout = TimeSpan . FromMinutes ( GlobalSettings . TimeOutInMinutes ) . TotalSeconds ;
2020-05-25 23:15:32 +10:00
2019-01-26 10:52:19 -05:00
// create a response with the userDetail object
2018-06-29 19:52:40 +02:00
var response = Request . CreateResponse ( HttpStatusCode . OK , userDetail ) ;
2019-01-26 10:52:19 -05:00
// ensure the user is set for the current request
2018-06-29 19:52:40 +02:00
Request . SetPrincipalForRequest ( principal ) ;
return response ;
}
private string ConstructCallbackUrl ( int userId , string code )
{
// Get an mvc helper to get the url
var http = EnsureHttpContext ( ) ;
var urlHelper = new UrlHelper ( http . Request . RequestContext ) ;
var action = urlHelper . Action ( "ValidatePasswordResetCode" , "BackOffice" ,
new
{
2020-04-03 11:03:06 +11:00
area = GlobalSettings . GetUmbracoMvcArea ( _hostingEnvironment ) ,
2018-06-29 19:52:40 +02:00
u = userId ,
r = code
} ) ;
// Construct full URL using configured application URL (which will fall back to request)
2020-05-07 09:34:16 +02:00
var applicationUri = _requestAccessor . GetApplicationUrl ( ) ;
2018-06-29 19:52:40 +02:00
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 )
{
2020-03-16 13:53:03 +00:00
ModelState . AddModelError ( prefix , error . Description ) ;
2018-06-29 19:52:40 +02:00
}
}
}
}