1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[PM-11408] domain verification stat in portal and add cs delete permission (#4943)

* Add delete permission to cs role

* Add domain verification stat to portal

* add feature flag and unit tests

* fix test

* Refactor from PR feedback

* update comment
This commit is contained in:
Brandon Treston 2024-10-30 10:45:53 -04:00 committed by GitHub
parent 6cc097ec49
commit 359c2787ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 14 deletions

View File

@ -4,6 +4,7 @@ using Bit.Admin.Enums;
using Bit.Admin.Models; using Bit.Admin.Models;
using Bit.Admin.Services; using Bit.Admin.Services;
using Bit.Admin.Utilities; using Bit.Admin.Utilities;
using Bit.Core;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
@ -24,6 +25,8 @@ public class UsersController : Controller
private readonly GlobalSettings _globalSettings; private readonly GlobalSettings _globalSettings;
private readonly IAccessControlService _accessControlService; private readonly IAccessControlService _accessControlService;
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery; private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
private readonly IUserService _userService;
private readonly IFeatureService _featureService;
public UsersController( public UsersController(
IUserRepository userRepository, IUserRepository userRepository,
@ -31,7 +34,9 @@ public class UsersController : Controller
IPaymentService paymentService, IPaymentService paymentService,
GlobalSettings globalSettings, GlobalSettings globalSettings,
IAccessControlService accessControlService, IAccessControlService accessControlService,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery) ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IUserService userService,
IFeatureService featureService)
{ {
_userRepository = userRepository; _userRepository = userRepository;
_cipherRepository = cipherRepository; _cipherRepository = cipherRepository;
@ -39,6 +44,8 @@ public class UsersController : Controller
_globalSettings = globalSettings; _globalSettings = globalSettings;
_accessControlService = accessControlService; _accessControlService = accessControlService;
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery; _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
_userService = userService;
_featureService = featureService;
} }
[RequirePermission(Permission.User_List_View)] [RequirePermission(Permission.User_List_View)]
@ -82,8 +89,8 @@ public class UsersController : Controller
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id); var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user); var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
var verifiedDomain = await AccountDeprovisioningEnabled(user.Id);
return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers)); return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, verifiedDomain));
} }
[SelfHosted(NotSelfHostedOnly = true)] [SelfHosted(NotSelfHostedOnly = true)]
@ -99,7 +106,8 @@ public class UsersController : Controller
var billingInfo = await _paymentService.GetBillingAsync(user); var billingInfo = await _paymentService.GetBillingAsync(user);
var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user); var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user);
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(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] [HttpPost]
@ -153,4 +161,12 @@ public class UsersController : Controller
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
// TODO: Feature flag to be removed in PM-14207
private async Task<bool?> AccountDeprovisioningEnabled(Guid userId)
{
return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
? await _userService.IsManagedByAnyOrganizationAsync(userId)
: null;
}
} }

View File

@ -20,9 +20,11 @@ public class UserEditModel
IEnumerable<Cipher> ciphers, IEnumerable<Cipher> ciphers,
BillingInfo billingInfo, BillingInfo billingInfo,
BillingHistoryInfo billingHistoryInfo, BillingHistoryInfo billingHistoryInfo,
GlobalSettings globalSettings) GlobalSettings globalSettings,
bool? domainVerified
)
{ {
User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers); User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, domainVerified);
BillingInfo = billingInfo; BillingInfo = billingInfo;
BillingHistoryInfo = billingHistoryInfo; BillingHistoryInfo = billingHistoryInfo;

View File

@ -14,6 +14,7 @@ public class UserViewModel
public bool Premium { get; } public bool Premium { get; }
public short? MaxStorageGb { get; } public short? MaxStorageGb { get; }
public bool EmailVerified { get; } public bool EmailVerified { get; }
public bool? DomainVerified { get; }
public bool TwoFactorEnabled { get; } public bool TwoFactorEnabled { get; }
public DateTime AccountRevisionDate { get; } public DateTime AccountRevisionDate { get; }
public DateTime RevisionDate { get; } public DateTime RevisionDate { get; }
@ -35,6 +36,7 @@ public class UserViewModel
bool premium, bool premium,
short? maxStorageGb, short? maxStorageGb,
bool emailVerified, bool emailVerified,
bool? domainVerified,
bool twoFactorEnabled, bool twoFactorEnabled,
DateTime accountRevisionDate, DateTime accountRevisionDate,
DateTime revisionDate, DateTime revisionDate,
@ -56,6 +58,7 @@ public class UserViewModel
Premium = premium; Premium = premium;
MaxStorageGb = maxStorageGb; MaxStorageGb = maxStorageGb;
EmailVerified = emailVerified; EmailVerified = emailVerified;
DomainVerified = domainVerified;
TwoFactorEnabled = twoFactorEnabled; TwoFactorEnabled = twoFactorEnabled;
AccountRevisionDate = accountRevisionDate; AccountRevisionDate = accountRevisionDate;
RevisionDate = revisionDate; RevisionDate = revisionDate;
@ -73,10 +76,10 @@ public class UserViewModel
public static IEnumerable<UserViewModel> MapViewModels( public static IEnumerable<UserViewModel> MapViewModels(
IEnumerable<User> users, IEnumerable<User> users,
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup) => 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, public static UserViewModel MapViewModel(User user,
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup) => IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup, bool? domainVerified) =>
new( new(
user.Id, user.Id,
user.Name, user.Name,
@ -86,6 +89,7 @@ public class UserViewModel
user.Premium, user.Premium,
user.MaxStorageGb, user.MaxStorageGb,
user.EmailVerified, user.EmailVerified,
domainVerified,
IsTwoFactorEnabled(user, lookup), IsTwoFactorEnabled(user, lookup),
user.AccountRevisionDate, user.AccountRevisionDate,
user.RevisionDate, user.RevisionDate,
@ -100,9 +104,9 @@ public class UserViewModel
Array.Empty<Cipher>()); Array.Empty<Cipher>());
public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled) => public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled) =>
MapViewModel(user, isTwoFactorEnabled, Array.Empty<Cipher>()); MapViewModel(user, isTwoFactorEnabled, Array.Empty<Cipher>(), false);
public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable<Cipher> ciphers) => public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable<Cipher> ciphers, bool? domainVerified) =>
new( new(
user.Id, user.Id,
user.Name, user.Name,
@ -112,6 +116,7 @@ public class UserViewModel
user.Premium, user.Premium,
user.MaxStorageGb, user.MaxStorageGb,
user.EmailVerified, user.EmailVerified,
domainVerified,
isTwoFactorEnabled, isTwoFactorEnabled,
user.AccountRevisionDate, user.AccountRevisionDate,
user.RevisionDate, user.RevisionDate,

View File

@ -110,6 +110,7 @@ public static class RolePermissionMapping
Permission.User_Licensing_View, Permission.User_Licensing_View,
Permission.User_Billing_View, Permission.User_Billing_View,
Permission.User_Billing_LaunchGateway, Permission.User_Billing_LaunchGateway,
Permission.User_Delete,
Permission.Org_List_View, Permission.Org_List_View,
Permission.Org_OrgInformation_View, Permission.Org_OrgInformation_View,
Permission.Org_GeneralDetails_View, Permission.Org_GeneralDetails_View,

View File

@ -1,4 +1,4 @@
@model UserViewModel @model UserViewModel
<dl class="row"> <dl class="row">
<dt class="col-sm-4 col-lg-3">Id</dt> <dt class="col-sm-4 col-lg-3">Id</dt>
<dd class="col-sm-8 col-lg-9"><code>@Model.Id</code></dd> <dd class="col-sm-8 col-lg-9"><code>@Model.Id</code></dd>
@ -12,6 +12,11 @@
<dt class="col-sm-4 col-lg-3">Email Verified</dt> <dt class="col-sm-4 col-lg-3">Email Verified</dt>
<dd class="col-sm-8 col-lg-9">@(Model.EmailVerified ? "Yes" : "No")</dd> <dd class="col-sm-8 col-lg-9">@(Model.EmailVerified ? "Yes" : "No")</dd>
@if(Model.DomainVerified.HasValue){
<dt class="col-sm-4 col-lg-3">Domain Verified</dt>
<dd class="col-sm-8 col-lg-9">@(Model.DomainVerified.Value == true ? "Yes" : "No")</dd>
}
<dt class="col-sm-4 col-lg-3">Using 2FA</dt> <dt class="col-sm-4 col-lg-3">Using 2FA</dt>
<dd class="col-sm-8 col-lg-9">@(Model.TwoFactorEnabled ? "Yes" : "No")</dd> <dd class="col-sm-8 col-lg-9">@(Model.TwoFactorEnabled ? "Yes" : "No")</dd>

View File

@ -2,6 +2,7 @@
using Bit.Admin.Models; using Bit.Admin.Models;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Vault.Entities;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
namespace Admin.Test.Models; namespace Admin.Test.Models;
@ -79,7 +80,7 @@ public class UserViewModelTests
{ {
var lookup = new List<(Guid, bool)> { (user.Id, true) }; 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); Assert.True(actual.TwoFactorEnabled);
} }
@ -90,7 +91,7 @@ public class UserViewModelTests
{ {
var lookup = new List<(Guid, bool)> { (user.Id, false) }; 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); Assert.False(actual.TwoFactorEnabled);
} }
@ -101,8 +102,44 @@ public class UserViewModelTests
{ {
var lookup = new List<(Guid, bool)> { (Guid.NewGuid(), true) }; 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); Assert.False(actual.TwoFactorEnabled);
} }
[Theory]
[BitAutoData]
public void MapUserViewModel_WithVerifiedDomain_ReturnsUserViewModel(User user)
{
var verifiedDomain = true;
var actual = UserViewModel.MapViewModel(user, true, Array.Empty<Cipher>(), 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<Cipher>(), verifiedDomain);
Assert.False(actual.DomainVerified);
}
[Theory]
[BitAutoData]
public void MapUserViewModel_WithNullVerifiedDomain_ReturnsUserViewModel(User user)
{
var actual = UserViewModel.MapViewModel(user, true, Array.Empty<Cipher>(), null);
Assert.Null(actual.DomainVerified);
}
} }