1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-24 12:35:25 +01:00

EC-198 Added feature flag for 2FA Email for new device login (#1993)

* EC-198 added global setting flag for 2FA email on new device login feature

* EC-198 Removed is development environment check on 2FA email new device login given that we can now rely on the global settings feature flag

* EC-198 Improved IGlobalSettings and UserService code for testing
This commit is contained in:
Federico Maccaroni 2022-05-13 10:48:48 -03:00 committed by GitHub
parent bbb55ef8de
commit 2e2d3075d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 21 deletions

View File

@ -17,9 +17,7 @@ using Bit.Core.Utilities;
using Fido2NetLib; using Fido2NetLib;
using Fido2NetLib.Objects; using Fido2NetLib.Objects;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using File = System.IO.File; using File = System.IO.File;
@ -50,11 +48,10 @@ namespace Bit.Core.Services
private readonly IReferenceEventService _referenceEventService; private readonly IReferenceEventService _referenceEventService;
private readonly IFido2 _fido2; private readonly IFido2 _fido2;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings; private readonly IGlobalSettings _globalSettings;
private readonly IOrganizationService _organizationService; private readonly IOrganizationService _organizationService;
private readonly IProviderUserRepository _providerUserRepository; private readonly IProviderUserRepository _providerUserRepository;
private readonly IDeviceRepository _deviceRepository; private readonly IDeviceRepository _deviceRepository;
private readonly IWebHostEnvironment _environment;
public UserService( public UserService(
IUserRepository userRepository, IUserRepository userRepository,
@ -81,11 +78,10 @@ namespace Bit.Core.Services
IReferenceEventService referenceEventService, IReferenceEventService referenceEventService,
IFido2 fido2, IFido2 fido2,
ICurrentContext currentContext, ICurrentContext currentContext,
GlobalSettings globalSettings, IGlobalSettings globalSettings,
IOrganizationService organizationService, IOrganizationService organizationService,
IProviderUserRepository providerUserRepository, IProviderUserRepository providerUserRepository,
IDeviceRepository deviceRepository, IDeviceRepository deviceRepository)
IWebHostEnvironment environment)
: base( : base(
store, store,
optionsAccessor, optionsAccessor,
@ -121,7 +117,6 @@ namespace Bit.Core.Services
_organizationService = organizationService; _organizationService = organizationService;
_providerUserRepository = providerUserRepository; _providerUserRepository = providerUserRepository;
_deviceRepository = deviceRepository; _deviceRepository = deviceRepository;
_environment = environment;
} }
public Guid? GetProperUserId(ClaimsPrincipal principal) public Guid? GetProperUserId(ClaimsPrincipal principal)
@ -1422,9 +1417,9 @@ namespace Bit.Core.Services
public async Task<bool> Needs2FABecauseNewDeviceAsync(User user, string deviceIdentifier, string grantType) public async Task<bool> Needs2FABecauseNewDeviceAsync(User user, string deviceIdentifier, string grantType)
{ {
return user.EmailVerified return _globalSettings.TwoFactorAuth.EmailOnNewDeviceLogin
&& user.EmailVerified
&& grantType != "authorization_code" && grantType != "authorization_code"
&& !_environment.IsDevelopment()
&& await IsNewDeviceAndNotTheFirstOneAsync(user, deviceIdentifier); && await IsNewDeviceAndNotTheFirstOneAsync(user, deviceIdentifier);
} }

View File

@ -69,6 +69,7 @@ namespace Bit.Core.Settings
public virtual AppleIapSettings AppleIap { get; set; } = new AppleIapSettings(); public virtual AppleIapSettings AppleIap { get; set; } = new AppleIapSettings();
public virtual SsoSettings Sso { get; set; } = new SsoSettings(); public virtual SsoSettings Sso { get; set; } = new SsoSettings();
public virtual StripeSettings Stripe { get; set; } = new StripeSettings(); public virtual StripeSettings Stripe { get; set; } = new StripeSettings();
public virtual ITwoFactorAuthSettings TwoFactorAuth { get; set; } = new TwoFactorAuthSettings();
public string BuildExternalUri(string explicitValue, string name) public string BuildExternalUri(string explicitValue, string name)
{ {
@ -480,5 +481,10 @@ namespace Bit.Core.Settings
public string ApiKey { get; set; } public string ApiKey { get; set; }
public int MaxNetworkRetries { get; set; } = 2; public int MaxNetworkRetries { get; set; } = 2;
} }
public class TwoFactorAuthSettings : ITwoFactorAuthSettings
{
public bool EmailOnNewDeviceLogin { get; set; } = true;
}
} }
} }

View File

@ -1,6 +1,4 @@
using static Bit.Core.Settings.GlobalSettings; namespace Bit.Core.Settings
namespace Bit.Core.Settings
{ {
public interface IGlobalSettings public interface IGlobalSettings
{ {
@ -10,9 +8,11 @@ namespace Bit.Core.Settings
string LicenseDirectory { get; set; } string LicenseDirectory { get; set; }
string LicenseCertificatePassword { get; set; } string LicenseCertificatePassword { get; set; }
int OrganizationInviteExpirationHours { get; set; } int OrganizationInviteExpirationHours { get; set; }
bool DisableUserRegistration { get; set; }
IInstallationSettings Installation { get; set; } IInstallationSettings Installation { get; set; }
IFileStorageSettings Attachment { get; set; } IFileStorageSettings Attachment { get; set; }
IConnectionStringSettings Storage { get; set; } IConnectionStringSettings Storage { get; set; }
IBaseServiceUriSettings BaseServiceUri { get; set; } IBaseServiceUriSettings BaseServiceUri { get; set; }
ITwoFactorAuthSettings TwoFactorAuth { get; set; }
} }
} }

View File

@ -0,0 +1,7 @@
namespace Bit.Core.Settings
{
public interface ITwoFactorAuthSettings
{
bool EmailOnNewDeviceLogin { get; set; }
}
}

View File

@ -12,9 +12,7 @@ using Bit.Core.Services;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers; using Bit.Test.Common.Helpers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Hosting;
using NSubstitute; using NSubstitute;
using NSubstitute.ReceivedExtensions; using NSubstitute.ReceivedExtensions;
using Xunit; using Xunit;
@ -38,8 +36,8 @@ namespace Bit.Core.Test.Services
user.EmailVerified = true; user.EmailVerified = true;
user.Email = userLicense.Email; user.Email = userLicense.Email;
sutProvider.GetDependency<Settings.GlobalSettings>().SelfHosted = true; sutProvider.GetDependency<Settings.IGlobalSettings>().SelfHosted = true;
sutProvider.GetDependency<Settings.GlobalSettings>().LicenseDirectory = tempDir.Directory; sutProvider.GetDependency<Settings.IGlobalSettings>().LicenseDirectory = tempDir.Directory;
sutProvider.GetDependency<ILicensingService>() sutProvider.GetDependency<ILicensingService>()
.VerifyLicense(userLicense) .VerifyLicense(userLicense)
.Returns(true); .Returns(true);
@ -175,6 +173,9 @@ namespace Bit.Core.Test.Services
new Device { Identifier = deviceIdInRepo } new Device { Identifier = deviceIdInRepo }
})); }));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.True(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); Assert.True(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
} }
@ -246,7 +247,7 @@ namespace Bit.Core.Test.Services
} }
[Theory, CustomAutoData(typeof(SutProviderCustomization))] [Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Environment_Is_Development(SutProvider<UserService> sutProvider, User user) public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{ {
user.Id = Guid.NewGuid(); user.Id = Guid.NewGuid();
user.EmailVerified = true; user.EmailVerified = true;
@ -260,9 +261,7 @@ namespace Bit.Core.Test.Services
new Device { Identifier = deviceIdInRepo } new Device { Identifier = deviceIdInRepo }
})); }));
sutProvider.GetDependency<IWebHostEnvironment>() sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false);
.EnvironmentName
.Returns(Environments.Development);
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
} }