From a16f24bf4a8ab2b653ed113e629cd11c6f1f2e3a Mon Sep 17 00:00:00 2001 From: Ben Carpenter Date: Fri, 2 Aug 2024 13:26:10 -0700 Subject: [PATCH] Update blob storage handler (#32) * update blob storage handler * add credential options * add and update packages * fix comment placements --- ...ft.Devices.HardwareDevCenterManager.csproj | 8 ++-- .../Utility/AuthorizationHandler.cs | 48 ++++++++++++++++--- .../AuthorizationHandlerCredentials.cs | 38 +++++++++++---- .../Utility/BlobStorageHandler.cs | 10 ++-- 4 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/Microsoft.Devices.HardwareDevCenterManager/Microsoft.Devices.HardwareDevCenterManager.csproj b/src/Microsoft.Devices.HardwareDevCenterManager/Microsoft.Devices.HardwareDevCenterManager.csproj index ff9b727..96d7fa0 100644 --- a/src/Microsoft.Devices.HardwareDevCenterManager/Microsoft.Devices.HardwareDevCenterManager.csproj +++ b/src/Microsoft.Devices.HardwareDevCenterManager/Microsoft.Devices.HardwareDevCenterManager.csproj @@ -17,10 +17,10 @@ - - - - + + + + diff --git a/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandler.cs b/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandler.cs index bd6c41f..32b3e57 100644 --- a/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandler.cs +++ b/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandler.cs @@ -6,12 +6,14 @@ using Azure.Core; using Azure.Identity; +using Microsoft.Identity.Client; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; @@ -61,6 +63,12 @@ protected override async Task SendAsync(HttpRequestMessage await ObtainAccessToken(); } + // don't attempt to make the call without a token + if (string.IsNullOrWhiteSpace(_accessToken) == true) + { + throw new CredentialUnavailableException("An access token wasn't available. Please check auth settings to ensure one can be created."); + } + while (tries < _maxRetries) { tries++; @@ -127,14 +135,22 @@ protected override async Task SendAsync(HttpRequestMessage private async Task ObtainAccessToken(CancellationToken cancellationToken = default) { - if (!string.IsNullOrEmpty(_authCredentials.ManagedIdentityClientId)) + if (_authCredentials.X509Certificate2 != null) + { + return await GetTokenUsingX509Certificate2Async(_authCredentials.X509Certificate2, cancellationToken); + } + + if (string.IsNullOrEmpty(_authCredentials.ManagedIdentityClientId) == false) { return await GetClientAssertionTokenAsync(cancellationToken); } - else + + if (string.IsNullOrEmpty(_authCredentials.Key) == false) { return await GetTokenUsingClientSecretAsync(cancellationToken); } + + return default; } private async Task GetClientAssertionTokenAsync(CancellationToken cancellationToken) @@ -152,12 +168,12 @@ private async Task GetClientAssertionTokenAsync(CancellationToken c cancellationToken ); - if (!string.IsNullOrEmpty(token.Token)) + if (string.IsNullOrEmpty(token.Token) == false) { _accessToken = token.Token; } - return token; + return default; } /// @@ -177,9 +193,27 @@ private async Task GetTokenUsingManagedIdentityAsync(CancellationToken c return token.Token; } - private async Task GetTokenUsingClientSecretAsync(CancellationToken cancellationToken) + private async Task GetTokenUsingX509Certificate2Async(X509Certificate2 x509Certificate2, CancellationToken cancellationToken) { + var app = ConfidentialClientApplicationBuilder.Create(_authCredentials.ClientId) + .WithCertificate(_authCredentials.X509Certificate2, sendX5C: true) + .WithAuthority(_authCredentials.Authority) + .Build(); + var authResult = await app.AcquireTokenForClient(new[] { "https://manage.devcenter.microsoft.com/.default" }) + .ExecuteAsync(cancellationToken) + .ConfigureAwait(false); + + if (string.IsNullOrEmpty(authResult.AccessToken) == false) + { + _accessToken = authResult.AccessToken; + } + + return default; + } + + private async Task GetTokenUsingClientSecretAsync(CancellationToken cancellationToken) + { var credential = new ClientSecretCredential( _authCredentials.TenantId, _authCredentials.ClientId, @@ -191,12 +225,12 @@ private async Task GetTokenUsingClientSecretAsync(CancellationToken cancellationToken ); - if (!string.IsNullOrEmpty(token.Token)) + if (string.IsNullOrEmpty(token.Token) == false) { _accessToken = token.Token; } - return token; + return default; } // diff --git a/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandlerCredentials.cs b/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandlerCredentials.cs index 3a520a9..709059f 100644 --- a/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandlerCredentials.cs +++ b/src/Microsoft.Devices.HardwareDevCenterManager/Utility/AuthorizationHandlerCredentials.cs @@ -4,30 +4,48 @@ Licensed under the MIT license. See LICENSE file in the project root for full license information. --*/ +using System.Security.Cryptography.X509Certificates; using System.Text.Json.Serialization; namespace Microsoft.Devices.HardwareDevCenterManager.Utility; public class AuthorizationHandlerCredentials { - [JsonPropertyName("key")] - public string Key { get; set; } - - [JsonPropertyName("clientId")] - public string ClientId { get; set; } + // used for Azure + [JsonPropertyName("authority")] + public string Authority { get; set; } [JsonPropertyName("tenantId")] public string TenantId { get; set; } - [JsonPropertyName("url")] - public System.Uri Url { get; set; } - - [JsonPropertyName("urlPrefix")] - public System.Uri UrlPrefix { get; set; } + [JsonPropertyName("clientId")] + public string ClientId { get; set; } + // used for managed identity [JsonPropertyName("managedIdentityClientId")] public string ManagedIdentityClientId { get; set; } [JsonPropertyName("scope")] public string Scope { get; set; } + + // used for certificate + [JsonPropertyName("x509Certificate2")] + public X509Certificate2 X509Certificate2 { get; set; } + + [JsonPropertyName("x509Certificate2Name")] + public string X509Certificate2Name { get; set; } + + [JsonPropertyName("keyVaultUrl")] + public string KeyVaultUrl { get; set; } + + // used for client credentials + [JsonPropertyName("key")] + public string Key { get; set; } + + // used for HDC + [JsonPropertyName("url")] + public System.Uri Url { get; set; } + + [JsonPropertyName("urlPrefix")] + public System.Uri UrlPrefix { get; set; } } diff --git a/src/Microsoft.Devices.HardwareDevCenterManager/Utility/BlobStorageHandler.cs b/src/Microsoft.Devices.HardwareDevCenterManager/Utility/BlobStorageHandler.cs index 5a029ca..c32cedc 100644 --- a/src/Microsoft.Devices.HardwareDevCenterManager/Utility/BlobStorageHandler.cs +++ b/src/Microsoft.Devices.HardwareDevCenterManager/Utility/BlobStorageHandler.cs @@ -20,10 +20,10 @@ public class BlobStorageHandler /// /// Handles upload and download of files for HDC Azure Blob Storage URLs /// - /// URL String to the blob - public BlobStorageHandler(string SASUrl) + /// URL String to the blob + public BlobStorageHandler(string sasUrl) { - _blockBlobClient = new BlockBlobClient(new Uri(SASUrl)); + _blockBlobClient = new BlockBlobClient(new Uri(sasUrl)); } /// @@ -35,8 +35,8 @@ public async Task Upload(string filePath) { try { - using System.IO.FileStream fileStream = new(filePath, FileMode.Open, FileAccess.Read); - await _blockBlobClient.UploadAsync(fileStream, null, default); + using System.IO.FileStream fileStream = File.OpenRead(filePath); + await _blockBlobClient.UploadAsync(fileStream); } catch (RequestFailedException rfe) {