mirror of
https://github.com/bitwarden/server.git
synced 2025-04-25 14:52:21 -05:00
wip
This commit is contained in:
parent
01a69176c5
commit
cec601ea59
@ -1,6 +1,6 @@
|
|||||||
@using Bit.Admin.Services
|
@using Bit.Admin.Services
|
||||||
@using Microsoft.AspNetCore.Identity
|
@using Microsoft.AspNetCore.Identity
|
||||||
@using Bit.Admin.Components.Navigation
|
|
||||||
@inject IHttpContextAccessor HttpContextAccessor
|
@inject IHttpContextAccessor HttpContextAccessor
|
||||||
@inject SignInManager<IdentityUser> SignInManager
|
@inject SignInManager<IdentityUser> SignInManager
|
||||||
@inject Core.Settings.GlobalSettings GlobalSettings
|
@inject Core.Settings.GlobalSettings GlobalSettings
|
@ -1,7 +1,8 @@
|
|||||||
using Bit.Admin.Enums;
|
using Bit.Admin.Components.Navigation;
|
||||||
|
using Bit.Admin.Enums;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace Bit.Admin.AdminConsole;
|
namespace Bit.Admin.AdminConsole.Components;
|
||||||
|
|
||||||
public partial class App : ComponentBase
|
public partial class App : ComponentBase
|
||||||
{
|
{
|
||||||
@ -17,8 +18,7 @@ public partial class App : ComponentBase
|
|||||||
var canPromoteAdmin = AccessControlService.UserHasPermission(Permission.Tools_PromoteAdmin);
|
var canPromoteAdmin = AccessControlService.UserHasPermission(Permission.Tools_PromoteAdmin);
|
||||||
var canGenerateLicense = AccessControlService.UserHasPermission(Permission.Tools_GenerateLicenseFile);
|
var canGenerateLicense = AccessControlService.UserHasPermission(Permission.Tools_GenerateLicenseFile);
|
||||||
var canManageTaxRates = AccessControlService.UserHasPermission(Permission.Tools_ManageTaxRates);
|
var canManageTaxRates = AccessControlService.UserHasPermission(Permission.Tools_ManageTaxRates);
|
||||||
var canManageStripeSubscriptions =
|
var canManageStripeSubscriptions = AccessControlService.UserHasPermission(Permission.Tools_ManageStripeSubscriptions);
|
||||||
AccessControlService.UserHasPermission(Permission.Tools_ManageStripeSubscriptions);
|
|
||||||
var canProcessStripeEvents = AccessControlService.UserHasPermission(Permission.Tools_ProcessStripeEvents);
|
var canProcessStripeEvents = AccessControlService.UserHasPermission(Permission.Tools_ProcessStripeEvents);
|
||||||
var canMigrateProviders = AccessControlService.UserHasPermission(Permission.Tools_MigrateProviders);
|
var canMigrateProviders = AccessControlService.UserHasPermission(Permission.Tools_MigrateProviders);
|
||||||
|
|
@ -1,156 +1,124 @@
|
|||||||
@page "/organizations2"
|
@page "/organizations2"
|
||||||
@using Bit.Core.Repositories
|
@using Bit.Core.Repositories
|
||||||
@using Bit.Core.Settings
|
@using Bit.Core.Settings
|
||||||
@using Bit.Infrastructure.EntityFramework.AdminConsole.Models
|
@using Bit.Admin.AdminConsole.Components.Shared.Table
|
||||||
|
|
||||||
@inject IGlobalSettings GlobalSettings
|
@inject IGlobalSettings GlobalSettings
|
||||||
@inject IOrganizationRepository OrganizationRepository
|
@inject IOrganizationRepository OrganizationRepository
|
||||||
|
|
||||||
<!-- You cannot set the title part of a layout due to the rendering order. Or you have to use JavaScript, which is not
|
|
||||||
ideal. -->
|
|
||||||
<BitPage Title="Organizations">
|
<BitPage Title="Organizations">
|
||||||
<EditForm class="form-inline mb-2" FormName="@SearchFormName" Model="SearchForm" OnSubmit="OnSearchAsync">
|
<EditForm FormName="@SearchFormName" Model="SearchForm" OnValidSubmit="OnSearchAsync">
|
||||||
<DataAnnotationsValidator/>
|
<div class="form-inline mb-2">
|
||||||
<ValidationSummary/>
|
<DataAnnotationsValidator/>
|
||||||
|
<label class="sr-only" for="name-filter">Name</label>
|
||||||
<label class="sr-only" for="name-filter">Name</label>
|
<InputText @bind-Value="SearchForm.Name" class="form-control mb-2 mr-2" id="name-filter" placeholder="Name"/>
|
||||||
<InputText @bind-Value="SearchForm.Name" class="form-control mb-2 mr-2" id="name-filter" placeholder="Name"/>
|
<label class="sr-only" for="email-filter">User email</label>
|
||||||
<label class="sr-only" for="email-filter">User email</label>
|
<InputText @bind-Value="SearchForm.Email" class="form-control mb-2 mr-2" id="email-filter" placeholder="User email"/>
|
||||||
<InputText @bind-Value="SearchForm.Email" class="form-control mb-2 mr-2" id="email-filter" placeholder="User email"/>
|
@if (!Model.SelfHosted)
|
||||||
|
|
||||||
@if (!Model.SelfHosted)
|
|
||||||
{
|
|
||||||
<label class="sr-only" asp-for="Paid">Customer</label>
|
|
||||||
<InputSelect @bind-Value="SearchForm.Paid" class="form-control mb-2 mr-2">
|
|
||||||
<option value="">-- Customer --</option>
|
|
||||||
<option value="true">Paid</option>
|
|
||||||
<option value="false">Freeloader</option>
|
|
||||||
</InputSelect>
|
|
||||||
}
|
|
||||||
<button type="submit" class="btn btn-primary mb-2" title="Search"><i class="fa fa-search"></i> Search</button>
|
|
||||||
</EditForm>
|
|
||||||
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th style="width: 190px;">Plan</th>
|
|
||||||
<th style="width: 80px;">Seats</th>
|
|
||||||
<th style="width: 150px;">Created</th>
|
|
||||||
<th style="width: 170px; min-width: 170px;">Details</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@if (Model.Items is { Count: > 0 })
|
|
||||||
{
|
{
|
||||||
@foreach (var organization in Model.Items)
|
<label class="sr-only" asp-for="Paid">Customer</label>
|
||||||
|
<InputSelect @bind-Value="SearchForm.Paid" class="form-control mb-2 mr-2">
|
||||||
|
<option value="">-- Customer --</option>
|
||||||
|
<option value="true">Paid</option>
|
||||||
|
<option value="false">Freeloader</option>
|
||||||
|
</InputSelect>
|
||||||
|
}
|
||||||
|
<button type="submit" class="btn btn-primary mb-2" title="Search"><i class="fa fa-search"></i> Search</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th style="width: 190px;">Plan</th>
|
||||||
|
<th style="width: 80px;">Seats</th>
|
||||||
|
<th style="width: 150px;">Created</th>
|
||||||
|
<th style="width: 170px; min-width: 170px;">Details</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@if (Model.Items is { Count: > 0 })
|
||||||
{
|
{
|
||||||
<tr>
|
@foreach (var organization in Model.Items)
|
||||||
<td>
|
{
|
||||||
<a href="@($"/organizations/edit/{organization.Id}")">@organization.DisplayName()</a>
|
<tr>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<a href="@($"/organizations/edit/{organization.Id}")">@organization.DisplayName()</a>
|
||||||
@organization.Plan
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
@organization.Plan
|
||||||
@organization.Seats
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
@organization.Seats
|
||||||
<span title="@organization.CreationDate.ToString()">
|
</td>
|
||||||
@organization.CreationDate.ToShortDateString()
|
<td>
|
||||||
</span>
|
<span title="@organization.CreationDate.ToString()">
|
||||||
</td>
|
@organization.CreationDate.ToShortDateString()
|
||||||
<td>
|
</span>
|
||||||
@if (!GlobalSettings.SelfHosted)
|
</td>
|
||||||
{
|
<td>
|
||||||
if (!string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
|
@if (!GlobalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
<i class="fa fa-usd fa-lg fa-fw" title="Paid"></i>
|
if (!string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
|
||||||
|
{
|
||||||
|
<i class="fa fa-usd fa-lg fa-fw" title="Paid"></i>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<i class="fa fa-smile-o fa-lg fa-fw text-muted" title="Freeloader"></i>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@if (organization.MaxStorageGb is > 1)
|
||||||
|
{
|
||||||
|
<i class="fa fa-plus-square fa-lg fa-fw"
|
||||||
|
title="Additional Storage, @(organization.MaxStorageGb - 1) GB">
|
||||||
|
</i>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<i class="fa fa-smile-o fa-lg fa-fw text-muted" title="Freeloader"></i>
|
<i class="fa fa-plus-square-o fa-lg fa-fw text-muted"
|
||||||
|
title="No Additional Storage">
|
||||||
|
</i>
|
||||||
}
|
}
|
||||||
}
|
@if (organization.Enabled)
|
||||||
@if (organization.MaxStorageGb is > 1)
|
{
|
||||||
{
|
<i class="fa fa-check-circle fa-lg fa-fw"
|
||||||
<i class="fa fa-plus-square fa-lg fa-fw"
|
title="Enabled, expires @(organization.ExpirationDate?.ToShortDateString() ?? "-")">
|
||||||
title="Additional Storage, @(organization.MaxStorageGb - 1) GB">
|
</i>
|
||||||
</i>
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
<i class="fa fa-times-circle-o fa-lg fa-fw text-muted" title="Disabled"></i>
|
||||||
<i class="fa fa-plus-square-o fa-lg fa-fw text-muted"
|
}
|
||||||
title="No Additional Storage">
|
@if (organization.TwoFactorIsEnabled())
|
||||||
</i>
|
{
|
||||||
}
|
<i class="fa fa-lock fa-lg fa-fw" title="2FA Enabled"></i>
|
||||||
@if (organization.Enabled)
|
}
|
||||||
{
|
else
|
||||||
<i class="fa fa-check-circle fa-lg fa-fw"
|
{
|
||||||
title="Enabled, expires @(organization.ExpirationDate?.ToShortDateString() ?? "-")">
|
<i class="fa fa-unlock fa-lg fa-fw text-muted" title="2FA Not Enabled"></i>
|
||||||
</i>
|
}
|
||||||
}
|
</td>
|
||||||
else
|
</tr>
|
||||||
{
|
}
|
||||||
<i class="fa fa-times-circle-o fa-lg fa-fw text-muted" title="Disabled"></i>
|
}
|
||||||
}
|
else
|
||||||
@if (organization.TwoFactorIsEnabled())
|
{
|
||||||
{
|
<tr>
|
||||||
<i class="fa fa-lock fa-lg fa-fw" title="2FA Enabled"></i>
|
<td colspan="5">No results to list.</td>
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<i class="fa fa-unlock fa-lg fa-fw text-muted" title="2FA Not Enabled"></i>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}
|
</tbody>
|
||||||
else
|
</table>
|
||||||
{
|
</div>
|
||||||
<tr>
|
|
||||||
<td colspan="5">No results to list.</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditForm FormName="PaginationForm" Model="SearchForm" OnSubmit="OnSearchAsync">
|
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="pagination">
|
<ul class="pagination">
|
||||||
@if (Model.Items != null && Model.PreviousPage.HasValue)
|
@if (Model.Items != null)
|
||||||
{
|
{
|
||||||
<li class="page-item">
|
<PageLink FormKey="SearchForm.Page" Label="Previous" Page="@Model.PreviousPage" />
|
||||||
<a class="page-link" asp-action="Index" asp-route-page="@Model.PreviousPage.Value"
|
<PageLink FormKey="SearchForm.Page" Label="Next" Page="@Model.NextPage" />
|
||||||
asp-route-count="@Model.Count" asp-route-userEmail="@SearchForm.Email"
|
|
||||||
asp-route-name="@SearchForm.Name" asp-route-paid="@SearchForm.Paid">
|
|
||||||
Previous
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<a class="page-link" href="#" tabindex="-1">Previous</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@if (Model.Items != null && 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-userEmail="@SearchForm.Email"
|
|
||||||
asp-route-name="@SearchForm.Name" asp-route-paid="@SearchForm.Paid">
|
|
||||||
Next
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<a class="page-link" href="#" tabindex="-1">Next</a>
|
|
||||||
</li>
|
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
@if (Page.HasValue)
|
||||||
|
{
|
||||||
|
<li class="page-item">
|
||||||
|
<button class="page-link" name="@(FormKey!)" value="@Page">
|
||||||
|
@Label
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<a class="page-link" href="#" tabindex="-1">
|
||||||
|
@Label
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Bit.Admin.AdminConsole.Components.Shared.Table;
|
||||||
|
|
||||||
|
public partial class PageLink : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public string Label { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string? FormKey { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int? Page { get; set; }
|
||||||
|
}
|
@ -1,3 +1,18 @@
|
|||||||
@attribute [Authorize]
|
@attribute [Authorize]
|
||||||
|
|
||||||
@using Bit.Admin.Components
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
|
||||||
|
@using Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using System.Text.Json
|
||||||
|
|
||||||
|
@using Bit.Admin.Components;
|
||||||
|
@using Bit.Admin.Components.Navigation;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
@attribute [Authorize]
|
|
||||||
|
|
||||||
@using Microsoft.AspNetCore.Authorization
|
|
||||||
@using Microsoft.AspNetCore.Components.Authorization
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
|
||||||
|
|
||||||
@using Microsoft.AspNetCore.Mvc.ViewFeatures
|
|
||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
|
||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
|
||||||
@using Microsoft.JSInterop
|
|
||||||
@using System.Net.Http
|
|
||||||
@using System.Net.Http.Json
|
|
||||||
@using System.Text.Json
|
|
@ -49,3 +49,11 @@ h3 {
|
|||||||
.form-check-input {
|
.form-check-input {
|
||||||
margin-top: .45rem;
|
margin-top: .45rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.invalid {
|
||||||
|
border-color: $danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validation-message {
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Bit.Admin.AdminConsole;
|
using Bit.Admin.AdminConsole;
|
||||||
|
using Bit.Admin.AdminConsole.Components;
|
||||||
using Bit.Admin.IdentityServer;
|
using Bit.Admin.IdentityServer;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<RootNamespace>Admin.Test</RootNamespace>
|
<RootNamespace>Admin.Test</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="bunit" Version="1.36.0" />
|
||||||
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorVersion)">
|
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorVersion)">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
using Bit.Admin.AdminConsole.Components.Shared.Table;
|
||||||
|
using Bunit;
|
||||||
|
|
||||||
|
namespace Admin.Test.AdminConsole.Components.Shared;
|
||||||
|
|
||||||
|
public class PageLinkTests : TestContext
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void PageLink_Renders_ClickableLinksCorrectlyWhenPageParameterIsSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string formKey = "Key";
|
||||||
|
const string label = "Test";
|
||||||
|
const int page = 1;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var cut = RenderComponent<PageLink>(
|
||||||
|
(nameof(PageLink.FormKey), formKey),
|
||||||
|
(nameof(PageLink.Label), label),
|
||||||
|
(nameof(PageLink.Page), page)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var button = cut.Find("button");
|
||||||
|
Assert.Equal(formKey, button.Attributes.Single(x => x.Name == "name").Value);
|
||||||
|
Assert.Equal(label, button.InnerHtml);
|
||||||
|
Assert.Equal(page.ToString(), button.Attributes.Single(x => x.Name == "value").Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PageLink_Renders_ClickableLinksCorrectlyWhenPageParameterIsNotSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string formKey = "Key";
|
||||||
|
const string label = "Test";
|
||||||
|
int? page = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var cut = RenderComponent<PageLink>(
|
||||||
|
(nameof(PageLink.FormKey), formKey),
|
||||||
|
(nameof(PageLink.Label), label),
|
||||||
|
(nameof(PageLink.Page), page)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var disabledButton = cut.Find("a");
|
||||||
|
Assert.Equal("#", disabledButton.Attributes.Single(x => x.Name == "href").Value);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user