Merge remote-tracking branch 'origin/v10/dev' into v11/dev

# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/common/login.controller.js
This commit is contained in:
Bjarke Berg
2023-12-11 14:41:17 +01:00
11 changed files with 67 additions and 19 deletions

View File

@@ -66,7 +66,17 @@ public class ExamineIndexRebuilder : IIndexRebuilder
_logger.LogInformation("Starting async background thread for rebuilding index {indexName}.", indexName);
_backgroundTaskQueue.QueueBackgroundWorkItem(
cancellationToken => Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken)));
cancellationToken =>
{
// Do not flow AsyncLocal to the child thread
using (ExecutionContext.SuppressFlow())
{
Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken));
// immediately return so the queue isn't waiting.
return Task.CompletedTask;
}
});
}
else
{
@@ -93,12 +103,16 @@ public class ExamineIndexRebuilder : IIndexRebuilder
_backgroundTaskQueue.QueueBackgroundWorkItem(
cancellationToken =>
{
// This is a fire/forget task spawned by the background thread queue (which means we
// don't need to worry about ExecutionContext flowing).
Task.Run(() => RebuildIndexes(onlyEmptyIndexes, delay.Value, cancellationToken));
// Do not flow AsyncLocal to the child thread
using (ExecutionContext.SuppressFlow())
{
// This is a fire/forget task spawned by the background thread queue (which means we
// don't need to worry about ExecutionContext flowing).
Task.Run(() => RebuildIndexes(onlyEmptyIndexes, delay.Value, cancellationToken));
// immediately return so the queue isn't waiting.
return Task.CompletedTask;
// immediately return so the queue isn't waiting.
return Task.CompletedTask;
}
});
}
else

View File

@@ -266,7 +266,12 @@ public class CreatedPackageSchemaRepository : ICreatedPackagesRepository
definition.Name.Replace(' ', '_')));
Directory.CreateDirectory(directoryName);
var expectedRoot = _hostingEnvironment.MapPathContentRoot(_createdPackagesFolderPath);
var finalPackagePath = Path.Combine(directoryName, fileName);
if (finalPackagePath.StartsWith(expectedRoot) == false)
{
throw new IOException("Invalid path due to the package name");
}
// Clean existing files
foreach (var packagePath in new[] { definition.PackagePath, finalPackagePath })

View File

@@ -1,9 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Authorization;
namespace Umbraco.Cms.Web.BackOffice.Controllers;
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class AnalyticsController : UmbracoAuthorizedJsonController
{
private readonly IMetricsConsentService _metricsConsentService;

View File

@@ -404,6 +404,9 @@ public class AuthenticationController : UmbracoApiControllerBase
}
BackOfficeIdentityUser? identityUser = await _userManager.FindByEmailAsync(model.Email);
await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500)); // To randomize response time preventing user enumeration
if (identityUser != null)
{
IUser? user = _userService.GetByEmail(model.Email);
@@ -424,14 +427,20 @@ public class AuthenticationController : UmbracoApiControllerBase
var mailMessage = new EmailMessage(from, user.Email, subject, message, true);
await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.PasswordReset, true);
try
{
await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.PasswordReset, true);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending email, please check your SMTP configuration: {ErrorMessage}", ex.Message);
return Ok();
}
_userManager.NotifyForgotPasswordRequested(User, user.Id.ToString());
}
}
await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500));
return Ok();
}

View File

@@ -18,6 +18,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers;
/// Backoffice controller supporting the dashboard for language administration.
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class LanguageController : UmbracoAuthorizedJsonController
{
private readonly ILocalizationService _localizationService;

View File

@@ -1,13 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Controllers;
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class PublishedSnapshotCacheStatusController : UmbracoAuthorizedApiController
{
private readonly DistributedCache _distributedCache;

View File

@@ -2,6 +2,7 @@
// See LICENSE for more details.
using System.Security;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -14,11 +15,13 @@ using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Controllers;
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessContent)]
public class RedirectUrlManagementController : UmbracoAuthorizedApiController
{
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
@@ -45,6 +48,8 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController
_configManipulator = configManipulator ?? throw new ArgumentNullException(nameof(configManipulator));
}
private bool IsEnabled => _webRoutingSettings.CurrentValue.DisableRedirectUrlTracking == false;
/// <summary>
/// Returns true/false of whether redirect tracking is enabled or not
/// </summary>
@@ -52,9 +57,8 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController
[HttpGet]
public IActionResult GetEnableState()
{
var enabled = _webRoutingSettings.CurrentValue.DisableRedirectUrlTracking == false;
var userIsAdmin = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.IsAdmin() ?? false;
return Ok(new { enabled, userIsAdmin });
return Ok(new { enabled = IsEnabled, userIsAdmin });
}
//add paging
@@ -104,6 +108,11 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController
[HttpPost]
public IActionResult DeleteRedirectUrl(Guid id)
{
if (IsEnabled is false)
{
return BadRequest("Redirect URL tracking is disabled, and therefore no URLs can be deleted.");
}
_redirectUrlService.Delete(id);
return Ok();
}

View File

@@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
using Stylesheet = Umbraco.Cms.Core.Models.ContentEditing.Stylesheet;
@@ -12,6 +14,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers;
/// The API controller used for retrieving available stylesheets
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class StylesheetController : UmbracoAuthorizedJsonController
{
private readonly IFileService _fileService;

View File

@@ -28,7 +28,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
* });
* </pre>
* @returns {Promise} resourcePromise object
*
*
*/
get2FAProviders: function () {
@@ -203,7 +203,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
"PostRequestPasswordReset"), {
email: email
}),
'Request password reset failed for email ' + email);
'An email with password reset instructions will be sent to the specified address if it matched our records');
},
/**

View File

@@ -11,11 +11,12 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events
//check if there's a returnPath query string, if so redirect to it
var locationObj = $location.search();
if (locationObj.returnPath) {
// decodeURIComponent(...) does not play nice with OAuth redirect URLs, so until we have a
// dedicated login screen for the new back-office, we need to hardcode this exception
path = locationObj.returnPath.indexOf("/security/back-office/authorize") > 0
? locationObj.returnPath
: decodeURIComponent(locationObj.returnPath);
// ensure that the returnPath is a valid URL under the current origin (prevents DOM-XSS among other things)
const returnPath = decodeURIComponent(locationObj.returnPath);
const url = new URL(returnPath, window.location.origin);
if (url.origin === window.location.origin) {
path = returnPath;
}
}
// Ensure path is not absolute

View File

@@ -158,7 +158,7 @@
</div>
<div ng-messages="vm.requestPasswordResetForm.$error" class="control-group" ng-show="vm.requestPasswordResetForm.$invalid">
<p ng-message="auth" class="text-error" role="alert" tabindex="0">{{vm.errorMsg}}</p>
<p ng-message="auth" class="text-info" role="alert" tabindex="0">{{vm.errorMsg}}</p>
</div>
<div class="control-group" ng-show="vm.showEmailResetConfirmation">