mirror of
https://github.com/bitwarden/server
synced 2025-12-25 04:33:26 +00:00
Revert filescoped (#2227)
* Revert "Add git blame entry (#2226)" This reverts commit239286737d. * Revert "Turn on file scoped namespaces (#2225)" This reverts commit34fb4cca2a.
This commit is contained in:
@@ -5,41 +5,42 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OtpNet;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class AuthenticatorTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public AuthenticatorTokenProvider(IServiceProvider serviceProvider)
|
||||
public class AuthenticatorTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
||||
if (string.IsNullOrWhiteSpace((string)provider?.MetaData["Key"]))
|
||||
public AuthenticatorTokenProvider(IServiceProvider serviceProvider)
|
||||
{
|
||||
return false;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
return await _serviceProvider.GetRequiredService<IUserService>()
|
||||
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Authenticator, user);
|
||||
}
|
||||
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
||||
if (string.IsNullOrWhiteSpace((string)provider?.MetaData["Key"]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return await _serviceProvider.GetRequiredService<IUserService>()
|
||||
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Authenticator, user);
|
||||
}
|
||||
|
||||
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
||||
var otp = new Totp(Base32Encoding.ToBytes((string)provider.MetaData["Key"]));
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
long timeStepMatched;
|
||||
var valid = otp.VerifyTotp(token, out timeStepMatched, new VerificationWindow(1, 1));
|
||||
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
||||
var otp = new Totp(Base32Encoding.ToBytes((string)provider.MetaData["Key"]));
|
||||
|
||||
return Task.FromResult(valid);
|
||||
long timeStepMatched;
|
||||
var valid = otp.VerifyTotp(token, out timeStepMatched, new VerificationWindow(1, 1));
|
||||
|
||||
return Task.FromResult(valid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,48 +2,49 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
// ref: https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs
|
||||
public static class CustomIdentityServiceCollectionExtensions
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
|
||||
this IServiceCollection services)
|
||||
where TUser : class
|
||||
where TRole : class
|
||||
// ref: https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs
|
||||
public static class CustomIdentityServiceCollectionExtensions
|
||||
{
|
||||
return services.AddIdentityWithoutCookieAuth<TUser, TRole>(setupAction: null);
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
|
||||
this IServiceCollection services,
|
||||
Action<IdentityOptions> setupAction)
|
||||
where TUser : class
|
||||
where TRole : class
|
||||
{
|
||||
// Hosting doesn't add IHttpContextAccessor by default
|
||||
services.AddHttpContextAccessor();
|
||||
// Identity services
|
||||
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
|
||||
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
|
||||
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
|
||||
services.TryAddScoped<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
|
||||
// No interface for the error describer so we can add errors without rev'ing the interface
|
||||
services.TryAddScoped<IdentityErrorDescriber>();
|
||||
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
|
||||
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
|
||||
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
|
||||
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
|
||||
services.TryAddScoped<UserManager<TUser>>();
|
||||
services.TryAddScoped<SignInManager<TUser>>();
|
||||
services.TryAddScoped<RoleManager<TRole>>();
|
||||
|
||||
if (setupAction != null)
|
||||
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
|
||||
this IServiceCollection services)
|
||||
where TUser : class
|
||||
where TRole : class
|
||||
{
|
||||
services.Configure(setupAction);
|
||||
return services.AddIdentityWithoutCookieAuth<TUser, TRole>(setupAction: null);
|
||||
}
|
||||
|
||||
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
|
||||
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
|
||||
this IServiceCollection services,
|
||||
Action<IdentityOptions> setupAction)
|
||||
where TUser : class
|
||||
where TRole : class
|
||||
{
|
||||
// Hosting doesn't add IHttpContextAccessor by default
|
||||
services.AddHttpContextAccessor();
|
||||
// Identity services
|
||||
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
|
||||
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
|
||||
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
|
||||
services.TryAddScoped<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
|
||||
// No interface for the error describer so we can add errors without rev'ing the interface
|
||||
services.TryAddScoped<IdentityErrorDescriber>();
|
||||
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
|
||||
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
|
||||
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
|
||||
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
|
||||
services.TryAddScoped<UserManager<TUser>>();
|
||||
services.TryAddScoped<SignInManager<TUser>>();
|
||||
services.TryAddScoped<RoleManager<TRole>>();
|
||||
|
||||
if (setupAction != null)
|
||||
{
|
||||
services.Configure(setupAction);
|
||||
}
|
||||
|
||||
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,80 +7,81 @@ using Bit.Core.Utilities.Duo;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class DuoWebTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public DuoWebTokenProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
GlobalSettings globalSettings)
|
||||
public class DuoWebTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
public DuoWebTokenProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
return false;
|
||||
_serviceProvider = serviceProvider;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||
if (!HasProperMetaData(provider))
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
return false;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Duo, user);
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Duo, user);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
return null;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var signatureRequest = DuoWeb.SignRequest((string)provider.MetaData["IKey"],
|
||||
(string)provider.MetaData["SKey"], _globalSettings.Duo.AKey, user.Email);
|
||||
return signatureRequest;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||
if (!HasProperMetaData(provider))
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
return null;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var response = DuoWeb.VerifyResponse((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"],
|
||||
_globalSettings.Duo.AKey, token);
|
||||
|
||||
return response == user.Email;
|
||||
}
|
||||
|
||||
var signatureRequest = DuoWeb.SignRequest((string)provider.MetaData["IKey"],
|
||||
(string)provider.MetaData["SKey"], _globalSettings.Duo.AKey, user.Email);
|
||||
return signatureRequest;
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return false;
|
||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
|
||||
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var response = DuoWeb.VerifyResponse((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"],
|
||||
_globalSettings.Duo.AKey, token);
|
||||
|
||||
return response == user.Email;
|
||||
}
|
||||
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
|
||||
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,79 +5,80 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class EmailTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public EmailTokenProvider(IServiceProvider serviceProvider)
|
||||
public class EmailTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||
if (!HasProperMetaData(provider))
|
||||
public EmailTokenProvider(IServiceProvider serviceProvider)
|
||||
{
|
||||
return false;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
return await _serviceProvider.GetRequiredService<IUserService>().
|
||||
TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Email, user);
|
||||
}
|
||||
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||
if (!HasProperMetaData(provider))
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
return null;
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await _serviceProvider.GetRequiredService<IUserService>().
|
||||
TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Email, user);
|
||||
}
|
||||
|
||||
return Task.FromResult(RedactEmail((string)provider.MetaData["Email"]));
|
||||
}
|
||||
|
||||
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
return _serviceProvider.GetRequiredService<IUserService>().VerifyTwoFactorEmailAsync(user, token);
|
||||
}
|
||||
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("Email") &&
|
||||
!string.IsNullOrWhiteSpace((string)provider.MetaData["Email"]);
|
||||
}
|
||||
|
||||
private static string RedactEmail(string email)
|
||||
{
|
||||
var emailParts = email.Split('@');
|
||||
|
||||
string shownPart = null;
|
||||
if (emailParts[0].Length > 2 && emailParts[0].Length <= 4)
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
shownPart = emailParts[0].Substring(0, 1);
|
||||
}
|
||||
else if (emailParts[0].Length > 4)
|
||||
{
|
||||
shownPart = emailParts[0].Substring(0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
shownPart = string.Empty;
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Task.FromResult(RedactEmail((string)provider.MetaData["Email"]));
|
||||
}
|
||||
|
||||
string redactedPart = null;
|
||||
if (emailParts[0].Length > 4)
|
||||
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
redactedPart = new string('*', emailParts[0].Length - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
redactedPart = new string('*', emailParts[0].Length - shownPart.Length);
|
||||
return _serviceProvider.GetRequiredService<IUserService>().VerifyTwoFactorEmailAsync(user, token);
|
||||
}
|
||||
|
||||
return $"{shownPart}{redactedPart}@{emailParts[1]}";
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("Email") &&
|
||||
!string.IsNullOrWhiteSpace((string)provider.MetaData["Email"]);
|
||||
}
|
||||
|
||||
private static string RedactEmail(string email)
|
||||
{
|
||||
var emailParts = email.Split('@');
|
||||
|
||||
string shownPart = null;
|
||||
if (emailParts[0].Length > 2 && emailParts[0].Length <= 4)
|
||||
{
|
||||
shownPart = emailParts[0].Substring(0, 1);
|
||||
}
|
||||
else if (emailParts[0].Length > 4)
|
||||
{
|
||||
shownPart = emailParts[0].Substring(0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
shownPart = string.Empty;
|
||||
}
|
||||
|
||||
string redactedPart = null;
|
||||
if (emailParts[0].Length > 4)
|
||||
{
|
||||
redactedPart = new string('*', emailParts[0].Length - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
redactedPart = new string('*', emailParts[0].Length - shownPart.Length);
|
||||
}
|
||||
|
||||
return $"{shownPart}{redactedPart}@{emailParts[1]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public interface IOrganizationTwoFactorTokenProvider
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization);
|
||||
Task<string> GenerateAsync(Organization organization, User user);
|
||||
Task<bool> ValidateAsync(string token, Organization organization, User user);
|
||||
public interface IOrganizationTwoFactorTokenProvider
|
||||
{
|
||||
Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization);
|
||||
Task<string> GenerateAsync(Organization organization, User user);
|
||||
Task<bool> ValidateAsync(string token, Organization organization, User user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class LowerInvariantLookupNormalizer : ILookupNormalizer
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public string NormalizeEmail(string email)
|
||||
public class LowerInvariantLookupNormalizer : ILookupNormalizer
|
||||
{
|
||||
return Normalize(email);
|
||||
}
|
||||
public string NormalizeEmail(string email)
|
||||
{
|
||||
return Normalize(email);
|
||||
}
|
||||
|
||||
public string NormalizeName(string name)
|
||||
{
|
||||
return Normalize(name);
|
||||
}
|
||||
public string NormalizeName(string name)
|
||||
{
|
||||
return Normalize(name);
|
||||
}
|
||||
|
||||
private string Normalize(string key)
|
||||
{
|
||||
return key?.Normalize().ToLowerInvariant();
|
||||
private string Normalize(string key)
|
||||
{
|
||||
return key?.Normalize().ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,72 +4,73 @@ using Bit.Core.Models;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities.Duo;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public interface IOrganizationDuoWebTokenProvider : IOrganizationTwoFactorTokenProvider { }
|
||||
|
||||
public class OrganizationDuoWebTokenProvider : IOrganizationDuoWebTokenProvider
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
public interface IOrganizationDuoWebTokenProvider : IOrganizationTwoFactorTokenProvider { }
|
||||
|
||||
public OrganizationDuoWebTokenProvider(GlobalSettings globalSettings)
|
||||
public class OrganizationDuoWebTokenProvider : IOrganizationDuoWebTokenProvider
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
|
||||
{
|
||||
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||
public OrganizationDuoWebTokenProvider(GlobalSettings globalSettings)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||
var canGenerate = organization.TwoFactorProviderIsEnabled(TwoFactorProviderType.OrganizationDuo)
|
||||
&& HasProperMetaData(provider);
|
||||
return Task.FromResult(canGenerate);
|
||||
}
|
||||
|
||||
public Task<string> GenerateAsync(Organization organization, User user)
|
||||
{
|
||||
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||
var canGenerate = organization.TwoFactorProviderIsEnabled(TwoFactorProviderType.OrganizationDuo)
|
||||
&& HasProperMetaData(provider);
|
||||
return Task.FromResult(canGenerate);
|
||||
}
|
||||
|
||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||
if (!HasProperMetaData(provider))
|
||||
public Task<string> GenerateAsync(Organization organization, User user)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
var signatureRequest = DuoWeb.SignRequest(provider.MetaData["IKey"].ToString(),
|
||||
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, user.Email);
|
||||
return Task.FromResult(signatureRequest);
|
||||
}
|
||||
|
||||
var signatureRequest = DuoWeb.SignRequest(provider.MetaData["IKey"].ToString(),
|
||||
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, user.Email);
|
||||
return Task.FromResult(signatureRequest);
|
||||
}
|
||||
|
||||
public Task<bool> ValidateAsync(string token, Organization organization, User user)
|
||||
{
|
||||
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||
public Task<bool> ValidateAsync(string token, Organization organization, User user)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var response = DuoWeb.VerifyResponse(provider.MetaData["IKey"].ToString(),
|
||||
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, token);
|
||||
|
||||
return Task.FromResult(response == user.Email);
|
||||
}
|
||||
|
||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||
if (!HasProperMetaData(provider))
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
|
||||
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
|
||||
}
|
||||
|
||||
var response = DuoWeb.VerifyResponse(provider.MetaData["IKey"].ToString(),
|
||||
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, token);
|
||||
|
||||
return Task.FromResult(response == user.Email);
|
||||
}
|
||||
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
|
||||
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,85 +5,86 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class PasswordlessSignInManager<TUser> : SignInManager<TUser> where TUser : class
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public const string PasswordlessSignInPurpose = "PasswordlessSignIn";
|
||||
|
||||
private readonly IMailService _mailService;
|
||||
|
||||
public PasswordlessSignInManager(UserManager<TUser> userManager,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
IUserClaimsPrincipalFactory<TUser> claimsFactory,
|
||||
IOptions<IdentityOptions> optionsAccessor,
|
||||
ILogger<SignInManager<TUser>> logger,
|
||||
IAuthenticationSchemeProvider schemes,
|
||||
IUserConfirmation<TUser> confirmation,
|
||||
IMailService mailService)
|
||||
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
|
||||
public class PasswordlessSignInManager<TUser> : SignInManager<TUser> where TUser : class
|
||||
{
|
||||
_mailService = mailService;
|
||||
}
|
||||
public const string PasswordlessSignInPurpose = "PasswordlessSignIn";
|
||||
|
||||
public async Task<SignInResult> PasswordlessSignInAsync(string email, string returnUrl)
|
||||
{
|
||||
var user = await UserManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
private readonly IMailService _mailService;
|
||||
|
||||
public PasswordlessSignInManager(UserManager<TUser> userManager,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
IUserClaimsPrincipalFactory<TUser> claimsFactory,
|
||||
IOptions<IdentityOptions> optionsAccessor,
|
||||
ILogger<SignInManager<TUser>> logger,
|
||||
IAuthenticationSchemeProvider schemes,
|
||||
IUserConfirmation<TUser> confirmation,
|
||||
IMailService mailService)
|
||||
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
|
||||
{
|
||||
return SignInResult.Failed;
|
||||
_mailService = mailService;
|
||||
}
|
||||
|
||||
var token = await UserManager.GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||
PasswordlessSignInPurpose);
|
||||
await _mailService.SendPasswordlessSignInAsync(returnUrl, token, email);
|
||||
return SignInResult.Success;
|
||||
}
|
||||
|
||||
public async Task<SignInResult> PasswordlessSignInAsync(TUser user, string token, bool isPersistent)
|
||||
{
|
||||
if (user == null)
|
||||
public async Task<SignInResult> PasswordlessSignInAsync(string email, string returnUrl)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var user = await UserManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
return SignInResult.Failed;
|
||||
}
|
||||
|
||||
var attempt = await CheckPasswordlessSignInAsync(user, token);
|
||||
return attempt.Succeeded ?
|
||||
await SignInOrTwoFactorAsync(user, isPersistent, bypassTwoFactor: true) : attempt;
|
||||
}
|
||||
|
||||
public async Task<SignInResult> PasswordlessSignInAsync(string email, string token, bool isPersistent)
|
||||
{
|
||||
var user = await UserManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
return SignInResult.Failed;
|
||||
}
|
||||
|
||||
return await PasswordlessSignInAsync(user, token, isPersistent);
|
||||
}
|
||||
|
||||
public virtual async Task<SignInResult> CheckPasswordlessSignInAsync(TUser user, string token)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var error = await PreSignInCheck(user);
|
||||
if (error != null)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||
PasswordlessSignInPurpose, token))
|
||||
{
|
||||
var token = await UserManager.GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||
PasswordlessSignInPurpose);
|
||||
await _mailService.SendPasswordlessSignInAsync(returnUrl, token, email);
|
||||
return SignInResult.Success;
|
||||
}
|
||||
|
||||
Logger.LogWarning(2, "User {userId} failed to provide the correct token.",
|
||||
await UserManager.GetUserIdAsync(user));
|
||||
return SignInResult.Failed;
|
||||
public async Task<SignInResult> PasswordlessSignInAsync(TUser user, string token, bool isPersistent)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var attempt = await CheckPasswordlessSignInAsync(user, token);
|
||||
return attempt.Succeeded ?
|
||||
await SignInOrTwoFactorAsync(user, isPersistent, bypassTwoFactor: true) : attempt;
|
||||
}
|
||||
|
||||
public async Task<SignInResult> PasswordlessSignInAsync(string email, string token, bool isPersistent)
|
||||
{
|
||||
var user = await UserManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
return SignInResult.Failed;
|
||||
}
|
||||
|
||||
return await PasswordlessSignInAsync(user, token, isPersistent);
|
||||
}
|
||||
|
||||
public virtual async Task<SignInResult> CheckPasswordlessSignInAsync(TUser user, string token)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var error = await PreSignInCheck(user);
|
||||
if (error != null)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||
PasswordlessSignInPurpose, token))
|
||||
{
|
||||
return SignInResult.Success;
|
||||
}
|
||||
|
||||
Logger.LogWarning(2, "User {userId} failed to provide the correct token.",
|
||||
await UserManager.GetUserIdAsync(user));
|
||||
return SignInResult.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,37 +2,38 @@
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public ReadOnlyDatabaseIdentityUserStore(
|
||||
IUserService userService,
|
||||
IUserRepository userRepository)
|
||||
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
{
|
||||
_userService = userService;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
private readonly IUserService _userService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
|
||||
}
|
||||
|
||||
public override async Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (!Guid.TryParse(userId, out var userIdGuid))
|
||||
public ReadOnlyDatabaseIdentityUserStore(
|
||||
IUserService userService,
|
||||
IUserRepository userRepository)
|
||||
{
|
||||
return null;
|
||||
_userService = userService;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
var user = await _userRepository.GetByIdAsync(userIdGuid);
|
||||
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
|
||||
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
|
||||
}
|
||||
|
||||
public override async Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (!Guid.TryParse(userId, out var userIdGuid))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var user = await _userRepository.GetByIdAsync(userIdGuid);
|
||||
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,65 +2,66 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public ReadOnlyEnvIdentityUserStore(IConfiguration configuration)
|
||||
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var usersCsv = _configuration["adminSettings:admins"];
|
||||
if (!CoreHelpers.SettingHasValue(usersCsv))
|
||||
public ReadOnlyEnvIdentityUserStore(IConfiguration configuration)
|
||||
{
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
var users = usersCsv.ToLowerInvariant().Split(',');
|
||||
var usersDict = new Dictionary<string, string>();
|
||||
foreach (var u in users)
|
||||
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var parts = u.Split(':');
|
||||
if (parts.Length == 2)
|
||||
var usersCsv = _configuration["adminSettings:admins"];
|
||||
if (!CoreHelpers.SettingHasValue(usersCsv))
|
||||
{
|
||||
var email = parts[0].Trim();
|
||||
var stamp = parts[1].Trim();
|
||||
usersDict.Add(email, stamp);
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
}
|
||||
else
|
||||
|
||||
var users = usersCsv.ToLowerInvariant().Split(',');
|
||||
var usersDict = new Dictionary<string, string>();
|
||||
foreach (var u in users)
|
||||
{
|
||||
var email = parts[0].Trim();
|
||||
usersDict.Add(email, email);
|
||||
var parts = u.Split(':');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
var email = parts[0].Trim();
|
||||
var stamp = parts[1].Trim();
|
||||
usersDict.Add(email, stamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
var email = parts[0].Trim();
|
||||
usersDict.Add(email, email);
|
||||
}
|
||||
}
|
||||
|
||||
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
|
||||
if (userStamp == null)
|
||||
{
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
}
|
||||
|
||||
return Task.FromResult(new IdentityUser
|
||||
{
|
||||
Id = normalizedEmail,
|
||||
Email = normalizedEmail,
|
||||
NormalizedEmail = normalizedEmail,
|
||||
EmailConfirmed = true,
|
||||
UserName = normalizedEmail,
|
||||
NormalizedUserName = normalizedEmail,
|
||||
SecurityStamp = userStamp
|
||||
});
|
||||
}
|
||||
|
||||
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
|
||||
if (userStamp == null)
|
||||
public override Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
return FindByEmailAsync(userId, cancellationToken);
|
||||
}
|
||||
|
||||
return Task.FromResult(new IdentityUser
|
||||
{
|
||||
Id = normalizedEmail,
|
||||
Email = normalizedEmail,
|
||||
NormalizedEmail = normalizedEmail,
|
||||
EmailConfirmed = true,
|
||||
UserName = normalizedEmail,
|
||||
NormalizedUserName = normalizedEmail,
|
||||
SecurityStamp = userStamp
|
||||
});
|
||||
}
|
||||
|
||||
public override Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return FindByEmailAsync(userId, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +1,120 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public abstract class ReadOnlyIdentityUserStore :
|
||||
IUserStore<IdentityUser>,
|
||||
IUserEmailStore<IdentityUser>,
|
||||
IUserSecurityStampStore<IdentityUser>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
||||
public Task<IdentityResult> CreateAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
public abstract class ReadOnlyIdentityUserStore :
|
||||
IUserStore<IdentityUser>,
|
||||
IUserEmailStore<IdentityUser>,
|
||||
IUserSecurityStampStore<IdentityUser>
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public void Dispose() { }
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<IdentityResult> CreateAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
public Task<IdentityResult> DeleteAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public abstract Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return await FindByEmailAsync(normalizedUserName, cancellationToken);
|
||||
}
|
||||
public abstract Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
public Task<string> GetEmailAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return await FindByEmailAsync(normalizedUserName, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<bool> GetEmailConfirmedAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.EmailConfirmed);
|
||||
}
|
||||
public Task<string> GetEmailAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedEmailAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task<bool> GetEmailConfirmedAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.EmailConfirmed);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedUserNameAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task<string> GetNormalizedEmailAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetUserIdAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Id);
|
||||
}
|
||||
public Task<string> GetNormalizedUserNameAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetUserNameAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task<string> GetUserIdAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Id);
|
||||
}
|
||||
|
||||
public Task SetEmailAsync(IdentityUser user, string email,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<string> GetUserNameAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task SetEmailConfirmedAsync(IdentityUser user, bool confirmed,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task SetEmailAsync(IdentityUser user, string email,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedEmailAsync(IdentityUser user, string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.NormalizedEmail = normalizedEmail;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetEmailConfirmedAsync(IdentityUser user, bool confirmed,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.NormalizedUserName = normalizedName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetNormalizedEmailAsync(IdentityUser user, string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.NormalizedEmail = normalizedEmail;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetUserNameAsync(IdentityUser user, string userName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.NormalizedUserName = normalizedName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
public Task SetUserNameAsync(IdentityUser user, string userName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetSecurityStampAsync(IdentityUser user, string stamp, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<IdentityResult> UpdateAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<string> GetSecurityStampAsync(IdentityUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.SecurityStamp);
|
||||
public Task SetSecurityStampAsync(IdentityUser user, string stamp, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetSecurityStampAsync(IdentityUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.SecurityStamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,61 @@
|
||||
using Bit.Core.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class RoleStore : IRoleStore<Role>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
||||
public Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
|
||||
public class RoleStore : IRoleStore<Role>
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public void Dispose() { }
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
public Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<string> GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
public Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
public Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken)
|
||||
{
|
||||
role.Name = roleName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken)
|
||||
{
|
||||
role.Name = roleName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(Role role, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,18 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class TwoFactorRememberTokenProvider : DataProtectorTokenProvider<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public TwoFactorRememberTokenProvider(
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
IOptions<TwoFactorRememberTokenProviderOptions> options,
|
||||
ILogger<DataProtectorTokenProvider<User>> logger)
|
||||
: base(dataProtectionProvider, options, logger)
|
||||
public class TwoFactorRememberTokenProvider : DataProtectorTokenProvider<User>
|
||||
{
|
||||
public TwoFactorRememberTokenProvider(
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
IOptions<TwoFactorRememberTokenProviderOptions> options,
|
||||
ILogger<DataProtectorTokenProvider<User>> logger)
|
||||
: base(dataProtectionProvider, options, logger)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class TwoFactorRememberTokenProviderOptions : DataProtectionTokenProviderOptions
|
||||
{ }
|
||||
}
|
||||
|
||||
public class TwoFactorRememberTokenProviderOptions : DataProtectionTokenProviderOptions
|
||||
{ }
|
||||
|
||||
@@ -5,179 +5,180 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class UserStore :
|
||||
IUserStore<User>,
|
||||
IUserPasswordStore<User>,
|
||||
IUserEmailStore<User>,
|
||||
IUserTwoFactorStore<User>,
|
||||
IUserSecurityStampStore<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public UserStore(
|
||||
IServiceProvider serviceProvider,
|
||||
IUserRepository userRepository,
|
||||
ICurrentContext currentContext)
|
||||
public class UserStore :
|
||||
IUserStore<User>,
|
||||
IUserPasswordStore<User>,
|
||||
IUserEmailStore<User>,
|
||||
IUserTwoFactorStore<User>,
|
||||
IUserSecurityStampStore<User>
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_userRepository = userRepository;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public async Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await _userRepository.CreateAsync(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await _userRepository.DeleteAsync(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public async Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (_currentContext?.User != null && _currentContext.User.Email == normalizedEmail)
|
||||
public UserStore(
|
||||
IServiceProvider serviceProvider,
|
||||
IUserRepository userRepository,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_userRepository = userRepository;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public async Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await _userRepository.CreateAsync(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await _userRepository.DeleteAsync(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public async Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (_currentContext?.User != null && _currentContext.User.Email == normalizedEmail)
|
||||
{
|
||||
return _currentContext.User;
|
||||
}
|
||||
|
||||
_currentContext.User = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return _currentContext.User;
|
||||
}
|
||||
|
||||
_currentContext.User = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return _currentContext.User;
|
||||
}
|
||||
|
||||
public async Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (_currentContext?.User != null &&
|
||||
string.Equals(_currentContext.User.Id.ToString(), userId, StringComparison.InvariantCultureIgnoreCase))
|
||||
public async Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (_currentContext?.User != null &&
|
||||
string.Equals(_currentContext.User.Id.ToString(), userId, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return _currentContext.User;
|
||||
}
|
||||
|
||||
Guid userIdGuid;
|
||||
if (!Guid.TryParse(userId, out userIdGuid))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_currentContext.User = await _userRepository.GetByIdAsync(userIdGuid);
|
||||
return _currentContext.User;
|
||||
}
|
||||
|
||||
Guid userIdGuid;
|
||||
if (!Guid.TryParse(userId, out userIdGuid))
|
||||
public async Task<User> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return null;
|
||||
return await FindByEmailAsync(normalizedUserName, cancellationToken);
|
||||
}
|
||||
|
||||
_currentContext.User = await _userRepository.GetByIdAsync(userIdGuid);
|
||||
return _currentContext.User;
|
||||
}
|
||||
public Task<string> GetEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public async Task<User> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return await FindByEmailAsync(normalizedUserName, cancellationToken);
|
||||
}
|
||||
public Task<bool> GetEmailConfirmedAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.EmailVerified);
|
||||
}
|
||||
|
||||
public Task<string> GetEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task<string> GetNormalizedEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<bool> GetEmailConfirmedAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.EmailVerified);
|
||||
}
|
||||
public Task<string> GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task<string> GetPasswordHashAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.MasterPassword);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task<string> GetUserIdAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Id.ToString());
|
||||
}
|
||||
|
||||
public Task<string> GetPasswordHashAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.MasterPassword);
|
||||
}
|
||||
public Task<string> GetUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetUserIdAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Id.ToString());
|
||||
}
|
||||
public Task<bool> HasPasswordAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(!string.IsNullOrWhiteSpace(user.MasterPassword));
|
||||
}
|
||||
|
||||
public Task<string> GetUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
public Task SetEmailAsync(User user, string email, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = email;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<bool> HasPasswordAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(!string.IsNullOrWhiteSpace(user.MasterPassword));
|
||||
}
|
||||
public Task SetEmailConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.EmailVerified = confirmed;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetEmailAsync(User user, string email, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = email;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetNormalizedEmailAsync(User user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = normalizedEmail;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetEmailConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.EmailVerified = confirmed;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = normalizedName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetNormalizedEmailAsync(User user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = normalizedEmail;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.MasterPassword = passwordHash;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = normalizedName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = userName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.MasterPassword = passwordHash;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public async Task<IdentityResult> UpdateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.Email = userName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetTwoFactorEnabledAsync(User user, bool enabled, CancellationToken cancellationToken)
|
||||
{
|
||||
// Do nothing...
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public async Task<IdentityResult> UpdateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
public async Task<bool> GetTwoFactorEnabledAsync(User user, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _serviceProvider.GetRequiredService<IUserService>().TwoFactorIsEnabledAsync(user);
|
||||
}
|
||||
|
||||
public Task SetTwoFactorEnabledAsync(User user, bool enabled, CancellationToken cancellationToken)
|
||||
{
|
||||
// Do nothing...
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken)
|
||||
{
|
||||
user.SecurityStamp = stamp;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public async Task<bool> GetTwoFactorEnabledAsync(User user, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _serviceProvider.GetRequiredService<IUserService>().TwoFactorIsEnabledAsync(user);
|
||||
}
|
||||
|
||||
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken)
|
||||
{
|
||||
user.SecurityStamp = stamp;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.SecurityStamp);
|
||||
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.SecurityStamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,145 +10,146 @@ using Fido2NetLib.Objects;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IFido2 _fido2;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public WebAuthnTokenProvider(IServiceProvider serviceProvider, IFido2 fido2, GlobalSettings globalSettings)
|
||||
public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_fido2 = fido2;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IFido2 _fido2;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
public WebAuthnTokenProvider(IServiceProvider serviceProvider, IFido2 fido2, GlobalSettings globalSettings)
|
||||
{
|
||||
return false;
|
||||
_serviceProvider = serviceProvider;
|
||||
_fido2 = fido2;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
var webAuthnProvider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
if (!HasProperMetaData(webAuthnProvider))
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
return false;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var webAuthnProvider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
if (!HasProperMetaData(webAuthnProvider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.WebAuthn, user);
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.WebAuthn, user);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
return null;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
var keys = LoadKeys(provider);
|
||||
var existingCredentials = keys.Select(key => key.Item2.Descriptor).ToList();
|
||||
|
||||
if (existingCredentials.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var exts = new AuthenticationExtensionsClientInputs()
|
||||
{
|
||||
UserVerificationMethod = true,
|
||||
AppID = CoreHelpers.U2fAppIdUrl(_globalSettings),
|
||||
};
|
||||
|
||||
var options = _fido2.GetAssertionOptions(existingCredentials, UserVerificationRequirement.Discouraged, exts);
|
||||
|
||||
// TODO: Remove this when newtonsoft legacy converters are gone
|
||||
provider.MetaData["login"] = JsonSerializer.Serialize(options);
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.WebAuthn] = provider;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
|
||||
|
||||
return options.ToJson();
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
var keys = LoadKeys(provider);
|
||||
var existingCredentials = keys.Select(key => key.Item2.Descriptor).ToList();
|
||||
|
||||
if (existingCredentials.Count == 0)
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
return null;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
var keys = LoadKeys(provider);
|
||||
|
||||
if (!provider.MetaData.ContainsKey("login"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
var jsonOptions = provider.MetaData["login"].ToString();
|
||||
var options = AssertionOptions.FromJson(jsonOptions);
|
||||
|
||||
var webAuthCred = keys.Find(k => k.Item2.Descriptor.Id.SequenceEqual(clientResponse.Id));
|
||||
|
||||
if (webAuthCred == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IsUserHandleOwnerOfCredentialIdAsync callback = (args) => Task.FromResult(true);
|
||||
|
||||
var res = await _fido2.MakeAssertionAsync(clientResponse, options, webAuthCred.Item2.PublicKey, webAuthCred.Item2.SignatureCounter, callback);
|
||||
|
||||
provider.MetaData.Remove("login");
|
||||
|
||||
// Update SignatureCounter
|
||||
webAuthCred.Item2.SignatureCounter = res.Counter;
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.WebAuthn].MetaData[webAuthCred.Item1] = webAuthCred.Item2;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
|
||||
|
||||
return res.Status == "ok";
|
||||
}
|
||||
|
||||
var exts = new AuthenticationExtensionsClientInputs()
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
UserVerificationMethod = true,
|
||||
AppID = CoreHelpers.U2fAppIdUrl(_globalSettings),
|
||||
};
|
||||
|
||||
var options = _fido2.GetAssertionOptions(existingCredentials, UserVerificationRequirement.Discouraged, exts);
|
||||
|
||||
// TODO: Remove this when newtonsoft legacy converters are gone
|
||||
provider.MetaData["login"] = JsonSerializer.Serialize(options);
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.WebAuthn] = provider;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
|
||||
|
||||
return options.ToJson();
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return false;
|
||||
return provider?.MetaData?.Any() ?? false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
var keys = LoadKeys(provider);
|
||||
|
||||
if (!provider.MetaData.ContainsKey("login"))
|
||||
private List<Tuple<string, TwoFactorProvider.WebAuthnData>> LoadKeys(TwoFactorProvider provider)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var keys = new List<Tuple<string, TwoFactorProvider.WebAuthnData>>();
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
// Support up to 5 keys
|
||||
for (var i = 1; i <= 5; i++)
|
||||
{
|
||||
var keyName = $"Key{i}";
|
||||
if (provider.MetaData.ContainsKey(keyName))
|
||||
{
|
||||
var key = new TwoFactorProvider.WebAuthnData((dynamic)provider.MetaData[keyName]);
|
||||
|
||||
var jsonOptions = provider.MetaData["login"].ToString();
|
||||
var options = AssertionOptions.FromJson(jsonOptions);
|
||||
keys.Add(new Tuple<string, TwoFactorProvider.WebAuthnData>(keyName, key));
|
||||
}
|
||||
}
|
||||
|
||||
var webAuthCred = keys.Find(k => k.Item2.Descriptor.Id.SequenceEqual(clientResponse.Id));
|
||||
|
||||
if (webAuthCred == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IsUserHandleOwnerOfCredentialIdAsync callback = (args) => Task.FromResult(true);
|
||||
|
||||
var res = await _fido2.MakeAssertionAsync(clientResponse, options, webAuthCred.Item2.PublicKey, webAuthCred.Item2.SignatureCounter, callback);
|
||||
|
||||
provider.MetaData.Remove("login");
|
||||
|
||||
// Update SignatureCounter
|
||||
webAuthCred.Item2.SignatureCounter = res.Counter;
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.WebAuthn].MetaData[webAuthCred.Item1] = webAuthCred.Item2;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
|
||||
|
||||
return res.Status == "ok";
|
||||
}
|
||||
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return provider?.MetaData?.Any() ?? false;
|
||||
}
|
||||
|
||||
private List<Tuple<string, TwoFactorProvider.WebAuthnData>> LoadKeys(TwoFactorProvider provider)
|
||||
{
|
||||
var keys = new List<Tuple<string, TwoFactorProvider.WebAuthnData>>();
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Support up to 5 keys
|
||||
for (var i = 1; i <= 5; i++)
|
||||
{
|
||||
var keyName = $"Key{i}";
|
||||
if (provider.MetaData.ContainsKey(keyName))
|
||||
{
|
||||
var key = new TwoFactorProvider.WebAuthnData((dynamic)provider.MetaData[keyName]);
|
||||
|
||||
keys.Add(new Tuple<string, TwoFactorProvider.WebAuthnData>(keyName, key));
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,70 +6,71 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YubicoDotNetClient;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class YubicoOtpTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public YubicoOtpTokenProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
GlobalSettings globalSettings)
|
||||
public class YubicoOtpTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
public YubicoOtpTokenProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
return false;
|
||||
_serviceProvider = serviceProvider;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
||||
if (!provider?.MetaData.Values.Any(v => !string.IsNullOrWhiteSpace((string)v)) ?? true)
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
return false;
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
||||
if (!provider?.MetaData.Values.Any(v => !string.IsNullOrWhiteSpace((string)v)) ?? true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.YubiKey, user);
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.YubiKey, user);
|
||||
}
|
||||
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
return false;
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48)
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var id = token.Substring(0, 12);
|
||||
if (string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
||||
if (!provider.MetaData.ContainsValue(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var id = token.Substring(0, 12);
|
||||
|
||||
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
|
||||
if (_globalSettings.Yubico.ValidationUrls != null && _globalSettings.Yubico.ValidationUrls.Length > 0)
|
||||
{
|
||||
client.SetUrls(_globalSettings.Yubico.ValidationUrls);
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
||||
if (!provider.MetaData.ContainsValue(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
|
||||
if (_globalSettings.Yubico.ValidationUrls != null && _globalSettings.Yubico.ValidationUrls.Length > 0)
|
||||
{
|
||||
client.SetUrls(_globalSettings.Yubico.ValidationUrls);
|
||||
}
|
||||
var response = await client.VerifyAsync(token);
|
||||
return response.Status == YubicoResponseStatus.Ok;
|
||||
}
|
||||
var response = await client.VerifyAsync(token);
|
||||
return response.Status == YubicoResponseStatus.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user