Fixed minor issues and added xml docs (#11943)

This commit is contained in:
Bjarke Berg
2022-02-07 14:32:58 +01:00
committed by GitHub
parent 25c52d507a
commit 23803a44b7
3 changed files with 79 additions and 13 deletions

View File

@@ -5,24 +5,57 @@ using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Services
{
/// <summary>
/// Service handling 2FA logins.
/// </summary>
public interface ITwoFactorLoginService : IService
{
/// <summary>
/// Deletes all user logins - normally used when a member is deleted
/// Deletes all user logins - normally used when a member is deleted.
/// </summary>
Task DeleteUserLoginsAsync(Guid userOrMemberKey);
Task<bool> IsTwoFactorEnabledAsync(Guid userKey);
Task<string> GetSecretForUserAndProviderAsync(Guid userKey, string providerName);
/// <summary>
/// Checks whether 2FA is enabled for the user or member with the specified key.
/// </summary>
Task<bool> IsTwoFactorEnabledAsync(Guid userOrMemberKey);
/// <summary>
/// Gets the secret for user or member and a specific provider.
/// </summary>
Task<string> GetSecretForUserAndProviderAsync(Guid userOrMemberKey, string providerName);
/// <summary>
/// Gets the setup info for a specific user or member and a specific provider.
/// </summary>
/// <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<object> GetSetupInfoAsync(Guid userOrMemberKey, string providerName);
/// <summary>
/// Gets all registered providers names.
/// </summary>
IEnumerable<string> GetAllProviderNames();
/// <summary>
/// Disables the 2FA provider with the specified provider name for the specified user or member.
/// </summary>
Task<bool> DisableAsync(Guid userOrMemberKey, string providerName);
/// <summary>
/// Validates the setup of the provider using the secret and code.
/// </summary>
bool ValidateTwoFactorSetup(string providerName, string secret, string code);
/// <summary>
/// Saves the 2FA login information.
/// </summary>
Task SaveAsync(TwoFactorLogin twoFactorLogin);
/// <summary>
/// Gets all the enabled 2FA providers for the user or member with the specified key.
/// </summary>
Task<IEnumerable<string>> GetEnabledTwoFactorProviderNamesAsync(Guid userOrMemberKey);
}
}

View File

@@ -11,31 +11,41 @@ using Umbraco.Cms.Core.Security;
namespace Umbraco.Cms.Core.Services
{
/// <inheritdoc />
public class TwoFactorLoginService : ITwoFactorLoginService
{
private readonly ITwoFactorLoginRepository _twoFactorLoginRepository;
private readonly IScopeProvider _scopeProvider;
private readonly IOptions<IdentityOptions> _identityOptions;
private readonly IOptions<BackOfficeIdentityOptions> _backOfficeIdentityOptions;
private readonly IDictionary<string, ITwoFactorProvider> _twoFactorSetupGenerators;
/// <summary>
/// Initializes a new instance of the <see cref="TwoFactorLoginService"/> class.
/// </summary>
public TwoFactorLoginService(
ITwoFactorLoginRepository twoFactorLoginRepository,
IScopeProvider scopeProvider,
IEnumerable<ITwoFactorProvider> twoFactorSetupGenerators,
IOptions<IdentityOptions> identityOptions)
IOptions<IdentityOptions> identityOptions,
IOptions<BackOfficeIdentityOptions> backOfficeIdentityOptions
)
{
_twoFactorLoginRepository = twoFactorLoginRepository;
_scopeProvider = scopeProvider;
_identityOptions = identityOptions;
_twoFactorSetupGenerators = twoFactorSetupGenerators.ToDictionary(x=>x.ProviderName);
_backOfficeIdentityOptions = backOfficeIdentityOptions;
_twoFactorSetupGenerators = twoFactorSetupGenerators.ToDictionary(x =>x.ProviderName);
}
/// <inheritdoc />
public async Task DeleteUserLoginsAsync(Guid userOrMemberKey)
{
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
await _twoFactorLoginRepository.DeleteUserLoginsAsync(userOrMemberKey);
}
/// <inheritdoc />
public async Task<IEnumerable<string>> GetEnabledTwoFactorProviderNamesAsync(Guid userOrMemberKey)
{
return await GetEnabledProviderNamesAsync(userOrMemberKey);
@@ -47,26 +57,46 @@ namespace Umbraco.Cms.Core.Services
var providersOnUser = (await _twoFactorLoginRepository.GetByUserOrMemberKeyAsync(userOrMemberKey))
.Select(x => x.ProviderName).ToArray();
return providersOnUser.Where(x => _identityOptions.Value.Tokens.ProviderMap.ContainsKey(x));
return providersOnUser.Where(IsKnownProviderName);
}
/// <summary>
/// The provider needs to be registered as either a member provider or backoffice provider to show up.
/// </summary>
private bool IsKnownProviderName(string providerName)
{
if (_identityOptions.Value.Tokens.ProviderMap.ContainsKey(providerName))
{
return true;
}
if (_backOfficeIdentityOptions.Value.Tokens.ProviderMap.ContainsKey(providerName))
{
return true;
}
return false;
}
/// <inheritdoc />
public async Task<bool> IsTwoFactorEnabledAsync(Guid userOrMemberKey)
{
return (await GetEnabledProviderNamesAsync(userOrMemberKey)).Any();
}
/// <inheritdoc />
public async Task<string> GetSecretForUserAndProviderAsync(Guid userOrMemberKey, string providerName)
{
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
return (await _twoFactorLoginRepository.GetByUserOrMemberKeyAsync(userOrMemberKey)).FirstOrDefault(x=>x.ProviderName == providerName)?.Secret;
return (await _twoFactorLoginRepository.GetByUserOrMemberKeyAsync(userOrMemberKey)).FirstOrDefault(x => x.ProviderName == providerName)?.Secret;
}
/// <inheritdoc />
public async Task<object> GetSetupInfoAsync(Guid userOrMemberKey, string providerName)
{
var secret = await GetSecretForUserAndProviderAsync(userOrMemberKey, providerName);
//Dont allow to generate a new secrets if user already has one
// Dont allow to generate a new secrets if user already has one
if (!string.IsNullOrEmpty(secret))
{
return default;
@@ -82,14 +112,17 @@ namespace Umbraco.Cms.Core.Services
return await generator.GetSetupDataAsync(userOrMemberKey, secret);
}
/// <inheritdoc />
public IEnumerable<string> GetAllProviderNames() => _twoFactorSetupGenerators.Keys;
/// <inheritdoc />
public async Task<bool> DisableAsync(Guid userOrMemberKey, string providerName)
{
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
return (await _twoFactorLoginRepository.DeleteUserLoginsAsync(userOrMemberKey, providerName));
return await _twoFactorLoginRepository.DeleteUserLoginsAsync(userOrMemberKey, providerName);
}
/// <inheritdoc />
public bool ValidateTwoFactorSetup(string providerName, string secret, string code)
{
if (!_twoFactorSetupGenerators.TryGetValue(providerName, out ITwoFactorProvider generator))
@@ -100,6 +133,7 @@ namespace Umbraco.Cms.Core.Services
return generator.ValidateTwoFactorSetup(secret, code);
}
/// <inheritdoc />
public Task SaveAsync(TwoFactorLogin twoFactorLogin)
{
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
@@ -108,7 +142,6 @@ namespace Umbraco.Cms.Core.Services
return Task.CompletedTask;
}
/// <summary>
/// Generates a new random unique secret.
/// </summary>

View File

@@ -15,7 +15,7 @@ namespace Umbraco.Cms.Infrastructure.Security
public class TwoFactorBackOfficeValidationProvider<TTwoFactorSetupGenerator> : TwoFactorValidationProvider<BackOfficeIdentityUser, TTwoFactorSetupGenerator>
where TTwoFactorSetupGenerator : ITwoFactorProvider
{
protected TwoFactorBackOfficeValidationProvider(IDataProtectionProvider dataProtectionProvider, IOptions<DataProtectionTokenProviderOptions> options, ILogger<TwoFactorBackOfficeValidationProvider<TTwoFactorSetupGenerator>> logger, ITwoFactorLoginService twoFactorLoginService, TTwoFactorSetupGenerator generator) : base(dataProtectionProvider, options, logger, twoFactorLoginService, generator)
public TwoFactorBackOfficeValidationProvider(IDataProtectionProvider dataProtectionProvider, IOptions<DataProtectionTokenProviderOptions> options, ILogger<TwoFactorBackOfficeValidationProvider<TTwoFactorSetupGenerator>> logger, ITwoFactorLoginService twoFactorLoginService, TTwoFactorSetupGenerator generator) : base(dataProtectionProvider, options, logger, twoFactorLoginService, generator)
{
}