1
0
mirror of https://github.com/bitwarden/server synced 2025-12-31 23:53:17 +00:00
Files
server/src/Admin/Views/Users/Index.cshtml
Rui Tomé 8d69bb0aaa [AC-1698] Check if a user has 2FA enabled more efficiently (#4524)
* feat: Add stored procedure for reading organization user details with premium access by organization ID

The code changes include:
- Addition of a new stored procedure [dbo].[OrganizationUserUserDetailsWithPremiumAccess_ReadByOrganizationId] to read organization user details with premium access by organization ID
- Modification of the IUserService interface to include an optional parameter for checking two-factor authentication with premium access
- Modification of the UserService class to handle the new optional parameter in the TwoFactorIsEnabledAsync method
- Addition of a new method GetManyDetailsWithPremiumAccessByOrganizationAsync in the IOrganizationUserRepository interface to retrieve organization user details with premium access by organization ID
- Addition of a new view [dbo].[OrganizationUserUserDetailsWithPremiumAccessView] to retrieve organization user details with premium access

* Add IUserRepository.SearchDetailsAsync that includes the field HasPremiumAccess

* Check the feature flag on Admin.UsersController to see if the optimization runs

* Modify PolicyService to run query optimization if the feature flag is enabled

* Refactor the parameter check on UserService.TwoFactorIsEnabledAsync

* Run query optimization on public MembersController if feature flag is enabled

* Restore refactor

* Reverted change used for development

* Add unit tests for OrganizationService.RestoreUser

* Separate new CheckPoliciesBeforeRestoreAsync optimization into new method

* Add more unit tests

* Apply refactor to bulk restore

* Add GetManyDetailsAsync method to IUserRepository. Add ConfirmUsersAsync_vNext method to IOrganizationService

* Add unit tests for ConfirmUser_vNext

* Refactor the optimization to use the new TwoFactorIsEnabledAsync method instead of changing the existing one

* Removed unused sql scripts and added migration script

* Remove unnecessary view

* chore: Remove unused SearchDetailsAsync method from IUserRepository and UserRepository

* refactor: Use UserDetails constructor in UserRepository

* Add summary to IUserRepository.GetManyDetailsAsync

* Add summary descriptions to IUserService.TwoFactorIsEnabledAsync

* Remove obsolete annotation from IUserRepository.UpdateUserKeyAndEncryptedDataAsync

* refactor: Rename UserDetails to UserWithCalculatedPremium across the codebase

* Extract IUserService.TwoFactorIsEnabledAsync into a new TwoFactorIsEnabledQuery class

* Add unit tests for TwoFactorIsEnabledQuery

* Update TwoFactorIsEnabledQueryTests to include additional provider types

* Refactor TwoFactorIsEnabledQuery

* Refactor TwoFactorIsEnabledQuery and update tests

* refactor: Update TwoFactorIsEnabledQueryTests to include test for null TwoFactorProviders

* refactor: Improve TwoFactorIsEnabledQuery and update tests

* refactor: Improve TwoFactorIsEnabledQuery and update tests

* Remove empty <returns> from summary

* Update User_ReadByIdsWithCalculatedPremium stored procedure to accept JSON array of IDs
2024-08-08 15:43:45 +01:00

134 lines
5.4 KiB
Plaintext

@model UsersModel
@inject Bit.Core.Services.IUserService userService
@inject Bit.Core.Services.IFeatureService featureService
@{
ViewData["Title"] = "Users";
}
<h1>Users</h1>
<form class="form-inline mb-2" method="get">
<label class="sr-only" asp-for="Email">Email</label>
<input type="text" class="form-control mb-2 mr-2" placeholder="Email" asp-for="Email" name="email">
<button type="submit" class="btn btn-primary mb-2" title="Search"><i class="fa fa-search"></i> Search</button>
</form>
<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>
</thead>
<tbody>
@if(!Model.Items.Any())
{
<tr>
<td colspan="4">No results to list.</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)>;
@if(usersTwoFactorIsEnabled.FirstOrDefault(tuple => tuple.userId == user.Id).twoFactorIsEnabled)
{
<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)
{
<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>
</li>
}
else
{
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
}
@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>
</li>
}
else
{
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Next</a>
</li>
}
</ul>
</nav>