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

# Conflicts:
#	build/build.ps1
#	build/templates/UmbracoPackage/.template.config/template.json
#	build/templates/UmbracoProject/.template.config/dotnetcli.host.json
#	build/templates/UmbracoProject/.template.config/ide.host.json
#	build/templates/UmbracoProject/.template.config/template.json
#	src/Umbraco.Core/Constants-System.cs
#	src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs
#	src/Umbraco.Infrastructure/Security/BackOfficeUserStore.cs
#	src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
#	umbraco-netcore-only.sln
This commit is contained in:
Bjarke Berg
2022-04-20 11:09:28 +02:00
171 changed files with 4661 additions and 1695 deletions

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Web.BackOffice.Controllers
{
public class AnalyticsController : UmbracoAuthorizedJsonController
{
private readonly IMetricsConsentService _metricsConsentService;
public AnalyticsController(IMetricsConsentService metricsConsentService)
{
_metricsConsentService = metricsConsentService;
}
public TelemetryLevel GetConsentLevel()
{
return _metricsConsentService.GetConsentLevel();
}
[HttpPost]
public IActionResult SetConsentLevel([FromBody]TelemetryResource telemetryResource)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
_metricsConsentService.SetConsentLevel(telemetryResource.TelemetryLevel);
return Ok();
}
public IEnumerable<TelemetryLevel> GetAllLevels() => new[] { TelemetryLevel.Minimal, TelemetryLevel.Basic, TelemetryLevel.Detailed };
}
}

View File

@@ -74,11 +74,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
private readonly IBackOfficeExternalLoginProviders _externalAuthenticationOptions;
private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ITwoFactorLoginService _twoFactorLoginService;
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(
public AuthenticationController(
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IBackOfficeUserManager backOfficeUserManager,
IBackOfficeSignInManager signInManager,
@@ -97,7 +98,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IBackOfficeExternalLoginProviders externalAuthenticationOptions,
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
IHttpContextAccessor httpContextAccessor,
IOptions<WebRoutingSettings> webRoutingSettings)
IOptions<WebRoutingSettings> webRoutingSettings,
ITwoFactorLoginService twoFactorLoginService)
{
_backofficeSecurityAccessor = backofficeSecurityAccessor;
_userManager = backOfficeUserManager;
@@ -118,6 +120,95 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
_backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
_httpContextAccessor = httpContextAccessor;
_webRoutingSettings = webRoutingSettings.Value;
_twoFactorLoginService = twoFactorLoginService;
}
[Obsolete("Use constructor that takes all params, 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,
IHttpContextAccessor httpContextAccessor,
IOptions<WebRoutingSettings> webRoutingSettings)
: 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>>(),
StaticServiceProvider.Instance.GetRequiredService<ITwoFactorLoginService>())
{
}
[Obsolete("Use constructor that takes all params, 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>
@@ -427,7 +518,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
return NotFound();
}
var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
var userFactors = await _twoFactorLoginService.GetEnabledTwoFactorProviderNamesAsync(user.Key);
return new ObjectResult(userFactors);
}

View File

@@ -241,6 +241,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
"authenticationApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<AuthenticationController>(
controller => controller.PostLogin(null))
},
{
"twoFactorLoginApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<TwoFactorLoginController>(
controller => controller.SetupInfo(null))
},
{
"currentUserApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<CurrentUserController>(
controller => controller.PostChangePassword(null))
@@ -387,7 +391,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{
"trackedReferencesApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<TrackedReferencesController>(
controller => controller.GetPagedReferences(0, 1, 1, false))
}
},
{
"analyticsApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<AnalyticsController>(
controller => controller.GetConsentLevel())
},
}
},
{

View File

@@ -22,6 +22,7 @@ using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Infrastructure.Packaging;
using Umbraco.Cms.Web.BackOffice.Filters;
using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
@@ -452,6 +453,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
/// </summary>
/// <param name="contentId"></param>
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes)]
[OutgoingEditorModelEvent]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(int contentId)
{
if (contentId == Constants.System.RecycleBinContent)

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -191,6 +193,39 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
return _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionary);
}
/// <summary>
/// Changes the structure for dictionary items
/// </summary>
/// <param name="move"></param>
/// <returns></returns>
public IActionResult PostMove(MoveOrCopy move)
{
var dictionaryItem = _localizationService.GetDictionaryItemById(move.Id);
if (dictionaryItem == null)
return ValidationProblem(_localizedTextService.Localize("dictionary", "itemDoesNotExists"));
var parent = _localizationService.GetDictionaryItemById(move.ParentId);
if (parent == null)
{
if (move.ParentId == Constants.System.Root)
dictionaryItem.ParentId = null;
else
return ValidationProblem(_localizedTextService.Localize("dictionary", "parentDoesNotExists"));
}
else
{
dictionaryItem.ParentId = parent.Key;
if (dictionaryItem.Key == parent.ParentId)
return ValidationProblem(_localizedTextService.Localize("moveOrCopy", "notAllowedByPath"));
}
_localizationService.Save(dictionaryItem);
var model = _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionaryItem);
return Content(model.Path, MediaTypeNames.Text.Plain, Encoding.UTF8);
}
/// <summary>
/// Saves a dictionary item
/// </summary>

View File

@@ -12,6 +12,7 @@ using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Web.BackOffice.Filters;
using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
@@ -339,6 +340,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
/// </summary>
/// <param name="contentId"></param>
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
[OutgoingEditorModelEvent]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(int contentId)
{
if (contentId == Constants.System.RecycleBinContent)

View File

@@ -252,5 +252,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
return display;
}
/// <summary>
/// Copy the member type
/// </summary>
/// <param name="copy"></param>
/// <returns></returns>
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)]
public IActionResult PostCopy(MoveOrCopy copy)
{
return PerformCopy(
copy,
i => _memberTypeService.Get(i),
(type, i) => _memberTypeService.Copy(type, i));
}
}
}

View File

@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Cms.Web.Common.Authorization;
namespace Umbraco.Cms.Web.BackOffice.Controllers
{
public class TwoFactorLoginController : UmbracoAuthorizedJsonController
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly ILogger<TwoFactorLoginController> _logger;
private readonly ITwoFactorLoginService2 _twoFactorLoginService;
private readonly IBackOfficeSignInManager _backOfficeSignInManager;
private readonly IBackOfficeUserManager _backOfficeUserManager;
private readonly IOptionsSnapshot<TwoFactorLoginViewOptions> _twoFactorLoginViewOptions;
public TwoFactorLoginController(
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
ILogger<TwoFactorLoginController> logger,
ITwoFactorLoginService twoFactorLoginService,
IBackOfficeSignInManager backOfficeSignInManager,
IBackOfficeUserManager backOfficeUserManager,
IOptionsSnapshot<TwoFactorLoginViewOptions> twoFactorLoginViewOptions)
{
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_logger = logger;
if (twoFactorLoginService is not ITwoFactorLoginService2 twoFactorLoginService2)
{
throw new ArgumentException("twoFactorLoginService needs to implement ITwoFactorLoginService2 until the interfaces are merged", nameof(twoFactorLoginService));
}
_twoFactorLoginService = twoFactorLoginService2;
_backOfficeSignInManager = backOfficeSignInManager;
_backOfficeUserManager = backOfficeUserManager;
_twoFactorLoginViewOptions = twoFactorLoginViewOptions;
}
/// <summary>
/// Used to retrieve the 2FA providers for code submission
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<string>>> GetEnabled2FAProvidersForCurrentUser()
{
var user = await _backOfficeSignInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
_logger.LogWarning("No verified user found, returning 404");
return NotFound();
}
var userFactors = await _backOfficeUserManager.GetValidTwoFactorProvidersAsync(user);
return new ObjectResult(userFactors);
}
[HttpGet]
public async Task<ActionResult<IEnumerable<UserTwoFactorProviderModel>>> Get2FAProvidersForUser(int userId)
{
var user = await _backOfficeUserManager.FindByIdAsync(userId.ToString());
var enabledProviderNameHashSet = new HashSet<string>(await _twoFactorLoginService.GetEnabledTwoFactorProviderNamesAsync(user.Key));
var providerNames = await _backOfficeUserManager.GetValidTwoFactorProvidersAsync(user);
return providerNames.Select(providerName =>
new UserTwoFactorProviderModel(providerName, enabledProviderNameHashSet.Contains(providerName))).ToArray();
}
[HttpGet]
public async Task<ActionResult<object>> SetupInfo(string providerName)
{
var user = _backOfficeSecurityAccessor?.BackOfficeSecurity.CurrentUser;
var setupInfo = await _twoFactorLoginService.GetSetupInfoAsync(user.Key, providerName);
return setupInfo;
}
[HttpPost]
public async Task<ActionResult<bool>> ValidateAndSave(string providerName, string secret, string code)
{
var user = _backOfficeSecurityAccessor?.BackOfficeSecurity.CurrentUser;
return await _twoFactorLoginService.ValidateAndSaveAsync(providerName, user.Key, secret, code);
}
[HttpPost]
[Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)]
public async Task<ActionResult<bool>> Disable(string providerName, Guid userKey)
{
return await _twoFactorLoginService.DisableAsync(userKey, providerName);
}
[HttpPost]
public async Task<ActionResult<bool>> DisableWithCode(string providerName, string code)
{
Guid key = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Key;
return await _twoFactorLoginService.DisableWithCodeAsync(providerName, key, code);
}
[HttpGet]
public ActionResult<string> ViewPathForProviderName(string providerName)
{
var options = _twoFactorLoginViewOptions.Get(providerName);
return options.SetupViewPath;
}
}
}