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
6 changes: 3 additions & 3 deletions KeyVaultExplorer/Models/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class AppSettings
public string PanePlacement { get; set; } = "Left";
public bool SettingsPageClientIdCheckbox { get; set; } = false;
public string CustomClientId { get; set; } = string.Empty;



[AllowedValues("Public", "USGovernment")]
public string AzureCloud { get; set; } = "Public";
}
49 changes: 49 additions & 0 deletions KeyVaultExplorer/Models/CloudEnvironment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
namespace KeyVaultExplorer.Models;

public enum AzureCloud
{
Public,
USGovernment
}

public class CloudEnvironment
{
public AzureCloud Cloud { get; set; }
public string Name { get; set; }
public string Authority { get; set; }
public string VaultScope { get; set; }
public string ManagementScope { get; set; }
public string PortalUrl { get; set; }

public static readonly CloudEnvironment Public = new()
{
Cloud = AzureCloud.Public,
Name = "Azure Public Cloud",
Authority = "https://login.microsoftonline.com",
VaultScope = "https://vault.azure.net/.default",
ManagementScope = "https://management.core.windows.net/.default",
PortalUrl = "https://portal.azure.com"
};

public static readonly CloudEnvironment USGovernment = new()
{
Cloud = AzureCloud.USGovernment,
Name = "Azure US Government",
Authority = "https://login.microsoftonline.us",
VaultScope = "https://vault.usgovcloudapi.net/.default",
ManagementScope = "https://management.usgovcloudapi.net/.default",
PortalUrl = "https://portal.azure.us"
};

public static CloudEnvironment GetCloudEnvironment(AzureCloud cloud)
{
return cloud switch
{
AzureCloud.Public => Public,
AzureCloud.USGovernment => USGovernment,
_ => Public
};
}

public static CloudEnvironment[] AllEnvironments => [Public, USGovernment];
}
17 changes: 17 additions & 0 deletions KeyVaultExplorer/Models/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,28 @@ public static class Constants
//Leaving the scope to its default values.
public static readonly string[] Scopes = ["openid", "offline_access", "profile", "email",];

// Legacy constants for backwards compatibility - use CloudEnvironment instead
public static readonly string[] AzureRMScope = ["https://management.core.windows.net//.default"];

public static readonly string[] KvScope = ["https://vault.azure.net/.default"];

public static readonly string[] AzureScopes = ["https://management.core.windows.net//.default", "https://vault.azure.net//.default", "user_impersonation"];

// Helper methods to get cloud-specific scopes
public static string[] GetKeyVaultScope(CloudEnvironment cloudEnvironment)
{
return [cloudEnvironment.VaultScope];
}

public static string[] GetAzureResourceManagerScope(CloudEnvironment cloudEnvironment)
{
return [cloudEnvironment.ManagementScope];
}

public static string[] GetAzureScopes(CloudEnvironment cloudEnvironment)
{
return [cloudEnvironment.ManagementScope, cloudEnvironment.VaultScope, "user_impersonation"];
}

// Cache settings
public const string CacheFileName = "keyvaultexplorer_msal_cache.txt";
Expand Down
24 changes: 22 additions & 2 deletions KeyVaultExplorer/Services/AuthService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class AuthService


public IAccount Account { get; private set; }

// Property to get the current cloud's portal URL
public string PortalUrl => GetCurrentCloudEnvironment().PortalUrl;

public AuthService()
{
Expand All @@ -33,13 +36,28 @@ public AuthService()
var customClientId = (string?)settings.AppSettings.CustomClientId ?? Constants.ClientId;
var settingsPageClientIdCheckbox = (bool?)settings.AppSettings.SettingsPageClientIdCheckbox ?? false;
string clientId = settingsPageClientIdCheckbox && !string.IsNullOrEmpty(customClientId) ? customClientId : Constants.ClientId;

// Get the cloud environment from settings
var cloudSetting = settings.AppSettings.AzureCloud ?? "Public";
var azureCloud = Enum.TryParse<AzureCloud>(cloudSetting, out var parsed) ? parsed : AzureCloud.Public;
var cloudEnvironment = CloudEnvironment.GetCloudEnvironment(azureCloud);

authenticationClient = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(cloudEnvironment.Authority)
.WithRedirectUri($"msal{clientId}://auth")
.WithRedirectUri("http://localhost")
.WithIosKeychainSecurityGroup("us.sidesteplabs.keyvaultexplorer")
.Build();
}

// Helper method to get current cloud environment
private CloudEnvironment GetCurrentCloudEnvironment()
{
var settings = Defaults.Locator.GetRequiredService<AppSettingReader>();
var cloudSetting = settings.AppSettings.AzureCloud ?? "Public";
var azureCloud = Enum.TryParse<AzureCloud>(cloudSetting, out var parsed) ? parsed : AzureCloud.Public;
return CloudEnvironment.GetCloudEnvironment(azureCloud);
}

// Propagates notification that the operation should be cancelled.
public async Task<AuthenticationResult> LoginAsync(CancellationToken cancellationToken)
Expand Down Expand Up @@ -166,13 +184,15 @@ public async Task<AuthenticationResult> GetAzureArmTokenSilent()
accounts = await authenticationClient.GetAccountsAsync();
Account = accounts.First();
}
return await authenticationClient.AcquireTokenSilent(Constants.AzureRMScope, accounts.First()).ExecuteAsync();
var cloudEnvironment = GetCurrentCloudEnvironment();
return await authenticationClient.AcquireTokenSilent(Constants.GetAzureResourceManagerScope(cloudEnvironment), accounts.First()).ExecuteAsync();
}

public async Task<AuthenticationResult> GetAzureKeyVaultTokenSilent()
{
await AttachTokenCache();
var accounts = await authenticationClient.GetAccountsAsync();
return await authenticationClient.AcquireTokenSilent(Constants.KvScope, accounts.First()).ExecuteAsync();
var cloudEnvironment = GetCurrentCloudEnvironment();
return await authenticationClient.AcquireTokenSilent(Constants.GetKeyVaultScope(cloudEnvironment), accounts.First()).ExecuteAsync();
}
}
2 changes: 1 addition & 1 deletion KeyVaultExplorer/ViewModels/PropertiesPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ private async Task NewVersion()
private void OpenInAzure()
{
if (OpenedItem is null) return;
var uri = $"https://portal.azure.com/#@{_authService.TenantName}/asset/Microsoft_Azure_KeyVault/{OpenedItem.Type}/{OpenedItem.Id}";
var uri = $"{_authService.PortalUrl}/#@{_authService.TenantName}/asset/Microsoft_Azure_KeyVault/{OpenedItem.Type}/{OpenedItem.Id}";
Process.Start(new ProcessStartInfo(uri) { UseShellExecute = true, Verb = "open" });
}

Expand Down
17 changes: 17 additions & 0 deletions KeyVaultExplorer/ViewModels/SettingsPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public partial class SettingsPageViewModel : ViewModelBase

[ObservableProperty]
private string customClientId;

[ObservableProperty]
private string[] azureClouds = ["Public", "USGovernment"];

[ObservableProperty]
private string selectedAzureCloud;

public SettingsPageViewModel()
{
Expand All @@ -66,6 +72,7 @@ public SettingsPageViewModel()
CurrentAppTheme = jsonSettings.AppTheme ?? "System";
CustomClientId = jsonSettings.CustomClientId;
SettingsPageClientIdCheckbox = jsonSettings.SettingsPageClientIdCheckbox;
SelectedAzureCloud = jsonSettings.AzureCloud ?? "Public";
//NavigationLayoutMode = s.NavigationLayoutMode;
}, DispatcherPriority.MaxValue);
}
Expand Down Expand Up @@ -145,6 +152,16 @@ partial void OnClearClipboardTimeoutChanging(int oldValue, int newValue)
}, DispatcherPriority.Background);
}

partial void OnSelectedAzureCloudChanged(string value)
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await Task.Delay(50);
await AddOrUpdateAppSettings(nameof(AppSettings.AzureCloud), SelectedAzureCloud);
},
DispatcherPriority.Background);
}

[RelayCommand]
private async Task SetSplitViewDisplayMode(string splitViewDisplayMode)
{
Expand Down
21 changes: 21 additions & 0 deletions KeyVaultExplorer/Views/Pages/SettingsPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@
</ComboBox>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander
Description="Select which Azure cloud environment to connect to"
Header="Azure Cloud Environment"
IconSource="Cloud">

<ui:SettingsExpander.Footer>
<ComboBox
MinWidth="150"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
ItemsSource="{Binding AzureClouds}"
SelectedItem="{Binding SelectedAzureCloud, Mode=TwoWay}">
<ComboBox.Styles>
<Style Selector="ComboBox /template/ ContentPresenter#ContentPresenter">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ComboBox.Styles>

</ComboBox>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</StackPanel>

<StackPanel
Expand Down