Skip to content

Commit 64d1dc5

Browse files
authored
Identity Lab: UI Polish, Authorization Playground & Diagnostics (#118)
* Identity Lab: Polish * Other Fixes * Layout, Login and Register Rage Design Improvements * Session Diagnostics Page * Fix Tests & Add Identifier Availability * Identifier Availability (Part 2) & Overview and MyIdentity Pages * MyAccess Page * Users & Authorization Lab Page and Show Version Info * Add Essential Cookie Information Popup & Settings Page and Minor Enhancements
1 parent 4d6bb39 commit 64d1dc5

83 files changed

Lines changed: 6896 additions & 2215 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Authorization/LabAccessContextProvider.cs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,11 @@ public async Task<AccessContext> CreateAsync(string action, string resource, Can
1111
var flow = await authFlow.BeginAsync(AuthFlowType.ValidateSession, cancellationToken);
1212
return await contextFactory.CreateAsync(flow, action, resource, ct: cancellationToken);
1313
}
14+
15+
16+
public async Task<AccessContext> CreateForUserAsync(string action, string resource, UserKey targetUserKey, CancellationToken ct = default)
17+
{
18+
var flow = await authFlow.BeginAsync(AuthFlowType.ValidateSession, ct);
19+
return await contextFactory.CreateAsync(flow, action, resource, resourceId: targetUserKey.Value, ct: ct);
20+
}
1421
}

‎apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Authorization/LabAdminGuard.cs‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CodeBeam.UltimateAuth.Authorization;
22
using CodeBeam.UltimateAuth.Core.Contracts;
3+
using CodeBeam.UltimateAuth.IdentityLab.Domain;
34

45
namespace CodeBeam.UltimateAuth.IdentityLab.Authorization;
56

@@ -17,18 +18,27 @@ context.ActorUserKey is null ||
1718
return false;
1819
}
1920

20-
var roleStore = roleStoreFactory.Create(context.ActorTenant);
21+
var tenant = context.ActorTenant;
22+
var roleStore = roleStoreFactory.Create(tenant);
2123

22-
var adminRole = await roleStore.GetByNameAsync(LabRoles.Admin.ToUpperInvariant(), ct);
24+
var roles = await roleStore.GetByIdsAsync(
25+
[LabRoleIds.Admin], ct);
26+
27+
var adminRole = roles.FirstOrDefault(x =>
28+
x.Id == LabRoleIds.Admin &&
29+
x.Tenant == tenant &&
30+
!x.IsDeleted);
2331

2432
if (adminRole is null)
2533
return false;
2634

27-
var userRoleStore = userRoleStoreFactory.Create(context.ActorTenant);
35+
var userRoleStore = userRoleStoreFactory.Create(tenant);
2836

29-
var assignments = await userRoleStore.GetAssignmentsAsync(context.ActorUserKey.Value, ct);
37+
var assignments = await userRoleStore
38+
.GetAssignmentsAsync(context.ActorUserKey.Value, ct);
3039

31-
return assignments.Any(x => x.RoleId == adminRole.Id);
40+
return assignments.Any(x =>
41+
x.RoleId == LabRoleIds.Admin);
3242
}
3343

3444
public async Task EnsureAdminAsync(AccessContext context, CancellationToken ct = default)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace CodeBeam.UltimateAuth.IdentityLab.Authorization;
2+
3+
public static class LabDemoPermissions
4+
{
5+
public const string ProductsRead = "demo.products.read";
6+
public const string ProductsCreate = "demo.products.create";
7+
public const string ProductsUpdate = "demo.products.update";
8+
public const string ProductsDelete = "demo.products.delete";
9+
public const string OrdersCreate = "demo.orders.create";
10+
11+
public static readonly IReadOnlyList<string> Manager =
12+
[
13+
ProductsRead,
14+
ProductsCreate,
15+
ProductsUpdate,
16+
ProductsDelete
17+
];
18+
19+
public static readonly IReadOnlyList<string> Customer =
20+
[
21+
ProductsRead,
22+
OrdersCreate
23+
];
24+
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
namespace CodeBeam.UltimateAuth.IdentityLab.Authorization;
2-
3-
public static class LabRoles
1+
public static class LabRoles
42
{
53
public const string Admin = "Ladmin";
64
public const string Owner = "Owner";
75
public const string User = "User";
86

7+
public const string Manager = "Manager";
8+
public const string Customer = "Customer";
9+
910
public static readonly IReadOnlyList<string> SystemRoles =
1011
[
1112
Admin,
1213
Owner,
1314
User
1415
];
16+
17+
public static readonly IReadOnlyList<string> DemoRoles =
18+
[
19+
Manager,
20+
Customer
21+
];
22+
23+
public static bool IsDemoRole(string? name) => DemoRoles.Contains(name ?? "", StringComparer.OrdinalIgnoreCase);
1524
}

‎apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Components/Custom/DashboardSidebar.razor‎

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
@using MudExtensions
2+
@using Microsoft.JSInterop
3+
@implements IAsyncDisposable
4+
5+
@inject IJSRuntime JS
6+
7+
<MudPopup Open="@_open" PopupPosition="PopupPosition.Bottom" Padding="16" Elevation="6" EnableAnimation="true" Class="lab-cookie-popup">
8+
9+
<ChildContent>
10+
<div class="lab-cookie-layout">
11+
12+
<div class="lab-cookie-content">
13+
<div class="lab-cookie-icon">
14+
<MudIcon Icon="@Icons.Material.Outlined.Shield" Size="Size.Medium" />
15+
</div>
16+
17+
<div class="lab-cookie-copy">
18+
<strong>Essential cookies</strong>
19+
20+
<p>
21+
Identity Lab uses essential cookies to keep authentication sessions secure and the application working correctly.
22+
These cookies are necessary for the service to function.
23+
</p>
24+
</div>
25+
</div>
26+
27+
<MudButton Color="Color.Primary" Variant="Variant.Filled" Disabled="@_saving" OnClick="AcknowledgeAsync" Class="lab-cookie-button">
28+
Got it
29+
</MudButton>
30+
31+
</div>
32+
</ChildContent>
33+
34+
</MudPopup>
35+
36+
37+
@code {
38+
private const string StorageKey = "identitylab.cookies.notice.v1";
39+
40+
private bool _open;
41+
private bool _saving;
42+
private bool _initialized;
43+
private bool _disposed;
44+
45+
protected override async Task OnAfterRenderAsync(bool firstRender)
46+
{
47+
if (!firstRender || _initialized)
48+
return;
49+
50+
try
51+
{
52+
var acknowledged = await JS.InvokeAsync<string?>("localStorage.getItem", StorageKey);
53+
54+
if (_disposed)
55+
return;
56+
57+
_open = acknowledged != "acknowledged";
58+
_initialized = true;
59+
60+
await InvokeAsync(StateHasChanged);
61+
}
62+
catch (JSException)
63+
{
64+
// Storage may be unavailable in restricted browsers.
65+
// Show the notice without persistence.
66+
_open = true;
67+
_initialized = true;
68+
await InvokeAsync(StateHasChanged);
69+
}
70+
catch (JSDisconnectedException)
71+
{
72+
// The circuit has disconnected.
73+
}
74+
}
75+
76+
private async Task AcknowledgeAsync()
77+
{
78+
if (_saving)
79+
return;
80+
81+
_saving = true;
82+
83+
try
84+
{
85+
await JS.InvokeVoidAsync("localStorage.setItem", StorageKey, "acknowledged");
86+
}
87+
catch (JSException)
88+
{
89+
// Dismiss for this circuit even if storage is unavailable.
90+
}
91+
catch (JSDisconnectedException)
92+
{
93+
// The browser connection was lost.
94+
}
95+
finally
96+
{
97+
_open = false;
98+
_saving = false;
99+
100+
if (!_disposed)
101+
await InvokeAsync(StateHasChanged);
102+
}
103+
}
104+
105+
public ValueTask DisposeAsync()
106+
{
107+
_disposed = true;
108+
return ValueTask.CompletedTask;
109+
}
110+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
::deep .lab-cookie-popup {
2+
max-width: 100%;
3+
border-radius: 14px;
4+
background: var(--mud-palette-surface);
5+
border: 1px solid var(--mud-palette-lines-default);
6+
color: var(--mud-palette-text-primary);
7+
z-index: 1200;
8+
}
9+
10+
.lab-cookie-content {
11+
display: flex;
12+
align-items: flex-start;
13+
gap: 14px;
14+
min-width: 0;
15+
}
16+
17+
.lab-cookie-icon {
18+
width: 42px;
19+
height: 42px;
20+
flex-shrink: 0;
21+
display: flex;
22+
align-items: center;
23+
justify-content: center;
24+
border-radius: 12px;
25+
background: var(--mud-palette-primary-hover);
26+
color: var(--mud-palette-primary);
27+
}
28+
29+
.lab-cookie-copy {
30+
display: flex;
31+
flex-direction: column;
32+
gap: 5px;
33+
max-width: 780px;
34+
}
35+
36+
.lab-cookie-copy strong {
37+
font-size: .88rem;
38+
font-weight: 750;
39+
}
40+
41+
.lab-cookie-copy p {
42+
margin: 0;
43+
color: var(--mud-palette-text-secondary);
44+
font-size: .78rem;
45+
line-height: 1.65;
46+
}
47+
48+
49+
.lab-cookie-layout {
50+
display: flex;
51+
align-items: center;
52+
justify-content: space-between;
53+
gap: 24px;
54+
width: 100%;
55+
}
56+
57+
::deep .lab-cookie-button {
58+
flex-shrink: 0;
59+
min-width: 100px;
60+
border-radius: 9px;
61+
font-weight: 650;
62+
}
63+
64+
@media (max-width: 600px) {
65+
.lab-cookie-layout {
66+
flex-direction: column;
67+
align-items: stretch;
68+
gap: 16px;
69+
}
70+
71+
::deep .lab-cookie-button {
72+
width: 100%;
73+
}
74+
}
75+
76+
@media (max-width: 600px) {
77+
.lab-cookie-content {
78+
gap: 10px;
79+
}
80+
81+
.lab-cookie-icon {
82+
width: 36px;
83+
height: 36px;
84+
}
85+
86+
.lab-cookie-copy p {
87+
font-size: .74rem;
88+
}
89+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
@using MudBlazor
2+
3+
<MudTooltip Placement="Placement.Top" Arrow="true" Duration="250">
4+
<TooltipContent>
5+
<MudStack Spacing="1" Class="pa-1" Style="max-width:300px">
6+
7+
<MudText Typo="Typo.subtitle2">
8+
@Title
9+
</MudText>
10+
11+
<MudText Typo="Typo.body2">
12+
@Description
13+
</MudText>
14+
15+
<MudDivider Class="my-1" />
16+
17+
<MudText Typo="Typo.caption">
18+
Configuration
19+
</MudText>
20+
21+
<MudText Typo="Typo.body2" Style="font-family:monospace">
22+
@OptionPath
23+
</MudText>
24+
25+
@if (!string.IsNullOrWhiteSpace(CurrentValue))
26+
{
27+
<MudText Typo="Typo.caption">
28+
Current Value: @CurrentValue
29+
</MudText>
30+
}
31+
32+
</MudStack>
33+
</TooltipContent>
34+
35+
<ChildContent>
36+
<MudIcon Icon="@Icons.Material.Outlined.Info"
37+
Size="Size.Small"
38+
Color="Color.Secondary"
39+
Style="cursor:help;opacity:.65" />
40+
</ChildContent>
41+
42+
</MudTooltip>
43+
44+
@code {
45+
[Parameter, EditorRequired]
46+
public string Title { get; set; } = string.Empty;
47+
48+
[Parameter, EditorRequired]
49+
public string Description { get; set; } = string.Empty;
50+
51+
[Parameter, EditorRequired]
52+
public string OptionPath { get; set; } = string.Empty;
53+
54+
[Parameter]
55+
public string? CurrentValue { get; set; }
56+
}

0 commit comments

Comments
 (0)