Skip to content

Commit 1cf285c

Browse files
committed
Fix Copy Password Bug, Improve UI, Update NuGet Packages
1 parent 6404cbd commit 1cf285c

13 files changed

+102
-86
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<MudText Typo="Typo.body2" Color="Color.Warning" Align="Align.Center" Class="px-4 py-1">
2+
Too much Items found - please refine your search
3+
</MudText>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<MudText Typo="Typo.body1" Align="Align.Center" Class="px-4">
2+
No items found
3+
</MudText>
Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
@inherits MudComponentBase
2+
@inject ISnackbar Snackbar
3+
@inject ClipboardService clipboard
4+
@using CurrieTechnologies.Razor.Clipboard
5+
26
@if (LapsInfo != null)
37
{
48
<MudStack Spacing="2">
5-
<MudField Label="Password" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Password" AdornmentColor="Color.Default" aria-label="LAPS Password">@LapsInfo.Password</MudField>
9+
10+
<MudTextFieldExtended T="string" Label="Password" Variant="Variant.Text" InputMode="InputMode.none" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Password" AdornmentColor="Color.Default" aria-label="LAPS Password" Text="@LapsInfo.Password" ReadOnly="true">
11+
<AdornmentStart>
12+
<MudIcon Icon="@Icons.Material.Outlined.Password" />
13+
</AdornmentStart>
14+
<AdornmentEnd>
15+
<MudTooltip Text="@(IsCopyToClipboardSupported ? "Copy Password to clipboard" : "Your browser does not support Clipboard API - please use STRG+C")">
16+
<MudIconButton Class="mb-1" Disabled="@(IsCopyButtonDisabled())" Icon="@Icons.Material.Outlined.ContentCopy" Color="Color.Primary" OnClick="@(async (_) => await CopyLAPSPasswordToClipboardAsync())" aria-label="Copy Password to clipbard" />
17+
</MudTooltip>
18+
19+
</AdornmentEnd>
20+
</MudTextFieldExtended>
621
@if (LapsInfo.Account != null)
722
{
8-
<MudField Label="Account" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.AccountCircle" AdornmentColor="Color.Default" aria-label="LAPS Managed Account">@LapsInfo.Account</MudField>
23+
<MudTextField T="string" ReadOnly="true" Label="Account" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.AccountCircle" AdornmentColor="Color.Default" aria-label="LAPS Managed Account" Text="@LapsInfo.Account"/>
924
}
1025
@if (LapsInfo.PasswordSetDate != null)
1126
{
12-
<MudField Label="Set Date" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.MoreTime" AdornmentColor="Color.Default" aria-label="LAPS Password Set Date">@LapsInfo.PasswordSetDate</MudField>
27+
<MudTextField T="string" ReadOnly="true" Label="Set Date" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.MoreTime" AdornmentColor="Color.Default" aria-label="LAPS Password Set Date" Text="@LapsInfo.PasswordSetDate.ToString()"/>
1328
}
14-
<MudField Label="Expire Date" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.HourglassBottom" AdornmentColor="Color.Default" aria-label="LAPS Password Expire Date">@LapsInfo.PasswordExpireDate</MudField>
29+
<MudTextField T="string" ReadOnly="true" Label="Expire Date" Variant="Variant.Text" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.HourglassBottom" AdornmentColor="Color.Default" aria-label="LAPS Password Expire Date" Text="@LapsInfo.PasswordExpireDate.ToString()"/>
1530
</MudStack>
1631
}

src/Components/LapsInformationDetail.razor.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,32 @@ namespace LAPS_WebUI.Components
77
public partial class LapsInformationDetail : MudComponentBase
88
{
99
[Parameter] public LapsInformation? LapsInfo { get; set; }
10+
[Parameter] public MudTabs? MudTab { get; set; }
11+
private bool IsCopyToClipboardSupported { get; set; }
12+
13+
protected override async Task OnAfterRenderAsync(bool firstRender)
14+
{
15+
if (firstRender)
16+
{
17+
IsCopyToClipboardSupported = await clipboard.IsSupportedAsync();
18+
}
19+
}
20+
21+
private bool IsCopyButtonDisabled()
22+
{
23+
return !IsCopyToClipboardSupported || LapsInfo is null || string.IsNullOrEmpty(LapsInfo.Password);
24+
}
25+
private async Task CopyLAPSPasswordToClipboardAsync()
26+
{
27+
if (LapsInfo != null && !string.IsNullOrEmpty(LapsInfo.Password))
28+
{
29+
await clipboard.WriteTextAsync(LapsInfo.Password);
30+
Snackbar.Add("Copied password to clipboard!", Severity.Success);
31+
}
32+
else
33+
{
34+
Snackbar.Add("Failed to copy password to clipboard!", Severity.Error);
35+
}
36+
}
1037
}
1138
}

src/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ RUN apt update && \
2727
ln -s /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2 /usr/lib/liblber.so.2 && \
2828
pip3 install dpapi-ng[kerberos]
2929
COPY --from=publish /app/publish .
30+
HEALTHCHECK CMD curl --fail http://localhost/healthz || exit
3031
ENTRYPOINT ["dotnet", "LAPS-WebUI.dll"]

src/LAPS-WebUI.csproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="Blazored.SessionStorage" Version="2.3.0" />
14+
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0" />
1515
<PackageReference Include="CliWrap" Version="3.6.4" />
16+
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="6.5.10" />
1617
<PackageReference Include="CurrieTechnologies.Razor.Clipboard" Version="1.6.0" />
1718
<PackageReference Include="LdapForNet" Version="2.7.15" />
1819
<PackageReference Include="Macross.Json.Extensions" Version="3.0.0" />
19-
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="7.0.9" />
20-
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
20+
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="7.0.11" />
21+
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
2122
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
22-
<PackageReference Include="MudBlazor" Version="6.7.0" />
23+
<PackageReference Include="MudBlazor" Version="6.10.0" />
2324
<PackageReference Include="Serilog" Version="3.0.1" />
2425
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
2526
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />

src/Models/LapsInformation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace LAPS_WebUI.Models
44
{
55
public class LapsInformation
66
{
7+
public required string ComputerName { get; set; }
78
public string? Password { get; set; }
89
public string? Account { get; set; }
910
public DateTime? PasswordExpireDate { get; set; }

src/Pages/LAPS.razor

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
@inject NavigationManager NavigationManager
44
@inject ILdapService LDAPService
55
@inject ISnackbar Snackbar
6-
@inject ClipboardService clipboard
7-
@using CurrieTechnologies.Razor.Clipboard
86

9-
<MudContainer MaxWidth="MaxWidth.Medium" Class="mt-5">
7+
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-5">
108

119
@if (!Authenticated)
1210
{
1311
<MudAlert Elevation="3" Severity="Severity.Error" Variant="Variant.Outlined">Access denied!</MudAlert>
1412
}
1513
else
1614
{
17-
<MudAutocomplete Label="Search by Computername" HelperTextOnFocus="true" HelperText="Minimum query length is 4. Wildcard (*) is supported" ShowProgressIndicator="true"
18-
MaxItems=null ValueChanged="@(async (ADComputer a) => OnSelectedItemChangedAsync(a).AndForget())" MinCharacters="4" SearchFunc="@SearchAsync" ToStringFunc="@_ADComputerToStringConverter" Immediate="true" ResetValueOnEmptyText="true"
19-
AdornmentIcon="@Icons.Material.Outlined.Search" AdornmentColor="Color.Default" Adornment="Adornment.Start" SelectValueOnTab=true Clearable=true aria-label="Search Computer" >
15+
<MudAutocomplete Label="Search by Computername" HelperTextOnFocus="true" HelperText="Minimum query length is 4. Wildcard (*) is supported" Variant="Variant.Outlined" ShowProgressIndicator="true" AutoFocus="true" Immediate="true" ResetValueOnEmptyText="true" @ref="AutoCompleteSearchBox"
16+
MaxItems=null ValueChanged="@(async (ADComputer a) => OnSelectedItemChangedAsync(a).AndForget())" MinCharacters="4" MaxLength="15" SearchFunc="@SearchAsync" ToStringFunc="@(x => (x is null ? string.Empty : x.Name))"
17+
AdornmentIcon="@Icons.Material.Outlined.Search" AdornmentColor="Color.Default" Adornment="Adornment.Start" SelectOnClick="true" SelectValueOnTab=true Clearable=true aria-label="Search Computer" >
2018
<ItemTemplate Context="e">
2119
<MudText>
2220
<MudIcon Icon="@Icons.Material.Filled.Computer" Class="mb-n1 mr-3" aria-label=@($"{e.Name}")/>@($"{e.Name}")
@@ -27,28 +25,39 @@
2725
<MudIcon Icon="@Icons.Material.Filled.Computer" Class="mb-n1 mr-3" aria-label=@($"{e.Name}")/>@($"{e.Name}")
2826
</MudText>
2927
</ItemSelectedTemplate>
28+
<MoreItemsTemplate>
29+
<AutoCompleteMoreItemsFound/>
30+
</MoreItemsTemplate>
31+
<NoItemsTemplate>
32+
<AutoCompleteNoItemsFound />
33+
</NoItemsTemplate>
3034
</MudAutocomplete>
3135

3236
<MudGrid Class="mt-8" Justify="Justify.SpaceEvenly">
3337

3438
@foreach(var computer in SelectedComputers)
3539
{
36-
<MudItem xs="10" sm="8">
40+
<MudItem xs="10" lg="6">
3741
<MudCard>
3842
<MudCardHeader>
3943
<CardHeaderContent>
4044
<MudText Typo="Typo.h5">@computer.Name</MudText>
4145
</CardHeaderContent>
4246
<CardHeaderActions>
43-
<MudIconButton Icon="@Icons.Material.Filled.Close" Color="Color.Default" OnClick="@(() => RemoveComputerCard(computer.Name))" aria-label="close" Disabled=@(computer.Loading)/>
47+
<MudStack Row="true" Spacing="0">
48+
<MudTooltip Text="Refresh">
49+
<MudIconButton Icon="@Icons.Material.Filled.Refresh" Color="Color.Primary" OnClick="@(async e => await RefreshComputerDetailsAsync(computer.Name))" aria-label="refresh" Disabled=@(computer.Loading) />
50+
</MudTooltip>
51+
<MudIconButton Icon="@Icons.Material.Filled.Close" Color="Color.Default" OnClick="@(() => RemoveComputerCard(computer.Name))" aria-label="close" Disabled=@(computer.Loading) />
52+
</MudStack>
4453
</CardHeaderActions>
4554
</MudCardHeader>
4655
<MudCardContent>
4756
@if(!computer.Loading)
4857
{
4958
@if(!computer.FailedToRetrieveLAPSDetails)
5059
{
51-
<MudTabs Position="Position.Top" Rounded="true" Border="false" ApplyEffectsToContainer="true" @ref="_tabs" PanelClass="pa-4">
60+
<MudTabs Position="Position.Top" Rounded="true" Border="false" ApplyEffectsToContainer="true" PanelClass="pa-4" @key="computer.Name" @ref="MudTabsDict[computer.Name]">
5261
<MudTabPanel Icon="@Icons.Material.Outlined.Filter1" ID="@("v1")" Text="v1" Disabled=@(!computer.LAPSInformations!.Any(x => x.Version == Enums.LAPSVersion.v1 && x.IsCurrent))>
5362
<LapsInformationDetail LapsInfo="computer.LAPSInformations!.SingleOrDefault(x => x.Version == Enums.LAPSVersion.v1)" />
5463
</MudTabPanel>
@@ -98,14 +107,6 @@
98107
</div>
99108
}
100109
</MudCardContent>
101-
<MudCardActions>
102-
<MudTooltip Text="@(IsCopyToClipboardSupported ? "Copy Password to clipboard" : "Your browser does not support Clipboard API - please use STRG+C")">
103-
<MudIconButton Disabled="@(!IsCopyToClipboardSupported || (_tabs != null && _tabs.ActivePanel != null && _tabs.ActivePanel.ID.ToString() == "history"))" Icon="@Icons.Material.Filled.FileCopy" Color="Color.Tertiary" OnClick="@(async e => await CopyLAPSPasswordToClipboardAsync(computer))" aria-label="Copy Password to clipbard"/>
104-
</MudTooltip>
105-
<MudTooltip Text="Refresh">
106-
<MudIconButton Icon="@Icons.Material.Filled.Refresh" Color="Color.Tertiary" OnClick="@(async e => await RefreshComputerDetailsAsync(computer.Name))" aria-label="refresh"/>
107-
</MudTooltip>
108-
</MudCardActions>
109110
</MudCard>
110111
</MudItem>
111112
}

src/Pages/LAPS.razor.cs

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,11 @@ namespace LAPS_WebUI.Pages
55
{
66
public partial class LAPS
77
{
8-
private bool IsCopyToClipboardSupported { get; set; }
9-
8+
private readonly Dictionary<string, MudTabs?> MudTabsDict = new();
9+
private MudAutocomplete<ADComputer>? AutoCompleteSearchBox;
1010
private bool Authenticated { get; set; } = true;
11-
1211
private LdapForNet.LdapCredential? LdapCredential { get; set; }
13-
1412
private List<ADComputer> SelectedComputers { get; set; } = new List<ADComputer>();
15-
16-
private MudTabs? _tabs;
17-
18-
readonly Func<ADComputer, string> _ADComputerToStringConverter = p => (p is null ? string.Empty : p.Name);
19-
2013
protected override async Task OnAfterRenderAsync(bool firstRender)
2114
{
2215
Authenticated = await sessionManager.IsUserLoggedInAsync();
@@ -26,14 +19,9 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
2619
NavigationManager.NavigateTo("/login");
2720
}
2821

29-
if (firstRender)
22+
if (firstRender && Authenticated)
3023
{
31-
if (Authenticated)
32-
{
33-
LdapCredential = await sessionManager.GetLdapCredentialsAsync();
34-
}
35-
36-
IsCopyToClipboardSupported = await clipboard.IsSupportedAsync();
24+
LdapCredential = await sessionManager.GetLdapCredentialsAsync();
3725
}
3826

3927
await InvokeAsync(StateHasChanged);
@@ -43,40 +31,11 @@ private async Task OnSelectedItemChangedAsync(ADComputer value)
4331
{
4432
if (value != null && !string.IsNullOrEmpty(value.Name) && !SelectedComputers.Exists(x => x.Name == value.Name))
4533
{
34+
AutoCompleteSearchBox?.Clear();
35+
MudTabsDict.Add(value.Name, null);
4636
await FetchComputerDetailsAsync(value.Name);
4737
}
4838
}
49-
50-
private async Task CopyLAPSPasswordToClipboardAsync(ADComputer computer)
51-
{
52-
string password = string.Empty;
53-
54-
if (_tabs != null && _tabs.ActivePanel != null)
55-
{
56-
if (_tabs.ActivePanel.ID.ToString() == "v1") // aka LAPS v1
57-
{
58-
password = computer.LAPSInformations!.Single(x => x.IsCurrent && x.Version == Enums.LAPSVersion.v1).Password!;
59-
}
60-
if (_tabs.ActivePanel.ID.ToString() == "v2") // aka LAPS v2
61-
{
62-
password = computer.LAPSInformations!.Single(x => x.IsCurrent && x.Version == Enums.LAPSVersion.v2).Password!;
63-
}
64-
}
65-
66-
if (!string.IsNullOrEmpty(password))
67-
{
68-
await clipboard.WriteTextAsync(password);
69-
Snackbar.Add("Copied password to clipboard!", Severity.Success);
70-
}
71-
else
72-
{
73-
Snackbar.Add("Failed to copy password to clipboard!", Severity.Error);
74-
}
75-
76-
77-
78-
}
79-
8039
private async Task RefreshComputerDetailsAsync(string computerName)
8140
{
8241

@@ -135,10 +94,12 @@ private async Task FetchComputerDetailsAsync(string computerName)
13594
selectedComputer.LAPSInformations = AdComputerObject.LAPSInformations;
13695
selectedComputer.FailedToRetrieveLAPSDetails = AdComputerObject.FailedToRetrieveLAPSDetails;
13796

138-
if (!selectedComputer.FailedToRetrieveLAPSDetails && _tabs != null)
97+
MudTabsDict.TryGetValue(computerName, out MudTabs? _tab);
98+
99+
if (!selectedComputer.FailedToRetrieveLAPSDetails && _tab != null)
139100
{
140101
await InvokeAsync(StateHasChanged);
141-
_tabs.ActivatePanel(_tabs.Panels.First(x => !x.Disabled));
102+
_tab.ActivatePanel(_tab.Panels.First(x => !x.Disabled));
142103
}
143104

144105
}
@@ -164,7 +125,7 @@ private async Task<IEnumerable<ADComputer>> SearchAsync(string value)
164125
return new List<ADComputer>();
165126
}
166127

167-
var tmp = (await LDAPService.SearchADComputersAsync(LdapCredential ?? await sessionManager.GetLdapCredentialsAsync(), value));
128+
var tmp = await LDAPService.SearchADComputersAsync(LdapCredential ?? await sessionManager.GetLdapCredentialsAsync(), value);
168129

169130
if (tmp != null)
170131
{

src/Pages/Login.razor

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,14 @@
1212
<MudTextField Label="Username" Immediate="true" @bind-Value="_loginRequest.Username" For="@(() => _loginRequest.Username)" aria-label="username"/>
1313
<MudTextField Label="Password" Immediate="true" Class="mt-3" @bind-Value="_loginRequest.Password" For="@(() => _loginRequest.Password)" InputType="InputType.Password" aria-label="password"/>
1414
</MudStack>
15-
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Disabled="@_processing" Color="Color.Primary" StartIcon="@Icons.Material.Outlined.Login" aria-label="login">
16-
@if (_processing)
17-
{
18-
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
19-
<MudText Class="ms-2">Logging you in...</MudText>
20-
}
21-
else
22-
{
23-
<MudText>Log in</MudText>
24-
}
25-
</MudButton>
15+
<MudLoadingButton @bind-Loading="_processing" LoadingAdornment="Adornment.Start" ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Outlined.Login" aria-label="login">
16+
<ChildContent>
17+
Login
18+
</ChildContent>
19+
<LoadingContent>
20+
Logging in...
21+
</LoadingContent>
22+
</MudLoadingButton>
2623
</MudStack>
2724
</MudPaper>
2825

src/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
builder.Services.AddBlazoredSessionStorage();
2626
builder.Services.AddClipboard();
2727
builder.Services.AddDataProtection();
28+
builder.Services.AddHealthChecks();
2829

2930
builder.Services.Configure<LdapOptions>(builder.Configuration.GetSection("LDAP"));
3031
builder.Services.Configure<LapsOptions>(builder.Configuration.GetSection("LAPS"));
@@ -50,5 +51,6 @@
5051

5152
app.MapBlazorHub();
5253
app.MapFallbackToPage("/_Host");
54+
app.MapHealthChecks("/healthz");
5355

5456
app.Run();

0 commit comments

Comments
 (0)