Add nullable to DataProtection (#22591)

* Add nullable to DataProtection

Contributes to https://github.com/dotnet/aspnetcore/issues/5680
This commit is contained in:
Pranav K 2020-06-13 20:39:41 -07:00 committed by GitHub
parent 9b63151b14
commit f04992fab5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 57 additions and 45 deletions

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.AspNetCore.DataProtection.Abstractions.netstandard2.0.cs" /> <Compile Include="Microsoft.AspNetCore.DataProtection.Abstractions.netstandard2.0.cs" />

View File

@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.DataProtection
// because we don't want the code provider.CreateProtector() [parameterless] to inadvertently compile. // because we don't want the code provider.CreateProtector() [parameterless] to inadvertently compile.
// The actual signature for this method forces at least one purpose to be provided at the call site. // The actual signature for this method forces at least one purpose to be provided at the call site.
IDataProtector protector = provider.CreateProtector(purpose); IDataProtector? protector = provider.CreateProtector(purpose);
if (subPurposes != null && subPurposes.Length > 0) if (subPurposes != null && subPurposes.Length > 0)
{ {
protector = protector?.CreateProtector((IEnumerable<string>)subPurposes); protector = protector?.CreateProtector((IEnumerable<string>)subPurposes);
@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.DataProtection
// We have our own implementation of GetRequiredService<T> since we don't want to // We have our own implementation of GetRequiredService<T> since we don't want to
// take a dependency on DependencyInjection.Interfaces. // take a dependency on DependencyInjection.Interfaces.
IDataProtectionProvider provider = (IDataProtectionProvider)services.GetService(typeof(IDataProtectionProvider)); var provider = (IDataProtectionProvider?)services.GetService(typeof(IDataProtectionProvider));
if (provider == null) if (provider == null)
{ {
throw new InvalidOperationException(Resources.FormatDataProtectionExtensions_NoService(typeof(IDataProtectionProvider).FullName)); throw new InvalidOperationException(Resources.FormatDataProtectionExtensions_NoService(typeof(IDataProtectionProvider).FullName));

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.DataProtection
{ {
internal static class Error internal static class Error
{ {
public static CryptographicException CryptCommon_GenericError(Exception inner = null) public static CryptographicException CryptCommon_GenericError(Exception? inner = null)
{ {
return new CryptographicException(Resources.CryptCommon_GenericError, inner); return new CryptographicException(Resources.CryptCommon_GenericError, inner);
} }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>ASP.NET Core data protection abstractions. <Description>ASP.NET Core data protection abstractions.
@ -9,6 +9,7 @@ Microsoft.AspNetCore.DataProtection.IDataProtector</Description>
<IsAspNetCoreApp>true</IsAspNetCoreApp> <IsAspNetCoreApp>true</IsAspNetCoreApp>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags> <PackageTags>aspnetcore;dataprotection</PackageTags>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.AspNetCore.Cryptography.Internal.netstandard2.0.cs" /> <Compile Include="Microsoft.AspNetCore.Cryptography.Internal.netstandard2.0.cs" />

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Cryptography
{ {
// This isn't a typical Debug.Assert; the check is always performed, even in retail builds. // This isn't a typical Debug.Assert; the check is always performed, even in retail builds.
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Assert(bool condition, string message) public static void Assert([DoesNotReturnIf(false)] bool condition, string message)
{ {
if (!condition) if (!condition)
{ {

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Infrastructure for ASP.NET Core cryptographic packages. Applications and libraries should not reference this package directly.</Description> <Description>Infrastructure for ASP.NET Core cryptographic packages. Applications and libraries should not reference this package directly.</Description>
@ -8,6 +8,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags> <PackageTags>aspnetcore;dataprotection</PackageTags>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.AspNetCore.Cryptography.KeyDerivation.netstandard2.0.cs" /> <Compile Include="Microsoft.AspNetCore.Cryptography.KeyDerivation.netstandard2.0.cs" />

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>ASP.NET Core utilities for key derivation.</Description> <Description>ASP.NET Core utilities for key derivation.</Description>
@ -7,6 +7,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags> <PackageTags>aspnetcore;dataprotection</PackageTags>
<Nullable>enable</Nullable>
<Nullable Condition="'$(TargetFramework)' == 'netstandard2.0'">annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.AspNetCore.DataProtection.netstandard2.0.cs" /> <Compile Include="Microsoft.AspNetCore.DataProtection.netstandard2.0.cs" />

View File

@ -9,6 +9,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags> <PackageTags>aspnetcore;dataprotection</PackageTags>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
@ -16,11 +16,11 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
/// <summary> /// <summary>
/// The friendly name of the <see cref="DataProtectionKey"/>. /// The friendly name of the <see cref="DataProtectionKey"/>.
/// </summary> /// </summary>
public string FriendlyName { get; set; } public string? FriendlyName { get; set; }
/// <summary> /// <summary>
/// The XML representation of the <see cref="DataProtectionKey"/>. /// The XML representation of the <see cref="DataProtectionKey"/>.
/// </summary> /// </summary>
public string Xml { get; set; } public string? Xml { get; set; }
} }
} }

View File

@ -40,13 +40,25 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
/// <inheritdoc /> /// <inheritdoc />
public virtual IReadOnlyCollection<XElement> GetAllElements() public virtual IReadOnlyCollection<XElement> GetAllElements()
{ {
using (var scope = _services.CreateScope()) // forces complete enumeration
{ return GetAllElementsCore().ToList().AsReadOnly();
var context = scope.ServiceProvider.GetRequiredService<TContext>();
// Put logger in a local such that `this` isn't captured. IEnumerable<XElement> GetAllElementsCore()
var logger = _logger; {
return context.DataProtectionKeys.AsNoTracking().Select(key => TryParseKeyXml(key.Xml, logger)).ToList().AsReadOnly(); using (var scope = _services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<TContext>();
foreach (var key in context.DataProtectionKeys.AsNoTracking())
{
_logger.ReadingXmlFromKey(key.FriendlyName!, key.Xml);
if (!string.IsNullOrEmpty(key.Xml))
{
yield return XElement.Parse(key.Xml);
}
}
}
} }
} }
@ -67,18 +79,5 @@ namespace Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
context.SaveChanges(); context.SaveChanges();
} }
} }
private static XElement TryParseKeyXml(string xml, ILogger logger)
{
try
{
return XElement.Parse(xml);
}
catch (Exception e)
{
logger?.LogExceptionWhileParsingKeyXml(xml, e);
return null;
}
}
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -7,23 +7,23 @@ namespace Microsoft.Extensions.Logging
{ {
internal static class LoggingExtensions internal static class LoggingExtensions
{ {
private static readonly Action<ILogger, string, Exception> _anExceptionOccurredWhileParsingKeyXml; private static readonly Action<ILogger, string?, string?, Exception?> _readingXmlFromKey;
private static readonly Action<ILogger, string, string, Exception> _savingKeyToDbContext; private static readonly Action<ILogger, string, string, Exception?> _savingKeyToDbContext;
static LoggingExtensions() static LoggingExtensions()
{ {
_anExceptionOccurredWhileParsingKeyXml = LoggerMessage.Define<string>( _readingXmlFromKey = LoggerMessage.Define<string?, string?>(
eventId: new EventId(1, "ExceptionOccurredWhileParsingKeyXml"), eventId: new EventId(1, "ReadKeyFromElement"),
logLevel: LogLevel.Warning, logLevel: LogLevel.Debug,
formatString: "An exception occurred while parsing the key xml '{Xml}'."); formatString: "Reading data with key '{FriendlyName}', value '{Value}'.");
_savingKeyToDbContext = LoggerMessage.Define<string, string>( _savingKeyToDbContext = LoggerMessage.Define<string, string>(
eventId: new EventId(2, "SavingKeyToDbContext"), eventId: new EventId(2, "SavingKeyToDbContext"),
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "Saving key '{FriendlyName}' to '{DbContext}'."); formatString: "Saving key '{FriendlyName}' to '{DbContext}'.");
} }
public static void LogExceptionWhileParsingKeyXml(this ILogger logger, string keyXml, Exception exception) public static void ReadingXmlFromKey(this ILogger logger, string? friendlyName, string? keyXml)
=> _anExceptionOccurredWhileParsingKeyXml(logger, keyXml, exception); => _readingXmlFromKey(logger, friendlyName, keyXml, null);
public static void LogSavingKeyToDbContext(this ILogger logger, string friendlyName, string contextName) public static void LogSavingKeyToDbContext(this ILogger logger, string friendlyName, string contextName)
=> _savingKeyToDbContext(logger, friendlyName, contextName, null); => _savingKeyToDbContext(logger, friendlyName, contextName, null);

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Support for storing keys using Entity Framework Core.</Description> <Description>Support for storing keys using Entity Framework Core.</Description>
@ -7,6 +7,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection;entityframeworkcore</PackageTags> <PackageTags>aspnetcore;dataprotection;entityframeworkcore</PackageTags>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks> <TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.AspNetCore.DataProtection.Extensions.netstandard2.0.cs" /> <Compile Include="Microsoft.AspNetCore.DataProtection.Extensions.netstandard2.0.cs" />

View File

@ -151,9 +151,9 @@ namespace Microsoft.AspNetCore.DataProtection
} }
internal static IDataProtectionProvider CreateProvider( internal static IDataProtectionProvider CreateProvider(
DirectoryInfo keyDirectory, DirectoryInfo? keyDirectory,
Action<IDataProtectionBuilder> setupAction, Action<IDataProtectionBuilder> setupAction,
X509Certificate2 certificate) X509Certificate2? certificate)
{ {
// build the service collection // build the service collection
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Additional APIs for ASP.NET Core data protection.</Description> <Description>Additional APIs for ASP.NET Core data protection.</Description>
@ -7,6 +7,7 @@
<IsAspNetCoreApp>true</IsAspNetCoreApp> <IsAspNetCoreApp>true</IsAspNetCoreApp>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags> <PackageTags>aspnetcore;dataprotection</PackageTags>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.DataProtection
private const string MyPurposeString = "Microsoft.AspNetCore.DataProtection.TimeLimitedDataProtector.v1"; private const string MyPurposeString = "Microsoft.AspNetCore.DataProtection.TimeLimitedDataProtector.v1";
private readonly IDataProtector _innerProtector; private readonly IDataProtector _innerProtector;
private IDataProtector _innerProtectorWithTimeLimitedPurpose; // created on-demand private IDataProtector? _innerProtectorWithTimeLimitedPurpose; // created on-demand
public TimeLimitedDataProtector(IDataProtector innerProtector) public TimeLimitedDataProtector(IDataProtector innerProtector)
{ {

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Support for storing data protection keys in Redis.</Description> <Description>Support for storing data protection keys in Redis.</Description>
@ -7,6 +7,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection;redis</PackageTags> <PackageTags>aspnetcore;dataprotection;redis</PackageTags>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable #nullable enable
using System; using System;
#if NETCOREAPP #if NETCOREAPP
using System.Buffers; using System.Buffers;