-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
855 additions
and
1,008 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
ControlR.Web.Client/Components/Permissions/TagsTabContent.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
ControlR.Web.Client/Components/Permissions/UsersTabContent.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<MudGrid> | ||
<MudItem md="4" sm="6" xs="12"> | ||
<MudText Typo="Typo.h6" Class="mt-2 mb-4 text-center"> | ||
Users | ||
</MudText> | ||
|
||
<MudTextField T="string" | ||
@bind-Value="_userSearchPattern" | ||
Label="Search" | ||
Adornment="Adornment.Start" | ||
AdornmentIcon="@Icons.Material.Filled.Search" | ||
Immediate="true" | ||
Class="my-2" | ||
Clearable="true" /> | ||
|
||
<MudPaper MaxHeight="500px" Class="overflow-y-auto"> | ||
<MudList T="UserResponseDto" | ||
@bind-SelectedValue="_selectedUser" | ||
SelectionMode="SelectionMode.SingleSelection" | ||
Color="Color.Info"> | ||
|
||
@foreach (var user in FilteredUsers) | ||
{ | ||
<MudListItem T="UserResponseDto" Value="@user"> | ||
@user.UserName | ||
</MudListItem> | ||
} | ||
|
||
</MudList> | ||
</MudPaper> | ||
</MudItem> | ||
<MudItem md="4" sm="6" xs="12"> | ||
<MudText Typo="Typo.h6" Class="mt-2 mb-4 text-center"> | ||
Roles | ||
</MudText> | ||
@if (_selectedUser is not null) | ||
{ | ||
<MudTextField T="string" | ||
@bind-Value="_roleSearchPattern" | ||
Label="Search" | ||
Adornment="Adornment.Start" | ||
AdornmentIcon="@Icons.Material.Filled.Search" | ||
Immediate="true" | ||
Class="my-2" | ||
Clearable="true" /> | ||
<MudPaper MaxHeight="500px" Class="pa-3 overflow-y-auto"> | ||
@foreach (var role in FilteredRoles) | ||
{ | ||
<MudSwitch T="bool" | ||
Value="@(role.UserIds.Contains(_selectedUser.Id))" | ||
Color="Color.Success" | ||
ValueChanged="@(async isToggled => await SetUserRole(isToggled, _selectedUser, role))"> | ||
@role.Name | ||
</MudSwitch> | ||
} | ||
</MudPaper> | ||
} | ||
</MudItem> | ||
<MudItem md="4" sm="6" xs="12"> | ||
<MudText Typo="Typo.h6" Class="mt-2 mb-4 text-center"> | ||
Tags | ||
</MudText> | ||
@if (_selectedUser is not null) | ||
{ | ||
<MudTextField T="string" | ||
@bind-Value="_tagSearchPattern" | ||
Label="Search" | ||
Adornment="Adornment.Start" | ||
AdornmentIcon="@Icons.Material.Filled.Search" | ||
Immediate="true" | ||
Class="my-2" | ||
Clearable="true" /> | ||
<MudPaper MaxHeight="500px" Class="pa-3 overflow-y-auto"> | ||
@foreach (var tag in FilteredTags) | ||
{ | ||
<MudSwitch T="bool" | ||
Value="@(tag.UserIds.Contains(_selectedUser.Id))" | ||
Color="Color.Success" | ||
ValueChanged="@(async isToggled => await SetUserTag(isToggled, _selectedUser, tag))"> | ||
@tag.Name | ||
</MudSwitch> | ||
} | ||
</MudPaper> | ||
} | ||
</MudItem> | ||
</MudGrid> |
166 changes: 166 additions & 0 deletions
166
ControlR.Web.Client/Components/Permissions/UsersTabContent.razor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
using ControlR.Web.Client.Extensions; | ||
using ControlR.Web.Client.Services.Stores; | ||
using ControlR.Web.Client.ViewModels; | ||
using Microsoft.AspNetCore.Components; | ||
using Microsoft.AspNetCore.Components.Authorization; | ||
using System.Collections.Immutable; | ||
|
||
namespace ControlR.Web.Client.Components.Permissions; | ||
|
||
public partial class UsersTabContent : ComponentBase, IDisposable | ||
{ | ||
private string _tagSearchPattern = string.Empty; | ||
private string _userSearchPattern = string.Empty; | ||
private string _roleSearchPattern = string.Empty; | ||
private UserResponseDto? _selectedUser; | ||
private bool _isServerAdmin; | ||
private Guid _currentUserId; | ||
|
||
private ImmutableArray<IDisposable>? _changeHandlers; | ||
|
||
[Inject] | ||
public required AuthenticationStateProvider AuthState { get; init; } | ||
[Inject] | ||
public required IControlrApi ControlrApi { get; init; } | ||
|
||
[Inject] | ||
public required ILogger<TagsTabContent> Logger { get; init; } | ||
|
||
[Inject] | ||
public required IRoleStore RoleStore { get; init; } | ||
|
||
[Inject] | ||
public required ISnackbar Snackbar { get; init; } | ||
|
||
[Inject] | ||
public required ITagStore TagStore { get; init; } | ||
|
||
[Inject] | ||
public required IUserStore UserStore { get; init; } | ||
|
||
private IOrderedEnumerable<RoleViewModel> FilteredRoles | ||
{ | ||
get | ||
{ | ||
var query = RoleStore.Items | ||
.Where(x => x.Name.Contains(_roleSearchPattern, StringComparison.OrdinalIgnoreCase)); | ||
|
||
if (!_isServerAdmin) | ||
{ | ||
query = query.Where(x => x.Name != RoleNames.ServerAdministrator); | ||
} | ||
|
||
if (_selectedUser?.Id == _currentUserId) | ||
{ | ||
query = query.Where(x => | ||
x.Name is not RoleNames.ServerAdministrator and not RoleNames.TenantAdministrator); | ||
} | ||
|
||
return query.OrderBy(x => x.Name); | ||
} | ||
} | ||
|
||
private IOrderedEnumerable<TagViewModel> FilteredTags => | ||
TagStore.Items | ||
.Where(x => x.Name.Contains(_tagSearchPattern, StringComparison.OrdinalIgnoreCase)) | ||
.OrderBy(x => x.Name); | ||
|
||
private IOrderedEnumerable<UserResponseDto> FilteredUsers => | ||
UserStore.Items | ||
.Where(x => x.UserName?.Contains(_userSearchPattern, StringComparison.OrdinalIgnoreCase) == true) | ||
.OrderBy(x => x.UserName); | ||
|
||
|
||
public void Dispose() | ||
{ | ||
_changeHandlers?.DisposeAll(); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
await base.OnInitializedAsync(); | ||
var state = await AuthState.GetAuthenticationStateAsync(); | ||
if (state.User.TryGetUserId(out var userId)) | ||
{ | ||
_currentUserId = userId; | ||
} | ||
_isServerAdmin = state.User.IsInRole(RoleNames.ServerAdministrator); | ||
} | ||
|
||
private async Task SetUserTag(bool isToggled, UserResponseDto user, TagViewModel tag) | ||
{ | ||
try | ||
{ | ||
if (isToggled) | ||
{ | ||
var addResult = await ControlrApi.AddUserTag(user.Id, tag.Id); | ||
if (!addResult.IsSuccess) | ||
{ | ||
Snackbar.Add(addResult.Reason, Severity.Error); | ||
return; | ||
} | ||
tag.UserIds.Add(user.Id); | ||
} | ||
else | ||
{ | ||
var removeResult = await ControlrApi.RemoveUserTag(user.Id, tag.Id); | ||
if (!removeResult.IsSuccess) | ||
{ | ||
Snackbar.Add(removeResult.Reason, Severity.Error); | ||
return; | ||
} | ||
tag.UserIds.Remove(user.Id); | ||
} | ||
|
||
await TagStore.InvokeItemsChanged(); | ||
|
||
Snackbar.Add(isToggled | ||
? "Tag added" | ||
: "Tag removed", Severity.Success); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Logger.LogError(ex, "Error while setting tag."); | ||
Snackbar.Add("An error occurred while setting tag", Severity.Error); | ||
} | ||
} | ||
|
||
private async Task SetUserRole(bool isToggled, UserResponseDto user, RoleViewModel role) | ||
{ | ||
try | ||
{ | ||
if (isToggled) | ||
{ | ||
var addResult = await ControlrApi.AddUserRole(user.Id, role.Id); | ||
if (!addResult.IsSuccess) | ||
{ | ||
Snackbar.Add(addResult.Reason, Severity.Error); | ||
return; | ||
} | ||
role.UserIds.Add(user.Id); | ||
} | ||
else | ||
{ | ||
var removeResult = await ControlrApi.RemoveUserRole(user.Id, role.Id); | ||
if (!removeResult.IsSuccess) | ||
{ | ||
Snackbar.Add(removeResult.Reason, Severity.Error); | ||
return; | ||
} | ||
role.UserIds.Remove(user.Id); | ||
} | ||
|
||
await TagStore.InvokeItemsChanged(); | ||
|
||
Snackbar.Add(isToggled | ||
? "Role added" | ||
: "Role removed", Severity.Success); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Logger.LogError(ex, "Error while setting role."); | ||
Snackbar.Add("An error occurred while setting role", Severity.Error); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using ControlR.Web.Client.ViewModels; | ||
|
||
namespace ControlR.Web.Client.Services.Stores; | ||
|
||
public interface IRoleStore : IStoreBase<RoleViewModel> | ||
{ } | ||
|
||
internal class RoleStore : StoreBase<RoleViewModel>, IRoleStore | ||
{ | ||
public RoleStore( | ||
IControlrApi controlrApi, | ||
ISnackbar snackbar, | ||
ILogger<RoleStore> logger) | ||
: base(controlrApi, snackbar, logger) | ||
{ | ||
|
||
} | ||
|
||
protected override async Task RefreshImpl() | ||
{ | ||
var getResult = await ControlrApi.GetAllRoles(); | ||
if (!getResult.IsSuccess) | ||
{ | ||
Snackbar.Add(getResult.Reason, Severity.Error); | ||
return; | ||
} | ||
|
||
foreach (var role in getResult.Value) | ||
{ | ||
var vm = new RoleViewModel(role); | ||
Cache.AddOrUpdate(vm.Id, vm, (_, _) => vm); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace ControlR.Web.Client.ViewModels; | ||
|
||
public class RoleViewModel(RoleResponseDto dto) : IHasPrimaryKey | ||
{ | ||
public Guid Id { get; } = dto.Id; | ||
public string Name { get; } = dto.Name; | ||
public ConcurrentHashSet<Guid> UserIds { get; } = new(dto.UserIds); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.