mirror of
https://github.com/bitwarden/server.git
synced 2025-02-16 01:51:21 +01:00
users listing page
This commit is contained in:
parent
d35d8185ed
commit
67bf801c15
45
src/Admin/Controllers/UsersController.cs
Normal file
45
src/Admin/Controllers/UsersController.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Bit.Core.Repositories;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Admin.Models;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Models.Table;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class UsersController : Controller
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public UsersController(IUserRepository userRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string email, int page = 1, int count = 25)
|
||||
{
|
||||
if(page < 1)
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if(count < 1)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
|
||||
var skip = (page - 1) * count;
|
||||
var users = await _userRepository.SearchByEmailAsync(email, skip, count);
|
||||
return View(new UsersModel
|
||||
{
|
||||
Users = users as List<User>,
|
||||
Email = string.IsNullOrWhiteSpace(email) ? null : email,
|
||||
Page = page,
|
||||
Count = count
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
16
src/Admin/Models/UsersModel.cs
Normal file
16
src/Admin/Models/UsersModel.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Models.Table;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class UsersModel
|
||||
{
|
||||
public List<User> Users { get; set; }
|
||||
public string Email { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int Count { get; set; }
|
||||
public int? PreviousPage => Page < 2 ? (int?)null : Page - 1;
|
||||
public int? NextPage => Users.Count < Count ? (int?)null : Page + 1;
|
||||
}
|
||||
}
|
@ -28,10 +28,10 @@
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
|
||||
<a class="nav-link" asp-controller="Home">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
<a class="nav-link" asp-controller="Users">Users</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
110
src/Admin/Views/Users/Index.cshtml
Normal file
110
src/Admin/Views/Users/Index.cshtml
Normal file
@ -0,0 +1,110 @@
|
||||
@model UsersModel
|
||||
@{
|
||||
ViewData["Title"] = "Users";
|
||||
}
|
||||
|
||||
<h1>Users</h1>
|
||||
|
||||
<form class="form-inline mb-3" method="get">
|
||||
<label class="sr-only" asp-for="Email">Email</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Email" asp-for="Email" name="email">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary" title="Search"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</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: 150px; min-width: 150px;">Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if(!Model.Users.Any())
|
||||
{
|
||||
<tr>
|
||||
<td colspan="4">No results to list.</td>
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach(var user in Model.Users)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#">@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() ?? "Never")"></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<i class="fa fa-star-o fa-lg fa-fw text-muted" title="Not Premium"></i>
|
||||
}
|
||||
@if(user.EmailVerified)
|
||||
{
|
||||
<i class="fa fa-check-circle fa-lg fa-fw" title="Email Verified"></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<i class="fa fa-check-circle-o fa-lg fa-fw text-muted" title="Email Not Verified"></i>
|
||||
}
|
||||
@if(user.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>
|
||||
}
|
||||
</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>
|
@ -8,6 +8,7 @@ namespace Bit.Core.Repositories
|
||||
public interface IUserRepository : IRepository<User, Guid>
|
||||
{
|
||||
Task<User> GetByEmailAsync(string email);
|
||||
Task<ICollection<User>> SearchByEmailAsync(string email, int skip, int take);
|
||||
Task<ICollection<User>> GetManyByPremiumAsync(bool premium);
|
||||
Task<string> GetPublicKeyAsync(Guid id);
|
||||
Task<DateTime> GetAccountRevisionDateAsync(Guid id);
|
||||
|
@ -37,6 +37,19 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<User>> SearchByEmailAsync(string email, int skip, int take)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<User>(
|
||||
$"[{Schema}].[{Table}_SearchByEmail]",
|
||||
new { Email = email, Skip = skip, Take = take },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<User>> GetManyByPremiumAsync(bool premium)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
|
@ -224,5 +224,6 @@
|
||||
<Build Include="dbo\Stored Procedures\Event_ReadPageByCipherId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Event_ReadPageByOrganizationIdActingUserId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Organization_ReadAbilities.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_SearchByEmail.sql" />
|
||||
</ItemGroup>
|
||||
</Project>
|
19
src/Sql/dbo/Stored Procedures/User_SearchByEmail.sql
Normal file
19
src/Sql/dbo/Stored Procedures/User_SearchByEmail.sql
Normal file
@ -0,0 +1,19 @@
|
||||
CREATE PROCEDURE [dbo].[User_SearchByEmail]
|
||||
@Email NVARCHAR(50),
|
||||
@Skip INT = 0,
|
||||
@Take INT = 25
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
DECLARE @EmailLikeSearch NVARCHAR(55) = @Email + '%'
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[UserView]
|
||||
WHERE
|
||||
(@Email IS NULL OR [Email] LIKE @EmailLikeSearch)
|
||||
ORDER BY [Email] ASC
|
||||
OFFSET @Skip ROWS
|
||||
FETCH NEXT @Take ROWS ONLY
|
||||
END
|
26
util/Setup/DbScripts/2018-03-21_00_AdminPortal.sql
Normal file
26
util/Setup/DbScripts/2018-03-21_00_AdminPortal.sql
Normal file
@ -0,0 +1,26 @@
|
||||
IF OBJECT_ID('[dbo].[User_SearchByEmail]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[User_SearchByEmail]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[User_SearchByEmail]
|
||||
@Email NVARCHAR(50),
|
||||
@Skip INT = 0,
|
||||
@Take INT = 25
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
DECLARE @EmailLikeSearch NVARCHAR(55) = @Email + '%'
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[UserView]
|
||||
WHERE
|
||||
(@Email IS NULL OR [Email] LIKE @EmailLikeSearch)
|
||||
ORDER BY [Email] ASC
|
||||
OFFSET @Skip ROWS
|
||||
FETCH NEXT @Take ROWS ONLY
|
||||
END
|
||||
GO
|
@ -8,11 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="DbScripts\2018-02-28_00_LoginUris.sql" />
|
||||
<None Remove="DbScripts\2018-03-12_00_FixLoginUris.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="DbScripts\2018-03-21_00_AdminPortal.sql" />
|
||||
<EmbeddedResource Include="DbScripts\2018-03-12_00_FixLoginUris.sql" />
|
||||
<EmbeddedResource Include="DbScripts\2018-02-28_00_LoginUris.sql" />
|
||||
<EmbeddedResource Include="DbScripts\2017-12-12_00_Events.sql" />
|
||||
|
Loading…
Reference in New Issue
Block a user