Skip to content

Commit

Permalink
Merge pull request #312 Generate Sync RequestAuthenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
AnaCoda authored Jul 25, 2023
2 parents a70a6b7 + a5654cb commit f5c01dc
Show file tree
Hide file tree
Showing 20 changed files with 226 additions and 19 deletions.
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
<PropertyGroup>
<LangVersion>11.0</LangVersion>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<WarningsAsErrors>CS8785</WarningsAsErrors>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.1" />
<PackageVersion Include="D2L.CodeStyle.Analyzers" Version="0.189.0" />
<PackageVersion Include="D2L.CodeStyle.Analyzers" Version="0.196.0" />
<PackageVersion Include="D2L.CodeStyle.Annotations" Version="0.31.0" />
<PackageVersion Include="D2L.Services.Core.Exceptions" Version="2.0.1.15" />
<PackageVersion Include="FluentAssertions" Version="6.6.0" />
Expand Down
1 change: 0 additions & 1 deletion src/D2L.Security.OAuth2.TestFramework/TestAccessToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Threading.Tasks;
using D2L.Security.OAuth2.Provisioning;
using D2L.Security.OAuth2.Scopes;
using D2L.Services;
using IAccessToken = D2L.Security.OAuth2.Provisioning.IAccessToken;

namespace D2L.Security.OAuth2.TestFramework {
Expand Down
5 changes: 4 additions & 1 deletion src/D2L.Security.OAuth2/Keys/Default/Data/IJwksProvider.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Default.Data {
internal interface IJwksProvider {
internal partial interface IJwksProvider {
[GenerateSync]
Task<JsonWebKeySet> RequestJwksAsync();
[GenerateSync]
Task<JsonWebKeySet> RequestJwkAsync( string keyId );
string Namespace { get; }
}
Expand Down
11 changes: 7 additions & 4 deletions src/D2L.Security.OAuth2/Keys/Default/Data/JwksProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Utilities;
using D2L.Security.OAuth2.Validation.Exceptions;
using D2L.Services;

namespace D2L.Security.OAuth2.Keys.Default.Data {
internal sealed class JwksProvider : IJwksProvider {
private readonly HttpClient m_httpClient;
internal sealed partial class JwksProvider : IJwksProvider {
private readonly ID2LHttpClient m_httpClient;
private readonly Uri m_jwksEndpoint;
private readonly Uri m_jwkEndpoint;

Expand All @@ -17,11 +18,12 @@ public JwksProvider(
Uri jwksEndpoint,
Uri jwkEndpoint
) {
m_httpClient = httpClient;
m_httpClient = new D2LHttpClient( httpClient );
m_jwksEndpoint = jwksEndpoint;
m_jwkEndpoint = jwkEndpoint;
}

[GenerateSync]
async Task<JsonWebKeySet> IJwksProvider.RequestJwksAsync() {
try {
using( HttpResponseMessage response = await m_httpClient.GetAsync( m_jwksEndpoint ).ConfigureAwait( false ) ) {
Expand All @@ -37,6 +39,7 @@ async Task<JsonWebKeySet> IJwksProvider.RequestJwksAsync() {
}
}

[GenerateSync]
async Task<JsonWebKeySet> IJwksProvider.RequestJwkAsync( string keyId ) {
var url = GetJwkEndpoint( m_jwkEndpoint, keyId );
if( url == null ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Utilities;
using D2L.Services;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed class ExpiringPublicKeyDataProvider : ISanePublicKeyDataProvider {
internal sealed partial class ExpiringPublicKeyDataProvider : ISanePublicKeyDataProvider {
private readonly IPublicKeyDataProvider m_inner;
private readonly IDateTimeProvider m_dateTimeProvider;

Expand All @@ -18,6 +19,7 @@ IDateTimeProvider dateTimeProvider
m_dateTimeProvider = dateTimeProvider ?? throw new ArgumentException( nameof( dateTimeProvider ) );
}

[GenerateSync]
async Task<JsonWebKey> IPublicKeyDataProvider.GetByIdAsync( Guid id ) {
// We are intentionally fetching *all* public keys from the database
// here. This allows us to clean up all expired public keys even if
Expand All @@ -44,6 +46,7 @@ async Task<JsonWebKey> IPublicKeyDataProvider.GetByIdAsync( Guid id ) {
return key;
}

[GenerateSync]
async Task<IEnumerable<JsonWebKey>> IPublicKeyDataProvider.GetAllAsync() {
IEnumerable<JsonWebKey> keys = await m_inner
.GetAllAsync()
Expand All @@ -62,14 +65,17 @@ async Task<IEnumerable<JsonWebKey>> IPublicKeyDataProvider.GetAllAsync() {
return result;
}

[GenerateSync]
Task IPublicKeyDataProvider.SaveAsync( Guid id, JsonWebKey key ) {
return m_inner.SaveAsync( id, key );
}

[GenerateSync]
Task IPublicKeyDataProvider.DeleteAsync( Guid id ) {
return m_inner.DeleteAsync( id );
}

[GenerateSync]
private async Task<bool> KeyExpiryHelperAsync( JsonWebKey key ) {
if( key == null ) {
return true;
Expand Down
8 changes: 5 additions & 3 deletions src/D2L.Security.OAuth2/Keys/Default/IPublicKeyProvider.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
using System;
using System.Threading.Tasks;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Default {

/// <summary>
/// An abstraction for fetching public keys
/// </summary>
internal interface IPublicKeyProvider {
internal partial interface IPublicKeyProvider {

/// <summary>
/// Gets an individual <see cref="D2LSecurityToken"/> by its <paramref name="id"/>
/// </summary>
/// <param name="id">The key id (kid)</param>
/// <returns>The <see cref="D2LSecurityToken"/> or null if the key doesn't exist or has expired</returns>
[GenerateSync]
Task<D2LSecurityToken> GetByIdAsync( string id );

/// <summary>
/// Perform steps to potentially make future key fetches faster.
/// </summary>
[GenerateSync]
Task PrefetchAsync();

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Keys.Caching;
using D2L.Security.OAuth2.Validation.Exceptions;
using D2L.Services;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed class LocalPublicKeyProvider : IPublicKeyProvider {
internal sealed partial class LocalPublicKeyProvider : IPublicKeyProvider {

private const string PUBLIC_KEY_SOURCE = "Local DB";

Expand All @@ -21,6 +22,7 @@ IInMemoryPublicKeyCache cache
m_cache = cache ?? throw new ArgumentNullException( nameof( cache ) );
}

[GenerateSync]
async Task IPublicKeyProvider.PrefetchAsync() {
IEnumerable<JsonWebKey> jwks = await m_publicKeyDataProvider
.GetAllAsync()
Expand All @@ -42,6 +44,7 @@ async Task IPublicKeyProvider.PrefetchAsync() {
}
}

[GenerateSync]
async Task<D2LSecurityToken> IPublicKeyProvider.GetByIdAsync( string id ) {
D2LSecurityToken result = m_cache.Get( PUBLIC_KEY_SOURCE, id );
if( result != null ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Keys.Caching;
using D2L.Security.OAuth2.Keys.Default.Data;
using D2L.Security.OAuth2.Validation.Exceptions;
using D2L.Services;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed class RemotePublicKeyProvider : IPublicKeyProvider {
internal sealed partial class RemotePublicKeyProvider : IPublicKeyProvider {
private readonly IJwksProvider m_jwksProvider;
private readonly IInMemoryPublicKeyCache m_cache;

Expand All @@ -18,6 +19,7 @@ IInMemoryPublicKeyCache cache
m_cache = cache;
}

[GenerateSync]
async Task<D2LSecurityToken> IPublicKeyProvider.GetByIdAsync( string id ) {
D2LSecurityToken result = m_cache.Get( m_jwksProvider.Namespace, id );
if( result != null ) {
Expand All @@ -38,6 +40,7 @@ async Task<D2LSecurityToken> IPublicKeyProvider.GetByIdAsync( string id ) {
throw new PublicKeyNotFoundException( id, jwks.Source.AbsoluteUri );
}

[GenerateSync]
async Task IPublicKeyProvider.PrefetchAsync() {
JsonWebKeySet jwks = await m_jwksProvider
.RequestJwksAsync()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,42 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Development {

/// <summary>
/// A simple in-memory key data provider to be used only for testing and prototyping purposes.
/// </summary>
[Obsolete( "Only use this in tests and for prototyping without a db" )]
public sealed class InMemoryPublicKeyDataProvider : IPublicKeyDataProvider {
public sealed partial class InMemoryPublicKeyDataProvider : IPublicKeyDataProvider {
private readonly ConcurrentDictionary<Guid, JsonWebKey> m_keys = new ConcurrentDictionary<Guid, JsonWebKey>();

[GenerateSync]
Task<JsonWebKey> IPublicKeyDataProvider.GetByIdAsync( Guid id ) {
if( !m_keys.TryGetValue( id, out JsonWebKey key ) ) {
return Task.FromResult<JsonWebKey>( null );
}
return Task.FromResult( key );
}

[GenerateSync]
Task<IEnumerable<JsonWebKey>> IPublicKeyDataProvider.GetAllAsync() {
IEnumerable<JsonWebKey> result =
new ReadOnlyCollection<JsonWebKey>( m_keys.Values.ToList() );

return Task.FromResult( result );
}

[GenerateSync]
Task IPublicKeyDataProvider.SaveAsync( Guid id, JsonWebKey key ) {
if( !m_keys.TryAdd( id, key ) ) {
throw new InvalidOperationException( "Attempted to add a key twice" );
}
return Task.CompletedTask;
}

[GenerateSync]
Task IPublicKeyDataProvider.DeleteAsync( Guid id ) {
m_keys.TryRemove( id, out JsonWebKey removedKey );
return Task.CompletedTask;
Expand Down
7 changes: 6 additions & 1 deletion src/D2L.Security.OAuth2/Keys/IPublicKeyDataProvider.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys {

/// <summary>
/// Data provider for public keys that belong to this service
/// </summary>
public interface IPublicKeyDataProvider {
public partial interface IPublicKeyDataProvider {

/// <summary>
/// Gets an individual <see cref="JsonWebKey"/> by its <paramref name="id"/>
/// </summary>
/// <param name="id">The key id (kid)</param>
/// <returns>The <see cref="JsonWebKey"/> or null if the key doesn't exist or has expired</returns>
[GenerateSync]
Task<JsonWebKey> GetByIdAsync( Guid id );

/// <summary>
/// Gets all the <see cref="JsonWebKey"/> instances
/// </summary>
/// <returns>All keys which haven't expired</returns>
[GenerateSync]
Task<IEnumerable<JsonWebKey>> GetAllAsync();

/// <summary>
/// Saves a key
/// </summary>
/// <param name="id">The key id (kid) of the key to save</param>
/// <param name="key">The key to save</param>
[GenerateSync]
Task SaveAsync( Guid id, JsonWebKey key );

/// <summary>
/// Deletes a key by key <paramref name="id"/> (kid)
/// </summary>
/// <param name="id">The key id (kid) of the key to delete</param>
[GenerateSync]
Task DeleteAsync( Guid id );

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text;
using System.Threading.Tasks;
using D2L.Security.OAuth2.Scopes;
using D2L.Security.OAuth2.Utilities;
using D2L.Services.Core.Exceptions;

#if NET6_0
Expand Down Expand Up @@ -39,7 +40,7 @@ internal sealed class AuthServiceClient : IAuthServiceClient {
SERIALIZATION_ERROR_MESSAGE_PREFIX +
"The Auth Service responded with: ";

private readonly HttpClient m_client;
private readonly ID2LHttpClient m_client;
private readonly Uri m_tokenProvisioningEndpoint;

/// <summary>
Expand All @@ -59,7 +60,7 @@ Uri authEndpoint
throw new ArgumentNullException( "authEndpoint" );
}

m_client = httpClient;
m_client = new D2LHttpClient( httpClient );
m_tokenProvisioningEndpoint = authEndpoint.RelativePathAsNonLeaf( TOKEN_PATH );
}

Expand Down
Loading

0 comments on commit f5c01dc

Please sign in to comment.