Skip to content
Draft
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
8 changes: 8 additions & 0 deletions KeyVaultExplorer/Services/VaultService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -357,4 +357,12 @@ public async Task<SecretProperties> UpdateSecret(SecretProperties properties, Ur
var client = new SecretClient(KeyVaultUri, token);
return await client.UpdateSecretPropertiesAsync(properties);
}

public async Task<CertificateProperties> UpdateCertificate(CertificateProperties properties, Uri KeyVaultUri)
{
var token = new CustomTokenCredential(await _authService.GetAzureKeyVaultTokenSilent());
var client = new CertificateClient(KeyVaultUri, token);
var response = await client.UpdateCertificatePropertiesAsync(properties);
return response.Value.Properties;
}
}
23 changes: 23 additions & 0 deletions KeyVaultExplorer/ViewModels/CreateNewSecretVersionViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using KeyVaultExplorer.Services;
using KeyVaultExplorer.Validations;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -64,6 +65,9 @@ public CreateNewSecretVersionViewModel()
ValidateAllProperties();
}

// Delegate to get updated tags from the UI
public Func<IDictionary<string, string>>? GetUpdatedTags { get; set; }

[ObservableProperty]
public bool hasActivationDateChecked;

Expand All @@ -86,6 +90,9 @@ public async Task EditDetails()
else
KeyVaultSecretModel.ExpiresOn = null;

// Update tags from the TagsEditor if available
UpdateTagsFromEditor();

var updatedProps = await _vaultService.UpdateSecret(KeyVaultSecretModel, KeyVaultSecretModel.VaultUri);
KeyVaultSecretModel = updatedProps;
}
Expand All @@ -102,6 +109,9 @@ private async Task NewVersion()

newSecret.Properties.ContentType = KeyVaultSecretModel.ContentType;

// Update tags from the TagsEditor before creating new version
UpdateTagsFromEditor();

foreach (var tag in KeyVaultSecretModel.Tags)
newSecret.Properties.Tags.Add(tag.Key, tag.Value);

Expand Down Expand Up @@ -137,6 +147,19 @@ partial void OnHasExpirationDateCheckedChanged(bool oldValue, bool newValue)
}
}

private void UpdateTagsFromEditor()
{
if (GetUpdatedTags != null)
{
var updatedTags = GetUpdatedTags();
KeyVaultSecretModel.Tags.Clear();
foreach (var tag in updatedTags)
{
KeyVaultSecretModel.Tags[tag.Key] = tag.Value;
}
}
}

//public async Task<ObservableCollection<SubscriptionDataItem>> GetAvailableSubscriptions()
//{
// var subscriptions = new List<SubscriptionDataItem>();
Expand Down
96 changes: 96 additions & 0 deletions KeyVaultExplorer/ViewModels/EditCertificateVersionViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Azure.Security.KeyVault.Certificates;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using KeyVaultExplorer.Services;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace KeyVaultExplorer.ViewModels;

public partial class EditCertificateVersionViewModel : ViewModelBase
{
private readonly AuthService _authService;
private readonly VaultService _vaultService;
private readonly NotificationViewModel _notificationViewModel;

[ObservableProperty]
private TimeSpan? expiresOnTimespan;

[ObservableProperty]
private bool isBusy = false;

[ObservableProperty]
private CertificateProperties keyVaultCertificateModel = new CertificateProperties("");

[ObservableProperty]
private TimeSpan? notBeforeTimespan;

[ObservableProperty]
public bool hasActivationDateChecked;

[ObservableProperty]
public bool hasExpirationDateChecked;

public EditCertificateVersionViewModel()
{
_authService = Defaults.Locator.GetRequiredService<AuthService>();
_vaultService = Defaults.Locator.GetRequiredService<VaultService>();
_notificationViewModel = Defaults.Locator.GetRequiredService<NotificationViewModel>();
}

// Delegate to get updated tags from the UI
public Func<IDictionary<string, string>>? GetUpdatedTags { get; set; }

public string? Identifier => KeyVaultCertificateModel?.Id?.ToString();
public string? Location => KeyVaultCertificateModel?.VaultUri.ToString();

[RelayCommand]
public async Task EditDetails()
{
// For certificates, we can only update tags and enabled status
// Date properties are typically managed by the certificate lifecycle

// Update tags from the TagsEditor if available
UpdateTagsFromEditor();

var updatedCert = await _vaultService.UpdateCertificate(KeyVaultCertificateModel, KeyVaultCertificateModel.VaultUri);
KeyVaultCertificateModel = updatedCert;
}

private void UpdateTagsFromEditor()
{
if (GetUpdatedTags != null)
{
var updatedTags = GetUpdatedTags();
KeyVaultCertificateModel.Tags.Clear();
foreach (var tag in updatedTags)
{
KeyVaultCertificateModel.Tags[tag.Key] = tag.Value;
}
}
}

partial void OnKeyVaultCertificateModelChanging(CertificateProperties value)
{
if (value != null)
{
HasActivationDateChecked = value.NotBefore.HasValue;
HasExpirationDateChecked = value.ExpiresOn.HasValue;
ExpiresOnTimespan = value.ExpiresOn.HasValue ? value.ExpiresOn.Value.LocalDateTime.TimeOfDay : null;
NotBeforeTimespan = value.NotBefore.HasValue ? value.NotBefore.Value.LocalDateTime.TimeOfDay : null;
}
}

partial void OnHasActivationDateCheckedChanged(bool oldValue, bool newValue)
{
// Certificate dates are usually managed automatically
// This might not be applicable for certificates
}

partial void OnHasExpirationDateCheckedChanged(bool oldValue, bool newValue)
{
// Certificate dates are usually managed automatically
// This might not be applicable for certificates
}
}
107 changes: 107 additions & 0 deletions KeyVaultExplorer/ViewModels/EditKeyVersionViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Azure.Security.KeyVault.Keys;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using KeyVaultExplorer.Services;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace KeyVaultExplorer.ViewModels;

public partial class EditKeyVersionViewModel : ViewModelBase
{
private readonly AuthService _authService;
private readonly VaultService _vaultService;
private readonly NotificationViewModel _notificationViewModel;

[ObservableProperty]
private TimeSpan? expiresOnTimespan;

[ObservableProperty]
private bool isBusy = false;

[ObservableProperty]
private KeyProperties keyVaultKeyModel = new KeyProperties("");

[ObservableProperty]
private TimeSpan? notBeforeTimespan;

[ObservableProperty]
public bool hasActivationDateChecked;

[ObservableProperty]
public bool hasExpirationDateChecked;

public EditKeyVersionViewModel()
{
_authService = Defaults.Locator.GetRequiredService<AuthService>();
_vaultService = Defaults.Locator.GetRequiredService<VaultService>();
_notificationViewModel = Defaults.Locator.GetRequiredService<NotificationViewModel>();
}

// Delegate to get updated tags from the UI
public Func<IDictionary<string, string>>? GetUpdatedTags { get; set; }

public string? Identifier => KeyVaultKeyModel?.Id?.ToString();
public string? Location => KeyVaultKeyModel?.VaultUri.ToString();

[RelayCommand]
public async Task EditDetails()
{
if (KeyVaultKeyModel.NotBefore.HasValue && HasActivationDateChecked)
KeyVaultKeyModel.NotBefore = KeyVaultKeyModel.NotBefore.Value.Date + (NotBeforeTimespan ?? TimeSpan.Zero);
else
KeyVaultKeyModel.NotBefore = null;

if (KeyVaultKeyModel.ExpiresOn.HasValue && HasExpirationDateChecked)
KeyVaultKeyModel.ExpiresOn = KeyVaultKeyModel.ExpiresOn.Value.Date + (ExpiresOnTimespan ?? TimeSpan.Zero);
else
KeyVaultKeyModel.ExpiresOn = null;

// Update tags from the TagsEditor if available
UpdateTagsFromEditor();

var updatedKey = await _vaultService.UpdateKey(KeyVaultKeyModel, KeyVaultKeyModel.VaultUri);
KeyVaultKeyModel = updatedKey.Properties;
}

private void UpdateTagsFromEditor()
{
if (GetUpdatedTags != null)
{
var updatedTags = GetUpdatedTags();
KeyVaultKeyModel.Tags.Clear();
foreach (var tag in updatedTags)
{
KeyVaultKeyModel.Tags[tag.Key] = tag.Value;
}
}
}

partial void OnKeyVaultKeyModelChanging(KeyProperties value)
{
if (value != null)
{
HasActivationDateChecked = value.NotBefore.HasValue;
HasExpirationDateChecked = value.ExpiresOn.HasValue;
ExpiresOnTimespan = value.ExpiresOn.HasValue ? value.ExpiresOn.Value.LocalDateTime.TimeOfDay : null;
NotBeforeTimespan = value.NotBefore.HasValue ? value.NotBefore.Value.LocalDateTime.TimeOfDay : null;
}
}

partial void OnHasActivationDateCheckedChanged(bool oldValue, bool newValue)
{
if (newValue is false)
{
KeyVaultKeyModel.NotBefore = null;
}
}

partial void OnHasExpirationDateCheckedChanged(bool oldValue, bool newValue)
{
if (newValue is false)
{
KeyVaultKeyModel.ExpiresOn = null;
}
}
}
66 changes: 66 additions & 0 deletions KeyVaultExplorer/ViewModels/PropertiesPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,72 @@ private async Task EditVersion()
DataContext = viewModel
};
}
else if (IsKey)
{
var currentItem = KeyPropertiesList.OrderByDescending(x => x.CreatedOn).First();
var viewModel = new EditKeyVersionViewModel();

viewModel.KeyVaultKeyModel = currentItem;
dialog.PrimaryButtonClick += async (sender, args) =>
{
var def = args.GetDeferral();
try
{
await viewModel.EditDetailsCommand.ExecuteAsync(null);
_notificationViewModel.ShowPopup(new Avalonia.Controls.Notifications.Notification("Success", "The key properties have been updated."));
}
catch (KeyVaultInsufficientPrivilegesException ex)
{
_notificationViewModel.ShowPopup(new Avalonia.Controls.Notifications.Notification { Message = ex.Message, Title = "Insufficient Privileges" });
}
catch (Exception ex)
{
_notificationViewModel.ShowPopup(new Avalonia.Controls.Notifications.Notification { Message = ex.Message, Title = "Error" });
}
finally
{
def.Complete();
}
};

dialog.Content = new EditKeyVersion()
{
DataContext = viewModel
};
}
else if (IsCertificate)
{
var currentItem = CertificatePropertiesList.OrderByDescending(x => x.CreatedOn).First();
var viewModel = new EditCertificateVersionViewModel();

viewModel.KeyVaultCertificateModel = currentItem;
dialog.PrimaryButtonClick += async (sender, args) =>
{
var def = args.GetDeferral();
try
{
await viewModel.EditDetailsCommand.ExecuteAsync(null);
_notificationViewModel.ShowPopup(new Avalonia.Controls.Notifications.Notification("Success", "The certificate properties have been updated."));
}
catch (KeyVaultInsufficientPrivilegesException ex)
{
_notificationViewModel.ShowPopup(new Avalonia.Controls.Notifications.Notification { Message = ex.Message, Title = "Insufficient Privileges" });
}
catch (Exception ex)
{
_notificationViewModel.ShowPopup(new Avalonia.Controls.Notifications.Notification { Message = ex.Message, Title = "Error" });
}
finally
{
def.Complete();
}
};

dialog.Content = new EditCertificateVersion()
{
DataContext = viewModel
};
}
var result = await dialog.ShowAsync();
}
catch (KeyVaultItemNotFoundException ex)
Expand Down
Loading