diff --git a/src/Core/Auth/UserFeatures/PremiumAccess/IPremiumAccessQuery.cs b/src/Core/Auth/UserFeatures/PremiumAccess/IPremiumAccessQuery.cs index ebd7aec402..dfea0cb659 100644 --- a/src/Core/Auth/UserFeatures/PremiumAccess/IPremiumAccessQuery.cs +++ b/src/Core/Auth/UserFeatures/PremiumAccess/IPremiumAccessQuery.cs @@ -22,15 +22,6 @@ public interface IPremiumAccessQuery /// True if user can access premium features; false otherwise Task CanAccessPremiumAsync(User user); - /// - /// Checks if a user has access to premium features (personal subscription or organization). - /// Use this overload when you already know the personal premium status and only need to check organization premium. - /// - /// The user ID to check for premium access - /// Whether the user has a personal premium subscription - /// True if user can access premium features; false otherwise - Task CanAccessPremiumAsync(Guid userId, bool hasPersonalPremium); - /// /// Checks if a user has access to premium features through organization membership only. /// This is useful for determining the source of premium access (personal vs organization). diff --git a/src/Core/Auth/UserFeatures/PremiumAccess/PremiumAccessQuery.cs b/src/Core/Auth/UserFeatures/PremiumAccess/PremiumAccessQuery.cs index 8da0f120e0..1489c4b3a4 100644 --- a/src/Core/Auth/UserFeatures/PremiumAccess/PremiumAccessQuery.cs +++ b/src/Core/Auth/UserFeatures/PremiumAccess/PremiumAccessQuery.cs @@ -22,17 +22,12 @@ public class PremiumAccessQuery : IPremiumAccessQuery public async Task CanAccessPremiumAsync(User user) { - return await CanAccessPremiumAsync(user.Id, user.Premium); - } - - public async Task CanAccessPremiumAsync(Guid userId, bool hasPersonalPremium) - { - if (hasPersonalPremium) + if (user.Premium) { return true; } - return await HasPremiumFromOrganizationAsync(userId); + return await HasPremiumFromOrganizationAsync(user.Id); } public async Task HasPremiumFromOrganizationAsync(Guid userId) diff --git a/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs b/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs index 9beb658a32..453249343c 100644 --- a/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs +++ b/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs @@ -1,4 +1,5 @@ using Bit.Core.Auth.Models; +using Bit.Core.Entities; namespace Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; @@ -42,5 +43,5 @@ public interface ITwoFactorIsEnabledQuery /// This version uses PremiumAccessQuery with cached organization abilities for better performance. /// /// The user to check. - Task TwoFactorIsEnabledVNextAsync(ITwoFactorProvidersUser user); + Task TwoFactorIsEnabledVNextAsync(User user); } diff --git a/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs b/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs index 9ed022184f..3809e01e68 100644 --- a/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs +++ b/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs @@ -6,7 +6,6 @@ using Bit.Core.Auth.Models; using Bit.Core.Auth.UserFeatures.PremiumAccess; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Entities; -using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; namespace Bit.Core.Auth.UserFeatures.TwoFactorAuth; @@ -147,38 +146,11 @@ public class TwoFactorIsEnabledQuery : ITwoFactorIsEnabledQuery return result; } - public async Task TwoFactorIsEnabledVNextAsync(ITwoFactorProvidersUser user) + public async Task TwoFactorIsEnabledVNextAsync(User user) { - var userId = user.GetUserId(); - if (!userId.HasValue) - { - return false; - } - - // Try to get premium status without fetching User entity if possible - bool hasPersonalPremium; - if (user is User userEntity) - { - hasPersonalPremium = userEntity.Premium; - } - else if (user is OrganizationUserUserDetails orgUserDetails) - { - hasPersonalPremium = orgUserDetails.Premium.GetValueOrDefault(false); - } - else - { - // Fallback: fetch the User entity - var fetchedUser = await _userRepository.GetByIdAsync(userId.Value); - if (fetchedUser == null) - { - return false; - } - hasPersonalPremium = fetchedUser.Premium; - } - return await TwoFactorEnabledAsync( user.GetTwoFactorProviders(), - async () => await _premiumAccessQuery.CanAccessPremiumAsync(userId.Value, hasPersonalPremium)); + async () => await _premiumAccessQuery.CanAccessPremiumAsync(user)); } /// diff --git a/test/Core.Test/Auth/UserFeatures/PremiumAccess/PremiumAccessQueryTests.cs b/test/Core.Test/Auth/UserFeatures/PremiumAccess/PremiumAccessQueryTests.cs index b30fec46e6..43b57ac29f 100644 --- a/test/Core.Test/Auth/UserFeatures/PremiumAccess/PremiumAccessQueryTests.cs +++ b/test/Core.Test/Auth/UserFeatures/PremiumAccess/PremiumAccessQueryTests.cs @@ -93,80 +93,6 @@ public class PremiumAccessQueryTests Assert.False(result); } - [Theory, BitAutoData] - public async Task CanAccessPremiumAsync_WithGuidAndPremiumFlag_WhenHasPersonalPremium_ReturnsTrue( - Guid userId, - SutProvider sutProvider) - { - // Act - var result = await sutProvider.Sut.CanAccessPremiumAsync(userId, hasPersonalPremium: true); - - // Assert - Assert.True(result); - - // Should not call repository since personal premium is enough - await sutProvider.GetDependency() - .DidNotReceive() - .GetManyByUserAsync(Arg.Any()); - } - - [Theory, BitAutoData] - public async Task CanAccessPremiumAsync_WithGuidAndPremiumFlag_WhenNoPersonalPremiumButHasOrgPremium_ReturnsTrue( - Guid userId, - OrganizationUser orgUser, - SutProvider sutProvider) - { - // Arrange - orgUser.UserId = userId; - - var orgAbilities = new Dictionary - { - { - orgUser.OrganizationId, new OrganizationAbility - { - Id = orgUser.OrganizationId, - UsersGetPremium = true, - Enabled = true - } - } - }; - - sutProvider.GetDependency() - .GetManyByUserAsync(userId) - .Returns(new List { orgUser }); - - sutProvider.GetDependency() - .GetOrganizationAbilitiesAsync() - .Returns(orgAbilities); - - // Act - var result = await sutProvider.Sut.CanAccessPremiumAsync(userId, hasPersonalPremium: false); - - // Assert - Assert.True(result); - } - - [Theory, BitAutoData] - public async Task CanAccessPremiumAsync_WithGuidAndPremiumFlag_WhenNoPersonalPremiumAndNoOrgPremium_ReturnsFalse( - Guid userId, - SutProvider sutProvider) - { - // Arrange - sutProvider.GetDependency() - .GetManyByUserAsync(userId) - .Returns(new List()); - - sutProvider.GetDependency() - .GetOrganizationAbilitiesAsync() - .Returns(new Dictionary()); - - // Act - var result = await sutProvider.Sut.CanAccessPremiumAsync(userId, hasPersonalPremium: false); - - // Assert - Assert.False(result); - } - [Theory, BitAutoData] public async Task HasPremiumFromOrganizationAsync_WhenUserHasNoOrganizations_ReturnsFalse( Guid userId, diff --git a/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs b/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs index 7619331009..098e6ab952 100644 --- a/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs +++ b/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs @@ -634,24 +634,6 @@ public class TwoFactorIsEnabledQueryTests .GetManyAsync(default); } - [Theory] - [BitAutoData] - public async Task TwoFactorIsEnabledVNextAsync_SingleUser_UserIdNull_ReturnsFalse( - SutProvider 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)] @@ -681,7 +663,7 @@ public class TwoFactorIsEnabledQueryTests // Should not need to check premium access for free providers await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() - .CanAccessPremiumAsync(default(Guid), default); + .CanAccessPremiumAsync(Arg.Any()); } [Theory] @@ -706,7 +688,7 @@ public class TwoFactorIsEnabledQueryTests await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() - .CanAccessPremiumAsync(default(Guid), default); + .CanAccessPremiumAsync(Arg.Any()); } [Theory] @@ -727,7 +709,7 @@ public class TwoFactorIsEnabledQueryTests user.SetTwoFactorProviders(twoFactorProviders); sutProvider.GetDependency() - .CanAccessPremiumAsync(user.Id, false) + .CanAccessPremiumAsync(user) .Returns(false); // Act @@ -738,7 +720,7 @@ public class TwoFactorIsEnabledQueryTests await sutProvider.GetDependency() .Received(1) - .CanAccessPremiumAsync(user.Id, false); + .CanAccessPremiumAsync(user); } [Theory] @@ -759,7 +741,7 @@ public class TwoFactorIsEnabledQueryTests user.SetTwoFactorProviders(twoFactorProviders); sutProvider.GetDependency() - .CanAccessPremiumAsync(user.Id, true) + .CanAccessPremiumAsync(user) .Returns(true); // Act @@ -770,7 +752,7 @@ public class TwoFactorIsEnabledQueryTests await sutProvider.GetDependency() .Received(1) - .CanAccessPremiumAsync(user.Id, true); + .CanAccessPremiumAsync(user); } [Theory] @@ -791,7 +773,7 @@ public class TwoFactorIsEnabledQueryTests user.SetTwoFactorProviders(twoFactorProviders); sutProvider.GetDependency() - .CanAccessPremiumAsync(user.Id, false) + .CanAccessPremiumAsync(user) .Returns(true); // Has premium from org // Act @@ -802,7 +784,7 @@ public class TwoFactorIsEnabledQueryTests await sutProvider.GetDependency() .Received(1) - .CanAccessPremiumAsync(user.Id, false); + .CanAccessPremiumAsync(user); } [Theory] @@ -821,85 +803,7 @@ public class TwoFactorIsEnabledQueryTests Assert.False(result); await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() - .CanAccessPremiumAsync(default(Guid), default); - } - - [Theory] - [BitAutoData(TwoFactorProviderType.Duo)] - [BitAutoData(TwoFactorProviderType.YubiKey)] - public async Task TwoFactorIsEnabledVNextAsync_SingleUser_OrganizationUserUserDetails_WithPremium_ReturnsTrue( - TwoFactorProviderType premiumProviderType, - SutProvider sutProvider, - OrganizationUserUserDetails orgUserDetails) - { - // Arrange - var twoFactorProviders = new Dictionary - { - { premiumProviderType, new TwoFactorProvider { Enabled = true } } - }; - - orgUserDetails.Premium = false; - orgUserDetails.TwoFactorProviders = JsonHelpers.LegacySerialize(twoFactorProviders, JsonHelpers.LegacyEnumKeyResolver); - - sutProvider.GetDependency() - .CanAccessPremiumAsync(orgUserDetails.UserId!.Value, false) - .Returns(true); - - // Act - var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(orgUserDetails); - - // Assert - Assert.True(result); - - await sutProvider.GetDependency() - .Received(1) - .CanAccessPremiumAsync(orgUserDetails.UserId.Value, false); - } - - [Theory] - [BitAutoData(TwoFactorProviderType.Duo)] - [BitAutoData(TwoFactorProviderType.YubiKey)] - public async Task TwoFactorIsEnabledVNextAsync_SingleUser_UnknownType_FetchesUser( - TwoFactorProviderType premiumProviderType, - SutProvider sutProvider, - User fetchedUser) - { - // Arrange - var twoFactorProviders = new Dictionary - { - { 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() - .GetByIdAsync(fetchedUser.Id) - .Returns(fetchedUser); - - sutProvider.GetDependency() - .CanAccessPremiumAsync(fetchedUser.Id, false) - .Returns(true); - - // Act - var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(testUser); - - // Assert - Assert.True(result); - - await sutProvider.GetDependency() - .Received(1) - .GetByIdAsync(fetchedUser.Id); - - await sutProvider.GetDependency() - .Received(1) - .CanAccessPremiumAsync(fetchedUser.Id, false); + .CanAccessPremiumAsync(Arg.Any()); } private class TestTwoFactorProviderUser : ITwoFactorProvidersUser