mirror of
https://github.com/bitwarden/server
synced 2025-12-31 23:53:17 +00:00
* 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
134 lines
5.4 KiB
Plaintext
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>
|