Adds MemberTwoFactorLoginService. (#18810)

This commit is contained in:
Andy Butland
2025-03-28 11:16:03 +01:00
committed by GitHub
parent 95e89f8481
commit 685a05827e
5 changed files with 116 additions and 6 deletions

View File

@@ -422,6 +422,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
// Two factor providers
Services.AddUnique<ITwoFactorLoginService, TwoFactorLoginService>();
Services.AddUnique<IUserTwoFactorLoginService, UserTwoFactorLoginService>();
Services.AddUnique<IMemberTwoFactorLoginService, MemberTwoFactorLoginService>();
// Add Query services
Services.AddUnique<IDocumentRecycleBinQueryService, DocumentRecycleBinQueryService>();

View File

@@ -0,0 +1,37 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
/// <summary>
/// A member specific Two factor service, that ensures the member exists before doing the job.
/// </summary>
public interface IMemberTwoFactorLoginService
{
/// <summary>
/// Disables a specific two factor provider on a specific member.
/// </summary>
Task<Attempt<TwoFactorOperationStatus>> DisableAsync(Guid memberKey, string providerName);
/// <summary>
/// Gets the two factor providers on a specific member.
/// </summary>
Task<Attempt<IEnumerable<UserTwoFactorProviderModel>, TwoFactorOperationStatus>> GetProviderNamesAsync(Guid memberKey);
/// <remarks>
/// The returned type can be anything depending on the setup providers. You will need to cast it to the type handled by
/// the provider.
/// </remarks>
Task<Attempt<ISetupTwoFactorModel, TwoFactorOperationStatus>> GetSetupInfoAsync(Guid memberKey, string providerName);
/// <summary>
/// Validates and Saves.
/// </summary>
Task<Attempt<TwoFactorOperationStatus>> ValidateAndSaveAsync(string providerName, Guid memberKey, string modelSecret, string modelCode);
/// <summary>
/// Disables 2FA with Code.
/// </summary>
Task<Attempt<TwoFactorOperationStatus>> DisableByCodeAsync(string providerName, Guid memberKey, string code);
}

View File

@@ -29,7 +29,7 @@ public interface ITwoFactorLoginService : IService
/// The returned type can be anything depending on the setup providers. You will need to cast it to the type handled by
/// the provider.
/// </remarks>
[Obsolete("Use IUserTwoFactorLoginService.GetSetupInfoAsync. This will be removed in Umbraco 15.")]
[Obsolete("Use IUserTwoFactorLoginService.GetSetupInfoAsync or IMemberTwoFactorLoginService.GetSetupInfoAsync. Scheduled for removal in Umbraco 16.")]
Task<object?> GetSetupInfoAsync(Guid userOrMemberKey, string providerName);
/// <summary>
@@ -60,13 +60,13 @@ public interface ITwoFactorLoginService : IService
/// <summary>
/// Disables 2FA with Code.
/// </summary>
[Obsolete("Use IUserTwoFactorLoginService.DisableByCodeAsync. This will be removed in Umbraco 15.")]
[Obsolete("Use IUserTwoFactorLoginService.DisableByCodeAsync or IMemberTwoFactorLoginService.DisableByCodeAsync. Scheduled for removal in Umbraco 16.")]
Task<bool> DisableWithCodeAsync(string providerName, Guid userOrMemberKey, string code);
/// <summary>
/// Validates and Saves.
/// </summary>
[Obsolete("Use IUserTwoFactorLoginService.ValidateAndSaveAsync. This will be removed in Umbraco 15.")]
[Obsolete("Use IUserTwoFactorLoginService.ValidateAndSaveAsync or IMemberTwoFactorLoginService.ValidateAndSaveAsync. Scheduled for removal in Umbraco 16.")]
Task<bool> ValidateAndSaveAsync(string providerName, Guid userKey, string secret, string code);
}

View File

@@ -0,0 +1,72 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
/// <inheritdoc cref="Umbraco.Cms.Core.Services.IMemberTwoFactorLoginService" />
internal class MemberTwoFactorLoginService : TwoFactorLoginServiceBase, IMemberTwoFactorLoginService
{
private readonly IMemberService _memberService;
public MemberTwoFactorLoginService(
ITwoFactorLoginService twoFactorLoginService,
IEnumerable<ITwoFactorProvider> twoFactorSetupGenerators,
IMemberService memberService,
ICoreScopeProvider scopeProvider)
: base(twoFactorLoginService, twoFactorSetupGenerators, scopeProvider) =>
_memberService = memberService;
/// <inheritdoc cref="IMemberTwoFactorLoginService.DisableAsync" />
public override async Task<Attempt<TwoFactorOperationStatus>> DisableAsync(Guid memberKey, string providerName)
{
IMember? member = _memberService.GetByKey(memberKey);
if (member is null)
{
return Attempt.Fail(TwoFactorOperationStatus.UserNotFound);
}
return await base.DisableAsync(memberKey, providerName);
}
/// <inheritdoc cref="IMemberTwoFactorLoginService.GetProviderNamesAsync" />
public override async Task<Attempt<IEnumerable<UserTwoFactorProviderModel>, TwoFactorOperationStatus>> GetProviderNamesAsync(Guid memberKey)
{
IMember? member = _memberService.GetByKey(memberKey);
if (member is null)
{
return Attempt.FailWithStatus(TwoFactorOperationStatus.UserNotFound, Enumerable.Empty<UserTwoFactorProviderModel>());
}
return await base.GetProviderNamesAsync(memberKey);
}
/// <inheritdoc cref="IMemberTwoFactorLoginService.GetSetupInfoAsync" />
public override async Task<Attempt<ISetupTwoFactorModel, TwoFactorOperationStatus>> GetSetupInfoAsync(Guid memberKey, string providerName)
{
IMember? member = _memberService.GetByKey(memberKey);
if (member is null)
{
return Attempt.FailWithStatus<ISetupTwoFactorModel, TwoFactorOperationStatus>(TwoFactorOperationStatus.UserNotFound, new NoopSetupTwoFactorModel());
}
return await base.GetSetupInfoAsync(memberKey, providerName);
}
/// <inheritdoc cref="IMemberTwoFactorLoginService.ValidateAndSaveAsync" />
public override async Task<Attempt<TwoFactorOperationStatus>> ValidateAndSaveAsync(string providerName, Guid memberKey, string secret, string code)
{
IMember? member = _memberService.GetByKey(memberKey);
if (member is null)
{
return Attempt.Fail(TwoFactorOperationStatus.UserNotFound);
}
return await base.ValidateAndSaveAsync(providerName, memberKey, secret, code);
}
}

View File

@@ -32,7 +32,7 @@ internal class UserTwoFactorLoginService : TwoFactorLoginServiceBase, IUserTwoFa
return await base.DisableAsync(userKey, providerName);
}
/// <inheritdoc cref="IUserTwoFactorLoginService.DisableAsync" />
/// <inheritdoc cref="IUserTwoFactorLoginService.GetProviderNamesAsync" />
public override async Task<Attempt<IEnumerable<UserTwoFactorProviderModel>, TwoFactorOperationStatus>> GetProviderNamesAsync(Guid userKey)
{
IUser? user = await _userService.GetAsync(userKey);
@@ -45,7 +45,7 @@ internal class UserTwoFactorLoginService : TwoFactorLoginServiceBase, IUserTwoFa
return await base.GetProviderNamesAsync(userKey);
}
/// <inheritdoc cref="IUserTwoFactorLoginService.DisableAsync" />
/// <inheritdoc cref="IUserTwoFactorLoginService.GetSetupInfoAsync" />
public override async Task<Attempt<ISetupTwoFactorModel, TwoFactorOperationStatus>> GetSetupInfoAsync(Guid userKey, string providerName)
{
IUser? user = await _userService.GetAsync(userKey);
@@ -58,7 +58,7 @@ internal class UserTwoFactorLoginService : TwoFactorLoginServiceBase, IUserTwoFa
return await base.GetSetupInfoAsync(userKey, providerName);
}
/// <inheritdoc cref="IUserTwoFactorLoginService.DisableAsync" />
/// <inheritdoc cref="IUserTwoFactorLoginService.ValidateAndSaveAsync" />
public override async Task<Attempt<TwoFactorOperationStatus>> ValidateAndSaveAsync(string providerName, Guid userKey, string secret, string code)
{
IUser? user = await _userService.GetAsync(userKey);