Skip to content

Commit c3a9d24

Browse files
Nirjan Chapagainnchapagain001
authored andcommitted
main changes
1 parent 3ba552b commit c3a9d24

9 files changed

Lines changed: 91 additions & 75 deletions

File tree

src/VirtualClient/VirtualClient.Core.UnitTests/EndpointUtilityTests.cs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -854,37 +854,5 @@ public void CreateKeyVaultStoreReference_ConnectionString_ThrowsOnInvalid()
854854
"InvalidConnectionString",
855855
this.mockFixture.CertificateManager.Object));
856856
}
857-
858-
[Test]
859-
[TestCase("https://anyvault.vault.azure.net/?cid=123456&tid=654321")]
860-
[TestCase("https://anycontentstorage.blob.core.windows.net?cid=123456&tid=654321")]
861-
[TestCase("https://anypackagestorage.blob.core.windows.net?tid=654321")]
862-
[TestCase("https://anynamespace.servicebus.windows.net?cid=123456&tid=654321")]
863-
[TestCase("https://my-keyvault.vault.azure.net/?;tid=654321")]
864-
public void TryParseMicrosoftEntraTenantIdReference_Uri_WorksAsExpected(string input)
865-
{
866-
// Arrange
867-
Uri uri = new Uri(input);
868-
bool result = EndpointUtility.TryParseMicrosoftEntraTenantIdReference(uri, out string actualTenantId);
869-
870-
// Assert
871-
Assert.True(result);
872-
Assert.AreEqual("654321", actualTenantId);
873-
}
874-
875-
[Test]
876-
[TestCase("https://anycontentstorage.blob.core.windows.net?cid=123456&tenantId=654321")]
877-
[TestCase("https://anypackagestorage.blob.core.windows.net?miid=654321")]
878-
[TestCase("https://my-keyvault.vault.azure.net/;cid=654321")]
879-
public void TryParseMicrosoftEntraTenantIdReference_Uri_ReturnFalseWhenInvalid(string input)
880-
{
881-
// Arrange
882-
Uri uri = new Uri(input);
883-
bool result = EndpointUtility.TryParseMicrosoftEntraTenantIdReference(uri, out string actualTenantId);
884-
885-
// Assert
886-
Assert.IsFalse(result);
887-
Assert.IsNull(actualTenantId);
888-
}
889857
}
890858
}

src/VirtualClient/VirtualClient.Core/EndpointUtility.cs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -398,26 +398,6 @@ public static bool TryParseCertificateReference(Uri uri, out string issuer, out
398398
return TryGetCertificateReferenceForUri(queryParameters, out issuer, out subject);
399399
}
400400

401-
/// <summary>
402-
/// Tries to parse the Microsoft Entra reference information from the provided uri. If the uri does not contain the correctly formatted client ID
403-
/// and tenant ID information the method will return false, and keep the two out parameters as null.
404-
/// Ex. https://anystore.blob.core.windows.net?cid={clientId};tid={tenantId}
405-
/// </summary>
406-
/// <param name="uri">The uri to attempt to parse the values from.</param>
407-
/// <param name="tenantId">The tenant ID from the Microsoft Entra reference.</param>
408-
/// <returns>True/False if the method was able to successfully parse both the client ID and the tenant ID from the Microsoft Entra reference.</returns>
409-
public static bool TryParseMicrosoftEntraTenantIdReference(Uri uri, out string tenantId)
410-
{
411-
string queryString = Uri.UnescapeDataString(uri.Query).Trim('?').Replace("&", ",,,");
412-
413-
IDictionary<string, string> queryParameters = TextParsingExtensions.ParseDelimitedValues(queryString)?.ToDictionary(
414-
entry => entry.Key,
415-
entry => entry.Value?.ToString(),
416-
StringComparer.OrdinalIgnoreCase);
417-
418-
return TryGetMicrosoftEntraTenantId(queryParameters, out tenantId);
419-
}
420-
421401
/// <summary>
422402
/// Returns the endpoint by verifying package uri checks.
423403
/// if the endpoint is a package uri without http or https protocols then append the protocol else return the endpoint value.

src/VirtualClient/VirtualClient.Dependencies.UnitTests/KeyVaultAccessTokenTests.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,6 @@ public Task InitializeAsyncInternal(EventContext context, CancellationToken toke
288288

289289
public TokenRequestContext GetTokenRequestContextInternal()
290290
{
291-
if (this.Parameters.ContainsKey(nameof(this.KeyVaultUri)))
292-
{
293-
this.KeyVaultUri = this.Parameters[nameof(this.KeyVaultUri)].ToString();
294-
}
295-
296291
return this.GetTokenRequestContext();
297292
}
298293

src/VirtualClient/VirtualClient.Dependencies/CertificateInstallation.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ public bool WithPrivateKey
8181
}
8282
}
8383

84+
/// <summary>
85+
///
86+
/// </summary>
87+
public string CertificateDownloadDir
88+
{
89+
get
90+
{
91+
return this.Parameters.GetValue<string>(nameof(this.CertificateDownloadDir), string.Empty);
92+
}
93+
}
94+
8495
/// <summary>
8596
/// Gets the access token used to authenticate with Azure services.
8697
/// </summary>
@@ -126,6 +137,26 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel
126137
{
127138
throw new PlatformNotSupportedException($"The '{nameof(CertificateInstallation)}' component is not supported on platform '{this.Platform}'.");
128139
}
140+
141+
// If a download directory is specified, we will also export the certificate to that location.
142+
if (!string.IsNullOrEmpty(this.CertificateDownloadDir))
143+
{
144+
string certificateFileName = this.WithPrivateKey
145+
? $"{this.CertificateName}.pfx"
146+
: $"{this.CertificateName}.cer";
147+
148+
string certificatePath = this.Combine(this.CertificateDownloadDir, certificateFileName);
149+
150+
// Delete existing certificate file
151+
if (!this.fileSystem.File.Exists(certificatePath))
152+
{
153+
this.fileSystem.File.Delete(certificatePath);
154+
}
155+
156+
// Export the new certificate
157+
await this.fileSystem.File.WriteAllBytesAsync(certificatePath, certificate.Export(X509ContentType.Pfx));
158+
Console.WriteLine($"Certificate exported to {certificatePath}");
159+
}
129160
}
130161
catch (Exception exc)
131162
{

src/VirtualClient/VirtualClient.Dependencies/KeyVaultAccessToken.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,24 @@ public KeyVaultAccessToken(IServiceCollection dependencies, IDictionary<string,
4141
/// Gets the Azure Key Vault URI for which the access token will be requested.
4242
/// Example: https://anyvault.vault.azure.net/
4343
/// </summary>
44-
protected string KeyVaultUri { get; set; }
44+
public string KeyVaultUri
45+
{
46+
get
47+
{
48+
return this.Parameters.GetValue<string>(nameof(this.KeyVaultUri));
49+
}
50+
}
4551

4652
/// <summary>
4753
/// Gets the Azure tenant ID used to acquire an access token.
4854
/// </summary>
49-
protected string TenantId { get; set; }
55+
protected string TenantId
56+
{
57+
get
58+
{
59+
return this.Parameters.GetValue<string>(nameof(this.TenantId));
60+
}
61+
}
5062

5163
/// <summary>
5264
/// Gets or sets the full file path where the acquired access token will be written when file logging is enabled.
@@ -84,16 +96,8 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can
8496
/// </summary>
8597
protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken)
8698
{
87-
this.KeyVaultUri = this.Parameters.GetValue<string>(nameof(this.KeyVaultUri));
8899
this.KeyVaultUri.ThrowIfNullOrWhiteSpace(nameof(this.KeyVaultUri));
89-
90-
string tenantId = this.Parameters.GetValue<string>(nameof(this.TenantId));
91-
if (string.IsNullOrWhiteSpace(tenantId))
92-
{
93-
EndpointUtility.TryParseMicrosoftEntraTenantIdReference(new Uri(this.KeyVaultUri), out tenantId);
94-
}
95-
96-
tenantId.ThrowIfNullOrWhiteSpace(nameof(tenantId));
100+
this.TenantId.ThrowIfNullOrWhiteSpace(nameof(this.TenantId));
97101

98102
string accessToken = null;
99103
if (!cancellationToken.IsCancellationRequested)
@@ -108,7 +112,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel
108112
InteractiveBrowserCredential credential = new InteractiveBrowserCredential(
109113
new InteractiveBrowserCredentialOptions
110114
{
111-
TenantId = tenantId
115+
TenantId = this.TenantId
112116
});
113117

114118
accessToken = await this.AcquireInteractiveTokenAsync(credential, requestContext, cancellationToken);
@@ -119,7 +123,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel
119123
// the user with a code and URL to complete authentication from another device.
120124
DeviceCodeCredential credential = new DeviceCodeCredential(new DeviceCodeCredentialOptions
121125
{
122-
TenantId = tenantId,
126+
TenantId = this.TenantId,
123127
DeviceCodeCallback = (codeInfo, token) =>
124128
{
125129
Console.WriteLine(string.Empty);

src/VirtualClient/VirtualClient.Main/BootstrapCommand.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ internal class BootstrapCommand : ExecuteProfileCommand
5353
/// </summary>
5454
public string TenantId { get; set; }
5555

56+
/// <summary>
57+
/// Directory path where downloaded certificates can be stored.
58+
/// </summary>
59+
/// <remarks>Set this property to a valid directory path to ensure that certificates can be
60+
/// downloaded and saved.
61+
/// </remarks>
62+
public string CertificateDownloadDir { get; set; }
63+
5664
/// <summary>
5765
/// Executes the bootstrap command.
5866
/// Supports:
@@ -142,6 +150,7 @@ protected void SetupCertificateInstallation()
142150
// Set certificate-related parameters
143151
this.Parameters["KeyVaultUri"] = this.KeyVault;
144152
this.Parameters["CertificateName"] = this.CertificateName;
153+
this.Parameters["CertificateDownloadDir"] = this.CertificateDownloadDir;
145154
}
146155

147156
protected void SetupPackageInstallation()

src/VirtualClient/VirtualClient.Main/OptionFactory.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,25 @@ public static Option CreateTenantIdOption(bool required = false, object defaultV
655655
return option;
656656
}
657657

658+
/// <summary>
659+
/// Command line option defines the directory to which certificates downloaded from Key Vault can be saved.
660+
/// </summary>
661+
/// <param name="required">Sets this option as required.</param>
662+
/// <param name="defaultValue">Sets the default value when none is provided.</param>
663+
public static Option CreateCertificateDownloadDirectoryOption(bool required = false, object defaultValue = null)
664+
{
665+
Option<string> option = new Option<string>(new string[] { "--certificateDownloadDir", "--certDownloadDir" })
666+
{
667+
Name = "CertificateDownloadDir",
668+
Description = "Set (optional) directory path which certificates downloaded from Key Vault can be saved.",
669+
ArgumentHelpName = "tid",
670+
AllowMultipleArgumentsPerToken = false
671+
};
672+
673+
OptionFactory.SetOptionRequirements(option, required, defaultValue);
674+
return option;
675+
}
676+
658677
/// <summary>
659678
/// Command line option defines the path to the environment layout file.
660679
/// </summary>

src/VirtualClient/VirtualClient.Main/Program.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,13 @@ internal static CommandLineBuilder SetupCommandLine(string[] args, CancellationT
313313
OptionFactory.CreateVerboseFlag(required: false, false),
314314

315315
// --token
316-
OptionFactory.CreateTokenOption(required: false)
316+
OptionFactory.CreateTokenOption(required: false),
317+
318+
// --CertificateDownloadDir
319+
OptionFactory.CreateCertificateDownloadDirectoryOption(required: false),
320+
321+
// --tenant-id
322+
OptionFactory.CreateTenantIdOption(required: false),
317323
};
318324

319325
// Single command execution is also supported. Behind the scenes this uses a
@@ -474,6 +480,9 @@ private static Command CreateBootstrapSubcommand(DefaultSettings settings)
474480

475481
// --token
476482
OptionFactory.CreateTokenOption(required: false),
483+
484+
// --CertificateDownloadDir
485+
OptionFactory.CreateCertificateDownloadDirectoryOption(required: false),
477486

478487
// --tenant-id
479488
OptionFactory.CreateTenantIdOption(required: false),

src/VirtualClient/VirtualClient.Main/profiles/BOOTSTRAP-CERTIFICATE.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"KeyVaultUri": null,
55
"CertificateName": null,
66
"AccessToken": null,
7-
"LogFileName": null
7+
"LogFileName": null,
8+
"CertificateDownloadDir": null
89
},
910
"Actions": [
1011
{
@@ -14,9 +15,9 @@
1415
"KeyVaultUri": "$.Parameters.KeyVaultUri",
1516
"CertificateName": "$.Parameters.CertificateName",
1617
"AccessToken": "$.Parameters.AccessToken",
17-
"AccessTokenPath": "$.Parameters.LogFileName"
18+
"AccessTokenPath": "$.Parameters.LogFileName",
19+
"CertificateDownloadDir": "$.Parameters.CertificateDownloadDir"
1820
}
1921
}
2022
]
21-
}
22-
23+
}

0 commit comments

Comments
 (0)