diff --git a/src/Admin/Controllers/UsersController.cs b/src/Admin/Controllers/UsersController.cs index 2842efcdb..54e43d8b4 100644 --- a/src/Admin/Controllers/UsersController.cs +++ b/src/Admin/Controllers/UsersController.cs @@ -4,6 +4,7 @@ using Bit.Admin.Enums; using Bit.Admin.Models; using Bit.Admin.Services; using Bit.Admin.Utilities; +using Bit.Core; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; @@ -24,6 +25,8 @@ public class UsersController : Controller private readonly GlobalSettings _globalSettings; private readonly IAccessControlService _accessControlService; private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery; + private readonly IUserService _userService; + private readonly IFeatureService _featureService; public UsersController( IUserRepository userRepository, @@ -31,7 +34,9 @@ public class UsersController : Controller IPaymentService paymentService, GlobalSettings globalSettings, IAccessControlService accessControlService, - ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery) + ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, + IUserService userService, + IFeatureService featureService) { _userRepository = userRepository; _cipherRepository = cipherRepository; @@ -39,6 +44,8 @@ public class UsersController : Controller _globalSettings = globalSettings; _accessControlService = accessControlService; _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery; + _userService = userService; + _featureService = featureService; } [RequirePermission(Permission.User_List_View)] @@ -82,8 +89,8 @@ public class UsersController : Controller var ciphers = await _cipherRepository.GetManyByUserIdAsync(id); var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user); - - return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers)); + var verifiedDomain = await AccountDeprovisioningEnabled(user.Id); + return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, verifiedDomain)); } [SelfHosted(NotSelfHostedOnly = true)] @@ -99,7 +106,8 @@ public class UsersController : Controller var billingInfo = await _paymentService.GetBillingAsync(user); var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user); var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user); - return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings)); + var verifiedDomain = await AccountDeprovisioningEnabled(user.Id); + return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings, verifiedDomain)); } [HttpPost] @@ -153,4 +161,12 @@ public class UsersController : Controller return RedirectToAction("Index"); } + + // TODO: Feature flag to be removed in PM-14207 + private async Task AccountDeprovisioningEnabled(Guid userId) + { + return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) + ? await _userService.IsManagedByAnyOrganizationAsync(userId) + : null; + } } diff --git a/src/Admin/Models/UserEditModel.cs b/src/Admin/Models/UserEditModel.cs index 52cdb4c80..2ad0b27cb 100644 --- a/src/Admin/Models/UserEditModel.cs +++ b/src/Admin/Models/UserEditModel.cs @@ -20,9 +20,11 @@ public class UserEditModel IEnumerable ciphers, BillingInfo billingInfo, BillingHistoryInfo billingHistoryInfo, - GlobalSettings globalSettings) + GlobalSettings globalSettings, + bool? domainVerified + ) { - User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers); + User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, domainVerified); BillingInfo = billingInfo; BillingHistoryInfo = billingHistoryInfo; diff --git a/src/Admin/Models/UserViewModel.cs b/src/Admin/Models/UserViewModel.cs index 09b3d5577..75c089ee5 100644 --- a/src/Admin/Models/UserViewModel.cs +++ b/src/Admin/Models/UserViewModel.cs @@ -14,6 +14,7 @@ public class UserViewModel public bool Premium { get; } public short? MaxStorageGb { get; } public bool EmailVerified { get; } + public bool? DomainVerified { get; } public bool TwoFactorEnabled { get; } public DateTime AccountRevisionDate { get; } public DateTime RevisionDate { get; } @@ -35,6 +36,7 @@ public class UserViewModel bool premium, short? maxStorageGb, bool emailVerified, + bool? domainVerified, bool twoFactorEnabled, DateTime accountRevisionDate, DateTime revisionDate, @@ -56,6 +58,7 @@ public class UserViewModel Premium = premium; MaxStorageGb = maxStorageGb; EmailVerified = emailVerified; + DomainVerified = domainVerified; TwoFactorEnabled = twoFactorEnabled; AccountRevisionDate = accountRevisionDate; RevisionDate = revisionDate; @@ -73,10 +76,10 @@ public class UserViewModel public static IEnumerable MapViewModels( IEnumerable users, IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup) => - users.Select(user => MapViewModel(user, lookup)); + users.Select(user => MapViewModel(user, lookup, false)); public static UserViewModel MapViewModel(User user, - IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup) => + IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup, bool? domainVerified) => new( user.Id, user.Name, @@ -86,6 +89,7 @@ public class UserViewModel user.Premium, user.MaxStorageGb, user.EmailVerified, + domainVerified, IsTwoFactorEnabled(user, lookup), user.AccountRevisionDate, user.RevisionDate, @@ -100,9 +104,9 @@ public class UserViewModel Array.Empty()); public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled) => - MapViewModel(user, isTwoFactorEnabled, Array.Empty()); + MapViewModel(user, isTwoFactorEnabled, Array.Empty(), false); - public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable ciphers) => + public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable ciphers, bool? domainVerified) => new( user.Id, user.Name, @@ -112,6 +116,7 @@ public class UserViewModel user.Premium, user.MaxStorageGb, user.EmailVerified, + domainVerified, isTwoFactorEnabled, user.AccountRevisionDate, user.RevisionDate, diff --git a/src/Admin/Utilities/RolePermissionMapping.cs b/src/Admin/Utilities/RolePermissionMapping.cs index e260c264f..ec357c7e9 100644 --- a/src/Admin/Utilities/RolePermissionMapping.cs +++ b/src/Admin/Utilities/RolePermissionMapping.cs @@ -110,6 +110,7 @@ public static class RolePermissionMapping Permission.User_Licensing_View, Permission.User_Billing_View, Permission.User_Billing_LaunchGateway, + Permission.User_Delete, Permission.Org_List_View, Permission.Org_OrgInformation_View, Permission.Org_GeneralDetails_View, diff --git a/src/Admin/Views/Users/_ViewInformation.cshtml b/src/Admin/Views/Users/_ViewInformation.cshtml index 490ebd78d..00afcc19d 100644 --- a/src/Admin/Views/Users/_ViewInformation.cshtml +++ b/src/Admin/Views/Users/_ViewInformation.cshtml @@ -1,4 +1,4 @@ -@model UserViewModel +@model UserViewModel
Id
@Model.Id
@@ -12,6 +12,11 @@
Email Verified
@(Model.EmailVerified ? "Yes" : "No")
+ @if(Model.DomainVerified.HasValue){ +
Domain Verified
+
@(Model.DomainVerified.Value == true ? "Yes" : "No")
+ } +
Using 2FA
@(Model.TwoFactorEnabled ? "Yes" : "No")
diff --git a/test/Admin.Test/Models/UserViewModelTests.cs b/test/Admin.Test/Models/UserViewModelTests.cs index f7a76d80e..fac5d5f0e 100644 --- a/test/Admin.Test/Models/UserViewModelTests.cs +++ b/test/Admin.Test/Models/UserViewModelTests.cs @@ -2,6 +2,7 @@ using Bit.Admin.Models; using Bit.Core.Entities; +using Bit.Core.Vault.Entities; using Bit.Test.Common.AutoFixture.Attributes; namespace Admin.Test.Models; @@ -79,7 +80,7 @@ public class UserViewModelTests { var lookup = new List<(Guid, bool)> { (user.Id, true) }; - var actual = UserViewModel.MapViewModel(user, lookup); + var actual = UserViewModel.MapViewModel(user, lookup, false); Assert.True(actual.TwoFactorEnabled); } @@ -90,7 +91,7 @@ public class UserViewModelTests { var lookup = new List<(Guid, bool)> { (user.Id, false) }; - var actual = UserViewModel.MapViewModel(user, lookup); + var actual = UserViewModel.MapViewModel(user, lookup, false); Assert.False(actual.TwoFactorEnabled); } @@ -101,8 +102,44 @@ public class UserViewModelTests { var lookup = new List<(Guid, bool)> { (Guid.NewGuid(), true) }; - var actual = UserViewModel.MapViewModel(user, lookup); + var actual = UserViewModel.MapViewModel(user, lookup, false); Assert.False(actual.TwoFactorEnabled); } + + [Theory] + [BitAutoData] + public void MapUserViewModel_WithVerifiedDomain_ReturnsUserViewModel(User user) + { + + var verifiedDomain = true; + + var actual = UserViewModel.MapViewModel(user, true, Array.Empty(), verifiedDomain); + + Assert.True(actual.DomainVerified); + } + + [Theory] + [BitAutoData] + public void MapUserViewModel_WithoutVerifiedDomain_ReturnsUserViewModel(User user) + { + + var verifiedDomain = false; + + var actual = UserViewModel.MapViewModel(user, true, Array.Empty(), verifiedDomain); + + Assert.False(actual.DomainVerified); + } + + [Theory] + [BitAutoData] + public void MapUserViewModel_WithNullVerifiedDomain_ReturnsUserViewModel(User user) + { + + var actual = UserViewModel.MapViewModel(user, true, Array.Empty(), null); + + Assert.Null(actual.DomainVerified); + } + + }