Browse Source

Update the web provider generator to support creating builder methods for certificates and ECDSA keys

pull/1635/head
Kévin Chalet 3 years ago
parent
commit
5d28210dc2
  1. 236
      gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs
  2. 69
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationBuilder.cs
  3. 1
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd

236
gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs

@ -49,11 +49,16 @@ namespace OpenIddict.Client.WebIntegration.Generators
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Client;
using OpenIddict.Client.WebIntegration;
using OpenIddict.Extensions;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
namespace Microsoft.Extensions.DependencyInjection;
@ -272,6 +277,79 @@ public sealed partial class OpenIddictClientWebIntegrationBuilder
return Configure(options => options.{{ setting.property_name }}.UnionWith({{ setting.parameter_name }}));
}
{{~ else if setting.clr_type == 'ECDsaSecurityKey' ~}}
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(ECDsaSecurityKey {{ setting.parameter_name }})
{
if ({{ setting.parameter_name }} is null)
{
throw new ArgumentNullException(nameof({{ setting.parameter_name }}));
}
if ({{ setting.parameter_name }}.PrivateKeyStatus is PrivateKeyStatus.DoesNotExist)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0055), nameof({{ setting.parameter_name }}));
}
return Configure(options => options.{{ setting.property_name }} = {{ setting.parameter_name }});
}
#if SUPPORTS_PEM_ENCODED_KEY_IMPORT
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""key"">
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm (ECDSA) signing key.
/// </param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(string key)
=> Set{{ setting.property_name }}(key.AsMemory());
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""key"">
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm (ECDSA) signing key.
/// </param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(ReadOnlyMemory<char> key)
=> Set{{ setting.property_name }}(key.Span);
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""key"">
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm (ECDSA) signing key.
/// </param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(ReadOnlySpan<char> key)
{
if (key.IsEmpty)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0346), nameof(key));
}
var algorithm = OpenIddictHelpers.CreateEcdsaKey();
try
{
algorithm.ImportFromPem(key);
}
catch
{
algorithm.Dispose();
throw;
}
return Set{{ setting.property_name }}(new ECDsaSecurityKey(algorithm));
}
#endif
{{~ else if setting.clr_type == 'Uri' ~}}
/// <summary>
/// Configures {{ setting.description }}.
@ -307,6 +385,161 @@ public sealed partial class OpenIddictClientWebIntegrationBuilder
return Set{{ setting.property_name }}(new Uri({{ setting.parameter_name }}, UriKind.RelativeOrAbsolute));
}
{{~ else if setting.clr_type == 'X509Certificate2' ~}}
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(X509Certificate2 {{ setting.parameter_name }})
{
if ({{ setting.parameter_name }} is null)
{
throw new ArgumentNullException(nameof({{ setting.parameter_name }}));
}
if (!{{ setting.parameter_name }}.HasPrivateKey)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0061), nameof({{ setting.parameter_name }}));
}
return Configure(options => options.{{ setting.property_name }} = {{ setting.parameter_name }});
}
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""assembly"">The assembly containing the certificate.</param>
/// <param name=""resource"">The name of the embedded resource.</param>
/// <param name=""password"">The password used to open the certificate.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(Assembly assembly, string resource, string? password)
#if SUPPORTS_EPHEMERAL_KEY_SETS
// Note: ephemeral key sets are currently not supported on macOS.
=> Set{{ setting.property_name }}(assembly, resource, password, RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
X509KeyStorageFlags.MachineKeySet :
X509KeyStorageFlags.EphemeralKeySet);
#else
=> Set{{ setting.property_name }}(assembly, resource, password, X509KeyStorageFlags.MachineKeySet);
#endif
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""assembly"">The assembly containing the certificate.</param>
/// <param name=""resource"">The name of the embedded resource.</param>
/// <param name=""password"">The password used to open the certificate.</param>
/// <param name=""flags"">An enumeration of flags indicating how and where to store the private key of the certificate.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(
Assembly assembly, string resource,
string? password, X509KeyStorageFlags flags)
{
if (assembly is null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (string.IsNullOrEmpty(resource))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0062), nameof(resource));
}
using var stream = assembly.GetManifestResourceStream(resource) ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0064));
return Set{{ setting.property_name }}(stream, password, flags);
}
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""stream"">The stream containing the certificate.</param>
/// <param name=""password"">The password used to open the certificate.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(Stream stream, string? password)
#if SUPPORTS_EPHEMERAL_KEY_SETS
// Note: ephemeral key sets are currently not supported on macOS.
=> Set{{ setting.property_name }}(stream, password, RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
X509KeyStorageFlags.MachineKeySet :
X509KeyStorageFlags.EphemeralKeySet);
#else
=> Set{{ setting.property_name }}(stream, password, X509KeyStorageFlags.MachineKeySet);
#endif
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""stream"">The stream containing the certificate.</param>
/// <param name=""password"">The password used to open the certificate.</param>
/// <param name=""flags"">
/// An enumeration of flags indicating how and where
/// to store the private key of the certificate.
/// </param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(Stream stream, string? password, X509KeyStorageFlags flags)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
using var buffer = new MemoryStream();
stream.CopyTo(buffer);
return Set{{ setting.property_name }}(new X509Certificate2(buffer.ToArray(), password, flags));
}
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""thumbprint"">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(string thumbprint)
{
if (string.IsNullOrEmpty(thumbprint))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0065), nameof(thumbprint));
}
return Set{{ setting.property_name }}(
GetCertificate(StoreLocation.CurrentUser, thumbprint) ??
GetCertificate(StoreLocation.LocalMachine, thumbprint) ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0066)));
static X509Certificate2? GetCertificate(StoreLocation location, string thumbprint)
{
using var store = new X509Store(StoreName.My, location);
store.Open(OpenFlags.ReadOnly);
return store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false)
.OfType<X509Certificate2>()
.SingleOrDefault();
}
}
/// <summary>
/// Configures {{ setting.description }}.
/// </summary>
/// <param name=""thumbprint"">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <param name=""name"">The name of the X.509 store.</param>
/// <param name=""location"">The location of the X.509 store.</param>
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
public {{ provider.name }} Set{{ setting.property_name }}(string thumbprint, StoreName name, StoreLocation location)
{
if (string.IsNullOrEmpty(thumbprint))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0065), nameof(thumbprint));
}
using var store = new X509Store(name, location);
store.Open(OpenFlags.ReadOnly);
return Set{{ setting.property_name }}(
store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false)
.OfType<X509Certificate2>()
.SingleOrDefault() ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0066)));
}
{{~ else ~}}
/// <summary>
/// Configures {{ setting.description }}.
@ -375,6 +608,7 @@ public sealed partial class OpenIddictClientWebIntegrationBuilder
is "PS256" or "PS384" or "PS512" or
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"Certificate" => "X509Certificate2",
"String" => "string",
"StringHashSet" => "HashSet<string>",
"Uri" => "Uri",
@ -832,6 +1066,7 @@ public static partial class OpenIddictClientWebIntegrationHelpers
{
var template = Template.Parse(@"#nullable enable
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Client.WebIntegration;
@ -915,6 +1150,7 @@ public sealed partial class OpenIddictClientWebIntegrationOptions
is "PS256" or "PS384" or "PS512" or
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"Certificate" => "X509Certificate2",
"String" => "string",
"StringHashSet" => "HashSet<string>",
"Uri" => "Uri",

69
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationBuilder.cs

@ -7,12 +7,6 @@
using System.ComponentModel;
using OpenIddict.Client.WebIntegration;
#if SUPPORTS_PEM_ENCODED_KEY_IMPORT
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions;
#endif
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
@ -53,69 +47,6 @@ public sealed partial class OpenIddictClientWebIntegrationBuilder
// Note: provider registration methods are automatically generated by the source generator.
/// <summary>
/// Exposes the necessary methods required to configure the Apple integration.
/// </summary>
public partial class Apple
{
#if SUPPORTS_PEM_ENCODED_KEY_IMPORT
/// <summary>
/// Configures the Elliptic Curve Digital Signature Algorithm
/// (ECDSA) signing key associated with the developer account.
/// </summary>
/// <param name="key">
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm
/// (ECDSA) signing key associated with the developer account.
/// </param>
/// <returns>The <see cref="Apple"/> instance.</returns>
public Apple SetSigningKey(string key) => SetSigningKey(key.AsMemory());
/// <summary>
/// Configures the Elliptic Curve Digital Signature Algorithm
/// (ECDSA) signing key associated with the developer account.
/// </summary>
/// <param name="key">
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm
/// (ECDSA) signing key associated with the developer account.
/// </param>
/// <returns>The <see cref="Apple"/> instance.</returns>
public Apple SetSigningKey(ReadOnlyMemory<char> key) => SetSigningKey(key.Span);
/// <summary>
/// Configures the Elliptic Curve Digital Signature Algorithm
/// (ECDSA) signing key associated with the developer account.
/// </summary>
/// <param name="key">
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm
/// (ECDSA) signing key associated with the developer account.
/// </param>
/// <returns>The <see cref="Apple"/> instance.</returns>
public Apple SetSigningKey(ReadOnlySpan<char> key)
{
if (key.IsEmpty)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0346), nameof(key));
}
var algorithm = OpenIddictHelpers.CreateEcdsaKey();
try
{
algorithm.ImportFromPem(key);
}
catch
{
algorithm.Dispose();
throw;
}
return SetSigningKey(new ECDsaSecurityKey(algorithm));
}
#endif
}
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => base.Equals(obj);

1
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd

@ -376,6 +376,7 @@
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Certificate" />
<xs:enumeration value="EncryptionKey" />
<xs:enumeration value="SigningKey" />
<xs:enumeration value="String" />

Loading…
Cancel
Save