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:
@@ -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 };
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user