Browse Source

Rename options.AddMvcBinders() to options.UseMvc() and add an option allowing to disable binding exceptions

pull/612/head
Kévin Chalet 8 years ago
parent
commit
33af961b0c
  1. 4
      samples/Mvc.Server/Startup.cs
  2. 47
      src/OpenIddict.Mvc/OpenIddictExtensions.cs
  3. 67
      src/OpenIddict.Mvc/OpenIddictMvcBinder.cs
  4. 42
      src/OpenIddict.Mvc/OpenIddictMvcBinderProvider.cs
  5. 67
      src/OpenIddict.Mvc/OpenIddictMvcBuilder.cs
  6. 157
      src/OpenIddict.Mvc/OpenIddictMvcExtensions.cs
  7. 24
      src/OpenIddict.Mvc/OpenIddictMvcOptions.cs
  8. 1
      src/OpenIddict/OpenIddict.csproj
  9. 9
      test/OpenIddict.EntityFramework.Tests/OpenIddictEntityFrameworkExtensionsTests.cs
  10. 12
      test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs
  11. 35
      test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs
  12. 43
      test/OpenIddict.Mvc.Tests/OpenIddictMvcBuilderTests.cs
  13. 75
      test/OpenIddict.Mvc.Tests/OpenIddictMvcExtensionsTests.cs
  14. 63
      test/OpenIddict.Mvc.Tests/OpenIddictMvcModelBinderProviderTests.cs
  15. 118
      test/OpenIddict.Mvc.Tests/OpenIddictMvcModelBinderTests.cs
  16. 31
      test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

4
samples/Mvc.Server/Startup.cs

@ -79,10 +79,10 @@ namespace Mvc.Server
// Register the OpenIddict server handler. // Register the OpenIddict server handler.
.AddServer(options => .AddServer(options =>
{ {
// Register the ASP.NET Core MVC binder used by OpenIddict. // Register the ASP.NET Core MVC services used by OpenIddict.
// Note: if you don't call this method, you won't be able to // Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters. // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.AddMvcBinders(); options.UseMvc();
// Enable the authorization, logout, token and userinfo endpoints. // Enable the authorization, logout, token and userinfo endpoints.
options.EnableAuthorizationEndpoint("/connect/authorize") options.EnableAuthorizationEndpoint("/connect/authorize")

47
src/OpenIddict.Mvc/OpenIddictExtensions.cs

@ -1,47 +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 JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Mvc;
namespace Microsoft.Extensions.DependencyInjection
{
public static class OpenIddictExtensions
{
/// <summary>
/// Registers the ASP.NET Core MVC model binders used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public static OpenIddictServerBuilder AddMvcBinders([NotNull] this OpenIddictServerBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.Services.Configure<MvcOptions>(options =>
{
// Skip the binder registration if it was already added to the providers collection.
for (var index = 0; index < options.ModelBinderProviders.Count; index++)
{
var provider = options.ModelBinderProviders[index];
if (provider is OpenIddictModelBinder)
{
return;
}
}
options.ModelBinderProviders.Insert(0, new OpenIddictModelBinder());
});
return builder;
}
}
}

67
src/OpenIddict.Mvc/OpenIddictModelBinder.cs → src/OpenIddict.Mvc/OpenIddictMvcBinder.cs

@ -1,10 +1,18 @@
using System; /*
* 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.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace OpenIddict.Mvc namespace OpenIddict.Mvc
{ {
@ -13,8 +21,15 @@ namespace OpenIddict.Mvc
/// <see cref="OpenIdConnectRequest"/> and /// <see cref="OpenIdConnectRequest"/> and
/// <see cref="OpenIdConnectResponse"/> instances. /// <see cref="OpenIdConnectResponse"/> instances.
/// </summary> /// </summary>
public class OpenIddictModelBinder : IModelBinder, IModelBinderProvider public class OpenIddictMvcBinder : IModelBinder
{ {
private readonly IOptionsMonitor<OpenIddictMvcOptions> _options;
public OpenIddictMvcBinder([NotNull] IOptionsMonitor<OpenIddictMvcOptions> options)
{
_options = options;
}
/// <summary> /// <summary>
/// Tries to bind a model from the request. /// Tries to bind a model from the request.
/// </summary> /// </summary>
@ -30,21 +45,27 @@ namespace OpenIddict.Mvc
if (context.ModelType == typeof(OpenIdConnectRequest)) if (context.ModelType == typeof(OpenIdConnectRequest))
{ {
var request = context.HttpContext.GetOpenIdConnectRequest(); var request = context.HttpContext.GetOpenIdConnectRequest();
if (request == null) if (request == null && !_options.CurrentValue.DisableBindingExceptions)
{ {
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved from the ASP.NET context. " + throw new InvalidOperationException(new StringBuilder()
"Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' " + .AppendLine("The OpenID Connect request cannot be retrieved from the ASP.NET context.")
"and that the action route corresponds to the endpoint path registered via " + .Append("Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' ")
"'services.AddOpenIddict().Enable[...]Endpoint(...)'."); .Append("and that the action route corresponds to the endpoint path registered via ")
.Append("'services.AddOpenIddict().AddServer().Enable[...]Endpoint(...)'.")
.ToString());
} }
// Add a new validation state entry to prevent the built-in if (request != null)
// model validators from validating the OpenID Connect request.
context.ValidationState.Add(request, new ValidationStateEntry
{ {
SuppressValidation = true // Add a new validation state entry to prevent the built-in
}); // model validators from validating the OpenID Connect request.
context.ValidationState.Add(request, new ValidationStateEntry
{
SuppressValidation = true
});
}
context.BindingSource = BindingSource.Special;
context.Result = ModelBindingResult.Success(request); context.Result = ModelBindingResult.Success(request);
return Task.CompletedTask; return Task.CompletedTask;
@ -63,6 +84,7 @@ namespace OpenIddict.Mvc
}); });
} }
context.BindingSource = BindingSource.Special;
context.Result = ModelBindingResult.Success(response); context.Result = ModelBindingResult.Success(response);
return Task.CompletedTask; return Task.CompletedTask;
@ -70,26 +92,5 @@ namespace OpenIddict.Mvc
throw new InvalidOperationException("The specified model type is not supported by this binder."); throw new InvalidOperationException("The specified model type is not supported by this binder.");
} }
/// <summary>
/// Tries to resolve the model binder corresponding to the given model.
/// </summary>
/// <param name="context">The model binding context.</param>
/// <returns>The current instance or <c>null</c> if the model is not supported.</returns>
public IModelBinder GetBinder([NotNull] ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(OpenIdConnectRequest) ||
context.Metadata.ModelType == typeof(OpenIdConnectResponse))
{
return this;
}
return null;
}
} }
} }

42
src/OpenIddict.Mvc/OpenIddictMvcBinderProvider.cs

@ -0,0 +1,42 @@
/*
* 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 AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
namespace OpenIddict.Mvc
{
/// <summary>
/// Represents an ASP.NET Core MVC model binder provider that is
/// able to provide instances of <see cref="OpenIddictMvcBinder"/>.
/// </summary>
public class OpenIddictMvcBinderProvider : IModelBinderProvider
{
/// <summary>
/// Tries to resolve the model binder corresponding to the given model.
/// </summary>
/// <param name="context">The model binding context.</param>
/// <returns>The current instance or <c>null</c> if the model is not supported.</returns>
public IModelBinder GetBinder([NotNull] ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(OpenIdConnectRequest) ||
context.Metadata.ModelType == typeof(OpenIdConnectResponse))
{
return new BinderTypeModelBinder(typeof(OpenIddictMvcBinder));
}
return null;
}
}
}

67
src/OpenIddict.Mvc/OpenIddictMvcBuilder.cs

@ -0,0 +1,67 @@
/*
* 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.ComponentModel;
using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using OpenIddict.Mvc;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Exposes the necessary methods required to configure the OpenIddict MVC integration.
/// </summary>
public class OpenIddictMvcBuilder
{
/// <summary>
/// Initializes a new instance of <see cref="OpenIddictMvcBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictMvcBuilder([NotNull] IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
Services = services;
}
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict MVC configuration.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictMvcBuilder"/>.</returns>
public OpenIddictMvcBuilder Configure([NotNull] Action<OpenIddictMvcOptions> configuration)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
Services.Configure(configuration);
return this;
}
/// <summary>
/// Configures the OpenIddict MVC binder to avoid throwing an exception
/// when it is unable to bind <see cref="OpenIdConnectRequest"/>
/// parameters (e.g because the endpoint is not an OpenID Connect endpoint).
/// </summary>
/// <returns>The <see cref="OpenIddictMvcBuilder"/>.</returns>
public OpenIddictMvcBuilder DisableBindingExceptions()
=> Configure(options => options.DisableBindingExceptions = true);
}
}

157
src/OpenIddict.Mvc/OpenIddictMvcExtensions.cs

@ -0,0 +1,157 @@
/*
* 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 JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Mvc;
namespace Microsoft.Extensions.DependencyInjection
{
public static class OpenIddictMvcExtensions
{
/// <summary>
/// Registers the ASP.NET Core MVC services used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictMvcBuilder"/>.</returns>
public static OpenIddictMvcBuilder UseMvc([NotNull] this OpenIddictServerBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return RegisterMvcServices(builder.Services);
}
/// <summary>
/// Registers the ASP.NET Core MVC model binders used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the MVC services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public static OpenIddictServerBuilder UseMvc(
[NotNull] this OpenIddictServerBuilder builder,
[NotNull] Action<OpenIddictMvcBuilder> configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
configuration(builder.UseMvc());
return builder;
}
/// <summary>
/// Registers the ASP.NET Core MVC services used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by ASP.NET Core MVC to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictMvcBuilder"/>.</returns>
public static OpenIddictMvcBuilder UseOpenIddict([NotNull] this IMvcCoreBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return RegisterMvcServices(builder.Services);
}
/// <summary>
/// Registers the ASP.NET Core MVC model binders used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by ASP.NET Core MVC to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the MVC services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
public static IMvcCoreBuilder UseOpenIddict(
[NotNull] this IMvcCoreBuilder builder,
[NotNull] Action<OpenIddictMvcBuilder> configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
configuration(builder.UseOpenIddict());
return builder;
}
/// <summary>
/// Registers the ASP.NET Core MVC services used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by ASP.NET Core MVC to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictMvcBuilder"/>.</returns>
public static OpenIddictMvcBuilder UseOpenIddict([NotNull] this IMvcBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return RegisterMvcServices(builder.Services);
}
/// <summary>
/// Registers the ASP.NET Core MVC model binders used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by ASP.NET Core MVC to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the MVC services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
public static IMvcBuilder UseOpenIddict(
[NotNull] this IMvcBuilder builder,
[NotNull] Action<OpenIddictMvcBuilder> configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
configuration(builder.UseOpenIddict());
return builder;
}
/// <summary>
/// Registers the ASP.NET Core MVC services used by OpenIddict.
/// </summary>
/// <param name="services">The services collection.</param>
/// <returns>The <see cref="OpenIddictMvcBuilder"/>.</returns>
private static OpenIddictMvcBuilder RegisterMvcServices([NotNull] IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.Configure<MvcOptions>(options =>
{
// Skip the binder registration if it was already added to the providers collection.
for (var index = 0; index < options.ModelBinderProviders.Count; index++)
{
var provider = options.ModelBinderProviders[index];
if (provider is OpenIddictMvcBinderProvider)
{
return;
}
}
options.ModelBinderProviders.Insert(0, new OpenIddictMvcBinderProvider());
});
return new OpenIddictMvcBuilder(services);
}
}
}

24
src/OpenIddict.Mvc/OpenIddictMvcOptions.cs

@ -0,0 +1,24 @@
/*
* 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 AspNet.Security.OpenIdConnect.Primitives;
namespace OpenIddict.Mvc
{
/// <summary>
/// Provides various settings needed to configure the OpenIddict MVC integration.
/// </summary>
public class OpenIddictMvcOptions
{
/// <summary>
/// Gets or sets a boolean indicating whether the OpenIddict MVC binder should throw
/// an exception when it is unable to bind <see cref="OpenIdConnectRequest"/>
/// parameters (e.g because the endpoint is not an OpenID Connect endpoint).
/// If exceptions are disabled, the model is automatically set to <c>null</c>.
/// </summary>
public bool DisableBindingExceptions { get; set; }
}
}

1
src/OpenIddict/OpenIddict.csproj

@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenIddict.Core\OpenIddict.Core.csproj" /> <ProjectReference Include="..\OpenIddict.Core\OpenIddict.Core.csproj" />
<ProjectReference Include="..\OpenIddict.Mvc\OpenIddict.Mvc.csproj" />
<ProjectReference Include="..\OpenIddict.Server\OpenIddict.Server.csproj" /> <ProjectReference Include="..\OpenIddict.Server\OpenIddict.Server.csproj" />
<ProjectReference Include="..\OpenIddict.Validation\OpenIddict.Validation.csproj" /> <ProjectReference Include="..\OpenIddict.Validation\OpenIddict.Validation.csproj" />
</ItemGroup> </ItemGroup>

9
test/OpenIddict.EntityFramework.Tests/OpenIddictEntityFrameworkExtensionsTests.cs

@ -8,7 +8,6 @@ using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using OpenIddict.Core; using OpenIddict.Core;
using OpenIddict.EntityFramework;
using OpenIddict.EntityFramework.Models; using OpenIddict.EntityFramework.Models;
using Xunit; using Xunit;
@ -24,8 +23,8 @@ namespace OpenIddict.EntityFramework.Tests
public void AddEntityFrameworkStores_RegistersEntityFrameworkStoreFactories(Type type) public void AddEntityFrameworkStores_RegistersEntityFrameworkStoreFactories(Type type)
{ {
// Arrange // Arrange
var services = new ServiceCollection(); var services = new ServiceCollection().AddOptions();
var builder = services.AddOpenIddict().AddCore(); var builder = new OpenIddictCoreBuilder(services);
// Act // Act
builder.UseEntityFramework(); builder.UseEntityFramework();
@ -38,8 +37,8 @@ namespace OpenIddict.EntityFramework.Tests
public void UseEntityFrameworkModels_KeyTypeDefaultsToString() public void UseEntityFrameworkModels_KeyTypeDefaultsToString()
{ {
// Arrange // Arrange
var services = new ServiceCollection(); var services = new ServiceCollection().AddOptions();
var builder = services.AddOpenIddict().AddCore(); var builder = new OpenIddictCoreBuilder(services);
// Act // Act
builder.UseEntityFramework(); builder.UseEntityFramework();

12
test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictEntityFrameworkCoreExtensionsTests.cs

@ -23,8 +23,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests
public void UseEntityFrameworkCore_RegistersEntityFrameworkCoreStoreFactories(Type type) public void UseEntityFrameworkCore_RegistersEntityFrameworkCoreStoreFactories(Type type)
{ {
// Arrange // Arrange
var services = new ServiceCollection(); var services = new ServiceCollection().AddOptions();
var builder = services.AddOpenIddict().AddCore(); var builder = new OpenIddictCoreBuilder(services);
// Act // Act
builder.UseEntityFrameworkCore(); builder.UseEntityFrameworkCore();
@ -37,8 +37,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests
public void UseEntityFrameworkCore_KeyTypeDefaultsToString() public void UseEntityFrameworkCore_KeyTypeDefaultsToString()
{ {
// Arrange // Arrange
var services = new ServiceCollection(); var services = new ServiceCollection().AddOptions();
var builder = services.AddOpenIddict().AddCore(); var builder = new OpenIddictCoreBuilder(services);
// Act // Act
builder.UseEntityFrameworkCore(); builder.UseEntityFrameworkCore();
@ -57,8 +57,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests
public void UseEntityFrameworkCore_KeyTypeCanBeOverriden() public void UseEntityFrameworkCore_KeyTypeCanBeOverriden()
{ {
// Arrange // Arrange
var services = new ServiceCollection(); var services = new ServiceCollection().AddOptions();
var builder = services.AddOpenIddict().AddCore(); var builder = new OpenIddictCoreBuilder(services);
// Act // Act
builder.UseEntityFrameworkCore().ReplaceDefaultEntities<Guid>(); builder.UseEntityFrameworkCore().ReplaceDefaultEntities<Guid>();

35
test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs

@ -1,35 +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 Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictExtensionsTests
{
[Fact]
public void AddMvcBinders_RegistersModelBinderProvider()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
var builder = services.AddOpenIddict().AddServer();
// Act
builder.AddMvcBinders();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<MvcOptions>>();
// Assert
Assert.Contains(options.Value.ModelBinderProviders, binder => binder is OpenIddictModelBinder);
}
}
}

43
test/OpenIddict.Mvc.Tests/OpenIddictMvcBuilderTests.cs

@ -0,0 +1,43 @@
/*
* 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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictMvcBuilderTests
{
[Fact]
public void Configure_OptionsAreCorrectlyAmended()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.Configure(configuration => configuration.DisableBindingExceptions = true);
var options = GetOptions(services);
// Assert
Assert.True(options.DisableBindingExceptions);
}
private static IServiceCollection CreateServices()
=> new ServiceCollection().AddOptions();
private static OpenIddictMvcBuilder CreateBuilder(IServiceCollection services)
=> new OpenIddictMvcBuilder(services);
private static OpenIddictMvcOptions GetOptions(IServiceCollection services)
{
var provider = services.BuildServiceProvider();
return provider.GetRequiredService<IOptionsMonitor<OpenIddictMvcOptions>>().CurrentValue;
}
}
}

75
test/OpenIddict.Mvc.Tests/OpenIddictMvcExtensionsTests.cs

@ -0,0 +1,75 @@
/*
* 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 Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictMvcExtensionsTests
{
[Fact]
public void UseMvc_RegistersModelBinderProvider()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
var builder = new OpenIddictServerBuilder(services);
// Act
builder.UseMvc();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<MvcOptions>>();
// Assert
Assert.Contains(options.Value.ModelBinderProviders, binder => binder is OpenIddictMvcBinderProvider);
}
[Fact]
public void UseOpenIddict_MvcCoreBuilder_RegistersModelBinderProvider()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
var builder = new MvcCoreBuilder(services, new ApplicationPartManager());
// Act
builder.UseOpenIddict();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<MvcOptions>>();
// Assert
Assert.Contains(options.Value.ModelBinderProviders, binder => binder is OpenIddictMvcBinderProvider);
}
[Fact]
public void UseOpenIddict_MvcBuilder_RegistersModelBinderProvider()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
var builder = new MvcBuilder(services, new ApplicationPartManager());
// Act
builder.UseOpenIddict();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptions<MvcOptions>>();
// Assert
Assert.Contains(options.Value.ModelBinderProviders, binder => binder is OpenIddictMvcBinderProvider);
}
}
}

63
test/OpenIddict.Mvc.Tests/OpenIddictMvcModelBinderProviderTests.cs

@ -0,0 +1,63 @@
/*
* 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 AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Moq;
using Xunit;
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictMvcModelBinderProviderTests
{
[Theory]
[InlineData(typeof(object))]
[InlineData(typeof(IList<int>))]
[InlineData(typeof(int[]))]
public void GetBinder_ReturnsNullForUnsupportedTypes(Type type)
{
// Arrange
var provider = new OpenIddictMvcBinderProvider();
var metadata = new Mock<ModelMetadata>(ModelMetadataIdentity.ForType(type));
var context = new Mock<ModelBinderProviderContext>();
context.Setup(mock => mock.Metadata)
.Returns(metadata.Object);
// Act and assert
Assert.Null(provider.GetBinder(context.Object));
}
[Theory]
[InlineData(typeof(OpenIdConnectRequest))]
[InlineData(typeof(OpenIdConnectResponse))]
public void GetBinder_ReturnsNonNullForSupportedTypes(Type type)
{
// Arrange
var provider = new OpenIddictMvcBinderProvider();
var metadata = new Mock<ModelMetadata>(ModelMetadataIdentity.ForType(type));
var context = new Mock<ModelBinderProviderContext>();
context.Setup(mock => mock.Metadata)
.Returns(metadata.Object);
// Act and assert
Assert.NotNull((BinderTypeModelBinder) provider.GetBinder(context.Object));
}
}
}

118
test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs → test/OpenIddict.Mvc.Tests/OpenIddictMvcModelBinderTests.cs

@ -6,6 +6,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
@ -13,14 +14,14 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
namespace OpenIddict.Mvc.Tests namespace OpenIddict.Mvc.Tests
{ {
public class OpenIddictModelBinderTests public class OpenIddictMvcModelBinderTests
{ {
[Theory] [Theory]
[InlineData(typeof(object))] [InlineData(typeof(object))]
@ -29,7 +30,10 @@ namespace OpenIddict.Mvc.Tests
public async Task BindModelAsync_ThrowsAnExceptionForUnsupportedTypes(Type type) public async Task BindModelAsync_ThrowsAnExceptionForUnsupportedTypes(Type type)
{ {
// Arrange // Arrange
var binder = new OpenIddictModelBinder(); var options = Mock.Of<IOptionsMonitor<OpenIddictMvcOptions>>(
mock => mock.CurrentValue == new OpenIddictMvcOptions());
var binder = new OpenIddictMvcBinder(options);
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext var context = new DefaultModelBindingContext
@ -50,7 +54,10 @@ namespace OpenIddict.Mvc.Tests
public async Task BindModelAsync_ThrowsAnExceptionWhenRequestCannotBeFound() public async Task BindModelAsync_ThrowsAnExceptionWhenRequestCannotBeFound()
{ {
// Arrange // Arrange
var binder = new OpenIddictModelBinder(); var options = Mock.Of<IOptionsMonitor<OpenIddictMvcOptions>>(
mock => mock.CurrentValue == new OpenIddictMvcOptions());
var binder = new OpenIddictMvcBinder(options);
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext var context = new DefaultModelBindingContext
@ -69,17 +76,54 @@ namespace OpenIddict.Mvc.Tests
return binder.BindModelAsync(context); return binder.BindModelAsync(context);
}); });
Assert.Equal("The OpenID Connect request cannot be retrieved from the ASP.NET context. " + Assert.Equal(new StringBuilder()
"Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' " + .AppendLine("The OpenID Connect request cannot be retrieved from the ASP.NET context.")
"and that the action route corresponds to the endpoint path registered via " + .Append("Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' ")
"'services.AddOpenIddict().Enable[...]Endpoint(...)'.", exception.Message); .Append("and that the action route corresponds to the endpoint path registered via ")
.Append("'services.AddOpenIddict().AddServer().Enable[...]Endpoint(...)'.")
.ToString(), exception.Message);
}
[Fact]
public async Task BindModelAsync_ReturnsNullWhenRequestCannotBeFoundAndExceptionsAreDisabled()
{
// Arrange
var options = Mock.Of<IOptionsMonitor<OpenIddictMvcOptions>>(
mock => mock.CurrentValue == new OpenIddictMvcOptions
{
DisableBindingExceptions = true
});
var binder = new OpenIddictMvcBinder(options);
var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(),
},
ModelMetadata = provider.GetMetadataForType(typeof(OpenIdConnectRequest))
};
// Act
await binder.BindModelAsync(context);
// Assert
Assert.True(context.Result.IsModelSet);
Assert.Null(context.Result.Model);
Assert.Equal(BindingSource.Special, context.BindingSource);
} }
[Fact] [Fact]
public async Task BindModelAsync_ReturnsNullWhenResponseCannotBeFound() public async Task BindModelAsync_ReturnsNullWhenResponseCannotBeFound()
{ {
// Arrange // Arrange
var binder = new OpenIddictModelBinder(); var options = Mock.Of<IOptionsMonitor<OpenIddictMvcOptions>>(
mock => mock.CurrentValue == new OpenIddictMvcOptions());
var binder = new OpenIddictMvcBinder(options);
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext var context = new DefaultModelBindingContext
@ -100,13 +144,17 @@ namespace OpenIddict.Mvc.Tests
// Assert // Assert
Assert.True(context.Result.IsModelSet); Assert.True(context.Result.IsModelSet);
Assert.Null(context.Result.Model); Assert.Null(context.Result.Model);
Assert.Equal(BindingSource.Special, context.BindingSource);
} }
[Fact] [Fact]
public async Task BindModelAsync_ReturnsAmbientRequest() public async Task BindModelAsync_ReturnsAmbientRequest()
{ {
// Arrange // Arrange
var binder = new OpenIddictModelBinder(); var options = Mock.Of<IOptionsMonitor<OpenIddictMvcOptions>>(
mock => mock.CurrentValue == new OpenIddictMvcOptions());
var binder = new OpenIddictMvcBinder(options);
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var request = new OpenIdConnectRequest(); var request = new OpenIdConnectRequest();
@ -136,13 +184,17 @@ namespace OpenIddict.Mvc.Tests
Assert.True(context.Result.IsModelSet); Assert.True(context.Result.IsModelSet);
Assert.Same(request, context.Result.Model); Assert.Same(request, context.Result.Model);
Assert.True(context.ValidationState[request].SuppressValidation); Assert.True(context.ValidationState[request].SuppressValidation);
Assert.Equal(BindingSource.Special, context.BindingSource);
} }
[Fact] [Fact]
public async Task BindModelAsync_ReturnsAmbientResponse() public async Task BindModelAsync_ReturnsAmbientResponse()
{ {
// Arrange // Arrange
var binder = new OpenIddictModelBinder(); var options = Mock.Of<IOptionsMonitor<OpenIddictMvcOptions>>(
mock => mock.CurrentValue == new OpenIddictMvcOptions());
var binder = new OpenIddictMvcBinder(options);
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var response = new OpenIdConnectResponse(); var response = new OpenIdConnectResponse();
@ -172,49 +224,7 @@ namespace OpenIddict.Mvc.Tests
Assert.True(context.Result.IsModelSet); Assert.True(context.Result.IsModelSet);
Assert.Same(response, context.Result.Model); Assert.Same(response, context.Result.Model);
Assert.True(context.ValidationState[response].SuppressValidation); Assert.True(context.ValidationState[response].SuppressValidation);
} Assert.Equal(BindingSource.Special, context.BindingSource);
[Theory]
[InlineData(typeof(object))]
[InlineData(typeof(IList<int>))]
[InlineData(typeof(int[]))]
public void GetBinder_ReturnsNullForUnsupportedTypes(Type type)
{
// Arrange
var provider = new OpenIddictModelBinder();
var metadata = new Mock<ModelMetadata>(ModelMetadataIdentity.ForType(type));
var context = new Mock<ModelBinderProviderContext>();
context.Setup(mock => mock.Metadata)
.Returns(metadata.Object);
// Act
var result = provider.GetBinder(context.Object);
// Assert
Assert.Null(result);
}
[Theory]
[InlineData(typeof(OpenIdConnectRequest))]
[InlineData(typeof(OpenIdConnectResponse))]
public void GetBinder_ReturnsNonNullForSupportedTypes(Type type)
{
// Arrange
var binder = new OpenIddictModelBinder();
var metadata = new Mock<ModelMetadata>(ModelMetadataIdentity.ForType(type));
var context = new Mock<ModelBinderProviderContext>();
context.Setup(mock => mock.Metadata)
.Returns(metadata.Object);
// Act
var result = binder.GetBinder(context.Object);
// Assert
Assert.Same(binder, result);
} }
} }
} }

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

@ -8,13 +8,9 @@ using System;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Reflection; using System.Reflection;
using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Moq; using Moq;
@ -669,36 +665,15 @@ namespace OpenIddict.Server.Tests
Assert.True(options.UseReferenceTokens); Assert.True(options.UseReferenceTokens);
} }
private static OpenIddictServerBuilder CreateBuilder(IServiceCollection services)
=> services.AddOpenIddict()
.AddCore(options =>
{
options.SetDefaultApplicationEntity<OpenIddictApplication>()
.SetDefaultAuthorizationEntity<OpenIddictAuthorization>()
.SetDefaultScopeEntity<OpenIddictScope>()
.SetDefaultTokenEntity<OpenIddictToken>();
})
.AddServer();
private static IServiceCollection CreateServices() private static IServiceCollection CreateServices()
{ => new ServiceCollection().AddOptions();
var services = new ServiceCollection();
services.AddAuthentication();
services.AddDistributedMemoryCache();
services.AddLogging();
services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
return services; private static OpenIddictServerBuilder CreateBuilder(IServiceCollection services)
} => new OpenIddictServerBuilder(services);
private static OpenIddictServerOptions GetOptions(IServiceCollection services) private static OpenIddictServerOptions GetOptions(IServiceCollection services)
{ {
services.RemoveAll<IPostConfigureOptions<OpenIdConnectServerOptions>>();
services.RemoveAll<IPostConfigureOptions<OpenIddictServerOptions>>();
var provider = services.BuildServiceProvider(); var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictServerOptions>>(); var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictServerOptions>>();
return options.Get(OpenIddictServerDefaults.AuthenticationScheme); return options.Get(OpenIddictServerDefaults.AuthenticationScheme);
} }

Loading…
Cancel
Save