1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

[PM-19029][PM-19203] Addressing UserService tech debt around ITwoFactorIsEnabledQuery (#5754)

* fix : split out the interface from the TwoFactorAuthenticationValidator into separate file.
* fix: replacing IUserService.TwoFactorEnabled with ITwoFactorEnabledQuery
* fix: combined logic for both bulk and single user look ups for TwoFactorIsEnabledQuery.
* fix: return two factor provider enabled on CanGenerate() method.

* tech debt: modfifying MFA providers to call the database less to validate if two factor is enabled. 
* tech debt: removed unused service from AuthenticatorTokenProvider

* doc: added documentation to ITwoFactorProviderUsers
* doc: updated comments for TwoFactorIsEnabled impl

* test: fixing tests for ITwoFactorIsEnabledQuery
* test: updating tests to have correct DI and removing test for automatic email of TOTP.
* test: adding better test coverage
This commit is contained in:
Ike
2025-05-09 11:39:57 -04:00
committed by GitHub
parent 80e7a0afd6
commit 3f95513d11
31 changed files with 372 additions and 259 deletions

View File

@ -5,6 +5,7 @@ using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
@ -53,6 +54,39 @@ public class TwoFactorIsEnabledQueryTests
}
}
[Theory, BitAutoData]
public async Task TwoFactorIsEnabledQuery_DatabaseReturnsEmpty_ResultEmpty(
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
List<UserWithCalculatedPremium> usersWithCalculatedPremium)
{
// Arrange
var userIds = usersWithCalculatedPremium.Select(u => u.Id).ToList();
sutProvider.GetDependency<IUserRepository>()
.GetManyWithCalculatedPremiumAsync(Arg.Any<IEnumerable<Guid>>())
.Returns([]);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(userIds);
// Assert
Assert.Empty(result);
}
[Theory]
[BitAutoData((IEnumerable<Guid>)null)]
[BitAutoData([])]
public async Task TwoFactorIsEnabledQuery_UserIdsNullorEmpty_ResultEmpty(
IEnumerable<Guid> userIds,
SutProvider<TwoFactorIsEnabledQuery> sutProvider)
{
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(userIds);
// Assert
Assert.Empty(result);
}
[Theory]
[BitAutoData]
public async Task TwoFactorIsEnabledQuery_WithNoTwoFactorEnabled_ReturnsAllTwoFactorDisabled(
@ -122,8 +156,11 @@ public class TwoFactorIsEnabledQueryTests
}
[Theory]
[BitAutoData]
public async Task TwoFactorIsEnabledQuery_WithNullTwoFactorProviders_ReturnsAllTwoFactorDisabled(
[BitAutoData("")]
[BitAutoData("{}")]
[BitAutoData((string)null)]
public async Task TwoFactorIsEnabledQuery_WithNullOrEmptyTwoFactorProviders_ReturnsAllTwoFactorDisabled(
string twoFactorProviders,
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
List<UserWithCalculatedPremium> usersWithCalculatedPremium)
{
@ -132,7 +169,7 @@ public class TwoFactorIsEnabledQueryTests
foreach (var user in usersWithCalculatedPremium)
{
user.TwoFactorProviders = null; // No two-factor providers configured
user.TwoFactorProviders = twoFactorProviders; // No two-factor providers configured
}
sutProvider.GetDependency<IUserRepository>()
@ -176,6 +213,24 @@ public class TwoFactorIsEnabledQueryTests
.GetManyWithCalculatedPremiumAsync(default);
}
[Theory]
[BitAutoData]
public async Task TwoFactorIsEnabledQuery_UserIdNull_ReturnsFalse(
SutProvider<TwoFactorIsEnabledQuery> sutProvider)
{
// Arrange
var user = new TestTwoFactorProviderUser
{
Id = null
};
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
// Assert
Assert.False(result);
}
[Theory]
[BitAutoData(TwoFactorProviderType.Authenticator)]
[BitAutoData(TwoFactorProviderType.Email)]
@ -193,10 +248,8 @@ public class TwoFactorIsEnabledQueryTests
{ freeProviderType, new TwoFactorProvider { Enabled = true } }
};
user.Premium = false;
user.SetTwoFactorProviders(twoFactorProviders);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
@ -205,7 +258,7 @@ public class TwoFactorIsEnabledQueryTests
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetManyWithCalculatedPremiumAsync(default);
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -230,7 +283,7 @@ public class TwoFactorIsEnabledQueryTests
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetManyWithCalculatedPremiumAsync(default);
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -252,14 +305,18 @@ public class TwoFactorIsEnabledQueryTests
user.SetTwoFactorProviders(twoFactorProviders);
sutProvider.GetDependency<IUserRepository>()
.GetManyWithCalculatedPremiumAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
.Returns(new List<UserWithCalculatedPremium> { user });
.GetCalculatedPremiumAsync(user.Id)
.Returns(user);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
// Assert
Assert.False(result);
await sutProvider.GetDependency<IUserRepository>()
.ReceivedWithAnyArgs(1)
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -268,7 +325,7 @@ public class TwoFactorIsEnabledQueryTests
public async Task TwoFactorIsEnabledQuery_WithProviderTypeRequiringPremium_WithUserPremium_ReturnsTrue(
TwoFactorProviderType premiumProviderType,
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
User user)
UserWithCalculatedPremium user)
{
// Arrange
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
@ -276,9 +333,14 @@ public class TwoFactorIsEnabledQueryTests
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
};
user.Premium = true;
user.Premium = false;
user.HasPremiumAccess = true;
user.SetTwoFactorProviders(twoFactorProviders);
sutProvider.GetDependency<IUserRepository>()
.GetCalculatedPremiumAsync(user.Id)
.Returns(user);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
@ -286,8 +348,8 @@ public class TwoFactorIsEnabledQueryTests
Assert.True(result);
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetManyWithCalculatedPremiumAsync(default);
.ReceivedWithAnyArgs(1)
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -309,14 +371,18 @@ public class TwoFactorIsEnabledQueryTests
user.SetTwoFactorProviders(twoFactorProviders);
sutProvider.GetDependency<IUserRepository>()
.GetManyWithCalculatedPremiumAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
.Returns(new List<UserWithCalculatedPremium> { user });
.GetCalculatedPremiumAsync(user.Id)
.Returns(user);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
// Assert
Assert.True(result);
await sutProvider.GetDependency<IUserRepository>()
.ReceivedWithAnyArgs(1)
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -333,5 +399,29 @@ public class TwoFactorIsEnabledQueryTests
// Assert
Assert.False(result);
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetCalculatedPremiumAsync(default);
}
private class TestTwoFactorProviderUser : ITwoFactorProvidersUser
{
public Guid? Id { get; set; }
public string TwoFactorProviders { get; set; }
public bool Premium { get; set; }
public Dictionary<TwoFactorProviderType, TwoFactorProvider> GetTwoFactorProviders()
{
return JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(TwoFactorProviders);
}
public Guid? GetUserId()
{
return Id;
}
public bool GetPremium()
{
return Premium;
}
}
}