Skip to content

Commit f3b0eb2

Browse files
committed
Completed User Role Management
1 parent cb4d979 commit f3b0eb2

7 files changed

Lines changed: 184 additions & 10 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
@using CodeBeam.UltimateAuth.Authorization.Contracts
2+
@inject IUAuthClient UAuthClient
3+
@inject IDialogService DialogService
4+
@inject ISnackbar Snackbar
5+
6+
<MudDialog Class="mud-width-full" ContentClass="uauth-dialog">
7+
8+
<TitleContent>
9+
<MudText Typo="Typo.h6">User Roles</MudText>
10+
<MudText Typo="Typo.subtitle2">UserKey: @UserKey.Value</MudText>
11+
</TitleContent>
12+
13+
<DialogContent>
14+
<MudStack Spacing="2">
15+
<MudText Typo="Typo.subtitle1">Assigned Roles</MudText>
16+
17+
@if (_roles.Count == 0)
18+
{
19+
<MudText Color="Color.Secondary">No roles assigned</MudText>
20+
}
21+
22+
<MudStack Row="true" Wrap="Wrap.Wrap" Spacing="1">
23+
@foreach (var role in _roles)
24+
{
25+
<MudChip T="string" Color="Color.Primary" Variant="Variant.Filled" OnClose="(() => RemoveRole(role))">@role</MudChip>
26+
}
27+
</MudStack>
28+
29+
<MudDivider Class="my-4" />
30+
31+
<MudText Typo="Typo.subtitle1">Add Role</MudText>
32+
<MudStack Row="true" Spacing="2">
33+
34+
<MudSelect T="string" @bind-Value="_selectedRole" Label="Role" Dense="true">
35+
@foreach (var role in _allRoles)
36+
{
37+
<MudSelectItem Value="@role.Name">@role.Name</MudSelectItem>
38+
}
39+
</MudSelect>
40+
41+
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="AddRole">Add</MudButton>
42+
</MudStack>
43+
</MudStack>
44+
</DialogContent>
45+
46+
<DialogActions>
47+
<MudButton OnClick="Close">Close</MudButton>
48+
</DialogActions>
49+
</MudDialog>
50+
51+
@code {
52+
53+
[CascadingParameter]
54+
private IMudDialogInstance MudDialog { get; set; } = default!;
55+
56+
[Parameter]
57+
public UAuthState AuthState { get; set; } = default!;
58+
59+
[Parameter]
60+
public UserKey UserKey { get; set; } = default!;
61+
62+
private List<string> _roles = new();
63+
private List<RoleInfo> _allRoles = new();
64+
65+
private string? _selectedRole;
66+
67+
protected override async Task OnInitializedAsync()
68+
{
69+
await LoadRoles();
70+
}
71+
72+
private async Task LoadRoles()
73+
{
74+
var userRoles = await UAuthClient.Authorization.GetUserRolesAsync(UserKey);
75+
76+
if (userRoles.IsSuccess && userRoles.Value != null)
77+
_roles = userRoles.Value.Roles.Items.Select(x => x.Name).ToList();
78+
79+
var roles = await UAuthClient.Authorization.QueryRolesAsync(new RoleQuery
80+
{
81+
PageNumber = 1,
82+
PageSize = 200
83+
});
84+
85+
if (roles.IsSuccess && roles.Value != null)
86+
_allRoles = roles.Value.Items.ToList();
87+
}
88+
89+
private async Task AddRole()
90+
{
91+
if (string.IsNullOrWhiteSpace(_selectedRole))
92+
return;
93+
94+
var result = await UAuthClient.Authorization.AssignRoleToUserAsync(UserKey, _selectedRole);
95+
96+
if (result.IsSuccess)
97+
{
98+
_roles.Add(_selectedRole);
99+
Snackbar.Add("Role assigned", Severity.Success);
100+
}
101+
else
102+
{
103+
Snackbar.Add(result.GetErrorText ?? "Failed", Severity.Error);
104+
}
105+
106+
_selectedRole = null;
107+
}
108+
109+
private async Task RemoveRole(string role)
110+
{
111+
var confirm = await DialogService.ShowMessageBoxAsync(
112+
"Remove Role",
113+
$"Remove {role} from user?",
114+
yesText: "Remove",
115+
noText: "Cancel",
116+
options: new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, BackgroundClass = "uauth-blur-slight" });
117+
118+
if (confirm != true)
119+
{
120+
Snackbar.Add("Role remove process cancelled.", Severity.Info);
121+
return;
122+
}
123+
124+
if (role == "Admin")
125+
{
126+
var confirm2 = await DialogService.ShowMessageBoxAsync(
127+
"Are You Sure",
128+
"You are going to remove admin role. This action may cause the application unuseable.",
129+
yesText: "Remove",
130+
noText: "Cancel",
131+
options: new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, BackgroundClass = "uauth-blur-slight" });
132+
133+
if (confirm2 != true)
134+
{
135+
Snackbar.Add("Role remove process cancelled.", Severity.Info);
136+
return;
137+
}
138+
}
139+
140+
var result = await UAuthClient.Authorization.RemoveRoleFromUserAsync(UserKey, role);
141+
142+
if (result.IsSuccess)
143+
{
144+
_roles.Remove(role);
145+
Snackbar.Add("Role removed", Severity.Success);
146+
}
147+
else
148+
{
149+
Snackbar.Add(result.GetErrorText ?? "Failed", Severity.Error);
150+
}
151+
}
152+
153+
private void Close() => MudDialog.Close();
154+
155+
}

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@
117117
</MudButton>
118118
</MudItem>
119119
<MudItem xs="12" sm="6">
120-
<MudButton FullWidth Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.AdminPanelSettings">
121-
Assign Role
120+
<MudButton FullWidth Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.AdminPanelSettings" OnClick="OpenUserRoleDialog">
121+
User Role Management
122122
</MudButton>
123123
</MudItem>
124124
</MudGrid>

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ private async Task OpenRoleDialog()
184184
await DialogService.ShowAsync<RoleDialog>("Role Management", GetDialogParameters(), GetDialogOptions());
185185
}
186186

187+
private async Task OpenUserRoleDialog()
188+
{
189+
var parameters = GetDialogParameters();
190+
parameters.Add("UserKey", AuthState.Identity.UserKey);
191+
await DialogService.ShowAsync<UserRoleDialog>("Role Management", parameters, GetDialogOptions());
192+
}
193+
187194
private DialogOptions GetDialogOptions()
188195
{
189196
return new DialogOptions

src/CodeBeam.UltimateAuth.Client/Services/Abstractions/IAuthorizationClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ namespace CodeBeam.UltimateAuth.Client.Services;
88
public interface IAuthorizationClient
99
{
1010
Task<UAuthResult<AuthorizationResult>> CheckAsync(AuthorizationCheckRequest request);
11-
Task<UAuthResult<UserRolesResponse>> GetMyRolesAsync();
12-
Task<UAuthResult<UserRolesResponse>> GetUserRolesAsync(UserKey userKey);
11+
Task<UAuthResult<UserRolesResponse>> GetMyRolesAsync(PageRequest? request = null);
12+
Task<UAuthResult<UserRolesResponse>> GetUserRolesAsync(UserKey userKey, PageRequest? request = null);
1313
Task<UAuthResult> AssignRoleToUserAsync(UserKey userKey, string role);
1414
Task<UAuthResult> RemoveRoleFromUserAsync(UserKey userKey, string role);
1515

src/CodeBeam.UltimateAuth.Client/Services/UAuthAuthorizationClient.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,17 @@ public async Task<UAuthResult<AuthorizationResult>> CheckAsync(AuthorizationChec
3030
return UAuthResultMapper.FromJson<AuthorizationResult>(raw);
3131
}
3232

33-
public async Task<UAuthResult<UserRolesResponse>> GetMyRolesAsync()
33+
public async Task<UAuthResult<UserRolesResponse>> GetMyRolesAsync(PageRequest? request = null)
3434
{
35-
var raw = await _request.SendFormAsync(Url("/authorization/users/me/roles/get"));
35+
request ??= new PageRequest();
36+
var raw = await _request.SendJsonAsync(Url("/authorization/users/me/roles/get"), request);
3637
return UAuthResultMapper.FromJson<UserRolesResponse>(raw);
3738
}
3839

39-
public async Task<UAuthResult<UserRolesResponse>> GetUserRolesAsync(UserKey userKey)
40+
public async Task<UAuthResult<UserRolesResponse>> GetUserRolesAsync(UserKey userKey, PageRequest? request = null)
4041
{
41-
var raw = await _request.SendFormAsync(Url($"/admin/authorization/users/{userKey}/roles/get"));
42+
request ??= new PageRequest();
43+
var raw = await _request.SendJsonAsync(Url($"/admin/authorization/users/{userKey}/roles/get"), request);
4244
return UAuthResultMapper.FromJson<UserRolesResponse>(raw);
4345
}
4446

@@ -49,7 +51,14 @@ public async Task<UAuthResult> AssignRoleToUserAsync(UserKey userKey, string rol
4951
Role = role
5052
});
5153

52-
return UAuthResultMapper.From(raw);
54+
var result = UAuthResultMapper.From(raw);
55+
56+
if (result.IsSuccess)
57+
{
58+
await _events.PublishAsync(new UAuthStateEventArgsEmpty(UAuthStateEvent.AuthorizationChanged, _options.StateEvents.HandlingMode));
59+
}
60+
61+
return result;
5362
}
5463

5564
public async Task<UAuthResult> RemoveRoleFromUserAsync(UserKey userKey, string role)

src/CodeBeam.UltimateAuth.Core/Contracts/Common/UAuthResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class UAuthResult
1111

1212
public HttpStatusInfo Http => new(Status);
1313

14+
public string? GetErrorText => Problem?.Detail ?? Problem?.Title;
15+
1416
public sealed class HttpStatusInfo
1517
{
1618
private readonly int _status;

src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/Stores/InMemoryUserRoleStore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CodeBeam.UltimateAuth.Authorization.Contracts;
22
using CodeBeam.UltimateAuth.Core.Domain;
3+
using CodeBeam.UltimateAuth.Core.Errors;
34
using CodeBeam.UltimateAuth.Core.MultiTenancy;
45
using System.Collections.Concurrent;
56

@@ -31,7 +32,7 @@ public Task AssignAsync(TenantKey tenant, UserKey userKey, RoleId roleId, DateTi
3132
lock (list)
3233
{
3334
if (list.Any(x => x.RoleId == roleId))
34-
return Task.CompletedTask;
35+
throw new UAuthConflictException("Role is already assigned to the user.");
3536

3637
list.Add(new UserRole
3738
{

0 commit comments

Comments
 (0)