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.
.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
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.AddMvcBinders();
options.UseMvc();
// Enable the authorization, logout, token and userinfo endpoints.
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 AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace OpenIddict.Mvc
{
@ -13,8 +21,15 @@ namespace OpenIddict.Mvc
/// <see cref="OpenIdConnectRequest"/> and
/// <see cref="OpenIdConnectResponse"/> instances.
/// </summary>
public class OpenIddictModelBinder : IModelBinder, IModelBinderProvider
public class OpenIddictMvcBinder : IModelBinder
{
private readonly IOptionsMonitor<OpenIddictMvcOptions> _options;
public OpenIddictMvcBinder([NotNull] IOptionsMonitor<OpenIddictMvcOptions> options)
{
_options = options;
}
/// <summary>
/// Tries to bind a model from the request.
/// </summary>
@ -30,21 +45,27 @@ namespace OpenIddict.Mvc
if (context.ModelType == typeof(OpenIdConnectRequest))
{
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. " +
"Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' " +
"and that the action route corresponds to the endpoint path registered via " +
"'services.AddOpenIddict().Enable[...]Endpoint(...)'.");
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The OpenID Connect request cannot be retrieved from the ASP.NET context.")
.Append("Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' ")
.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
// model validators from validating the OpenID Connect request.
context.ValidationState.Add(request, new ValidationStateEntry
if (request != null)
{
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);
return Task.CompletedTask;
@ -63,6 +84,7 @@ namespace OpenIddict.Mvc
});
}
context.BindingSource = BindingSource.Special;
context.Result = ModelBindingResult.Success(response);
return Task.CompletedTask;
@ -70,26 +92,5 @@ namespace OpenIddict.Mvc
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>
<ProjectReference Include="..\OpenIddict.Core\OpenIddict.Core.csproj" />
<ProjectReference Include="..\OpenIddict.Mvc\OpenIddict.Mvc.csproj" />
<ProjectReference Include="..\OpenIddict.Server\OpenIddict.Server.csproj" />
<ProjectReference Include="..\OpenIddict.Validation\OpenIddict.Validation.csproj" />
</ItemGroup>

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

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

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

@ -23,8 +23,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests
public void UseEntityFrameworkCore_RegistersEntityFrameworkCoreStoreFactories(Type type)
{
// Arrange
var services = new ServiceCollection();
var builder = services.AddOpenIddict().AddCore();
var services = new ServiceCollection().AddOptions();
var builder = new OpenIddictCoreBuilder(services);
// Act
builder.UseEntityFrameworkCore();
@ -37,8 +37,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests
public void UseEntityFrameworkCore_KeyTypeDefaultsToString()
{
// Arrange
var services = new ServiceCollection();
var builder = services.AddOpenIddict().AddCore();
var services = new ServiceCollection().AddOptions();
var builder = new OpenIddictCoreBuilder(services);
// Act
builder.UseEntityFrameworkCore();
@ -57,8 +57,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests
public void UseEntityFrameworkCore_KeyTypeCanBeOverriden()
{
// Arrange
var services = new ServiceCollection();
var builder = services.AddOpenIddict().AddCore();
var services = new ServiceCollection().AddOptions();
var builder = new OpenIddictCoreBuilder(services);
// Act
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.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
@ -13,14 +14,14 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictModelBinderTests
public class OpenIddictMvcModelBinderTests
{
[Theory]
[InlineData(typeof(object))]
@ -29,7 +30,10 @@ namespace OpenIddict.Mvc.Tests
public async Task BindModelAsync_ThrowsAnExceptionForUnsupportedTypes(Type type)
{
// 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 context = new DefaultModelBindingContext
@ -50,7 +54,10 @@ namespace OpenIddict.Mvc.Tests
public async Task BindModelAsync_ThrowsAnExceptionWhenRequestCannotBeFound()
{
// 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 context = new DefaultModelBindingContext
@ -69,17 +76,54 @@ namespace OpenIddict.Mvc.Tests
return binder.BindModelAsync(context);
});
Assert.Equal("The OpenID Connect request cannot be retrieved from the ASP.NET context. " +
"Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' " +
"and that the action route corresponds to the endpoint path registered via " +
"'services.AddOpenIddict().Enable[...]Endpoint(...)'.", exception.Message);
Assert.Equal(new StringBuilder()
.AppendLine("The OpenID Connect request cannot be retrieved from the ASP.NET context.")
.Append("Make sure that 'app.UseAuthentication()' is called before 'app.UseMvc()' ")
.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]
public async Task BindModelAsync_ReturnsNullWhenResponseCannotBeFound()
{
// 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 context = new DefaultModelBindingContext
@ -100,13 +144,17 @@ namespace OpenIddict.Mvc.Tests
// Assert
Assert.True(context.Result.IsModelSet);
Assert.Null(context.Result.Model);
Assert.Equal(BindingSource.Special, context.BindingSource);
}
[Fact]
public async Task BindModelAsync_ReturnsAmbientRequest()
{
// 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 request = new OpenIdConnectRequest();
@ -136,13 +184,17 @@ namespace OpenIddict.Mvc.Tests
Assert.True(context.Result.IsModelSet);
Assert.Same(request, context.Result.Model);
Assert.True(context.ValidationState[request].SuppressValidation);
Assert.Equal(BindingSource.Special, context.BindingSource);
}
[Fact]
public async Task BindModelAsync_ReturnsAmbientResponse()
{
// 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 response = new OpenIdConnectResponse();
@ -172,49 +224,7 @@ namespace OpenIddict.Mvc.Tests
Assert.True(context.Result.IsModelSet);
Assert.Same(response, context.Result.Model);
Assert.True(context.ValidationState[response].SuppressValidation);
}
[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);
Assert.Equal(BindingSource.Special, context.BindingSource);
}
}
}

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

@ -8,13 +8,9 @@ using System;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Moq;
@ -669,36 +665,15 @@ namespace OpenIddict.Server.Tests
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()
{
var services = new ServiceCollection();
services.AddAuthentication();
services.AddDistributedMemoryCache();
services.AddLogging();
services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
=> new ServiceCollection().AddOptions();
return services;
}
private static OpenIddictServerBuilder CreateBuilder(IServiceCollection services)
=> new OpenIddictServerBuilder(services);
private static OpenIddictServerOptions GetOptions(IServiceCollection services)
{
services.RemoveAll<IPostConfigureOptions<OpenIdConnectServerOptions>>();
services.RemoveAll<IPostConfigureOptions<OpenIddictServerOptions>>();
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictServerOptions>>();
return options.Get(OpenIddictServerDefaults.AuthenticationScheme);
}

Loading…
Cancel
Save