1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-22 16:57:36 +01:00

[PM-12074] - Refactored Index to use UserViewModel (#4797)

* Refactored View and Edit models to have all needed fields.
This commit is contained in:
Jared McCannon 2024-09-30 13:21:30 -05:00 committed by GitHub
parent 72b7f6c065
commit 81b151b1c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 367 additions and 153 deletions

View File

@ -1,11 +1,11 @@
using Bit.Admin.Enums;
#nullable enable
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.Context;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
@ -24,9 +24,9 @@ public class UsersController : Controller
private readonly IPaymentService _paymentService;
private readonly GlobalSettings _globalSettings;
private readonly IAccessControlService _accessControlService;
private readonly ICurrentContext _currentContext;
private readonly IFeatureService _featureService;
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
private readonly IFeatureService _featureService;
private readonly IUserService _userService;
public UsersController(
IUserRepository userRepository,
@ -34,18 +34,18 @@ public class UsersController : Controller
IPaymentService paymentService,
GlobalSettings globalSettings,
IAccessControlService accessControlService,
ICurrentContext currentContext,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IFeatureService featureService,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery)
IUserService userService)
{
_userRepository = userRepository;
_cipherRepository = cipherRepository;
_paymentService = paymentService;
_globalSettings = globalSettings;
_accessControlService = accessControlService;
_currentContext = currentContext;
_featureService = featureService;
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
_featureService = featureService;
_userService = userService;
}
[RequirePermission(Permission.User_List_View)]
@ -64,19 +64,26 @@ public class UsersController : Controller
var skip = (page - 1) * count;
var users = await _userRepository.SearchAsync(email, skip, count);
var userModels = new List<UserViewModel>();
if (_featureService.IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization))
{
var user2Fa = (await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(users.Select(u => u.Id))).ToList();
// TempDataSerializer is having an issue serializing an empty IEnumerable<Tuple<T1,T2>>, do not set if empty.
if (user2Fa.Count != 0)
var twoFactorAuthLookup = (await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(users.Select(u => u.Id))).ToList();
userModels = UserViewModel.MapViewModels(users, twoFactorAuthLookup).ToList();
}
else
{
foreach (var user in users)
{
TempData["UsersTwoFactorIsEnabled"] = user2Fa;
var isTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
userModels.Add(UserViewModel.MapViewModel(user, isTwoFactorEnabled));
}
}
return View(new UsersModel
{
Items = users as List<User>,
Items = userModels,
Email = string.IsNullOrWhiteSpace(email) ? null : email,
Page = page,
Count = count,
@ -87,13 +94,17 @@ public class UsersController : Controller
public async Task<IActionResult> View(Guid id)
{
var user = await _userRepository.GetByIdAsync(id);
if (user == null)
{
return RedirectToAction("Index");
}
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
return View(new UserViewModel(user, ciphers));
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers));
}
[SelfHosted(NotSelfHostedOnly = true)]
@ -108,7 +119,8 @@ public class UsersController : Controller
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
var billingInfo = await _paymentService.GetBillingAsync(user);
var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user);
return View(new UserEditModel(user, ciphers, billingInfo, billingHistoryInfo, _globalSettings));
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings));
}
[HttpPost]

View File

@ -7,18 +7,23 @@ using Bit.Core.Vault.Entities;
namespace Bit.Admin.Models;
public class UserEditModel : UserViewModel
public class UserEditModel
{
public UserEditModel() { }
public UserEditModel()
{
}
public UserEditModel(
User user,
bool isTwoFactorEnabled,
IEnumerable<Cipher> ciphers,
BillingInfo billingInfo,
BillingHistoryInfo billingHistoryInfo,
GlobalSettings globalSettings)
: base(user, ciphers)
{
User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers);
BillingInfo = billingInfo;
BillingHistoryInfo = billingHistoryInfo;
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
@ -35,32 +40,32 @@ public class UserEditModel : UserViewModel
PremiumExpirationDate = user.PremiumExpirationDate;
}
public BillingInfo BillingInfo { get; set; }
public BillingHistoryInfo BillingHistoryInfo { get; set; }
public UserViewModel User { get; init; }
public BillingInfo BillingInfo { get; init; }
public BillingHistoryInfo BillingHistoryInfo { get; init; }
public string RandomLicenseKey => CoreHelpers.SecureRandomString(20);
public string OneYearExpirationDate => DateTime.Now.AddYears(1).ToString("yyyy-MM-ddTHH:mm");
public string BraintreeMerchantId { get; set; }
public string BraintreeMerchantId { get; init; }
[Display(Name = "Name")]
public string Name { get; set; }
public string Name { get; init; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
public string Email { get; init; }
[Display(Name = "Email Verified")]
public bool EmailVerified { get; set; }
public bool EmailVerified { get; init; }
[Display(Name = "Premium")]
public bool Premium { get; set; }
public bool Premium { get; init; }
[Display(Name = "Max. Storage GB")]
public short? MaxStorageGb { get; set; }
public short? MaxStorageGb { get; init; }
[Display(Name = "Gateway")]
public Core.Enums.GatewayType? Gateway { get; set; }
public Core.Enums.GatewayType? Gateway { get; init; }
[Display(Name = "Gateway Customer Id")]
public string GatewayCustomerId { get; set; }
public string GatewayCustomerId { get; init; }
[Display(Name = "Gateway Subscription Id")]
public string GatewaySubscriptionId { get; set; }
public string GatewaySubscriptionId { get; init; }
[Display(Name = "License Key")]
public string LicenseKey { get; set; }
public string LicenseKey { get; init; }
[Display(Name = "Premium Expiration Date")]
public DateTime? PremiumExpirationDate { get; set; }
public DateTime? PremiumExpirationDate { get; init; }
}

View File

@ -1,18 +1,131 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Vault.Entities;
namespace Bit.Admin.Models;
public class UserViewModel
{
public UserViewModel() { }
public Guid Id { get; }
public string Name { get; }
public string Email { get; }
public DateTime CreationDate { get; }
public DateTime? PremiumExpirationDate { get; }
public bool Premium { get; }
public short? MaxStorageGb { get; }
public bool EmailVerified { get; }
public bool TwoFactorEnabled { get; }
public DateTime AccountRevisionDate { get; }
public DateTime RevisionDate { get; }
public DateTime? LastEmailChangeDate { get; }
public DateTime? LastKdfChangeDate { get; }
public DateTime? LastKeyRotationDate { get; }
public DateTime? LastPasswordChangeDate { get; }
public GatewayType? Gateway { get; }
public string GatewayCustomerId { get; }
public string GatewaySubscriptionId { get; }
public string LicenseKey { get; }
public int CipherCount { get; set; }
public UserViewModel(User user, IEnumerable<Cipher> ciphers)
public UserViewModel(Guid id,
string name,
string email,
DateTime creationDate,
DateTime? premiumExpirationDate,
bool premium,
short? maxStorageGb,
bool emailVerified,
bool twoFactorEnabled,
DateTime accountRevisionDate,
DateTime revisionDate,
DateTime? lastEmailChangeDate,
DateTime? lastKdfChangeDate,
DateTime? lastKeyRotationDate,
DateTime? lastPasswordChangeDate,
GatewayType? gateway,
string gatewayCustomerId,
string gatewaySubscriptionId,
string licenseKey,
IEnumerable<Cipher> ciphers)
{
User = user;
Id = id;
Name = name;
Email = email;
CreationDate = creationDate;
PremiumExpirationDate = premiumExpirationDate;
Premium = premium;
MaxStorageGb = maxStorageGb;
EmailVerified = emailVerified;
TwoFactorEnabled = twoFactorEnabled;
AccountRevisionDate = accountRevisionDate;
RevisionDate = revisionDate;
LastEmailChangeDate = lastEmailChangeDate;
LastKdfChangeDate = lastKdfChangeDate;
LastKeyRotationDate = lastKeyRotationDate;
LastPasswordChangeDate = lastPasswordChangeDate;
Gateway = gateway;
GatewayCustomerId = gatewayCustomerId;
GatewaySubscriptionId = gatewaySubscriptionId;
LicenseKey = licenseKey;
CipherCount = ciphers.Count();
}
public User User { get; set; }
public int CipherCount { get; set; }
public static IEnumerable<UserViewModel> MapViewModels(
IEnumerable<User> users,
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup) =>
users.Select(user => MapViewModel(user, lookup));
public static UserViewModel MapViewModel(User user,
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup) =>
new(
user.Id,
user.Name,
user.Email,
user.CreationDate,
user.PremiumExpirationDate,
user.Premium,
user.MaxStorageGb,
user.EmailVerified,
IsTwoFactorEnabled(user, lookup),
user.AccountRevisionDate,
user.RevisionDate,
user.LastEmailChangeDate,
user.LastKdfChangeDate,
user.LastKeyRotationDate,
user.LastPasswordChangeDate,
user.Gateway,
user.GatewayCustomerId ?? string.Empty,
user.GatewaySubscriptionId ?? string.Empty,
user.LicenseKey ?? string.Empty,
Array.Empty<Cipher>());
public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled) =>
MapViewModel(user, isTwoFactorEnabled, Array.Empty<Cipher>());
public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable<Cipher> ciphers) =>
new(
user.Id,
user.Name,
user.Email,
user.CreationDate,
user.PremiumExpirationDate,
user.Premium,
user.MaxStorageGb,
user.EmailVerified,
isTwoFactorEnabled,
user.AccountRevisionDate,
user.RevisionDate,
user.LastEmailChangeDate,
user.LastKdfChangeDate,
user.LastKeyRotationDate,
user.LastPasswordChangeDate,
user.Gateway,
user.GatewayCustomerId ?? string.Empty,
user.GatewaySubscriptionId ?? string.Empty,
user.LicenseKey ?? string.Empty,
ciphers);
public static bool IsTwoFactorEnabled(User user,
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> twoFactorIsEnabledLookup) =>
twoFactorIsEnabledLookup.FirstOrDefault(x => x.userId == user.Id).twoFactorIsEnabled;
}

View File

@ -1,8 +1,6 @@
using Bit.Core.Entities;
namespace Bit.Admin.Models;
namespace Bit.Admin.Models;
public class UsersModel : PagedModel<User>
public class UsersModel : PagedModel<UserViewModel>
{
public string Email { get; set; }
public string Action { get; set; }

View File

@ -86,7 +86,7 @@
@if (canViewUserInformation)
{
<h2>User Information</h2>
@await Html.PartialAsync("_ViewInformation", Model)
@await Html.PartialAsync("_ViewInformation", Model.User)
}
@if (canViewBillingInformation)
{

View File

@ -1,6 +1,4 @@
@model UsersModel
@inject Bit.Core.Services.IUserService userService
@inject Bit.Core.Services.IFeatureService featureService
@{
ViewData["Title"] = "Users";
}
@ -16,100 +14,88 @@
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Email</th>
<th style="width: 150px;">Created</th>
<th style="width: 170px; min-width: 170px;">Details</th>
</tr>
<tr>
<th>Email</th>
<th style="width: 150px;">Created</th>
<th style="width: 170px; min-width: 170px;">Details</th>
</tr>
</thead>
<tbody>
@if(!Model.Items.Any())
@if (!Model.Items.Any())
{
<tr>
<td colspan="4">No results to list.</td>
</tr>
}
else
{
@foreach (var user in Model.Items)
{
<tr>
<td colspan="4">No results to list.</td>
<td>
<a asp-action="@Model.Action" asp-route-id="@user.Id">@user.Email</a>
</td>
<td>
<span title="@user.CreationDate.ToString()">
@user.CreationDate.ToShortDateString()
</span>
</td>
<td>
@if (user.Premium)
{
<i class="fa fa-star fa-lg fa-fw"
title="Premium, expires @(user.PremiumExpirationDate?.ToShortDateString() ?? "-")">
</i>
}
else
{
<i class="fa fa-star-o fa-lg fa-fw text-muted" title="Not Premium"></i>
}
@if (user.MaxStorageGb.HasValue && user.MaxStorageGb > 1)
{
<i class="fa fa-plus-square fa-lg fa-fw"
title="Additional Storage, @(user.MaxStorageGb - 1) GB">
</i>
}
else
{
<i class="fa fa-plus-square-o fa-lg fa-fw text-muted"
title="No Additional Storage">
</i>
}
@if (user.EmailVerified)
{
<i class="fa fa-check-circle fa-lg fa-fw" title="Email Verified"></i>
}
else
{
<i class="fa fa-times-circle-o fa-lg fa-fw text-muted" title="Email Not Verified"></i>
}
@if (user.TwoFactorEnabled)
{
<i class="fa fa-lock fa-lg fa-fw" title="2FA Enabled"></i>
}
else
{
<i class="fa fa-unlock fa-lg fa-fw text-muted" title="2FA Not Enabled"></i>
}
</td>
</tr>
}
else
{
@foreach(var user in Model.Items)
{
<tr>
<td>
<a asp-action="@Model.Action" asp-route-id="@user.Id">@user.Email</a>
</td>
<td>
<span title="@user.CreationDate.ToString()">
@user.CreationDate.ToShortDateString()
</span>
</td>
<td>
@if(user.Premium)
{
<i class="fa fa-star fa-lg fa-fw"
title="Premium, expires @(user.PremiumExpirationDate?.ToShortDateString() ?? "-")"></i>
}
else
{
<i class="fa fa-star-o fa-lg fa-fw text-muted" title="Not Premium"></i>
}
@if(user.MaxStorageGb.HasValue && user.MaxStorageGb > 1)
{
<i class="fa fa-plus-square fa-lg fa-fw"
title="Additional Storage, @(user.MaxStorageGb - 1) GB"></i>
}
else
{
<i class="fa fa-plus-square-o fa-lg fa-fw text-muted"
title="No Additional Storage"></i>
}
@if(user.EmailVerified)
{
<i class="fa fa-check-circle fa-lg fa-fw" title="Email Verified"></i>
}
else
{
<i class="fa fa-times-circle-o fa-lg fa-fw text-muted" title="Email Not Verified"></i>
}
@if (featureService.IsEnabled(Bit.Core.FeatureFlagKeys.MembersTwoFAQueryOptimization))
{
var usersTwoFactorIsEnabled = TempData["UsersTwoFactorIsEnabled"] as IEnumerable<(Guid userId, bool twoFactorIsEnabled)>;
var matchingUser2Fa = usersTwoFactorIsEnabled?.FirstOrDefault(tuple => tuple.userId == user.Id);
@if(matchingUser2Fa is { twoFactorIsEnabled: true })
{
<i class="fa fa-lock fa-lg fa-fw" title="2FA Enabled"></i>
}
else
{
<i class="fa fa-unlock fa-lg fa-fw text-muted" title="2FA Not Enabled"></i>
}
}
else
{
@if(await userService.TwoFactorIsEnabledAsync(user))
{
<i class="fa fa-lock fa-lg fa-fw" title="2FA Enabled"></i>
}
else
{
<i class="fa fa-unlock fa-lg fa-fw text-muted" title="2FA Not Enabled"></i>
}
}
</td>
</tr>
}
}
}
</tbody>
</table>
</div>
<nav>
<ul class="pagination">
@if(Model.PreviousPage.HasValue)
@if (Model.PreviousPage.HasValue)
{
<li class="page-item">
<a class="page-link" asp-action="Index" asp-route-page="@Model.PreviousPage.Value"
asp-route-count="@Model.Count" asp-route-email="@Model.Email">Previous</a>
asp-route-count="@Model.Count" asp-route-email="@Model.Email">
Previous
</a>
</li>
}
else
@ -118,11 +104,13 @@
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
}
@if(Model.NextPage.HasValue)
@if (Model.NextPage.HasValue)
{
<li class="page-item">
<a class="page-link" asp-action="Index" asp-route-page="@Model.NextPage.Value"
asp-route-count="@Model.Count" asp-route-email="@Model.Email">Next</a>
asp-route-count="@Model.Count" asp-route-email="@Model.Email">
Next
</a>
</li>
}
else

View File

@ -1,13 +1,13 @@
@model UserViewModel
@{
ViewData["Title"] = "User: " + Model.User.Email;
ViewData["Title"] = "User: " + Model.Email;
}
<h1>User <small>@Model.User.Email</small></h1>
<h1>User <small>@Model.Email</small></h1>
<h2>Information</h2>
@await Html.PartialAsync("_ViewInformation", Model)
<form asp-action="Delete" asp-route-id="@Model.User.Id"
<form asp-action="Delete" asp-route-id="@Model.Id"
onsubmit="return confirm('Are you sure you want to delete this user?')">
<button class="btn btn-danger" type="submit">Delete</button>
</form>

View File

@ -1,43 +1,42 @@
@model UserViewModel
@inject Bit.Core.Services.IUserService userService
<dl class="row">
<dt class="col-sm-4 col-lg-3">Id</dt>
<dd class="col-sm-8 col-lg-9"><code>@Model.User.Id</code></dd>
<dd class="col-sm-8 col-lg-9"><code>@Model.Id</code></dd>
<dt class="col-sm-4 col-lg-3">Premium</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.Premium ? "Yes" : "No")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.Premium ? "Yes" : "No")</dd>
<dt class="col-sm-4 col-lg-3">Premium Expires</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.PremiumExpirationDate?.ToString() ?? "-")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.PremiumExpirationDate?.ToString() ?? "-")</dd>
<dt class="col-sm-4 col-lg-3">Email Verified</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.EmailVerified ? "Yes" : "No")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.EmailVerified ? "Yes" : "No")</dd>
<dt class="col-sm-4 col-lg-3">Using 2FA</dt>
<dd class="col-sm-8 col-lg-9">@(await userService.TwoFactorIsEnabledAsync(Model.User) ? "Yes" : "No")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.TwoFactorEnabled ? "Yes" : "No")</dd>
<dt class="col-sm-4 col-lg-3">Items</dt>
<dd class="col-sm-8 col-lg-9">@Model.CipherCount</dd>
<dt class="col-sm-4 col-lg-3">Vault Modified</dt>
<dd class="col-sm-8 col-lg-9">@Model.User.AccountRevisionDate.ToString()</dd>
<dd class="col-sm-8 col-lg-9">@Model.AccountRevisionDate.ToString()</dd>
<dt class="col-sm-4 col-lg-3">Created</dt>
<dd class="col-sm-8 col-lg-9">@Model.User.CreationDate.ToString()</dd>
<dd class="col-sm-8 col-lg-9">@Model.CreationDate.ToString()</dd>
<dt class="col-sm-4 col-lg-3">Modified</dt>
<dd class="col-sm-8 col-lg-9">@Model.User.RevisionDate.ToString()</dd>
<dd class="col-sm-8 col-lg-9">@Model.RevisionDate.ToString()</dd>
<dt class="col-sm-4 col-lg-3">Last Email Address Change</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.LastEmailChangeDate?.ToString() ?? "-")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.LastEmailChangeDate?.ToString() ?? "-")</dd>
<dt class="col-sm-4 col-lg-3">Last KDF Change</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.LastKdfChangeDate?.ToString() ?? "-")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.LastKdfChangeDate?.ToString() ?? "-")</dd>
<dt class="col-sm-4 col-lg-3">Last Key Rotation</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.LastKeyRotationDate?.ToString() ?? "-")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.LastKeyRotationDate?.ToString() ?? "-")</dd>
<dt class="col-sm-4 col-lg-3">Last Password Change</dt>
<dd class="col-sm-8 col-lg-9">@(Model.User.LastPasswordChangeDate?.ToString() ?? "-")</dd>
<dd class="col-sm-8 col-lg-9">@(Model.LastPasswordChangeDate?.ToString() ?? "-")</dd>
</dl>

View File

@ -18,5 +18,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Admin\Admin.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,108 @@
#nullable enable
using Bit.Admin.Models;
using Bit.Core.Entities;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Admin.Test.Models;
public class UserViewModelTests
{
[Theory]
[BitAutoData]
public void IsTwoFactorEnabled_GivenUserAndIsInLookup_WhenUserHasTwoFactorEnabled_ThenReturnsTrue(User user)
{
var lookup = new List<(Guid, bool)>
{
(user.Id, true)
};
var actual = UserViewModel.IsTwoFactorEnabled(user, lookup);
Assert.True(actual);
}
[Theory]
[BitAutoData]
public void IsTwoFactorEnabled_GivenUserAndIsInLookup_WhenUserDoesNotHaveTwoFactorEnabled_ThenReturnsFalse(User user)
{
var lookup = new List<(Guid, bool)>
{
(Guid.NewGuid(), true)
};
var actual = UserViewModel.IsTwoFactorEnabled(user, lookup);
Assert.False(actual);
}
[Theory]
[BitAutoData]
public void IsTwoFactorEnabled_GivenUserAndIsNotInLookup_WhenUserDoesNotHaveTwoFactorEnabled_ThenReturnsFalse(User user)
{
var lookup = new List<(Guid, bool)>();
var actual = UserViewModel.IsTwoFactorEnabled(user, lookup);
Assert.False(actual);
}
[Theory]
[BitAutoData]
public void MapUserViewModel_GivenUser_WhenPopulated_ThenMapsToUserViewModel(User user)
{
var actual = UserViewModel.MapViewModel(user, true);
Assert.Equal(actual.Id, user.Id);
Assert.Equal(actual.Email, user.Email);
Assert.Equal(actual.CreationDate, user.CreationDate);
Assert.Equal(actual.PremiumExpirationDate, user.PremiumExpirationDate);
Assert.Equal(actual.Premium, user.Premium);
Assert.Equal(actual.MaxStorageGb, user.MaxStorageGb);
Assert.Equal(actual.EmailVerified, user.EmailVerified);
Assert.True(actual.TwoFactorEnabled);
Assert.Equal(actual.AccountRevisionDate, user.AccountRevisionDate);
Assert.Equal(actual.RevisionDate, user.RevisionDate);
Assert.Equal(actual.LastEmailChangeDate, user.LastEmailChangeDate);
Assert.Equal(actual.LastKdfChangeDate, user.LastKdfChangeDate);
Assert.Equal(actual.LastKeyRotationDate, user.LastKeyRotationDate);
Assert.Equal(actual.LastPasswordChangeDate, user.LastPasswordChangeDate);
Assert.Equal(actual.Gateway, user.Gateway);
Assert.Equal(actual.GatewayCustomerId, user.GatewayCustomerId);
Assert.Equal(actual.GatewaySubscriptionId, user.GatewaySubscriptionId);
Assert.Equal(actual.LicenseKey, user.LicenseKey);
}
[Theory]
[BitAutoData]
public void MapUserViewModel_GivenUserWithTwoFactorEnabled_WhenPopulated_ThenMapsToUserViewModel(User user)
{
var lookup = new List<(Guid, bool)> { (user.Id, true) };
var actual = UserViewModel.MapViewModel(user, lookup);
Assert.True(actual.TwoFactorEnabled);
}
[Theory]
[BitAutoData]
public void MapUserViewModel_GivenUserWithoutTwoFactorEnabled_WhenPopulated_ThenTwoFactorIsEnabled(User user)
{
var lookup = new List<(Guid, bool)> { (user.Id, false) };
var actual = UserViewModel.MapViewModel(user, lookup);
Assert.False(actual.TwoFactorEnabled);
}
[Theory]
[BitAutoData]
public void MapUserViewModel_GivenUser_WhenNotInLookUpList_ThenTwoFactorIsDisabled(User user)
{
var lookup = new List<(Guid, bool)> { (Guid.NewGuid(), true) };
var actual = UserViewModel.MapViewModel(user, lookup);
Assert.False(actual.TwoFactorEnabled);
}
}

View File

@ -1,10 +0,0 @@
namespace Admin.Test;
// Delete this file once you have real tests
public class PlaceholderUnitTest
{
[Fact]
public void Test1()
{
}
}

View File

@ -35,7 +35,7 @@ public class OrganizationUsersControllerTests
{
[Theory]
[BitAutoData]
public async Task PutResetPasswordEnrollment_InivitedUser_AcceptsInvite(Guid orgId, Guid userId, OrganizationUserResetPasswordEnrollmentRequestModel model,
public async Task PutResetPasswordEnrollment_InvitedUser_AcceptsInvite(Guid orgId, Guid userId, OrganizationUserResetPasswordEnrollmentRequestModel model,
User user, OrganizationUser orgUser, SutProvider<OrganizationUsersController> sutProvider)
{
orgUser.Status = Core.Enums.OrganizationUserStatusType.Invited;