Skip to content

Commit 40d69b4

Browse files
committed
Merge branch 'main' into dev-feature-sdkmigration
2 parents dec8b76 + 11a6d8b commit 40d69b4

34 files changed

Lines changed: 643 additions & 176 deletions

File tree

Directory.Packages.props

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
<PackageVersion Include="Autofac.Owin" Version="4.2.0" />
1313
<PackageVersion Include="Autofac.WebApi2" Version="4.1.0" />
1414
<PackageVersion Include="Autofac" Version="4.9.1" />
15-
<PackageVersion Include="Azure.Core" Version="1.40.0" />
15+
<PackageVersion Include="Azure.Core" Version="1.44.1" />
1616
<PackageVersion Include="Azure.Data.Tables" Version="12.8.3" />
17-
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
17+
<PackageVersion Include="Azure.Identity" Version="1.12.1" />
1818
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.16.1" />
1919
<PackageVersion Include="Azure.Search.Documents" Version="11.4.0-beta.6" />
2020
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.4.0" />
21-
<PackageVersion Include="Azure.Storage.Blobs" Version="12.20.0" />
21+
<PackageVersion Include="Azure.Storage.Blobs" Version="12.22.2" />
2222
<PackageVersion Include="Azure.Storage.Queues" Version="12.18.0" />
2323
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
2424
<PackageVersion Include="CsvHelper" Version="7.1.1" />
@@ -40,18 +40,18 @@
4040
<PackageVersion Include="Microsoft.ApplicationInsights" Version="2.21.0" />
4141
<PackageVersion Include="Microsoft.AspNet.DynamicData.EFProvider" Version="6.0.0" />
4242
<PackageVersion Include="Microsoft.AspNet.Identity.Core" Version="1.0.0" />
43-
<PackageVersion Include="Microsoft.AspNet.Mvc" Version="5.2.3" />
43+
<PackageVersion Include="Microsoft.AspNet.Mvc" Version="5.2.9" />
4444
<PackageVersion Include="Microsoft.AspNet.Razor" Version="3.2.9" />
4545
<PackageVersion Include="Microsoft.AspNet.Web.Optimization" Version="1.1.3" />
46-
<PackageVersion Include="Microsoft.AspNet.WebApi.Client" Version="5.2.3" />
47-
<PackageVersion Include="Microsoft.AspNet.WebApi.Core" Version="5.2.3" />
46+
<PackageVersion Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
47+
<PackageVersion Include="Microsoft.AspNet.WebApi.Core" Version="5.2.9" />
4848
<PackageVersion Include="Microsoft.AspNet.WebApi.MessageHandlers.Compression.StrongName" Version="1.3.0" />
4949
<PackageVersion Include="Microsoft.AspNet.WebApi.OData" Version="5.7.0" />
50-
<PackageVersion Include="Microsoft.AspNet.WebApi.WebHost" Version="5.2.3" />
51-
<PackageVersion Include="Microsoft.AspNet.WebHelpers" Version="3.2.3" />
52-
<PackageVersion Include="Microsoft.AspNet.WebPages.Data" Version="3.2.3" />
53-
<PackageVersion Include="Microsoft.AspNet.WebPages.WebData" Version="3.2.3" />
54-
<PackageVersion Include="Microsoft.AspNet.WebPages" Version="3.2.3" />
50+
<PackageVersion Include="Microsoft.AspNet.WebApi.WebHost" Version="5.2.9" />
51+
<PackageVersion Include="Microsoft.AspNet.WebHelpers" Version="3.2.9" />
52+
<PackageVersion Include="Microsoft.AspNet.WebPages.Data" Version="3.2.9" />
53+
<PackageVersion Include="Microsoft.AspNet.WebPages.WebData" Version="3.2.9" />
54+
<PackageVersion Include="Microsoft.AspNet.WebPages" Version="3.2.9" />
5555
<PackageVersion Include="Microsoft.AspNetCore.Cryptography.Internal" Version="1.0.0" />
5656
<PackageVersion Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.0.0" />
5757
<PackageVersion Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
@@ -77,7 +77,7 @@
7777
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
7878
<PackageVersion Include="Microsoft.Extensions.Options" Version="2.2.0" />
7979
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="2.2.0" />
80-
<PackageVersion Include="Microsoft.Identity.Client" Version="4.61.3" />
80+
<PackageVersion Include="Microsoft.Identity.Client" Version="4.65.0" />
8181
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.3.1" />
8282
<PackageVersion Include="Microsoft.Net.Http" Version="2.2.29" />
8383
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
@@ -133,13 +133,14 @@
133133
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
134134
<PackageVersion Include="System.Diagnostics.Debug" Version="4.3.0" />
135135
<PackageVersion Include="System.Drawing.Common" Version="4.7.2" />
136-
<PackageVersion Include="System.Formats.Asn1" Version="6.0.1" />
136+
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
137+
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="5.7.0" />
137138
<PackageVersion Include="System.Linq.Expressions" Version="4.3.0" />
138139
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
139140
<PackageVersion Include="System.Reflection.Metadata" Version="1.7.0-preview1-26717-04" />
140141
<PackageVersion Include="System.Runtime" Version="4.3.1" />
141142
<PackageVersion Include="System.Text.Encodings.Web" Version="8.0.0" />
142-
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
143+
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
143144
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
144145
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
145146
<PackageVersion Include="Test.Utility" Version="6.4.2-rc.25" />
@@ -151,4 +152,4 @@
151152
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
152153
<PackageVersion Include="xunit" Version="2.9.0" />
153154
</ItemGroup>
154-
</Project>
155+
</Project>

src/Catalog/Persistence/AzureStorage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ protected override async Task OnDeleteAsync(Uri resourceUri, DeleteRequestOption
421421
string blobName = GetName(resourceUri);
422422
BlobRequestConditions accessCondition = (deleteRequestOptions as DeleteRequestOptionsWithAccessCondition)?.BlobRequestConditions;
423423
BlockBlobClient blobClient = GetBlockBlobReference(blobName);
424-
await blobClient.DeleteAsync(DeleteSnapshotsOption.IncludeSnapshots, accessCondition, cancellationToken);
424+
await blobClient.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots, accessCondition, cancellationToken);
425425
}
426426

427427
public override Uri GetUri(string name)

src/Catalog/Persistence/Storage.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
using System.Net;
99
using System.Threading;
1010
using System.Threading.Tasks;
11+
using Azure;
1112
using Newtonsoft.Json;
12-
using NuGetGallery;
1313

1414
namespace NuGet.Services.Metadata.Catalog.Persistence
1515
{
@@ -125,21 +125,9 @@ public async Task DeleteAsync(Uri resourceUri, CancellationToken cancellationTok
125125
{
126126
await OnDeleteAsync(resourceUri, deleteRequestOptions, cancellationToken);
127127
}
128-
catch (CloudBlobStorageException e)
128+
catch (RequestFailedException e) when (e.Status == (int)HttpStatusCode.NotFound)
129129
{
130-
WebException webException = e.InnerException as WebException;
131-
if (webException != null)
132-
{
133-
HttpStatusCode statusCode = ((HttpWebResponse)webException.Response).StatusCode;
134-
if (statusCode != HttpStatusCode.NotFound)
135-
{
136-
throw;
137-
}
138-
}
139-
else
140-
{
141-
throw;
142-
}
130+
// continue
143131
}
144132
catch (Exception e)
145133
{

src/NuGet.Protocol.Catalog/NuGet.Protocol.Catalog.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageReference Include="Newtonsoft.Json" />
1212
<PackageReference Include="NuGet.Protocol" />
1313
<PackageReference Include="System.Formats.Asn1" />
14+
<PackageReference Include="System.Text.Json" />
1415
</ItemGroup>
1516

1617
</Project>

src/NuGet.Services.Configuration/ConfigurationUtility.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
55
using System.ComponentModel;
6+
using Microsoft.Extensions.Configuration;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Options;
10+
using NuGet.Services.KeyVault;
611

712
namespace NuGet.Services.Configuration
813
{
@@ -25,5 +30,40 @@ public static T ConvertFromString<T>(string value)
2530

2631
throw new NotSupportedException("No converter exists from string to " + typeof(T).Name + "!");
2732
}
33+
34+
/// <summary>
35+
/// Injects secret into a string trying to use cached value first. If the value is absent
36+
/// in cache, falls back to actually querying underlying secret store.
37+
/// </summary>
38+
/// <param name="value">String to inject secret into.</param>
39+
/// <param name="secretInjector">Caching secret injector to use.</param>
40+
/// <param name="logger">Logger.</param>
41+
/// <returns>String with secrets injected.</returns>
42+
public static string InjectCachedSecret(string value, ICachingSecretInjector secretInjector, ILogger logger)
43+
{
44+
if (secretInjector.TryInjectCached(value, logger, out var injectedValue))
45+
{
46+
return injectedValue;
47+
}
48+
return secretInjector.Inject(value, logger);
49+
}
50+
51+
public static IServiceCollection ConfigureInjected<T>(this IServiceCollection services, string sectionPrefix)
52+
where T : class
53+
=> services.AddSingleton(sp => GetInjectedOptions<T>(sp, sectionPrefix));
54+
55+
private static IConfigureOptions<T> GetInjectedOptions<T>(IServiceProvider sp, string sectionPrefix)
56+
where T : class
57+
{
58+
return new ConfigureNamedOptions<T, IConfiguration>(
59+
Options.DefaultName,
60+
sp.GetRequiredService<IConfiguration>(),
61+
(settings, configuration) =>
62+
new SecretInjectedConfiguration(
63+
configuration.GetSection(sectionPrefix),
64+
sp.GetRequiredService<ICachingSecretInjector>(),
65+
sp.GetRequiredService<ILogger<SecretInjectedConfiguration>>())
66+
.Bind(settings));
67+
}
2868
}
2969
}

src/NuGet.Services.Configuration/NuGet.Services.Configuration.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
<ItemGroup>
1010
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
11+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
1112
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
1213
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" />
1314
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.Extensions.Configuration;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Primitives;
10+
using NuGet.Services.KeyVault;
11+
12+
namespace NuGet.Services.Configuration
13+
{
14+
public class SecretInjectedConfiguration : IConfiguration
15+
{
16+
protected readonly IConfiguration _baseConfiguration;
17+
protected readonly ICachingSecretInjector _secretInjector;
18+
protected readonly ILogger _logger;
19+
20+
public SecretInjectedConfiguration(
21+
IConfiguration baseConfiguration,
22+
ICachingSecretInjector secretInjector,
23+
ILogger logger)
24+
{
25+
_baseConfiguration = baseConfiguration ?? throw new ArgumentNullException(nameof(baseConfiguration));
26+
_secretInjector = secretInjector ?? throw new ArgumentNullException(nameof(secretInjector));
27+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
28+
}
29+
30+
public string this[string key]
31+
{
32+
get => ConfigurationUtility.InjectCachedSecret(_baseConfiguration[key], _secretInjector, _logger);
33+
set => _baseConfiguration[key] = value;
34+
}
35+
36+
public IEnumerable<IConfigurationSection> GetChildren() =>
37+
_baseConfiguration.GetChildren().Select(originalSection => new SecretInjectedConfigurationSection(originalSection, _secretInjector, _logger));
38+
39+
40+
public IChangeToken GetReloadToken() =>
41+
_baseConfiguration.GetReloadToken();
42+
43+
public IConfigurationSection GetSection(string key) =>
44+
new SecretInjectedConfigurationSection(_baseConfiguration.GetSection(key), _secretInjector, _logger);
45+
}
46+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Microsoft.Extensions.Configuration;
10+
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Primitives;
12+
using NuGet.Services.KeyVault;
13+
14+
namespace NuGet.Services.Configuration
15+
{
16+
public class SecretInjectedConfigurationSection : SecretInjectedConfiguration, IConfigurationSection
17+
{
18+
public SecretInjectedConfigurationSection(
19+
IConfigurationSection baseSection,
20+
ICachingSecretInjector secretInjector,
21+
ILogger logger)
22+
: base(baseSection, secretInjector, logger)
23+
{
24+
}
25+
26+
private IConfigurationSection BaseSection => (IConfigurationSection)_baseConfiguration;
27+
28+
public string Key => BaseSection.Key;
29+
30+
public string Path => BaseSection.Path;
31+
32+
public string Value
33+
{
34+
get => ConfigurationUtility.InjectCachedSecret(BaseSection.Value, _secretInjector, _logger);
35+
set => BaseSection.Value = value;
36+
}
37+
}
38+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Data;
6+
using System.Data.SqlClient;
7+
8+
namespace NuGet.Services.Entities
9+
{
10+
public static class ExceptionExtensions
11+
{
12+
public static bool IsSqlUniqueConstraintViolation(this DataException exception)
13+
{
14+
Exception current = exception;
15+
while (current is not null)
16+
{
17+
if (current is SqlException sqlException)
18+
{
19+
switch (sqlException.Number)
20+
{
21+
case 547: // Constraint check violation: https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors-0-to-999
22+
case 2601: // Duplicated key row error: https://learn.microsoft.com/en-us/sql/relational-databases/replication/mssql-eng002601
23+
case 2627: // Unique constraint error: https://learn.microsoft.com/en-us/sql/relational-databases/replication/mssql-eng002627
24+
return true;
25+
}
26+
}
27+
28+
current = current.InnerException;
29+
}
30+
31+
return false;
32+
}
33+
}
34+
}

src/NuGet.Services.KeyVault/KeyVaultReader.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@ namespace NuGet.Services.KeyVault
1313
{
1414
/// <summary>
1515
/// Reads secretes from KeyVault.
16-
/// Authentication with KeyVault is done using either a managed identity or a certificate in location:LocalMachine store name:My
16+
/// Authentication with KeyVault is done using either a managed identity or a certificate in location:LocalMachine store name:My
1717
/// </summary>
1818
public class KeyVaultReader : ISecretReader
1919
{
2020
private readonly KeyVaultConfiguration _configuration;
2121
private readonly Lazy<SecretClient> _keyVaultClient;
22-
2322
protected SecretClient KeyVaultClient => _keyVaultClient.Value;
23+
internal bool _testMode;
24+
internal bool _isUsingSendx5c;
25+
26+
internal KeyVaultReader(SecretClient secretClient, KeyVaultConfiguration configuration, bool testMode = false)
27+
{
28+
_configuration = configuration;
29+
_keyVaultClient = new Lazy<SecretClient>(() => secretClient);
30+
_testMode = testMode;
31+
InitializeClient();
32+
}
2433

2534
public KeyVaultReader(KeyVaultConfiguration configuration)
2635
{
@@ -97,10 +106,27 @@ private SecretClient InitializeClient()
97106
credential = new ManagedIdentityCredential(_configuration.ClientId);
98107
}
99108
}
109+
else if (_configuration.SendX5c)
110+
{
111+
var clientCredentialOptions = new ClientCertificateCredentialOptions
112+
{
113+
SendCertificateChain = true
114+
};
115+
116+
credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate, clientCredentialOptions);
117+
118+
// If we are in unit testing mode, we dont actually create a SecretClient
119+
if (_testMode)
120+
{
121+
_isUsingSendx5c = true;
122+
return _keyVaultClient.Value;
123+
}
124+
}
100125
else
101126
{
102127
credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate);
103128
}
129+
104130
return new SecretClient(GetKeyVaultUri(_configuration), credential);
105131
}
106132

0 commit comments

Comments
 (0)