Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes Needed to Generate Sync RequestAuthenticator #312

Merged
merged 28 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4862eb6
Add ID2LHttpClient, [GenerateSync], and other required
AnaCoda Jul 7, 2023
a657eb2
Update Analyzers version
AnaCoda Jul 7, 2023
c61ec63
More fixes and uses of D2LHttpClient
AnaCoda Jul 7, 2023
f8007f4
Edit test to match actual async SaveAsync
AnaCoda Jul 7, 2023
6b2e458
asyncify uthenticateAsync(HttpRequestMessage)
AnaCoda Jul 7, 2023
090ff7b
Add using statements and asyncify InMemoryPublicKeyDataProvider
AnaCoda Jul 10, 2023
417d8b8
Replace params and declaration ident with interface
AnaCoda Jul 10, 2023
a861d75
Revert some previous changes
AnaCoda Jul 11, 2023
102d6e3
Discard changes to Directory.Packages.props
AnaCoda Jul 11, 2023
0e0e4de
Discard changes to test/D2L.Security.OAuth2.UnitTests/Keys/Developmen…
AnaCoda Jul 12, 2023
e85fceb
Spacing fix
AnaCoda Jul 12, 2023
6fa3655
Merge branch 'master' into anacoda/changes-for-sync-reqauth
AnaCoda Jul 12, 2023
b048026
Discard changes to Directory.Packages.props
AnaCoda Jul 12, 2023
e0f1c6a
Update Directory.Packages.props
AnaCoda Jul 12, 2023
b27ac45
Merge branch 'anacoda/changes-for-sync-reqauth' of https://github.com…
AnaCoda Jul 12, 2023
b4566df
Add generators to ExpiringPublicKeyDataProvider
AnaCoda Jul 14, 2023
c6fb9eb
Analyzer version 195
AnaCoda Jul 14, 2023
2d3d2d2
D2L.CodeStyle.Analyzers to 0.196.0
AnaCoda Jul 20, 2023
553583a
Remove Dispose from ID2LHttpClient since already extends IDisposable
AnaCoda Jul 20, 2023
007b236
Merge branch 'anacoda/changes-for-sync-reqauth' of https://github.com…
AnaCoda Jul 20, 2023
c0a3371
Weird styling revert
AnaCoda Jul 20, 2023
fe815e1
Styling and spacing for (I)D2LHttpClient
AnaCoda Jul 21, 2023
f8a32e8
Refactor D2LHttpClient as a wrapper
AnaCoda Jul 21, 2023
a76de10
Remove stray Utilities imports
AnaCoda Jul 21, 2023
dede849
Un-"real" asyncify AuthenticateAsync( HttpRequestMessage )
AnaCoda Jul 21, 2023
d30468a
More remove Utilities import
AnaCoda Jul 21, 2023
95dff15
Unused or wrong order nits
AnaCoda Jul 21, 2023
a5654cb
Replace pragma warning with [DangerousMethodUsage.Audited(...)]
AnaCoda Jul 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
omsmith marked this conversation as resolved.
Show resolved Hide resolved
</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
3 changes: 2 additions & 1 deletion src/D2L.Security.OAuth2.TestFramework/TestAccessToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using D2L.Security.OAuth2.Provisioning;
using D2L.Security.OAuth2.Scopes;
using D2L.Security.OAuth2.Utilities;
AnaCoda marked this conversation as resolved.
Show resolved Hide resolved
using D2L.Services;
using IAccessToken = D2L.Security.OAuth2.Provisioning.IAccessToken;

Expand All @@ -31,7 +32,7 @@ public static async Task<string> GetToken( string tokenProvisioningEndpoint, IEn

claims.Add( new Claim( Constants.Claims.ISSUER, DEFAULT_ISSUER ) );

using( var httpClient = new HttpClient() ) {
using( var httpClient = new D2LHttpClient() ) {
IAccessTokenProvider provider = TestAccessTokenProviderFactory.Create( httpClient, tokenProvisioningEndpoint );
IAccessToken token = await provider.ProvisionAccessTokenAsync( claims, scopes ).ConfigureAwait( false );
return token.Token;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using D2L.Security.OAuth2.Keys.Development;
using D2L.Security.OAuth2.Provisioning;
using D2L.Security.OAuth2.Provisioning.Default;
using D2L.Security.OAuth2.Utilities;

namespace D2L.Security.OAuth2.TestFramework {
public static class TestAccessTokenProviderFactory {
Expand All @@ -19,7 +20,7 @@ public static class TestAccessTokenProviderFactory {
/// <param name="httpClient">The httpClient that makes the request to the auth server</param>
/// <param name="tokenProvisioningEndpoint">The auth server</param>
/// <returns>An IAccessTokenProvider with test credentials</returns>
public static IAccessTokenProvider Create( HttpClient httpClient, String tokenProvisioningEndpoint ) {
public static IAccessTokenProvider Create( ID2LHttpClient httpClient, String tokenProvisioningEndpoint ) {
return Create( httpClient, tokenProvisioningEndpoint, TestKeyId, TestStaticKeyProvider.TestRSAParameters );
}

Expand All @@ -31,7 +32,7 @@ public static IAccessTokenProvider Create( HttpClient httpClient, String tokenPr
/// <param name="keyId">The id of the security token</param>
/// <param name="rsaParameters">The public and private key for the supplied key id</param>
/// <returns>An IAccessTokenProvider with the supplied test credentials</returns>
public static IAccessTokenProvider Create( HttpClient httpClient, String tokenProvisioningEndpoint, string keyId, RSAParameters rsaParameters ) {
public static IAccessTokenProvider Create( ID2LHttpClient httpClient, String tokenProvisioningEndpoint, string keyId, RSAParameters rsaParameters ) {
#pragma warning disable 618
IPrivateKeyProvider privateKeyProvider = new StaticPrivateKeyProvider( keyId, rsaParameters );
#pragma warning restore 618
Expand Down
7 changes: 5 additions & 2 deletions 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;
using System.Threading.Tasks;
AnaCoda marked this conversation as resolved.
Show resolved Hide resolved

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
13 changes: 9 additions & 4 deletions src/D2L.Security.OAuth2/Keys/Default/Data/JwksProvider.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using System;
using System.IO;
omsmith marked this conversation as resolved.
Show resolved Hide resolved
using System.Net;
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;

public JwksProvider(
HttpClient httpClient,
ID2LHttpClient httpClient,
Uri jwksEndpoint,
Uri jwkEndpoint
) {
Expand All @@ -22,11 +25,12 @@ Uri jwkEndpoint
m_jwkEndpoint = jwkEndpoint;
}

[GenerateSync]
async Task<JsonWebKeySet> IJwksProvider.RequestJwksAsync() {
try {
using( HttpResponseMessage response = await m_httpClient.GetAsync( m_jwksEndpoint ).ConfigureAwait( false ) ) {
response.EnsureSuccessStatusCode();
string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait( false );
string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
AnaCoda marked this conversation as resolved.
Show resolved Hide resolved
var jwks = new JsonWebKeySet( jsonResponse, m_jwksEndpoint );
return jwks;
}
Expand All @@ -37,6 +41,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
7 changes: 5 additions & 2 deletions src/D2L.Security.OAuth2/Keys/Default/IPublicKeyProvider.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
using System;
using D2L.CodeStyle.Annotations;
using System;
using System.Threading.Tasks;

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
@@ -1,4 +1,5 @@
using System;
using D2L.CodeStyle.Annotations;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
Expand All @@ -11,30 +12,34 @@ namespace D2L.Security.OAuth2.Keys.Development {
/// 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
9 changes: 7 additions & 2 deletions src/D2L.Security.OAuth2/Keys/IPublicKeyDataProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using D2L.CodeStyle.Annotations;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

Expand All @@ -7,32 +8,36 @@ 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 @@ -2,6 +2,7 @@
using System.Net.Http;
using D2L.Security.OAuth2.Keys;
using D2L.Security.OAuth2.Provisioning.Default;
using D2L.Security.OAuth2.Utilities;

namespace D2L.Security.OAuth2.Provisioning {

Expand All @@ -16,7 +17,7 @@ public static class AccessTokenProviderFactory {
/// <returns>A new <see cref="IAccessTokenProvider"/></returns>
public static IAccessTokenProvider Create(
ITokenSigner tokenSigner,
HttpClient httpClient,
ID2LHttpClient httpClient,
Uri authEndpoint,
TimeSpan tokenRefreshGracePeriod
) {
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 @@ -48,7 +49,7 @@ internal sealed class AuthServiceClient : IAuthServiceClient {
/// <param name="httpClient">An http client used to communicate with the auth service.</param>
/// <param name="authEndpoint">The token provisioning endpoint on the auth service</param>
public AuthServiceClient(
HttpClient httpClient,
ID2LHttpClient httpClient,
Uri authEndpoint
) {
if( httpClient == null ) {
Expand Down
Loading