mirror of
https://github.com/bitwarden/server
synced 2026-01-03 17:14:00 +00:00
Refactor TwoFactorIsEnabledQuery to introduce VNextAsync methods for improved premium access checks and user detail handling. Removed obsolete feature service dependency and enhanced test coverage for new functionality.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.UserFeatures.PremiumAccess;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data;
|
||||
@@ -404,6 +405,503 @@ public class TwoFactorIsEnabledQueryTests
|
||||
.GetCalculatedPremiumAsync(default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Authenticator)]
|
||||
[BitAutoData(TwoFactorProviderType.Email)]
|
||||
[BitAutoData(TwoFactorProviderType.Remember)]
|
||||
[BitAutoData(TwoFactorProviderType.OrganizationDuo)]
|
||||
[BitAutoData(TwoFactorProviderType.WebAuthn)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_WithProviderTypeNotRequiringPremium_ReturnsAllTwoFactorEnabled(
|
||||
TwoFactorProviderType freeProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
List<User> users)
|
||||
{
|
||||
// Arrange
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ freeProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
user.Premium = false;
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
}
|
||||
|
||||
var premiumStatus = users.ToDictionary(u => u.Id, u => false);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.All(userIds.Contains)))
|
||||
.Returns(users);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(Arg.Is<IEnumerable<User>>(u => u.All(users.Contains)))
|
||||
.Returns(premiumStatus);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds);
|
||||
|
||||
// Assert
|
||||
foreach (var user in users)
|
||||
{
|
||||
Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == true);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_DatabaseReturnsEmpty_ResultEmpty(
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
List<User> users)
|
||||
{
|
||||
// Arrange
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([]);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(Arg.Any<IEnumerable<User>>())
|
||||
.Returns(new Dictionary<Guid, bool>());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData((IEnumerable<Guid>)null)]
|
||||
[BitAutoData([])]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_UserIdsNullorEmpty_ResultEmpty(
|
||||
IEnumerable<Guid> userIds,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider)
|
||||
{
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_WithNoTwoFactorEnabled_ReturnsAllTwoFactorDisabled(
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
List<User> users)
|
||||
{
|
||||
// Arrange
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ TwoFactorProviderType.Email, new TwoFactorProvider { Enabled = false } }
|
||||
};
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
}
|
||||
|
||||
var premiumStatus = users.ToDictionary(u => u.Id, u => false);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.All(userIds.Contains)))
|
||||
.Returns(users);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(Arg.Is<IEnumerable<User>>(u => u.All(users.Contains)))
|
||||
.Returns(premiumStatus);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds);
|
||||
|
||||
// Assert
|
||||
foreach (var user in users)
|
||||
{
|
||||
Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == false);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.YubiKey)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_WithProviderTypeRequiringPremium_ReturnsMixedResults(
|
||||
TwoFactorProviderType premiumProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
List<User> users)
|
||||
{
|
||||
// Arrange
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ TwoFactorProviderType.Email, new TwoFactorProvider { Enabled = false } },
|
||||
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
user.Premium = false;
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
}
|
||||
|
||||
// Only the first user has premium access
|
||||
var premiumStatus = users.ToDictionary(
|
||||
u => u.Id,
|
||||
u => users.IndexOf(u) == 0);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.All(userIds.Contains)))
|
||||
.Returns(users);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(Arg.Is<IEnumerable<User>>(u => u.All(users.Contains)))
|
||||
.Returns(premiumStatus);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds);
|
||||
|
||||
// Assert
|
||||
foreach (var user in users)
|
||||
{
|
||||
var expectedEnabled = premiumStatus[user.Id];
|
||||
Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == expectedEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData("")]
|
||||
[BitAutoData("{}")]
|
||||
[BitAutoData((string)null)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_WithNullOrEmptyTwoFactorProviders_ReturnsAllTwoFactorDisabled(
|
||||
string twoFactorProviders,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
List<User> users)
|
||||
{
|
||||
// Arrange
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
user.TwoFactorProviders = twoFactorProviders;
|
||||
}
|
||||
|
||||
var premiumStatus = users.ToDictionary(u => u.Id, u => false);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.All(userIds.Contains)))
|
||||
.Returns(users);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(Arg.Is<IEnumerable<User>>(u => u.All(users.Contains)))
|
||||
.Returns(premiumStatus);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds);
|
||||
|
||||
// Assert
|
||||
foreach (var user in users)
|
||||
{
|
||||
Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == false);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_Generic_WithNoUserIds_ReturnsAllTwoFactorDisabled(
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
List<OrganizationUserUserDetails> users)
|
||||
{
|
||||
// Arrange
|
||||
foreach (var user in users)
|
||||
{
|
||||
user.UserId = null;
|
||||
}
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(users);
|
||||
|
||||
// Assert
|
||||
foreach (var user in users)
|
||||
{
|
||||
Assert.Contains(result, res => res.user.Equals(user) && res.twoFactorIsEnabled == false);
|
||||
}
|
||||
|
||||
// No UserIds were supplied so no calls to the UserRepository should have been made
|
||||
await sutProvider.GetDependency<IUserRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.GetManyAsync(default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_UserIdNull_ReturnsFalse(
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var user = new TestTwoFactorProviderUser
|
||||
{
|
||||
Id = null
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Authenticator)]
|
||||
[BitAutoData(TwoFactorProviderType.Email)]
|
||||
[BitAutoData(TwoFactorProviderType.Remember)]
|
||||
[BitAutoData(TwoFactorProviderType.OrganizationDuo)]
|
||||
[BitAutoData(TwoFactorProviderType.WebAuthn)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeNotRequiringPremium_ReturnsTrue(
|
||||
TwoFactorProviderType freeProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ freeProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
user.Premium = false;
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
// Should not need to check premium access for free providers
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.CanAccessPremiumAsync(default(Guid), default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithNoTwoFactorEnabled_ReturnsFalse(
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ TwoFactorProviderType.Email, new TwoFactorProvider { Enabled = false } }
|
||||
};
|
||||
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.CanAccessPremiumAsync(default(Guid), default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.YubiKey)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeRequiringPremium_WithoutPremium_ReturnsFalse(
|
||||
TwoFactorProviderType premiumProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
user.Premium = false;
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(user.Id, false)
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.Received(1)
|
||||
.CanAccessPremiumAsync(user.Id, false);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.YubiKey)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeRequiringPremium_WithPersonalPremium_ReturnsTrue(
|
||||
TwoFactorProviderType premiumProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
user.Premium = true;
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(user.Id, true)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.Received(1)
|
||||
.CanAccessPremiumAsync(user.Id, true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.YubiKey)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeRequiringPremium_WithOrgPremium_ReturnsTrue(
|
||||
TwoFactorProviderType premiumProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
user.Premium = false;
|
||||
user.SetTwoFactorProviders(twoFactorProviders);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(user.Id, false)
|
||||
.Returns(true); // Has premium from org
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.Received(1)
|
||||
.CanAccessPremiumAsync(user.Id, false);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithNullTwoFactorProviders_ReturnsFalse(
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
user.TwoFactorProviders = null;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.CanAccessPremiumAsync(default(Guid), default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.YubiKey)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_OrganizationUserUserDetails_WithPremium_ReturnsTrue(
|
||||
TwoFactorProviderType premiumProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
OrganizationUserUserDetails orgUserDetails)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
orgUserDetails.Premium = false;
|
||||
orgUserDetails.TwoFactorProviders = JsonHelpers.LegacySerialize(twoFactorProviders, JsonHelpers.LegacyEnumKeyResolver);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(orgUserDetails.UserId!.Value, false)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(orgUserDetails);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.Received(1)
|
||||
.CanAccessPremiumAsync(orgUserDetails.UserId.Value, false);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.YubiKey)]
|
||||
public async Task TwoFactorIsEnabledVNextAsync_SingleUser_UnknownType_FetchesUser(
|
||||
TwoFactorProviderType premiumProviderType,
|
||||
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
|
||||
User fetchedUser)
|
||||
{
|
||||
// Arrange
|
||||
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
|
||||
};
|
||||
|
||||
var testUser = new TestTwoFactorProviderUser
|
||||
{
|
||||
Id = fetchedUser.Id,
|
||||
Premium = false,
|
||||
TwoFactorProviders = JsonHelpers.LegacySerialize(twoFactorProviders, JsonHelpers.LegacyEnumKeyResolver)
|
||||
};
|
||||
|
||||
fetchedUser.Premium = false;
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetByIdAsync(fetchedUser.Id)
|
||||
.Returns(fetchedUser);
|
||||
|
||||
sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.CanAccessPremiumAsync(fetchedUser.Id, false)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(testUser);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
await sutProvider.GetDependency<IUserRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(fetchedUser.Id);
|
||||
|
||||
await sutProvider.GetDependency<IPremiumAccessQuery>()
|
||||
.Received(1)
|
||||
.CanAccessPremiumAsync(fetchedUser.Id, false);
|
||||
}
|
||||
|
||||
private class TestTwoFactorProviderUser : ITwoFactorProvidersUser
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
@@ -418,10 +916,5 @@ public class TwoFactorIsEnabledQueryTests
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
|
||||
public bool GetPremium()
|
||||
{
|
||||
return Premium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user