mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 13:38:13 -05:00
added org duo to 2fa flow
This commit is contained in:
parent
0d4ea5ce5b
commit
389512d51e
@ -4,13 +4,16 @@ using System.Linq;
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Bit.Core.Repositories;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Core
|
namespace Bit.Core
|
||||||
{
|
{
|
||||||
public class CurrentContext
|
public class CurrentContext
|
||||||
{
|
{
|
||||||
private string _ip;
|
private string _ip;
|
||||||
|
private Dictionary<Guid, ICollection<OrganizationUser>> _orgUsers =
|
||||||
|
new Dictionary<Guid, ICollection<OrganizationUser>>();
|
||||||
|
|
||||||
public virtual HttpContext HttpContext { get; set; }
|
public virtual HttpContext HttpContext { get; set; }
|
||||||
public virtual Guid? UserId { get; set; }
|
public virtual Guid? UserId { get; set; }
|
||||||
@ -18,7 +21,8 @@ namespace Bit.Core
|
|||||||
public virtual string DeviceIdentifier { get; set; }
|
public virtual string DeviceIdentifier { get; set; }
|
||||||
public virtual DeviceType? DeviceType { get; set; }
|
public virtual DeviceType? DeviceType { get; set; }
|
||||||
public virtual string IpAddress => GetRequestIp();
|
public virtual string IpAddress => GetRequestIp();
|
||||||
public virtual List<CurrentContentOrganization> Organizations { get; set; } = new List<CurrentContentOrganization>();
|
public virtual List<CurrentContentOrganization> Organizations { get; set; } =
|
||||||
|
new List<CurrentContentOrganization>();
|
||||||
public virtual Guid? InstallationId { get; set; }
|
public virtual Guid? InstallationId { get; set; }
|
||||||
|
|
||||||
public bool OrganizationUser(Guid orgId)
|
public bool OrganizationUser(Guid orgId)
|
||||||
@ -35,6 +39,17 @@ namespace Bit.Core
|
|||||||
return Organizations.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Owner);
|
return Organizations.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ICollection<OrganizationUser>> OrganizationMembershipAsync(
|
||||||
|
IOrganizationUserRepository organizationUserRepository, Guid userId)
|
||||||
|
{
|
||||||
|
if(!_orgUsers.ContainsKey(userId))
|
||||||
|
{
|
||||||
|
_orgUsers.Add(userId, await organizationUserRepository.GetManyByUserAsync(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _orgUsers[userId];
|
||||||
|
}
|
||||||
|
|
||||||
private string GetRequestIp()
|
private string GetRequestIp()
|
||||||
{
|
{
|
||||||
if(!string.IsNullOrWhiteSpace(_ip))
|
if(!string.IsNullOrWhiteSpace(_ip))
|
||||||
|
12
src/Core/Identity/IOrganizationTwoFactorTokenProvider.cs
Normal file
12
src/Core/Identity/IOrganizationTwoFactorTokenProvider.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
|
namespace Bit.Core.Identity
|
||||||
|
{
|
||||||
|
public interface IOrganizationTwoFactorTokenProvider
|
||||||
|
{
|
||||||
|
Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization);
|
||||||
|
Task<string> GenerateAsync(Organization organization, User user);
|
||||||
|
Task<bool> ValidateAsync(string token, Organization organization, User user);
|
||||||
|
}
|
||||||
|
}
|
76
src/Core/Identity/OrganizationDuoWebTokenProvider.cs
Normal file
76
src/Core/Identity/OrganizationDuoWebTokenProvider.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Utilities.Duo;
|
||||||
|
using Bit.Core.Models;
|
||||||
|
|
||||||
|
namespace Bit.Core.Identity
|
||||||
|
{
|
||||||
|
public interface IOrganizationDuoWebTokenProvider : IOrganizationTwoFactorTokenProvider { }
|
||||||
|
|
||||||
|
public class OrganizationDuoWebTokenProvider : IOrganizationDuoWebTokenProvider
|
||||||
|
{
|
||||||
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
|
public OrganizationDuoWebTokenProvider(GlobalSettings globalSettings)
|
||||||
|
{
|
||||||
|
_globalSettings = globalSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> GenerateAsync(Organization organization, User user)
|
||||||
|
{
|
||||||
|
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((string)provider.MetaData["IKey"],
|
||||||
|
(string)provider.MetaData["SKey"], _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)
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||||
|
if(!HasProperMetaData(provider))
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = DuoWeb.VerifyResponse((string)provider.MetaData["IKey"],
|
||||||
|
(string)provider.MetaData["SKey"], _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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,17 +17,20 @@ namespace Bit.Core.IdentityServer
|
|||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly ILicensingService _licensingService;
|
private readonly ILicensingService _licensingService;
|
||||||
|
private readonly CurrentContext _currentContext;
|
||||||
|
|
||||||
public ProfileService(
|
public ProfileService(
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
ILicensingService licensingService)
|
ILicensingService licensingService,
|
||||||
|
CurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_licensingService = licensingService;
|
_licensingService = licensingService;
|
||||||
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||||
@ -53,7 +56,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Orgs that this user belongs to
|
// Orgs that this user belongs to
|
||||||
var orgs = await _organizationUserRepository.GetManyByUserAsync(user.Id);
|
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
|
||||||
if(orgs.Any())
|
if(orgs.Any())
|
||||||
{
|
{
|
||||||
var groupedOrgs = orgs.Where(o => o.Status == Enums.OrganizationUserStatusType.Confirmed)
|
var groupedOrgs = orgs.Where(o => o.Status == Enums.OrganizationUserStatusType.Confirmed)
|
||||||
|
@ -12,6 +12,8 @@ using System.Threading.Tasks;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
|
using Bit.Core.Identity;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer
|
namespace Bit.Core.IdentityServer
|
||||||
{
|
{
|
||||||
@ -22,19 +24,34 @@ namespace Bit.Core.IdentityServer
|
|||||||
private readonly IDeviceService _deviceService;
|
private readonly IDeviceService _deviceService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
|
private readonly IOrganizationDuoWebTokenProvider _organizationDuoWebTokenProvider;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
|
private readonly CurrentContext _currentContext;
|
||||||
|
|
||||||
public ResourceOwnerPasswordValidator(
|
public ResourceOwnerPasswordValidator(
|
||||||
UserManager<User> userManager,
|
UserManager<User> userManager,
|
||||||
IDeviceRepository deviceRepository,
|
IDeviceRepository deviceRepository,
|
||||||
IDeviceService deviceService,
|
IDeviceService deviceService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IEventService eventService)
|
IEventService eventService,
|
||||||
|
IOrganizationDuoWebTokenProvider organizationDuoWebTokenProvider,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IApplicationCacheService applicationCacheService,
|
||||||
|
CurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
_deviceService = deviceService;
|
_deviceService = deviceService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
|
_organizationDuoWebTokenProvider = organizationDuoWebTokenProvider;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_applicationCacheService = applicationCacheService;
|
||||||
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
|
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
|
||||||
@ -42,7 +59,8 @@ namespace Bit.Core.IdentityServer
|
|||||||
var twoFactorToken = context.Request.Raw["TwoFactorToken"]?.ToString();
|
var twoFactorToken = context.Request.Raw["TwoFactorToken"]?.ToString();
|
||||||
var twoFactorProvider = context.Request.Raw["TwoFactorProvider"]?.ToString();
|
var twoFactorProvider = context.Request.Raw["TwoFactorProvider"]?.ToString();
|
||||||
var twoFactorRemember = context.Request.Raw["TwoFactorRemember"]?.ToString() == "1";
|
var twoFactorRemember = context.Request.Raw["TwoFactorRemember"]?.ToString() == "1";
|
||||||
var twoFactorRequest = !string.IsNullOrWhiteSpace(twoFactorToken) && !string.IsNullOrWhiteSpace(twoFactorProvider);
|
var twoFactorRequest = !string.IsNullOrWhiteSpace(twoFactorToken) &&
|
||||||
|
!string.IsNullOrWhiteSpace(twoFactorProvider);
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(context.UserName))
|
if(string.IsNullOrWhiteSpace(context.UserName))
|
||||||
{
|
{
|
||||||
@ -57,16 +75,18 @@ namespace Bit.Core.IdentityServer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(await TwoFactorRequiredAsync(user))
|
var twoFactorRequirement = await RequiresTwoFactorAsync(user);
|
||||||
|
if(twoFactorRequirement.Item1)
|
||||||
{
|
{
|
||||||
var twoFactorProviderType = TwoFactorProviderType.Authenticator; // Just defaulting it
|
var twoFactorProviderType = TwoFactorProviderType.Authenticator; // Just defaulting it
|
||||||
if(!twoFactorRequest || !Enum.TryParse(twoFactorProvider, out twoFactorProviderType))
|
if(!twoFactorRequest || !Enum.TryParse(twoFactorProvider, out twoFactorProviderType))
|
||||||
{
|
{
|
||||||
await BuildTwoFactorResultAsync(user, context);
|
await BuildTwoFactorResultAsync(user, twoFactorRequirement.Item2, context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var verified = await VerifyTwoFactor(user, twoFactorProviderType, twoFactorToken);
|
var verified = await VerifyTwoFactor(user, twoFactorRequirement.Item2,
|
||||||
|
twoFactorProviderType, twoFactorToken);
|
||||||
if(!verified && twoFactorProviderType != TwoFactorProviderType.Remember)
|
if(!verified && twoFactorProviderType != TwoFactorProviderType.Remember)
|
||||||
{
|
{
|
||||||
await BuildErrorResultAsync(true, context, user);
|
await BuildErrorResultAsync(true, context, user);
|
||||||
@ -75,7 +95,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
else if(!verified && twoFactorProviderType == TwoFactorProviderType.Remember)
|
else if(!verified && twoFactorProviderType == TwoFactorProviderType.Remember)
|
||||||
{
|
{
|
||||||
await Task.Delay(2000); // Delay for brute force.
|
await Task.Delay(2000); // Delay for brute force.
|
||||||
await BuildTwoFactorResultAsync(user, context);
|
await BuildTwoFactorResultAsync(user, twoFactorRequirement.Item2, context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +136,8 @@ namespace Bit.Core.IdentityServer
|
|||||||
|
|
||||||
if(sendRememberToken)
|
if(sendRememberToken)
|
||||||
{
|
{
|
||||||
var token = await _userManager.GenerateTwoFactorTokenAsync(user, TwoFactorProviderType.Remember.ToString());
|
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
|
||||||
|
TwoFactorProviderType.Remember.ToString());
|
||||||
customResponse.Add("TwoFactorToken", token);
|
customResponse.Add("TwoFactorToken", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,12 +147,26 @@ namespace Bit.Core.IdentityServer
|
|||||||
customResponse: customResponse);
|
customResponse: customResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task BuildTwoFactorResultAsync(User user, ResourceOwnerPasswordValidationContext context)
|
private async Task BuildTwoFactorResultAsync(User user, Organization organization,
|
||||||
|
ResourceOwnerPasswordValidationContext context)
|
||||||
{
|
{
|
||||||
var providerKeys = new List<byte>();
|
var providerKeys = new List<byte>();
|
||||||
var providers = new Dictionary<byte, Dictionary<string, object>>();
|
var providers = new Dictionary<byte, Dictionary<string, object>>();
|
||||||
var enabledProviders = user.GetTwoFactorProviders()?.Where(p => user.TwoFactorProviderIsEnabled(p.Key));
|
|
||||||
if(enabledProviders == null)
|
var enabledProviders = new List<KeyValuePair<TwoFactorProviderType, TwoFactorProvider>>();
|
||||||
|
if(organization?.GetTwoFactorProviders() != null)
|
||||||
|
{
|
||||||
|
enabledProviders.AddRange(organization.GetTwoFactorProviders().Where(
|
||||||
|
p => organization.TwoFactorProviderIsEnabled(p.Key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(user.GetTwoFactorProviders() != null)
|
||||||
|
{
|
||||||
|
enabledProviders.AddRange(
|
||||||
|
user.GetTwoFactorProviders().Where(p => user.TwoFactorProviderIsEnabled(p.Key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!enabledProviders.Any())
|
||||||
{
|
{
|
||||||
await BuildErrorResultAsync(false, context, user);
|
await BuildErrorResultAsync(false, context, user);
|
||||||
return;
|
return;
|
||||||
@ -140,7 +175,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
foreach(var provider in enabledProviders)
|
foreach(var provider in enabledProviders)
|
||||||
{
|
{
|
||||||
providerKeys.Add((byte)provider.Key);
|
providerKeys.Add((byte)provider.Key);
|
||||||
var infoDict = await BuildTwoFactorParams(user, provider.Key, provider.Value);
|
var infoDict = await BuildTwoFactorParams(organization, user, provider.Key, provider.Value);
|
||||||
providers.Add((byte)provider.Key, infoDict);
|
providers.Add((byte)provider.Key, infoDict);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,11 +211,34 @@ namespace Bit.Core.IdentityServer
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> TwoFactorRequiredAsync(User user)
|
public async Task<Tuple<bool, Organization>> RequiresTwoFactorAsync(User user)
|
||||||
{
|
{
|
||||||
return _userManager.SupportsUserTwoFactor &&
|
var individualRequired = _userManager.SupportsUserTwoFactor &&
|
||||||
await _userManager.GetTwoFactorEnabledAsync(user) &&
|
await _userManager.GetTwoFactorEnabledAsync(user) &&
|
||||||
(await _userManager.GetValidTwoFactorProvidersAsync(user)).Count > 0;
|
(await _userManager.GetValidTwoFactorProvidersAsync(user)).Count > 0;
|
||||||
|
|
||||||
|
Organization firstEnabledOrg = null;
|
||||||
|
var orgs = (await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id))
|
||||||
|
.Where(o => o.Status == OrganizationUserStatusType.Confirmed).ToList();
|
||||||
|
if(orgs.Any())
|
||||||
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
var twoFactorOrgs = orgs.Where(o => OrgUsing2fa(orgAbilities, o.OrganizationId));
|
||||||
|
if(twoFactorOrgs.Any())
|
||||||
|
{
|
||||||
|
var userOrgs = await _organizationRepository.GetManyByUserIdAsync(user.Id);
|
||||||
|
firstEnabledOrg = userOrgs.FirstOrDefault(
|
||||||
|
o => orgs.Any(om => om.OrganizationId == o.Id) && o.TwoFactorIsEnabled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple<bool, Organization>(individualRequired || firstEnabledOrg != null, firstEnabledOrg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OrgUsing2fa(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
|
||||||
|
{
|
||||||
|
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
|
||||||
|
orgAbilities[orgId].Enabled && orgAbilities[orgId].Using2fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Device GetDeviceFromRequest(ResourceOwnerPasswordValidationContext context)
|
private Device GetDeviceFromRequest(ResourceOwnerPasswordValidationContext context)
|
||||||
@ -205,13 +263,9 @@ namespace Bit.Core.IdentityServer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> VerifyTwoFactor(User user, TwoFactorProviderType type, string token)
|
private async Task<bool> VerifyTwoFactor(User user, Organization organization, TwoFactorProviderType type,
|
||||||
|
string token)
|
||||||
{
|
{
|
||||||
if(type != TwoFactorProviderType.Remember && !user.TwoFactorProviderIsEnabled(type))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case TwoFactorProviderType.Authenticator:
|
case TwoFactorProviderType.Authenticator:
|
||||||
@ -219,28 +273,43 @@ namespace Bit.Core.IdentityServer
|
|||||||
case TwoFactorProviderType.YubiKey:
|
case TwoFactorProviderType.YubiKey:
|
||||||
case TwoFactorProviderType.U2f:
|
case TwoFactorProviderType.U2f:
|
||||||
case TwoFactorProviderType.Remember:
|
case TwoFactorProviderType.Remember:
|
||||||
|
if(type != TwoFactorProviderType.Remember && !user.TwoFactorProviderIsEnabled(type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return await _userManager.VerifyTwoFactorTokenAsync(user, type.ToString(), token);
|
return await _userManager.VerifyTwoFactorTokenAsync(user, type.ToString(), token);
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
|
if(!user.TwoFactorProviderIsEnabled(type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return await _userService.VerifyTwoFactorEmailAsync(user, token);
|
return await _userService.VerifyTwoFactorEmailAsync(user, token);
|
||||||
|
case TwoFactorProviderType.OrganizationDuo:
|
||||||
|
if(!organization?.TwoFactorProviderIsEnabled(type) ?? true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _organizationDuoWebTokenProvider.ValidateAsync(token, organization, user);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, object>> BuildTwoFactorParams(User user, TwoFactorProviderType type,
|
private async Task<Dictionary<string, object>> BuildTwoFactorParams(Organization organization, User user,
|
||||||
TwoFactorProvider provider)
|
TwoFactorProviderType type, TwoFactorProvider provider)
|
||||||
{
|
{
|
||||||
if(!user.TwoFactorProviderIsEnabled(type))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case TwoFactorProviderType.Duo:
|
case TwoFactorProviderType.Duo:
|
||||||
case TwoFactorProviderType.U2f:
|
case TwoFactorProviderType.U2f:
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
case TwoFactorProviderType.YubiKey:
|
case TwoFactorProviderType.YubiKey:
|
||||||
|
if(!user.TwoFactorProviderIsEnabled(type))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var token = await _userManager.GenerateTwoFactorTokenAsync(user, type.ToString());
|
var token = await _userManager.GenerateTwoFactorTokenAsync(user, type.ToString());
|
||||||
if(type == TwoFactorProviderType.Duo)
|
if(type == TwoFactorProviderType.Duo)
|
||||||
{
|
{
|
||||||
@ -272,6 +341,16 @@ namespace Bit.Core.IdentityServer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
case TwoFactorProviderType.OrganizationDuo:
|
||||||
|
if(await _organizationDuoWebTokenProvider.CanGenerateTwoFactorTokenAsync(organization))
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["Host"] = provider.MetaData["Host"],
|
||||||
|
["Signature"] = await _organizationDuoWebTokenProvider.GenerateAsync(organization, user)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,15 @@ namespace Bit.Core.Models.Data
|
|||||||
Id = organization.Id;
|
Id = organization.Id;
|
||||||
UseEvents = organization.UseEvents;
|
UseEvents = organization.UseEvents;
|
||||||
Use2fa = organization.Use2fa;
|
Use2fa = organization.Use2fa;
|
||||||
|
Using2fa = organization.Use2fa && organization.TwoFactorProviders != null &&
|
||||||
|
organization.TwoFactorProviders != "{}";
|
||||||
Enabled = organization.Enabled;
|
Enabled = organization.Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public bool UseEvents { get; set; }
|
public bool UseEvents { get; set; }
|
||||||
public bool Use2fa { get; set; }
|
public bool Use2fa { get; set; }
|
||||||
|
public bool Using2fa { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,13 @@ namespace Bit.Core.Models.Table
|
|||||||
|
|
||||||
public void SetTwoFactorProviders(Dictionary<TwoFactorProviderType, TwoFactorProvider> providers)
|
public void SetTwoFactorProviders(Dictionary<TwoFactorProviderType, TwoFactorProvider> providers)
|
||||||
{
|
{
|
||||||
|
if(!providers.Any())
|
||||||
|
{
|
||||||
|
TwoFactorProviders = null;
|
||||||
|
_twoFactorProviders = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TwoFactorProviders = JsonConvert.SerializeObject(providers, new JsonSerializerSettings
|
TwoFactorProviders = JsonConvert.SerializeObject(providers, new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
ContractResolver = new EnumKeyResolver<byte>()
|
ContractResolver = new EnumKeyResolver<byte>()
|
||||||
|
@ -62,7 +62,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var orgs = await _organizationUserRepository.GetManyByUserAsync(userId);
|
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, userId);
|
||||||
orgEvents = orgs
|
orgEvents = orgs
|
||||||
.Where(o => o.Status == OrganizationUserStatusType.Confirmed &&
|
.Where(o => o.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
CanUseEvents(orgAbilities, o.OrganizationId))
|
CanUseEvents(orgAbilities, o.OrganizationId))
|
||||||
|
@ -155,6 +155,7 @@ namespace Bit.Core.Utilities
|
|||||||
this IServiceCollection services, GlobalSettings globalSettings)
|
this IServiceCollection services, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
services.AddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
services.AddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||||
|
services.AddSingleton<IOrganizationDuoWebTokenProvider, OrganizationDuoWebTokenProvider>();
|
||||||
|
|
||||||
services.Configure<TwoFactorRememberTokenProviderOptions>(options =>
|
services.Configure<TwoFactorRememberTokenProviderOptions>(options =>
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,12 @@ BEGIN
|
|||||||
[Id],
|
[Id],
|
||||||
[UseEvents],
|
[UseEvents],
|
||||||
[Use2fa],
|
[Use2fa],
|
||||||
|
CASE
|
||||||
|
WHEN [Use2fa] = 1 AND [TwoFactorProviders] IS NOT NULL AND [TwoFactorProviders] != '{}' THEN
|
||||||
|
1
|
||||||
|
ELSE
|
||||||
|
0
|
||||||
|
END AS [Using2fa],
|
||||||
[Enabled]
|
[Enabled]
|
||||||
FROM
|
FROM
|
||||||
[dbo].[Organization]
|
[dbo].[Organization]
|
||||||
|
@ -236,6 +236,12 @@ BEGIN
|
|||||||
[Id],
|
[Id],
|
||||||
[UseEvents],
|
[UseEvents],
|
||||||
[Use2fa],
|
[Use2fa],
|
||||||
|
CASE
|
||||||
|
WHEN [Use2fa] = 1 AND [TwoFactorProviders] IS NOT NULL AND [TwoFactorProviders] != '{}' THEN
|
||||||
|
1
|
||||||
|
ELSE
|
||||||
|
0
|
||||||
|
END AS [Using2fa],
|
||||||
[Enabled]
|
[Enabled]
|
||||||
FROM
|
FROM
|
||||||
[dbo].[Organization]
|
[dbo].[Organization]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user