V9: Use current request for emails (#11778)
* Use request url for email * Fixed potential null ref exceptions Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -29,6 +30,7 @@ using Umbraco.Cms.Web.Common.ActionsResults;
|
||||
using Umbraco.Cms.Web.Common.Attributes;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Cms.Web.Common.Controllers;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Web.Common.Filters;
|
||||
using Umbraco.Cms.Web.Common.Models;
|
||||
using Umbraco.Extensions;
|
||||
@@ -71,9 +73,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly IBackOfficeExternalLoginProviders _externalAuthenticationOptions;
|
||||
private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly WebRoutingSettings _webRoutingSettings;
|
||||
|
||||
// TODO: We need to review all _userManager.Raise calls since many/most should be on the usermanager or signinmanager, very few should be here
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public AuthenticationController(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IBackOfficeUserManager backOfficeUserManager,
|
||||
@@ -91,7 +95,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
LinkGenerator linkGenerator,
|
||||
IBackOfficeExternalLoginProviders externalAuthenticationOptions,
|
||||
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions)
|
||||
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IOptions<WebRoutingSettings> webRoutingSettings)
|
||||
{
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_userManager = backOfficeUserManager;
|
||||
@@ -110,6 +116,50 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
_linkGenerator = linkGenerator;
|
||||
_externalAuthenticationOptions = externalAuthenticationOptions;
|
||||
_backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_webRoutingSettings = webRoutingSettings.Value;
|
||||
}
|
||||
|
||||
[Obsolete("Use constructor that also takes IHttpAccessor and IOptions<WebRoutingSettings>, scheduled for removal in V11")]
|
||||
public AuthenticationController(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IBackOfficeUserManager backOfficeUserManager,
|
||||
IBackOfficeSignInManager signInManager,
|
||||
IUserService userService,
|
||||
ILocalizedTextService textService,
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IOptions<SecuritySettings> securitySettings,
|
||||
ILogger<AuthenticationController> logger,
|
||||
IIpResolver ipResolver,
|
||||
IOptions<UserPasswordConfigurationSettings> passwordConfiguration,
|
||||
IEmailSender emailSender,
|
||||
ISmsSender smsSender,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
LinkGenerator linkGenerator,
|
||||
IBackOfficeExternalLoginProviders externalAuthenticationOptions,
|
||||
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions)
|
||||
: this(
|
||||
backofficeSecurityAccessor,
|
||||
backOfficeUserManager,
|
||||
signInManager,
|
||||
userService,
|
||||
textService,
|
||||
umbracoMapper,
|
||||
globalSettings,
|
||||
securitySettings,
|
||||
logger,
|
||||
ipResolver,
|
||||
passwordConfiguration,
|
||||
emailSender,
|
||||
smsSender,
|
||||
hostingEnvironment,
|
||||
linkGenerator,
|
||||
externalAuthenticationOptions,
|
||||
backOfficeTwoFactorOptions,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IHttpContextAccessor>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IOptions<WebRoutingSettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -629,7 +679,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
});
|
||||
|
||||
// Construct full URL using configured application URL (which will fall back to request)
|
||||
var applicationUri = _hostingEnvironment.ApplicationMainUrl;
|
||||
Uri applicationUri = _httpContextAccessor.GetRequiredHttpContext().Request.GetApplicationUri(_webRoutingSettings);
|
||||
var callbackUri = new Uri(applicationUri, action);
|
||||
return callbackUri.ToString();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
@@ -42,6 +43,7 @@ using Umbraco.Cms.Web.BackOffice.Security;
|
||||
using Umbraco.Cms.Web.Common.ActionsResults;
|
||||
using Umbraco.Cms.Web.Common.Attributes;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Web.Common.Security;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
@@ -75,7 +77,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
private readonly UserEditorAuthorizationHelper _userEditorAuthorizationHelper;
|
||||
private readonly IPasswordChanger<BackOfficeIdentityUser> _passwordChanger;
|
||||
private readonly ILogger<UsersController> _logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly WebRoutingSettings _webRoutingSettings;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public UsersController(
|
||||
MediaFileManager mediaFileManager,
|
||||
IOptions<ContentSettings> contentSettings,
|
||||
@@ -96,7 +101,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
LinkGenerator linkGenerator,
|
||||
IBackOfficeExternalLoginProviders externalLogins,
|
||||
UserEditorAuthorizationHelper userEditorAuthorizationHelper,
|
||||
IPasswordChanger<BackOfficeIdentityUser> passwordChanger)
|
||||
IPasswordChanger<BackOfficeIdentityUser> passwordChanger,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IOptions<WebRoutingSettings> webRoutingSettings)
|
||||
{
|
||||
_mediaFileManager = mediaFileManager;
|
||||
_contentSettings = contentSettings.Value;
|
||||
@@ -119,6 +126,55 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
_userEditorAuthorizationHelper = userEditorAuthorizationHelper;
|
||||
_passwordChanger = passwordChanger;
|
||||
_logger = _loggerFactory.CreateLogger<UsersController>();
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_webRoutingSettings = webRoutingSettings.Value;
|
||||
}
|
||||
|
||||
[Obsolete("Use constructor that also takes IHttpAccessor and IOptions<WebRoutingSettings>, scheduled for removal in V11")]
|
||||
public UsersController(
|
||||
MediaFileManager mediaFileManager,
|
||||
IOptions<ContentSettings> contentSettings,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
ISqlContext sqlContext,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IOptions<SecuritySettings> securitySettings,
|
||||
IEmailSender emailSender,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
AppCaches appCaches,
|
||||
IShortStringHelper shortStringHelper,
|
||||
IUserService userService,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IBackOfficeUserManager backOfficeUserManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
LinkGenerator linkGenerator,
|
||||
IBackOfficeExternalLoginProviders externalLogins,
|
||||
UserEditorAuthorizationHelper userEditorAuthorizationHelper,
|
||||
IPasswordChanger<BackOfficeIdentityUser> passwordChanger)
|
||||
: this(mediaFileManager,
|
||||
contentSettings,
|
||||
hostingEnvironment,
|
||||
sqlContext,
|
||||
imageUrlGenerator,
|
||||
securitySettings,
|
||||
emailSender,
|
||||
backofficeSecurityAccessor,
|
||||
appCaches,
|
||||
shortStringHelper,
|
||||
userService,
|
||||
localizedTextService,
|
||||
umbracoMapper,
|
||||
globalSettings,
|
||||
backOfficeUserManager,
|
||||
loggerFactory,
|
||||
linkGenerator,
|
||||
externalLogins,
|
||||
userEditorAuthorizationHelper,
|
||||
passwordChanger,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IHttpContextAccessor>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IOptions<WebRoutingSettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -421,20 +477,25 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
/// </remarks>
|
||||
public async Task<ActionResult<UserDisplay>> PostInviteUser(UserInvite userSave)
|
||||
{
|
||||
if (userSave == null) throw new ArgumentNullException("userSave");
|
||||
if (userSave == null)
|
||||
{
|
||||
throw new ArgumentNullException("userSave");
|
||||
}
|
||||
|
||||
if (userSave.Message.IsNullOrWhiteSpace())
|
||||
{
|
||||
ModelState.AddModelError("Message", "Message cannot be empty");
|
||||
}
|
||||
|
||||
IUser user;
|
||||
if (_securitySettings.UsernameIsEmail)
|
||||
{
|
||||
//ensure it's the same
|
||||
// ensure it's the same
|
||||
userSave.Username = userSave.Email;
|
||||
}
|
||||
else
|
||||
{
|
||||
//first validate the username if we're showing it
|
||||
// first validate the username if we're showing it
|
||||
var userResult = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);
|
||||
if (!(userResult.Result is null))
|
||||
{
|
||||
@@ -443,6 +504,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
|
||||
user = userResult.Value;
|
||||
}
|
||||
|
||||
user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);
|
||||
|
||||
if (ModelState.IsValid == false)
|
||||
@@ -455,7 +517,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
return ValidationProblem("No Email server is configured");
|
||||
}
|
||||
|
||||
//Perform authorization here to see if the current user can actually save this user with the info being requested
|
||||
// Perform authorization here to see if the current user can actually save this user with the info being requested
|
||||
var canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, user, null, null, userSave.UserGroups);
|
||||
if (canSaveUser == false)
|
||||
{
|
||||
@@ -464,8 +526,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
//we want to create the user with the UserManager, this ensures the 'empty' (special) password
|
||||
//format is applied without us having to duplicate that logic
|
||||
// we want to create the user with the UserManager, this ensures the 'empty' (special) password
|
||||
// format is applied without us having to duplicate that logic
|
||||
var identityUser = BackOfficeIdentityUser.CreateNew(_globalSettings, userSave.Username, userSave.Email, _globalSettings.DefaultUILanguage);
|
||||
identityUser.Name = userSave.Name;
|
||||
|
||||
@@ -475,21 +537,21 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
return ValidationProblem(created.Errors.ToErrorMessage());
|
||||
}
|
||||
|
||||
//now re-look the user back up
|
||||
// now re-look the user back up
|
||||
user = _userService.GetByEmail(userSave.Email);
|
||||
}
|
||||
|
||||
//map the save info over onto the user
|
||||
// map the save info over onto the user
|
||||
user = _umbracoMapper.Map(userSave, user);
|
||||
|
||||
//ensure the invited date is set
|
||||
// ensure the invited date is set
|
||||
user.InvitedDate = DateTime.Now;
|
||||
|
||||
//Save the updated user (which will process the user groups too)
|
||||
// Save the updated user (which will process the user groups too)
|
||||
_userService.Save(user);
|
||||
var display = _umbracoMapper.Map<UserDisplay>(user);
|
||||
|
||||
//send the email
|
||||
// send the email
|
||||
await SendUserInviteEmailAsync(display, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Email, user, userSave.Message);
|
||||
|
||||
display.AddSuccessNotification(_localizedTextService.Localize("speechBubbles","resendInviteHeader"), _localizedTextService.Localize("speechBubbles","resendInviteSuccess", new[] { user.Name }));
|
||||
@@ -544,14 +606,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
});
|
||||
|
||||
// Construct full URL using configured application URL (which will fall back to request)
|
||||
var applicationUri = _hostingEnvironment.ApplicationMainUrl;
|
||||
Uri applicationUri = _httpContextAccessor.GetRequiredHttpContext().Request.GetApplicationUri(_webRoutingSettings);
|
||||
var inviteUri = new Uri(applicationUri, action);
|
||||
|
||||
var emailSubject = _localizedTextService.Localize("user","inviteEmailCopySubject",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
// Ensure the culture of the found user is used for the email!
|
||||
UmbracoUserExtensions.GetUserCulture(to.Language, _localizedTextService, _globalSettings));
|
||||
var emailBody = _localizedTextService.Localize("user","inviteEmailCopyFormat",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
// Ensure the culture of the found user is used for the email!
|
||||
UmbracoUserExtensions.GetUserCulture(to.Language, _localizedTextService, _globalSettings),
|
||||
new[] { userDisplay.Name, from, message, inviteUri.ToString(), senderEmail });
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
@@ -107,5 +110,31 @@ namespace Umbraco.Extensions
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application URI, will use the one specified in settings if present
|
||||
/// </summary>
|
||||
public static Uri GetApplicationUri(this HttpRequest request, WebRoutingSettings routingSettings)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
if (routingSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(routingSettings));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(routingSettings.UmbracoApplicationUrl))
|
||||
{
|
||||
var requestUri = new Uri(request.GetDisplayUrl());
|
||||
|
||||
// Create a new URI with the relative uri as /, this ensures that only the base path is returned.
|
||||
return new Uri(requestUri, "/");
|
||||
}
|
||||
|
||||
return new Uri(routingSettings.UmbracoApplicationUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user