Browse Source

Backport the events model changes to OpenIddict 1.x

pull/670/head
Kévin Chalet 8 years ago
parent
commit
ed9f2117cc
  1. 11
      OpenIddict.sln
  2. 24
      shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
  3. 14
      shared/OpenIddict.Extensions/OpenIddict.Extensions.csproj
  4. 4
      src/OpenIddict.Core/OpenIddict.Core.csproj
  5. 17
      src/OpenIddict.Core/OpenIddictCoreBuilder.cs
  6. 6
      src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj
  7. 4
      src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs
  8. 4
      src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs
  9. 4
      src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs
  10. 4
      src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs
  11. 4
      src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj
  12. 4
      src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs
  13. 5
      src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs
  14. 4
      src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs
  15. 4
      src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs
  16. 9
      src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
  17. 26
      src/OpenIddict.Server/IOpenIddictServerEventService.cs
  18. 164
      src/OpenIddict.Server/Internal/OpenIddictServerEventService.cs
  19. 4
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
  20. 1
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
  21. 4
      src/OpenIddict.Server/OpenIddict.Server.csproj
  22. 67
      src/OpenIddict.Server/OpenIddictServerBuilder.cs
  23. 26
      src/OpenIddict.Server/OpenIddictServerEventHandler.cs
  24. 52
      src/OpenIddict.Server/OpenIddictServerEventService.cs
  25. 33
      src/OpenIddict.Server/OpenIddictServerEventState.cs
  26. 2
      src/OpenIddict.Server/OpenIddictServerExtensions.cs
  27. 9
      src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
  28. 25
      src/OpenIddict.Validation/IOpenIddictValidationEventService.cs
  29. 77
      src/OpenIddict.Validation/Internal/OpenIddictValidationEventService.cs
  30. 20
      src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
  31. 4
      src/OpenIddict.Validation/OpenIddict.Validation.csproj
  32. 67
      src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
  33. 26
      src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
  34. 52
      src/OpenIddict.Validation/OpenIddictValidationEventService.cs
  35. 33
      src/OpenIddict.Validation/OpenIddictValidationEventState.cs
  36. 2
      src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
  37. 65
      test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
  38. 60
      test/OpenIddict.Server.Tests/OpenIddictServerEventHandlerTests.cs
  39. 92
      test/OpenIddict.Server.Tests/OpenIddictServerEventServiceTests.cs
  40. 2
      test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
  41. 65
      test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
  42. 60
      test/OpenIddict.Validation.Tests/OpenIddictValidationEventHandlerTests.cs
  43. 92
      test/OpenIddict.Validation.Tests/OpenIddictValidationEventServiceTests.cs
  44. 2
      test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs

11
OpenIddict.sln

@ -61,7 +61,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.MongoDb.Models",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Abstractions.Tests", "test\OpenIddict.Abstractions.Tests\OpenIddict.Abstractions.Tests.csproj", "{8FACE85E-EF8F-4AB1-85DD-4010D5E2165D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.MongoDb.Tests", "test\OpenIddict.MongoDb.Tests\OpenIddict.MongoDb.Tests.csproj", "{27F603EF-D335-445B-9443-6B5A6CA3C110}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.MongoDb.Tests", "test\OpenIddict.MongoDb.Tests\OpenIddict.MongoDb.Tests.csproj", "{27F603EF-D335-445B-9443-6B5A6CA3C110}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{D8075F1F-6257-463B-B481-BDC7C5ABA292}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.Extensions", "shared\OpenIddict.Extensions\OpenIddict.Extensions.csproj", "{B90761B9-7582-44CB-AB0D-3C4058693227}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -157,6 +161,10 @@ Global
{27F603EF-D335-445B-9443-6B5A6CA3C110}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27F603EF-D335-445B-9443-6B5A6CA3C110}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27F603EF-D335-445B-9443-6B5A6CA3C110}.Release|Any CPU.Build.0 = Release|Any CPU
{B90761B9-7582-44CB-AB0D-3C4058693227}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B90761B9-7582-44CB-AB0D-3C4058693227}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B90761B9-7582-44CB-AB0D-3C4058693227}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B90761B9-7582-44CB-AB0D-3C4058693227}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -184,6 +192,7 @@ Global
{14C55FB6-9626-4BDE-8961-3BE91DDD6418} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{8FACE85E-EF8F-4AB1-85DD-4010D5E2165D} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{27F603EF-D335-445B-9443-6B5A6CA3C110} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{B90761B9-7582-44CB-AB0D-3C4058693227} = {D8075F1F-6257-463B-B481-BDC7C5ABA292}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616}

24
src/OpenIddict.Core/OpenIddictCoreHelpers.cs → shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs

@ -1,23 +1,31 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace OpenIddict.Core
namespace OpenIddict.Extensions
{
/// <summary>
/// Exposes common helpers used by the OpenIddict assemblies.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class OpenIddictCoreHelpers
internal static class OpenIddictHelpers
{
/// <summary>
/// Finds the base type that matches the specified generic type definition.
/// Finds the first base type that matches the specified generic type definition.
/// </summary>
/// <param name="type">The type to introspect.</param>
/// <param name="definition">The generic type definition.</param>
/// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns>
public static Type FindGenericBaseType(Type type, Type definition)
=> FindGenericBaseTypes(type, definition).FirstOrDefault();
/// <summary>
/// Finds all the base types that matches the specified generic type definition.
/// </summary>
/// <param name="type">The type to introspect.</param>
/// <param name="definition">The generic type definition.</param>
/// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns>
public static IEnumerable<Type> FindGenericBaseTypes(Type type, Type definition)
{
if (type == null)
{
@ -45,7 +53,7 @@ namespace OpenIddict.Core
if (contract.GetGenericTypeDefinition() == definition)
{
return contract;
yield return contract;
}
}
}
@ -61,12 +69,10 @@ namespace OpenIddict.Core
if (candidate.GetGenericTypeDefinition() == definition)
{
return candidate;
yield return candidate;
}
}
}
return null;
}
}
}

14
shared/OpenIddict.Extensions/OpenIddict.Extensions.csproj

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Reflection.TypeExtensions" Version="$(TypeExtensionsVersion)" />
</ItemGroup>
</Project>

4
src/OpenIddict.Core/OpenIddict.Core.csproj

@ -30,4 +30,8 @@
<PackageReference Include="System.Security.Claims" Version="$(ClaimsVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
</Project>

17
src/OpenIddict.Core/OpenIddictCoreBuilder.cs

@ -11,6 +11,7 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
@ -87,7 +88,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictApplicationStore<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictApplicationStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -144,7 +145,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictAuthorizationStore<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictAuthorizationStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -201,7 +202,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictScopeStore<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictScopeStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -258,7 +259,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictTokenStore<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictTokenStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -315,7 +316,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictApplicationManager<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictApplicationManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -412,7 +413,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictAuthorizationManager<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictAuthorizationManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -508,7 +509,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictScopeManager<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictScopeManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@ -605,7 +606,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictTokenManager<>));
var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictTokenManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));

6
src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\packages.props" />
@ -23,4 +23,8 @@
<PackageReference Include="JetBrains.Annotations" Version="$(JetBrainsVersion)" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
</Project>

4
src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TApplication), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

4
src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

4
src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TScope), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

4
src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TToken), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

4
src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj

@ -23,4 +23,8 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(AspNetCoreVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
</Project>

4
src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TApplication), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

5
src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs

@ -8,12 +8,11 @@ using System;
using System.Collections.Concurrent;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@ -50,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

4
src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TScope), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

4
src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs

@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@ -49,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TToken), key =>
{
var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()

9
src/OpenIddict.Server/IOpenIddictServerEventHandler.cs

@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -20,12 +19,10 @@ namespace OpenIddict.Server
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result determines whether next handlers in the pipeline are invoked.
/// </returns>
Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
Task<OpenIddictServerEventState> HandleAsync([NotNull] TEvent notification);
}
}

26
src/OpenIddict.Server/IOpenIddictServerEventService.cs

@ -0,0 +1,26 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Server
{
/// <summary>
/// Dispatches events by invoking the corresponding handlers.
/// </summary>
public interface IOpenIddictServerEventService
{
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task PublishAsync<TEvent>([NotNull] TEvent notification) where TEvent : class, IOpenIddictServerEvent;
}
}

164
src/OpenIddict.Server/Internal/OpenIddictServerEventService.cs

@ -1,164 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.DependencyInjection;
using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server.Internal
{
/// <summary>
/// Dispatches events by invoking the corresponding notification handlers.
/// Note: this API supports the OpenIddict infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future minor releases.
/// </summary>
public class OpenIddictServerEventService
{
private readonly IServiceProvider _provider;
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictServerEventService"/> class.
/// Note: this API supports the OpenIddict infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future minor releases.
/// </summary>
public OpenIddictServerEventService([NotNull] IServiceProvider provider)
{
_provider = provider;
}
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public async Task PublishAsync<TEvent>([NotNull] TEvent notification, CancellationToken cancellationToken = default)
where TEvent : class, IOpenIddictServerEvent
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
foreach (var handler in _provider.GetServices<IOpenIddictServerEventHandler<TEvent>>())
{
cancellationToken.ThrowIfCancellationRequested();
await handler.HandleAsync(notification, cancellationToken);
// Note: the following logic determines whether next handlers should be invoked
// depending on whether the underlying event context was substantially updated.
switch (notification)
{
case MatchEndpoint value when value.Context.State != EventResultState.Continue: return;
case MatchEndpoint value when value.Context.IsAuthorizationEndpoint ||
value.Context.IsConfigurationEndpoint ||
value.Context.IsCryptographyEndpoint ||
value.Context.IsIntrospectionEndpoint ||
value.Context.IsLogoutEndpoint ||
value.Context.IsRevocationEndpoint ||
value.Context.IsTokenEndpoint ||
value.Context.IsUserinfoEndpoint: return;
case ExtractAuthorizationRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractConfigurationRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractCryptographyRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractIntrospectionRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractLogoutRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractRevocationRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractTokenRequest value when value.Context.State != EventResultState.Continue: return;
case ExtractUserinfoRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateAuthorizationRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateConfigurationRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateCryptographyRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateIntrospectionRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateLogoutRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateRevocationRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateTokenRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateUserinfoRequest value when value.Context.State != EventResultState.Continue: return;
case ValidateAuthorizationRequest value when value.Context.IsRejected: return;
case ValidateConfigurationRequest value when value.Context.IsRejected: return;
case ValidateCryptographyRequest value when value.Context.IsRejected: return;
case ValidateIntrospectionRequest value when value.Context.IsRejected: return;
case ValidateLogoutRequest value when value.Context.IsRejected: return;
case ValidateRevocationRequest value when value.Context.IsRejected: return;
case ValidateTokenRequest value when value.Context.IsRejected: return;
case ValidateUserinfoRequest value when value.Context.IsRejected: return;
case ValidateIntrospectionRequest value when value.Context.IsSkipped: return;
case ValidateRevocationRequest value when value.Context.IsSkipped: return;
case ValidateTokenRequest value when value.Context.IsSkipped: return;
case HandleAuthorizationRequest value when value.Context.State != EventResultState.Continue: return;
case HandleConfigurationRequest value when value.Context.State != EventResultState.Continue: return;
case HandleCryptographyRequest value when value.Context.State != EventResultState.Continue: return;
case HandleIntrospectionRequest value when value.Context.State != EventResultState.Continue: return;
case HandleLogoutRequest value when value.Context.State != EventResultState.Continue: return;
case HandleRevocationRequest value when value.Context.State != EventResultState.Continue: return;
case HandleTokenRequest value when value.Context.State != EventResultState.Continue: return;
case HandleUserinfoRequest value when value.Context.State != EventResultState.Continue: return;
case HandleAuthorizationRequest value when value.Context.Ticket != null: return;
case HandleTokenRequest value when value.Context.Ticket != null &&
!value.Context.Request.IsAuthorizationCodeGrantType() &&
!value.Context.Request.IsRefreshTokenGrantType(): return;
case HandleTokenRequest value when value.Context.Ticket == null &&
(value.Context.Request.IsAuthorizationCodeGrantType() ||
value.Context.Request.IsRefreshTokenGrantType()): return;
case HandleAuthorizationRequest value when value.Context.Ticket != null: return;
case ProcessChallengeResponse value when value.Context.State != EventResultState.Continue: return;
case ProcessSigninResponse value when value.Context.State != EventResultState.Continue: return;
case ProcessSignoutResponse value when value.Context.State != EventResultState.Continue: return;
case ProcessChallengeResponse value when value.Context.IsRejected: return;
case ProcessSigninResponse value when value.Context.IsRejected: return;
case ProcessSignoutResponse value when value.Context.IsRejected: return;
case ApplyAuthorizationResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyConfigurationResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyCryptographyResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyIntrospectionResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyLogoutResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyRevocationResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyTokenResponse value when value.Context.State != EventResultState.Continue: return;
case ApplyUserinfoResponse value when value.Context.State != EventResultState.Continue: return;
case DeserializeAuthorizationCode value when value.Context.State != EventResultState.Continue: return;
case DeserializeAccessToken value when value.Context.State != EventResultState.Continue: return;
case DeserializeIdentityToken value when value.Context.State != EventResultState.Continue: return;
case DeserializeRefreshToken value when value.Context.State != EventResultState.Continue: return;
case DeserializeAuthorizationCode value when value.Context.Ticket != null: return;
case DeserializeAccessToken value when value.Context.Ticket != null: return;
case DeserializeIdentityToken value when value.Context.Ticket != null: return;
case DeserializeRefreshToken value when value.Context.Ticket != null: return;
case SerializeAuthorizationCode value when value.Context.State != EventResultState.Continue: return;
case SerializeAccessToken value when value.Context.State != EventResultState.Continue: return;
case SerializeIdentityToken value when value.Context.State != EventResultState.Continue: return;
case SerializeRefreshToken value when value.Context.State != EventResultState.Continue: return;
case SerializeAuthorizationCode value when !string.IsNullOrEmpty(value.Context.AuthorizationCode): return;
case SerializeAccessToken value when !string.IsNullOrEmpty(value.Context.AccessToken): return;
case SerializeIdentityToken value when !string.IsNullOrEmpty(value.Context.IdentityToken): return;
case SerializeRefreshToken value when !string.IsNullOrEmpty(value.Context.RefreshToken): return;
}
}
}
}
}

4
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs

@ -684,8 +684,8 @@ namespace OpenIddict.Server.Internal
private static ILogger GetLogger(IServiceProvider provider)
=> provider.GetRequiredService<ILogger<OpenIddictServerProvider>>();
private static OpenIddictServerEventService GetEventService(IServiceProvider provider)
=> provider.GetRequiredService<OpenIddictServerEventService>();
private static IOpenIddictServerEventService GetEventService(IServiceProvider provider)
=> provider.GetRequiredService<IOpenIddictServerEventService>();
private static IOpenIddictApplicationManager GetApplicationManager(IServiceProvider provider)
=> provider.GetService<IOpenIddictApplicationManager>() ?? throw new InvalidOperationException(new StringBuilder()

1
src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs

@ -5,7 +5,6 @@
*/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;

4
src/OpenIddict.Server/OpenIddict.Server.csproj

@ -24,4 +24,8 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(AspNetCoreVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
</Project>

67
src/OpenIddict.Server/OpenIddictServerBuilder.cs

@ -12,7 +12,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
@ -21,6 +20,7 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions;
using OpenIddict.Server;
namespace Microsoft.Extensions.DependencyInjection
@ -51,13 +51,13 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
/// <summary>
/// Registers an event handler for the specified event type.
/// Registers an inline event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <param name="handler">The handler delegate.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>(
[NotNull] IOpenIddictServerEventHandler<TEvent> handler)
[NotNull] Func<TEvent, Task<OpenIddictServerEventState>> handler)
where TEvent : class, IOpenIddictServerEvent
{
if (handler == null)
@ -65,62 +65,30 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(handler));
}
Services.AddSingleton(handler);
Services.AddSingleton<IOpenIddictServerEventHandler<TEvent>>(
new OpenIddictServerEventHandler<TEvent>(handler));
return this;
}
/// <summary>
/// Registers an event handler for the specified event type.
/// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, Task> handler)
where TEvent : class, IOpenIddictServerEvent
=> AddEventHandler<TEvent>((notification, cancellationToken) => handler(notification));
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, CancellationToken, Task> handler)
where TEvent : class, IOpenIddictServerEvent
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
return AddEventHandler(new OpenIddictServerEventHandler<TEvent>(handler));
}
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <typeparam name="TEvent">The type of the event.</typeparam>
/// <typeparam name="THandler">The type of the handler.</typeparam>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent, THandler>(
ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictServerEvent
where THandler : IOpenIddictServerEventHandler<TEvent>
=> AddEventHandler<TEvent>(typeof(THandler));
public OpenIddictServerBuilder AddEventHandler<THandler>(ServiceLifetime lifetime = ServiceLifetime.Scoped)
=> AddEventHandler(typeof(THandler), lifetime);
/// <summary>
/// Registers an event handler for the specified event type.
/// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
/// </summary>
/// <param name="type">The type of the handler.</param>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>(
[NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictServerEvent
public OpenIddictServerBuilder AddEventHandler([NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
if (type == null)
{
@ -132,12 +100,21 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
if (!typeof(IOpenIddictServerEventHandler<TEvent>).IsAssignableFrom(type))
if (type.GetTypeInfo().IsGenericTypeDefinition)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
Services.Add(new ServiceDescriptor(typeof(IOpenIddictServerEventHandler<TEvent>), type, lifetime));
var services = OpenIddictHelpers.FindGenericBaseTypes(type, typeof(IOpenIddictServerEventHandler<>)).ToArray();
if (services.Length == 0)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
foreach (var service in services)
{
Services.Add(new ServiceDescriptor(service, type, lifetime));
}
return this;
}

26
src/OpenIddict.Server/OpenIddictServerEventHandler.cs

@ -5,7 +5,6 @@
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -18,35 +17,24 @@ namespace OpenIddict.Server
public class OpenIddictServerEventHandler<TEvent> : IOpenIddictServerEventHandler<TEvent>
where TEvent : class, IOpenIddictServerEvent
{
private readonly Func<TEvent, CancellationToken, Task> _handler;
private readonly Func<TEvent, Task<OpenIddictServerEventState>> _handler;
/// <summary>
/// Creates a new event using the specified handler delegate.
/// </summary>
/// <param name="handler">The event handler delegate</param>
public OpenIddictServerEventHandler([NotNull] Func<TEvent, CancellationToken, Task> handler)
/// <param name="handler">The event handler delegate.</param>
public OpenIddictServerEventHandler([NotNull] Func<TEvent, Task<OpenIddictServerEventState>> handler)
=> _handler = handler ?? throw new ArgumentNullException(nameof(handler));
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result determines whether next handlers in the pipeline are invoked.
/// </returns>
public async Task HandleAsync(TEvent notification, CancellationToken cancellationToken)
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
cancellationToken.ThrowIfCancellationRequested();
await _handler.Invoke(notification, cancellationToken);
}
public Task<OpenIddictServerEventState> HandleAsync(TEvent notification)
=> _handler(notification ?? throw new ArgumentNullException(nameof(notification)));
}
}

52
src/OpenIddict.Server/OpenIddictServerEventService.cs

@ -0,0 +1,52 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Server
{
/// <summary>
/// Dispatches events by invoking the corresponding notification handlers.
/// </summary>
public class OpenIddictServerEventService : IOpenIddictServerEventService
{
private readonly IServiceProvider _provider;
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictServerEventService"/> class.
/// </summary>
public OpenIddictServerEventService([NotNull] IServiceProvider provider)
=> _provider = provider;
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public async Task PublishAsync<TEvent>([NotNull] TEvent notification) where TEvent : class, IOpenIddictServerEvent
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
foreach (var handler in _provider.GetServices<IOpenIddictServerEventHandler<TEvent>>())
{
switch (await handler.HandleAsync(notification))
{
case OpenIddictServerEventState.Unhandled: continue;
case OpenIddictServerEventState.Handled: return;
default: throw new InvalidOperationException("The specified event state is not valid.");
}
}
}
}
}

33
src/OpenIddict.Server/OpenIddictServerEventState.cs

@ -0,0 +1,33 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
namespace OpenIddict.Server
{
/// <summary>
/// Represents the state of an event triggered by the OpenIddict
/// server components and processed by user-defined handlers.
/// </summary>
public enum OpenIddictServerEventState
{
/// <summary>
/// Marks the event as unhandled, allowing the event service to invoke the
/// other event handlers registered in the dependency injection container.
/// Using this value is recommended for event handlers that don't produce
/// an immediate response (i.e that don't call context.HandleResponse(),
/// context.SkipToNextMiddleware(), context.Validate() or context.Reject()).
/// </summary>
Unhandled = 0,
/// <summary>
/// Marks the event as fully handled, preventing the event service from invoking
/// other event handlers registered in the dependency injection container.
/// Using this value is recommended for event handlers that produce an
/// immediate response (i.e that call context.SkipToNextMiddleware(),
/// context.HandleResponse(), context.Validate() or context.Reject()).
/// </summary>
Handled = 1
}
}

2
src/OpenIddict.Server/OpenIddictServerExtensions.cs

@ -46,7 +46,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddMemoryCache();
builder.Services.AddOptions();
builder.Services.TryAddScoped<OpenIddictServerEventService>();
builder.Services.TryAddScoped<IOpenIddictServerEventService, OpenIddictServerEventService>();
return new OpenIddictServerBuilder(builder.Services);
}

9
src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs

@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -20,12 +19,10 @@ namespace OpenIddict.Validation
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result determines whether next handlers in the pipeline are invoked.
/// </returns>
Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
Task<OpenIddictValidationEventState> HandleAsync([NotNull] TEvent notification);
}
}

25
src/OpenIddict.Validation/IOpenIddictValidationEventService.cs

@ -0,0 +1,25 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Validation
{
/// <summary>
/// Dispatches events by invoking the corresponding handlers.
/// </summary>
public interface IOpenIddictValidationEventService
{
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task PublishAsync<TEvent>([NotNull] TEvent notification) where TEvent : class, IOpenIddictValidationEvent;
}
}

77
src/OpenIddict.Validation/Internal/OpenIddictValidationEventService.cs

@ -1,77 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Validation.Internal
{
/// <summary>
/// Dispatches events by invoking the corresponding notification handlers.
/// Note: this API supports the OpenIddict infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future minor releases.
/// </summary>
public class OpenIddictValidationEventService
{
private readonly IServiceProvider _provider;
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictValidationEventService"/> class.
/// Note: this API supports the OpenIddict infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future minor releases.
/// </summary>
public OpenIddictValidationEventService([NotNull] IServiceProvider provider)
{
_provider = provider;
}
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public async Task PublishAsync<TEvent>([NotNull] TEvent notification, CancellationToken cancellationToken = default)
where TEvent : class, IOpenIddictValidationEvent
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
foreach (var handler in _provider.GetServices<IOpenIddictValidationEventHandler<TEvent>>())
{
cancellationToken.ThrowIfCancellationRequested();
await handler.HandleAsync(notification, cancellationToken);
// Note: the following logic determines whether next handlers should be invoked
// depending on whether the underlying event context was substantially updated.
switch (notification)
{
case OpenIddictValidationEvents.ApplyChallenge value when value.Context.State != EventResultState.Continue: return;
case OpenIddictValidationEvents.CreateTicket value when value.Context.State != EventResultState.Continue: return;
case OpenIddictValidationEvents.CreateTicket value when value.Context.Ticket == null: return;
case OpenIddictValidationEvents.DecryptToken value when value.Context.State != EventResultState.Continue: return;
case OpenIddictValidationEvents.DecryptToken value when value.Context.Ticket != null: return;
case OpenIddictValidationEvents.RetrieveToken value when value.Context.State != EventResultState.Continue: return;
case OpenIddictValidationEvents.RetrieveToken value when value.Context.Ticket != null: return;
case OpenIddictValidationEvents.ValidateToken value when value.Context.State != EventResultState.Continue: return;
case OpenIddictValidationEvents.ValidateToken value when value.Context.Ticket == null: return;
}
}
}
}
}

20
src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs

@ -23,11 +23,11 @@ namespace OpenIddict.Validation.Internal
public sealed class OpenIddictValidationProvider : OAuthValidationEvents
{
public override Task ApplyChallenge([NotNull] ApplyChallengeContext context)
=> context.HttpContext.RequestServices.GetRequiredService<OpenIddictValidationEventService>()
=> GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.ApplyChallenge(context));
public override Task CreateTicket([NotNull] CreateTicketContext context)
=> context.HttpContext.RequestServices.GetRequiredService<OpenIddictValidationEventService>()
=> GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.CreateTicket(context));
public override async Task DecryptToken([NotNull] DecryptTokenContext context)
@ -47,7 +47,7 @@ namespace OpenIddict.Validation.Internal
.ToString());
}
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictValidationProvider>>();
var logger = GetLogger(context.HttpContext.RequestServices);
// Retrieve the token entry from the database. If it
// cannot be found, assume the token is not valid.
@ -103,12 +103,12 @@ namespace OpenIddict.Validation.Internal
context.HandleResponse();
}
await context.HttpContext.RequestServices.GetRequiredService<OpenIddictValidationEventService>()
await GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.DecryptToken(context));
}
public override Task RetrieveToken([NotNull] RetrieveTokenContext context)
=> context.HttpContext.RequestServices.GetRequiredService<OpenIddictValidationEventService>()
=> GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.RetrieveToken(context));
public override async Task ValidateToken([NotNull] ValidateTokenContext context)
@ -128,7 +128,7 @@ namespace OpenIddict.Validation.Internal
.ToString());
}
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictValidationProvider>>();
var logger = GetLogger(context.HttpContext.RequestServices);
var identifier = context.Ticket.Properties.GetProperty(OpenIddictConstants.Properties.InternalAuthorizationId);
if (!string.IsNullOrEmpty(identifier))
@ -145,8 +145,14 @@ namespace OpenIddict.Validation.Internal
}
}
await context.HttpContext.RequestServices.GetRequiredService<OpenIddictValidationEventService>()
await GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.ValidateToken(context));
}
private static ILogger GetLogger(IServiceProvider provider)
=> provider.GetRequiredService<ILogger<OpenIddictValidationProvider>>();
private static IOpenIddictValidationEventService GetEventService(IServiceProvider provider)
=> provider.GetRequiredService<IOpenIddictValidationEventService>();
}
}

4
src/OpenIddict.Validation/OpenIddict.Validation.csproj

@ -22,4 +22,8 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(AspNetCoreVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
</Project>

67
src/OpenIddict.Validation/OpenIddictValidationBuilder.cs

@ -8,10 +8,10 @@ using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
using OpenIddict.Extensions;
using OpenIddict.Validation;
namespace Microsoft.Extensions.DependencyInjection
@ -42,13 +42,13 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
/// <summary>
/// Registers an event handler for the specified event type.
/// Registers an inline event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <param name="handler">The handler delegate.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent>(
[NotNull] IOpenIddictValidationEventHandler<TEvent> handler)
[NotNull] Func<TEvent, Task<OpenIddictValidationEventState>> handler)
where TEvent : class, IOpenIddictValidationEvent
{
if (handler == null)
@ -56,62 +56,30 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(handler));
}
Services.AddSingleton(handler);
Services.AddSingleton<IOpenIddictValidationEventHandler<TEvent>>(
new OpenIddictValidationEventHandler<TEvent>(handler));
return this;
}
/// <summary>
/// Registers an event handler for the specified event type.
/// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, Task> handler)
where TEvent : class, IOpenIddictValidationEvent
=> AddEventHandler<TEvent>((notification, cancellationToken) => handler(notification));
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, CancellationToken, Task> handler)
where TEvent : class, IOpenIddictValidationEvent
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
return AddEventHandler(new OpenIddictValidationEventHandler<TEvent>(handler));
}
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <typeparam name="TEvent">The type of the event.</typeparam>
/// <typeparam name="THandler">The type of the handler.</typeparam>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent, THandler>(
ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictValidationEvent
where THandler : IOpenIddictValidationEventHandler<TEvent>
=> AddEventHandler<TEvent>(typeof(THandler));
public OpenIddictValidationBuilder AddEventHandler<THandler>(ServiceLifetime lifetime = ServiceLifetime.Scoped)
=> AddEventHandler(typeof(THandler), lifetime);
/// <summary>
/// Registers an event handler for the specified event type.
/// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
/// </summary>
/// <param name="type">The type of the handler.</param>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent>(
[NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictValidationEvent
public OpenIddictValidationBuilder AddEventHandler([NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
if (type == null)
{
@ -123,12 +91,21 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
if (!typeof(IOpenIddictValidationEventHandler<TEvent>).IsAssignableFrom(type))
if (type.GetTypeInfo().IsGenericTypeDefinition)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
Services.Add(new ServiceDescriptor(typeof(IOpenIddictValidationEventHandler<TEvent>), type, lifetime));
var services = OpenIddictHelpers.FindGenericBaseTypes(type, typeof(IOpenIddictValidationEventHandler<>)).ToArray();
if (services.Length == 0)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
foreach (var service in services)
{
Services.Add(new ServiceDescriptor(service, type, lifetime));
}
return this;
}

26
src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs

@ -5,7 +5,6 @@
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -18,35 +17,24 @@ namespace OpenIddict.Validation
public class OpenIddictValidationEventHandler<TEvent> : IOpenIddictValidationEventHandler<TEvent>
where TEvent : class, IOpenIddictValidationEvent
{
private readonly Func<TEvent, CancellationToken, Task> _handler;
private readonly Func<TEvent, Task<OpenIddictValidationEventState>> _handler;
/// <summary>
/// Creates a new event using the specified handler delegate.
/// </summary>
/// <param name="handler">The event handler delegate</param>
public OpenIddictValidationEventHandler([NotNull] Func<TEvent, CancellationToken, Task> handler)
/// <param name="handler">The event handler delegate.</param>
public OpenIddictValidationEventHandler([NotNull] Func<TEvent, Task<OpenIddictValidationEventState>> handler)
=> _handler = handler ?? throw new ArgumentNullException(nameof(handler));
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result determines whether next handlers in the pipeline are invoked.
/// </returns>
public async Task HandleAsync(TEvent notification, CancellationToken cancellationToken)
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
cancellationToken.ThrowIfCancellationRequested();
await _handler.Invoke(notification, cancellationToken);
}
public Task<OpenIddictValidationEventState> HandleAsync(TEvent notification)
=> _handler(notification ?? throw new ArgumentNullException(nameof(notification)));
}
}

52
src/OpenIddict.Validation/OpenIddictValidationEventService.cs

@ -0,0 +1,52 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Validation
{
/// <summary>
/// Dispatches events by invoking the corresponding notification handlers.
/// </summary>
public class OpenIddictValidationEventService : IOpenIddictValidationEventService
{
private readonly IServiceProvider _provider;
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictValidationEventService"/> class.
/// </summary>
public OpenIddictValidationEventService([NotNull] IServiceProvider provider)
=> _provider = provider;
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public async Task PublishAsync<TEvent>([NotNull] TEvent notification) where TEvent : class, IOpenIddictValidationEvent
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
foreach (var handler in _provider.GetServices<IOpenIddictValidationEventHandler<TEvent>>())
{
switch (await handler.HandleAsync(notification))
{
case OpenIddictValidationEventState.Unhandled: continue;
case OpenIddictValidationEventState.Handled: return;
default: throw new InvalidOperationException("The specified event state is not valid.");
}
}
}
}
}

33
src/OpenIddict.Validation/OpenIddictValidationEventState.cs

@ -0,0 +1,33 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
namespace OpenIddict.Validation
{
/// <summary>
/// Represents the state of an event triggered by the OpenIddict
/// validation components and processed by user-defined handlers.
/// </summary>
public enum OpenIddictValidationEventState
{
/// <summary>
/// Marks the event as unhandled, allowing the event service to invoke the
/// other event handlers registered in the dependency injection container.
/// Using this value is recommended for event handlers that don't produce
/// an immediate response (i.e that don't call context.HandleResponse(),
/// context.Fail(), context.NoResult() or context.Success()).
/// </summary>
Unhandled = 0,
/// <summary>
/// Marks the event as fully handled, preventing the event service from invoking
/// other event handlers registered in the dependency injection container.
/// Using this value is recommended for event handlers that produce an
/// immediate response (i.e that call context.HandleResponse(),
/// context.Fail(), context.NoResult() or context.Success()).
/// </summary>
Handled = 1
}
}

2
src/OpenIddict.Validation/OpenIddictValidationExtensions.cs

@ -42,7 +42,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddLogging();
builder.Services.AddOptions();
builder.Services.TryAddScoped<OpenIddictValidationEventService>();
builder.Services.TryAddScoped<IOpenIddictValidationEventService, OpenIddictValidationEventService>();
return new OpenIddictValidationBuilder(builder.Services);
}

65
test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

@ -7,7 +7,6 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.DataProtection;
@ -42,20 +41,18 @@ namespace OpenIddict.Server.Tests
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
var handler = new OpenIddictServerEventHandler<ApplyAuthorizationResponse>(
(notification, cancellationToken) => Task.FromResult(0));
// Act
builder.AddEventHandler(handler);
builder.AddEventHandler<ApplyAuthorizationResponse>(notification => Task.FromResult(OpenIddictServerEventState.Handled));
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler<ApplyAuthorizationResponse>) &&
service.ImplementationInstance == handler);
service.ImplementationInstance.GetType() == typeof(OpenIddictServerEventHandler<ApplyAuthorizationResponse>));
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
public void AddEventHandler_ThrowsAnExceptionForUnsupportedLifetime()
{
// Arrange
var services = CreateServices();
@ -64,7 +61,41 @@ namespace OpenIddict.Server.Tests
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler<ApplyAuthorizationResponse>(typeof(object));
return builder.AddEventHandler<CustomHandler>(ServiceLifetime.Transient);
});
Assert.Equal("lifetime", exception.ParamName);
Assert.StartsWith("Handlers cannot be registered as transient services.", exception.Message);
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForOpenGenericHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler(typeof(OpenIddictServerEventHandler<>));
});
Assert.Equal("type", exception.ParamName);
Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForNonHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler(typeof(object));
});
Assert.Equal("type", exception.ParamName);
@ -79,12 +110,17 @@ namespace OpenIddict.Server.Tests
var builder = CreateBuilder(services);
// Act
builder.AddEventHandler<ApplyAuthorizationResponse, CustomHandler>();
builder.AddEventHandler<CustomHandler>(ServiceLifetime.Singleton);
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler<ApplyAuthorizationResponse>) &&
service.ImplementationType == typeof(CustomHandler));
service.ImplementationType == typeof(CustomHandler) &&
service.Lifetime == ServiceLifetime.Singleton);
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler<HandleAuthorizationRequest>) &&
service.ImplementationType == typeof(CustomHandler) &&
service.Lifetime == ServiceLifetime.Singleton);
}
[Fact]
@ -802,10 +838,17 @@ namespace OpenIddict.Server.Tests
return options.Value;
}
public class CustomHandler : OpenIddictServerEventHandler<ApplyAuthorizationResponse>
public class CustomHandler : IOpenIddictServerEventHandler<ApplyAuthorizationResponse>,
IOpenIddictServerEventHandler<HandleAuthorizationRequest>
{
public CustomHandler(Func<ApplyAuthorizationResponse, CancellationToken, Task> handler) : base(handler)
public Task<OpenIddictServerEventState> HandleAsync(ApplyAuthorizationResponse notification)
{
throw new NotImplementedException();
}
public Task<OpenIddictServerEventState> HandleAsync(HandleAuthorizationRequest notification)
{
throw new NotImplementedException();
}
}
}

60
test/OpenIddict.Server.Tests/OpenIddictServerEventHandlerTests.cs

@ -0,0 +1,60 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading.Tasks;
using Xunit;
namespace OpenIddict.Server.Tests
{
public class OpenIddictServerEventHandlerTests
{
[Fact]
public void Constructor_ThrowsAnExceptionForNullHandler()
{
// Arrange, act and assert
var exception = Assert.Throws<ArgumentNullException>(()
=> new OpenIddictServerEventHandler<Event>(handler: null));
Assert.Equal("handler", exception.ParamName);
}
[Fact]
public async Task HandleAsync_ThrowsAnExceptionForNullNotification()
{
// Arrange
var handler = new OpenIddictServerEventHandler<Event>(
notification => Task.FromResult(OpenIddictServerEventState.Handled));
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(()
=> handler.HandleAsync(notification: null));
Assert.Equal("notification", exception.ParamName);
}
[Fact]
public async Task HandleAsync_InvokesInlineHandler()
{
// Arrange
var marker = false;
var handler = new OpenIddictServerEventHandler<Event>(
notification =>
{
marker = true;
return Task.FromResult(OpenIddictServerEventState.Handled);
});
// Act
await handler.HandleAsync(new Event());
// Assert
Assert.True(marker);
}
public class Event : IOpenIddictServerEvent { }
}
}

92
test/OpenIddict.Server.Tests/OpenIddictServerEventServiceTests.cs

@ -0,0 +1,92 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Moq;
using Xunit;
namespace OpenIddict.Server.Tests
{
public class OpenIddictServerEventServiceTests
{
[Fact]
public async Task PublishAsync_ThrowsAnExceptionForNullNotification()
{
// Arrange
var provider = Mock.Of<IServiceProvider>();
var service = new OpenIddictServerEventService(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(()
=> service.PublishAsync<Event>(notification: null));
Assert.Equal("notification", exception.ParamName);
}
[Fact]
public async Task PublishAsync_InvokesHandlers()
{
// Arrange
var handlers = new List<IOpenIddictServerEventHandler<Event>>
{
Mock.Of<IOpenIddictServerEventHandler<Event>>(),
Mock.Of<IOpenIddictServerEventHandler<Event>>()
};
var provider = new Mock<IServiceProvider>();
provider.Setup(mock => mock.GetService(typeof(IEnumerable<IOpenIddictServerEventHandler<Event>>)))
.Returns(handlers);
var service = new OpenIddictServerEventService(provider.Object);
var notification = new Event();
// Act
await service.PublishAsync(notification);
// Assert
Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
}
[Fact]
public async Task PublishAsync_StopsInvokingHandlersWhenHandledIsReturned()
{
// Arrange
var handlers = new List<IOpenIddictServerEventHandler<Event>>
{
Mock.Of<IOpenIddictServerEventHandler<Event>>(
mock => mock.HandleAsync(It.IsAny<Event>()) == Task.FromResult(OpenIddictServerEventState.Unhandled)),
Mock.Of<IOpenIddictServerEventHandler<Event>>(
mock => mock.HandleAsync(It.IsAny<Event>()) == Task.FromResult(OpenIddictServerEventState.Unhandled)),
Mock.Of<IOpenIddictServerEventHandler<Event>>(
mock => mock.HandleAsync(It.IsAny<Event>()) == Task.FromResult(OpenIddictServerEventState.Handled)),
Mock.Of<IOpenIddictServerEventHandler<Event>>()
};
var provider = new Mock<IServiceProvider>();
provider.Setup(mock => mock.GetService(typeof(IEnumerable<IOpenIddictServerEventHandler<Event>>)))
.Returns(handlers);
var service = new OpenIddictServerEventService(provider.Object);
var notification = new Event();
// Act
await service.PublishAsync(notification);
// Assert
Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[2]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[3]).Verify(mock => mock.HandleAsync(notification), Times.Never());
}
public class Event : IOpenIddictServerEvent { }
}
}

2
test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs

@ -105,7 +105,7 @@ namespace OpenIddict.Server.Tests
// Assert
Assert.Contains(services, service => service.Lifetime == ServiceLifetime.Scoped &&
service.ServiceType == typeof(OpenIddictServerEventService) &&
service.ServiceType == typeof(IOpenIddictServerEventService) &&
service.ImplementationType == typeof(OpenIddictServerEventService));
}

65
test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs

@ -5,7 +5,6 @@
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
@ -35,20 +34,18 @@ namespace OpenIddict.Validation.Tests
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
var handler = new OpenIddictValidationEventHandler<CreateTicket>(
(notification, cancellationToken) => Task.FromResult(0));
// Act
builder.AddEventHandler(handler);
builder.AddEventHandler<CreateTicket>(notification => Task.FromResult(OpenIddictValidationEventState.Handled));
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler<CreateTicket>) &&
service.ImplementationInstance == handler);
service.ImplementationInstance.GetType() == typeof(OpenIddictValidationEventHandler<CreateTicket>));
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
public void AddEventHandler_ThrowsAnExceptionForUnsupportedLifetime()
{
// Arrange
var services = CreateServices();
@ -57,7 +54,41 @@ namespace OpenIddict.Validation.Tests
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler<CreateTicket>(typeof(object));
return builder.AddEventHandler<CustomHandler>(ServiceLifetime.Transient);
});
Assert.Equal("lifetime", exception.ParamName);
Assert.StartsWith("Handlers cannot be registered as transient services.", exception.Message);
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForOpenGenericHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler(typeof(OpenIddictValidationEventHandler<>));
});
Assert.Equal("type", exception.ParamName);
Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForNonHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler(typeof(object));
});
Assert.Equal("type", exception.ParamName);
@ -72,12 +103,17 @@ namespace OpenIddict.Validation.Tests
var builder = CreateBuilder(services);
// Act
builder.AddEventHandler<CreateTicket, CustomHandler>();
builder.AddEventHandler<CustomHandler>(ServiceLifetime.Singleton);
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler<ApplyChallenge>) &&
service.ImplementationType == typeof(CustomHandler) &&
service.Lifetime == ServiceLifetime.Singleton);
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler<CreateTicket>) &&
service.ImplementationType == typeof(CustomHandler));
service.ImplementationType == typeof(CustomHandler) &&
service.Lifetime == ServiceLifetime.Singleton);
}
[Fact]
@ -204,10 +240,17 @@ namespace OpenIddict.Validation.Tests
return provider.GetRequiredService<IOptions<OpenIddictValidationOptions>>().Value;
}
public class CustomHandler : OpenIddictValidationEventHandler<CreateTicket>
public class CustomHandler : IOpenIddictValidationEventHandler<ApplyChallenge>,
IOpenIddictValidationEventHandler<CreateTicket>
{
public CustomHandler(Func<CreateTicket, CancellationToken, Task> handler) : base(handler)
public Task<OpenIddictValidationEventState> HandleAsync(ApplyChallenge notification)
{
throw new NotImplementedException();
}
public Task<OpenIddictValidationEventState> HandleAsync(CreateTicket notification)
{
throw new NotImplementedException();
}
}
}

60
test/OpenIddict.Validation.Tests/OpenIddictValidationEventHandlerTests.cs

@ -0,0 +1,60 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading.Tasks;
using Xunit;
namespace OpenIddict.Validation.Tests
{
public class OpenIddictValidationEventHandlerTests
{
[Fact]
public void Constructor_ThrowsAnExceptionForNullHandler()
{
// Arrange, act and assert
var exception = Assert.Throws<ArgumentNullException>(()
=> new OpenIddictValidationEventHandler<Event>(handler: null));
Assert.Equal("handler", exception.ParamName);
}
[Fact]
public async Task HandleAsync_ThrowsAnExceptionForNullNotification()
{
// Arrange
var handler = new OpenIddictValidationEventHandler<Event>(
notification => Task.FromResult(OpenIddictValidationEventState.Handled));
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(()
=> handler.HandleAsync(notification: null));
Assert.Equal("notification", exception.ParamName);
}
[Fact]
public async Task HandleAsync_InvokesInlineHandler()
{
// Arrange
var marker = false;
var handler = new OpenIddictValidationEventHandler<Event>(
notification =>
{
marker = true;
return Task.FromResult(OpenIddictValidationEventState.Handled);
});
// Act
await handler.HandleAsync(new Event());
// Assert
Assert.True(marker);
}
public class Event : IOpenIddictValidationEvent { }
}
}

92
test/OpenIddict.Validation.Tests/OpenIddictValidationEventServiceTests.cs

@ -0,0 +1,92 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Moq;
using Xunit;
namespace OpenIddict.Validation.Tests
{
public class OpenIddictValidationEventServiceTests
{
[Fact]
public async Task PublishAsync_ThrowsAnExceptionForNullNotification()
{
// Arrange
var provider = Mock.Of<IServiceProvider>();
var service = new OpenIddictValidationEventService(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(()
=> service.PublishAsync<Event>(notification: null));
Assert.Equal("notification", exception.ParamName);
}
[Fact]
public async Task PublishAsync_InvokesHandlers()
{
// Arrange
var handlers = new List<IOpenIddictValidationEventHandler<Event>>
{
Mock.Of<IOpenIddictValidationEventHandler<Event>>(),
Mock.Of<IOpenIddictValidationEventHandler<Event>>()
};
var provider = new Mock<IServiceProvider>();
provider.Setup(mock => mock.GetService(typeof(IEnumerable<IOpenIddictValidationEventHandler<Event>>)))
.Returns(handlers);
var service = new OpenIddictValidationEventService(provider.Object);
var notification = new Event();
// Act
await service.PublishAsync(notification);
// Assert
Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
}
[Fact]
public async Task PublishAsync_StopsInvokingHandlersWhenHandledIsReturned()
{
// Arrange
var handlers = new List<IOpenIddictValidationEventHandler<Event>>
{
Mock.Of<IOpenIddictValidationEventHandler<Event>>(
mock => mock.HandleAsync(It.IsAny<Event>()) == Task.FromResult(OpenIddictValidationEventState.Unhandled)),
Mock.Of<IOpenIddictValidationEventHandler<Event>>(
mock => mock.HandleAsync(It.IsAny<Event>()) == Task.FromResult(OpenIddictValidationEventState.Unhandled)),
Mock.Of<IOpenIddictValidationEventHandler<Event>>(
mock => mock.HandleAsync(It.IsAny<Event>()) == Task.FromResult(OpenIddictValidationEventState.Handled)),
Mock.Of<IOpenIddictValidationEventHandler<Event>>()
};
var provider = new Mock<IServiceProvider>();
provider.Setup(mock => mock.GetService(typeof(IEnumerable<IOpenIddictValidationEventHandler<Event>>)))
.Returns(handlers);
var service = new OpenIddictValidationEventService(provider.Object);
var notification = new Event();
// Act
await service.PublishAsync(notification);
// Assert
Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[2]).Verify(mock => mock.HandleAsync(notification), Times.Once());
Mock.Get(handlers[3]).Verify(mock => mock.HandleAsync(notification), Times.Never());
}
public class Event : IOpenIddictValidationEvent { }
}
}

2
test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs

@ -83,7 +83,7 @@ namespace OpenIddict.Validation.Tests
// Assert
Assert.Contains(services, service => service.Lifetime == ServiceLifetime.Scoped &&
service.ServiceType == typeof(OpenIddictValidationEventService) &&
service.ServiceType == typeof(IOpenIddictValidationEventService) &&
service.ImplementationType == typeof(OpenIddictValidationEventService));
}

Loading…
Cancel
Save