Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -457,3 +457,4 @@ $RECYCLE.BIN/
/KeyVaultExplorer.Desktop/mac
/mpdev
.DS_Store
/src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/msbuild.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
#endif
.UseLogging(configure: (context, logBuilder) =>
{
var logLevel = LogLevel.Debug;

Check warning on line 64 in src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Build macOS

The variable 'logLevel' is assigned but its value is never used
// Configure log levels for different categories of logging
logBuilder
.SetMinimumLevel(
Expand All @@ -70,7 +70,7 @@
LogLevel.Error)

// Default filters for core Uno Platform namespaces
.CoreLogLevel(LogLevel.Warning);
.CoreLogLevel(LogLevel.Critical);

#if DEBUG
//Uno Platform namespace filter groups
Expand Down Expand Up @@ -113,7 +113,7 @@
if (!await authService.LoginOrRefreshAsync(cancellationToken))
return null;
credentials ??= new Dictionary<string, string>();
credentials[nameof(AuthenticatedUserClaims.Username)] = authService.AuthenticatedUserClaims.Username;

Check warning on line 116 in src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Build macOS

Dereference of a possibly null reference.
credentials["Expiry"] = authService.Expiry.AddMinutes(-10).ToString("g");
return await ValueTask.FromResult<IDictionary<string, string>?>(credentials);
}
Expand All @@ -135,7 +135,7 @@
if (!forcedRefresh)
{
tokenDictionary ??= new Dictionary<string, string>();
tokenDictionary[nameof(AuthenticatedUserClaims.Username)] = authService.AuthenticatedUserClaims.Username;

Check warning on line 138 in src/uno/AzureKeyVaultStudio/AzureKeyVaultStudio/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Build macOS

Dereference of a possibly null reference.
tokenDictionary["Expiry"] = authService.Expiry.AddMinutes(-10).ToString("g");
return await ValueTask.FromResult<IDictionary<string, string>?>(tokenDictionary);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<AssemblyName>AzureKeyVaultStudio</AssemblyName>
<ApplicationId>io.github.cricketthomas.AzureKeyVaultExplorer</ApplicationId>

<ApplicationDisplayVersion>2.0.5.0</ApplicationDisplayVersion>
<ApplicationDisplayVersion>2.1.0.0</ApplicationDisplayVersion>
<ApplicationVersion>2</ApplicationVersion>

<ApplicationPublisher>cricketthomas</ApplicationPublisher>
Expand Down Expand Up @@ -77,6 +77,7 @@
</PropertyGroup>



<ItemGroup>
<PackageReference Include="Azure.ResourceManager.KeyVault" />
<PackageReference Include="Azure.Security.KeyVault.Certificates" />
Expand Down Expand Up @@ -114,11 +115,4 @@
<PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.DataGrid" />
</ItemGroup>


<ItemGroup>
<Content Update="Assets\Images\kv-gray-3.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Keys;
Expand Down Expand Up @@ -33,6 +34,7 @@ public sealed class KeyVaultItemProperties
public string[] TagValues => Tags is not null ? [.. Tags.Values] : [];
public string[] TagKeys => Tags is not null ? [.. Tags.Keys] : [];
public string TagValuesString => string.Join(", ", Tags?.Values ?? []);
public ObservableCollection<TagItem> EditableTags { get; set; } = new ObservableCollection<TagItem>();

public DateTimeOffset? LastModifiedDate => UpdatedOn.HasValue ? UpdatedOn.Value.ToLocalTime() : CreatedOn?.ToLocalTime();
public string? WhenLastModified => LastModifiedDate.HasValue ? FormatRelativeDate(LastModifiedDate.Value, true) : null;
Expand Down Expand Up @@ -108,15 +110,7 @@ public SecretProperties ToSecretProperties()
properties.Enabled = Enabled;
properties.NotBefore = NotBefore;
properties.ExpiresOn = ExpiresOn;

if (Tags != null && Tags.Count > 0)
{
foreach (var tag in Tags)
{
properties.Tags[tag.Key] = tag.Value;
}
}

ApplyEditableTags(properties.Tags);
return properties;
}

Expand All @@ -126,30 +120,15 @@ public KeyProperties ToKeyProperties()
properties.Enabled = Enabled;
properties.NotBefore = NotBefore;
properties.ExpiresOn = ExpiresOn;

if (Tags != null && Tags.Count > 0)
{
foreach (var tag in Tags)
{
properties.Tags[tag.Key] = tag.Value;
}
}

ApplyEditableTags(properties.Tags);
return properties;
}

public CertificateProperties ToCertificateProperties()
{
var properties = new CertificateProperties(Id);
properties.Enabled = Enabled;
if (Tags != null && Tags.Count > 0)
{
foreach (var tag in Tags)
{
properties.Tags[tag.Key] = tag.Value;
}
}

ApplyEditableTags(properties.Tags);
return properties;
}

Expand Down Expand Up @@ -188,7 +167,8 @@ private static KeyVaultItemProperties Create(
RecoveryLevel = recoveryLevel,
Managed = managed,
Tags = tags is null ? new Dictionary<string, string>() : new Dictionary<string, string>(tags),
Type = type
Type = type,
EditableTags = tags is null ? []: new ObservableCollection<TagItem>(tags.Select(t => new TagItem { Key = t.Key, Value = t.Value }))
};
}

Expand Down Expand Up @@ -225,6 +205,32 @@ private static KeyVaultItemProperties Create(
( < 366, _) => $"{(isPast ? string.Empty : "in ")}{months} {(months == 1 ? "month" : "months")}{(isPast ? " ago" : string.Empty)}",
(_, _) => $"{(isPast ? string.Empty : "in ")}{years} {(years == 1 ? "year" : "years")}{(isPast ? " ago" : string.Empty)}"
};



}

public void ApplyEditableTags(IDictionary<string, string> targetTags)
{
targetTags.Clear();

if (EditableTags is null || EditableTags.Count == 0)
return;

var seenKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

foreach (var tag in EditableTags)
{
var key = tag.Key?.Trim();

if (string.IsNullOrWhiteSpace(key))
continue;

if (!seenKeys.Add(key))
throw new InvalidOperationException("Duplicate tag keys are not allowed.");

targetTags[key] = tag.Value;
Comment thread
cricketthomas marked this conversation as resolved.
}
}
}

Expand All @@ -235,3 +241,12 @@ public enum KeyVaultItemType
Key = 2,
All = 3
}

public partial class TagItem : ObservableObject
{
[ObservableProperty]
public partial string Key { get; set; } = string.Empty;

[ObservableProperty]
public partial string Value { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
xmlns:uap18="http://schemas.microsoft.com/appx/manifest/uap/windows10/18"
IgnorableNamespaces="uap rescap uap18">

<Identity Version="2.0.5.0" Publisher="CN=FE9032D7-9FA7-4DED-9087-469BC45B4D99" Name="ArthurThomasIV.AzureKeyVaultExplorer-forAzure"/>
<Identity Version="2.1.0.0" Publisher="CN=FE9032D7-9FA7-4DED-9087-469BC45B4D99" Name="ArthurThomasIV.AzureKeyVaultExplorer-forAzure"/>
<Properties>
<DisplayName>Key Vault Explorer</DisplayName>
<PublisherDisplayName>Arthur Thomas IV</PublisherDisplayName>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@ namespace AzureKeyVaultStudio.Presentation;

public partial class MainViewModel : ObservableObject
{
private const double PaneMaxWidth = 800;
private const double PaneMinWidth = 120;
private readonly IAuthenticationService _authentication;
private readonly AuthService _authService;
private readonly IDispatcher _dispatcher;
private readonly KeyVaultTreeViewModel _keyVaultTreeViewModel;
private readonly ILocalSettingsService _localSettings;
private readonly INavigator _navigator;
private readonly IDispatcher _dispatcher;
private readonly VaultService _vaultService;
private readonly AuthService _authService;
private readonly IServiceProvider _serviceProvider;

private readonly KeyVaultTreeViewModel _keyVaultTreeViewModel;
public KeyVaultTreeViewModel KeyVaultTreeViewModel => _keyVaultTreeViewModel;


private readonly VaultService _vaultService;
public MainViewModel(
IDispatcher dispatcher,
IStringLocalizer localizer,
Expand All @@ -37,7 +35,7 @@ public MainViewModel(
_dispatcher = dispatcher;
_vaultService = vaultService;
_serviceProvider = serviceProvider;
keyVaultTreeViewModel.SetDispatcher(dispatcher); // HACK
keyVaultTreeViewModel.SetDispatcher(dispatcher); // HACK
_keyVaultTreeViewModel = keyVaultTreeViewModel;
_authService = authService;

Expand All @@ -56,30 +54,33 @@ public MainViewModel(
});
}

public string? Title { get; }

public string FontIconType => PanePlacement == SplitViewPanePlacement.Left ? "\uE8E4" : "\uE8E2";
public ICommand GoToSecond { get; }

public ICommand Logout { get; }

//private async Task GoToSecondView()
//{
// await _navigator.NavigateViewModelAsync<SecondViewModel>(this, data: new Entity(Name!));
//}

public async Task DoLogout(CancellationToken token)
public double InvertedSplitViewWidth
{
await _authentication.LogoutAsync(token);
}
get => PaneMaxWidth - (SplitViewWidth - PaneMinWidth);
set
{
var clamped = Math.Clamp(value, PaneMinWidth, PaneMaxWidth);
var newActual = PaneMaxWidth - (clamped - PaneMinWidth);

[ObservableProperty]
public partial SplitViewDisplayMode SplitViewDisplay { get; set; } = SplitViewDisplayMode.Inline;
if (SplitViewWidth != newActual)
{
SplitViewWidth = newActual;
}
}
}

public bool IsPaneLeft => PanePlacement == SplitViewPanePlacement.Left;
[ObservableProperty]
public partial bool IsPaneOpen { get; set; } = true;

public KeyVaultTreeViewModel KeyVaultTreeViewModel => _keyVaultTreeViewModel;
public ICommand Logout { get; }
[ObservableProperty]
public partial double SplitViewWidth { get; set; } = 220;
[NotifyPropertyChangedFor(nameof(IsPaneLeft))]
[NotifyPropertyChangedFor(nameof(FontIconType))]
public partial SplitViewPanePlacement PanePlacement { get; set; } = SplitViewPanePlacement.Right;

[ObservableProperty]
public partial string SearchQuery { get; set; } = string.Empty;
Expand All @@ -88,43 +89,24 @@ public async Task DoLogout(CancellationToken token)
public partial object? SelectedKeyVaultItem { get; set; }

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsPaneLeft))]
[NotifyPropertyChangedFor(nameof(FontIconType))]
public partial SplitViewPanePlacement PanePlacement { get; set; } = SplitViewPanePlacement.Right;

public bool IsPaneLeft => PanePlacement == SplitViewPanePlacement.Left;

public string FontIconType => PanePlacement == SplitViewPanePlacement.Left ? "\uE8E4" : "\uE8E2";
//public FontIcon FontIconType => PanePlacement == SplitViewPanePlacement.Left ? new FontIcon() { Glyph = "&#xE8E4;" } : new FontIcon() { Glyph = "&#xE8E2;" };

private void LoadSplitViewSettings()
{
SplitViewDisplay = GetSettingsValue(nameof(SplitViewDisplay), SplitViewDisplayMode.Inline);
SplitViewWidth = _localSettings.GetValue(nameof(SplitViewWidth), 300d);
IsPaneOpen = _localSettings.GetValue(nameof(IsPaneOpen), true);
PanePlacement = GetSettingsValue(nameof(PanePlacement), SplitViewPanePlacement.Right);
}

public partial SplitViewDisplayMode SplitViewDisplay { get; set; } = SplitViewDisplayMode.Inline;

[ObservableProperty]
public partial double SplitViewWidth { get; set; } = 220;

public string? Title { get; }
[RelayCommand]
public void ToggleSplitViewDisplay()
public void ClosePane()
{
SplitViewDisplay = SplitViewDisplay == SplitViewDisplayMode.Overlay ? SplitViewDisplayMode.Inline : SplitViewDisplayMode.Overlay;
IsPaneOpen = false;
WeakReferenceMessenger.Default.Send(new PaneStateChangedMessage(false));
}

[RelayCommand]
public void TogglePaneLocation()
public async Task DoLogout(CancellationToken token)
{
PanePlacement = PanePlacement == SplitViewPanePlacement.Right ? SplitViewPanePlacement.Left : SplitViewPanePlacement.Right;
await _authentication.LogoutAsync(token);
}



private void Save<T>(string key, T value)
{
_localSettings.SetValue(key, value);
}
[RelayCommand]
public void SaveAllSettings()
{
Expand All @@ -134,35 +116,33 @@ public void SaveAllSettings()
Save(nameof(IsPaneOpen), IsPaneOpen);
}


[RelayCommand]
public void ClosePane()
public void TogglePaneLocation()
{
IsPaneOpen = false;
WeakReferenceMessenger.Default.Send(new PaneStateChangedMessage(false));
PanePlacement = PanePlacement == SplitViewPanePlacement.Right ? SplitViewPanePlacement.Left : SplitViewPanePlacement.Right;
}

private const double PaneMinWidth = 100;
private const double PaneMaxWidth = 1000;

public double InvertedSplitViewWidth
[RelayCommand]
public void ToggleSplitViewDisplay()
{
get => PaneMaxWidth - (SplitViewWidth - PaneMinWidth);
set
{
var clamped = Math.Clamp(value, PaneMinWidth, PaneMaxWidth);
var newActual = PaneMaxWidth - (clamped - PaneMinWidth);

if (SplitViewWidth != newActual)
{
SplitViewWidth = newActual;
OnPropertyChanged(nameof(SplitViewWidth));
}
}
SplitViewDisplay = SplitViewDisplay == SplitViewDisplayMode.Overlay ? SplitViewDisplayMode.Inline : SplitViewDisplayMode.Overlay;
}

private T GetSettingsValue<T>(string key, T defaultValue) where T : struct, Enum
{
var stringValue = _localSettings.GetValue(key, defaultValue.ToString());
return Enum.TryParse<T>(stringValue, out var result) ? result : defaultValue;
}

private void LoadSplitViewSettings()
{
SplitViewDisplay = GetSettingsValue(nameof(SplitViewDisplay), SplitViewDisplayMode.Inline);
SplitViewWidth = _localSettings.GetValue(nameof(SplitViewWidth), 300d);
IsPaneOpen = _localSettings.GetValue(nameof(IsPaneOpen), true);
PanePlacement = GetSettingsValue(nameof(PanePlacement), SplitViewPanePlacement.Right);
}
private void Save<T>(string key, T value)
{
_localSettings.SetValue(key, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,11 @@
Height="20"
MaxWidth="80"
Padding="5,2"
Background="{ThemeResource SystemAccentColor}"
Background="{ThemeResource AccentAcrylicBackgroundFillColorDefaultBrush}"
CornerRadius="2">
<TextBlock
VerticalAlignment="Center"
FontSize="11"
Foreground="White"
IsTextSelectionEnabled="False"
Text="{x:Bind Mode=OneTime}"
TextTrimming="CharacterEllipsis" />
Expand Down
Loading
Loading