Enabling an Umbraco admin user to disable 2FA for a member (#13369)

* Fix Invalid authentication code bug

* Add translation keys for 2fa

* Display toggle for 2FA on member

* Add TwoFactorEnabled prop when saving member

* Handle disabling of 2FA

* Fix tests

* Changing obsolete msg
This commit is contained in:
Elitsa Marinovska
2022-11-28 13:42:38 +01:00
committed by GitHub
parent 37b33641c3
commit af6b8fc5cb
8 changed files with 170 additions and 40 deletions

View File

@@ -34,6 +34,9 @@ public class MemberSave : ContentBaseSave<IMember>
[DataMember(Name = "isApproved")]
public bool IsApproved { get; set; }
[DataMember(Name = "isTwoFactorEnabled")]
public bool IsTwoFactorEnabled { get; set; }
private T? GetPropertyValue<T>(string alias)
{
ContentPropertyBasic? prop = Properties.FirstOrDefault(x => x.Alias == alias);

View File

@@ -1,3 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Dictionary;
@@ -6,6 +7,7 @@ using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Models.Mapping;
@@ -26,8 +28,36 @@ public class MemberTabsAndPropertiesMapper : TabsAndPropertiesMapper<IMember>
private readonly IMemberService _memberService;
private readonly IMemberGroupService _memberGroupService;
private readonly MemberPasswordConfigurationSettings _memberPasswordConfiguration;
private readonly PropertyEditorCollection _propertyEditorCollection;
private readonly ITwoFactorLoginService _twoFactorLoginService;
// PropertyEditorCollection is still injected as when removing it, the number of
// parameters matches with the obsolete ctor and the two ctors become ambiguous
// [ActivatorUtilitiesConstructor] won't solve the problem in this case.
// PropertyEditorCollection can be removed when the obsolete ctor is removed for
// Umbraco 13
public MemberTabsAndPropertiesMapper(
ICultureDictionary cultureDictionary,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
ILocalizedTextService localizedTextService,
IMemberTypeService memberTypeService,
IMemberService memberService,
IMemberGroupService memberGroupService,
IOptions<MemberPasswordConfigurationSettings> memberPasswordConfiguration,
IContentTypeBaseServiceProvider contentTypeBaseServiceProvider,
PropertyEditorCollection propertyEditorCollection,
ITwoFactorLoginService twoFactorLoginService)
: base(cultureDictionary, localizedTextService, contentTypeBaseServiceProvider)
{
_backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor));
_localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
_memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
_memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService));
_memberPasswordConfiguration = memberPasswordConfiguration.Value;
_twoFactorLoginService = twoFactorLoginService ?? throw new ArgumentNullException(nameof(twoFactorLoginService));
}
[Obsolete("Use constructor that also takes an ITwoFactorLoginService. Scheduled for removal in V13")]
public MemberTabsAndPropertiesMapper(
ICultureDictionary cultureDictionary,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
@@ -38,15 +68,18 @@ public class MemberTabsAndPropertiesMapper : TabsAndPropertiesMapper<IMember>
IOptions<MemberPasswordConfigurationSettings> memberPasswordConfiguration,
IContentTypeBaseServiceProvider contentTypeBaseServiceProvider,
PropertyEditorCollection propertyEditorCollection)
: base(cultureDictionary, localizedTextService, contentTypeBaseServiceProvider)
: this(
cultureDictionary,
backofficeSecurityAccessor,
localizedTextService,
memberTypeService,
memberService,
memberGroupService,
memberPasswordConfiguration,
contentTypeBaseServiceProvider,
propertyEditorCollection,
StaticServiceProvider.Instance.GetRequiredService<ITwoFactorLoginService>())
{
_backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor));
_localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
_memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
_memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService));
_memberPasswordConfiguration = memberPasswordConfiguration.Value;
_propertyEditorCollection = propertyEditorCollection;
}
/// <inheritdoc />
@@ -181,6 +214,8 @@ public class MemberTabsAndPropertiesMapper : TabsAndPropertiesMapper<IMember>
public IEnumerable<ContentPropertyDisplay> MapMembershipProperties(IMember member, MapperContext? context)
{
var isTwoFactorEnabled = _twoFactorLoginService.IsTwoFactorEnabledAsync(member.Key).Result;
var properties = new List<ContentPropertyDisplay>
{
GetLoginProperty(member, _localizedTextService),
@@ -246,6 +281,17 @@ public class MemberTabsAndPropertiesMapper : TabsAndPropertiesMapper<IMember>
Readonly = !member.IsLockedOut, // IMember.IsLockedOut can't be set to true, so make it readonly when that's the case (you can only unlock)
},
// Toggle for disabling Two-Factor Authentication for a Member
new()
{
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}twoFactorEnabled",
Label = _localizedTextService.Localize("member", "2fa"),
Value = isTwoFactorEnabled,
View = "boolean",
IsSensitive = true,
Readonly = !isTwoFactorEnabled, // The value can't be set to true, so make it readonly when that's the case (you can only disable)
},
new()
{
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}lastLockoutDate",