From 84055f634c220db43c76d90b4b5eb8973d2d0c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sat, 15 Sep 2018 11:09:40 +0200 Subject: [PATCH 1/2] Move the handlers registration logic to OpenIddictServerConfiguration/OpenIddictValidationConfiguration --- ...er.cs => OpenIddictServerConfiguration.cs} | 30 ++++++++++++++-- .../OpenIddictServerExtensions.cs | 30 ++-------------- ...s => OpenIddictValidationConfiguration.cs} | 31 ++++++++++++++-- .../OpenIddictValidationExtensions.cs | 35 +++---------------- ... => OpenIddictServerConfigurationTests.cs} | 29 ++++++++++++++- .../OpenIddictServerExtensionsTests.cs | 2 +- ...OpenIddictValidationConfigurationTests.cs} | 27 +++++++++++++- .../OpenIddictValidationExtensionsTests.cs | 2 +- 8 files changed, 117 insertions(+), 69 deletions(-) rename src/OpenIddict.Server/Internal/{OpenIddictServerInitializer.cs => OpenIddictServerConfiguration.cs} (87%) rename src/OpenIddict.Validation/Internal/{OpenIddictValidationInitializer.cs => OpenIddictValidationConfiguration.cs} (65%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictServerInitializerTests.cs => OpenIddictServerConfigurationTests.cs} (92%) rename test/OpenIddict.Validation.Tests/Internal/{OpenIddictValidationInitializerTests.cs => OpenIddictValidationConfigurationTests.cs} (74%) diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs b/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs similarity index 87% rename from src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs index 1507cc0b..3464e538 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs @@ -23,17 +23,18 @@ namespace OpenIddict.Server.Internal /// 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. /// - public class OpenIddictServerInitializer : IPostConfigureOptions + public class OpenIddictServerConfiguration : IConfigureOptions, + IPostConfigureOptions { private readonly IDistributedCache _cache; private readonly IDataProtectionProvider _dataProtectionProvider; /// - /// Creates a new instance of the class. + /// Creates a new instance of the 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. /// - public OpenIddictServerInitializer( + public OpenIddictServerConfiguration( [NotNull] IDistributedCache cache, [NotNull] IDataProtectionProvider dataProtectionProvider) { @@ -41,6 +42,29 @@ namespace OpenIddict.Server.Internal _dataProtectionProvider = dataProtectionProvider; } + /// + /// Registers the OpenIddict server handler in the global authentication options. + /// + /// The options instance to initialize. + public void Configure(AuthenticationOptions options) + { + // If a handler was already registered and the type doesn't correspond to the OpenIddict handler, throw an exception. + if (options.SchemeMap.TryGetValue(OpenIddictServerDefaults.AuthenticationScheme, out var builder) && + builder.HandlerType != typeof(OpenIddictServerHandler)) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The OpenIddict server handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OpenID Connect server was registered.") + .Append("Make sure that 'services.AddAuthentication().AddOpenIdConnectServer()' is not used.") + .ToString()); + } + + options.AddScheme(OpenIddictServerDefaults.AuthenticationScheme, scheme => + { + scheme.HandlerType = typeof(OpenIddictServerHandler); + }); + } + /// /// Populates the default OpenID Connect server options and ensure /// that the configuration is in a consistent and valid state. diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs index eeb3ca40..fc29f981 100644 --- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs +++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs @@ -65,37 +65,11 @@ namespace Microsoft.Extensions.DependencyInjection // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. builder.Services.TryAddEnumerable(new[] { - ServiceDescriptor.Singleton, OpenIddictServerInitializer>(), + ServiceDescriptor.Singleton, OpenIddictServerConfiguration>(), + ServiceDescriptor.Singleton, OpenIddictServerConfiguration>(), ServiceDescriptor.Singleton, OpenIdConnectServerInitializer>() }); - // Register the OpenID Connect server handler in the authentication options, - // so it can be discovered by the default authentication handler provider. - builder.Services.Configure(options => - { - // Note: this method is guaranteed to be idempotent. To prevent multiple schemes from being - // registered (which would result in an exception being thrown), a manual check is made here. - if (options.SchemeMap.TryGetValue(OpenIddictServerDefaults.AuthenticationScheme, out var handler)) - { - // If the handler type doesn't correspond to the OpenIddict handler, throw an exception. - if (handler.HandlerType != typeof(OpenIddictServerHandler)) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The OpenIddict server handler cannot be registered as an authentication scheme.") - .AppendLine("This may indicate that an instance of the OpenID Connect server was registered.") - .Append("Make sure that 'services.AddAuthentication().AddOpenIdConnectServer()' is not used.") - .ToString()); - } - - return; - } - - options.AddScheme(OpenIddictServerDefaults.AuthenticationScheme, scheme => - { - scheme.HandlerType = typeof(OpenIddictServerHandler); - }); - }); - return new OpenIddictServerBuilder(builder.Services); } diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs similarity index 65% rename from src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs rename to src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs index ea39812e..46a98afc 100644 --- a/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs +++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs @@ -18,20 +18,45 @@ namespace OpenIddict.Validation.Internal /// 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. /// - public class OpenIddictValidationInitializer : IPostConfigureOptions + public class OpenIddictValidationConfiguration : IConfigureOptions, + IPostConfigureOptions { private readonly IDataProtectionProvider _dataProtectionProvider; /// - /// Creates a new instance of the class. + /// Creates a new instance of the 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. /// - public OpenIddictValidationInitializer([NotNull] IDataProtectionProvider dataProtectionProvider) + public OpenIddictValidationConfiguration([NotNull] IDataProtectionProvider dataProtectionProvider) { _dataProtectionProvider = dataProtectionProvider; } + /// + /// Registers the OpenIddict server handler in the global authentication options. + /// + /// The options instance to initialize. + public void Configure(AuthenticationOptions options) + { + // If a handler was already registered and the type doesn't correspond to the OpenIddict handler, throw an exception. + if (options.SchemeMap.TryGetValue(OpenIddictValidationDefaults.AuthenticationScheme, out var builder) && + builder.HandlerType != typeof(OpenIddictValidationHandler)) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The OpenIddict validation handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OAuth validation or JWT bearer handler was registered.") + .Append("Make sure that neither 'services.AddAuthentication().AddOAuthValidation()' nor ") + .Append("'services.AddAuthentication().AddJwtBearer()' are called from 'ConfigureServices'.") + .ToString()); + } + + options.AddScheme(OpenIddictValidationDefaults.AuthenticationScheme, scheme => + { + scheme.HandlerType = typeof(OpenIddictValidationHandler); + }); + } + /// /// Populates the default OpenIddict validation options and ensure /// that the configuration is in a consistent and valid state. diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs index 202f4a9e..b368eec1 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs @@ -5,7 +5,6 @@ */ using System; -using System.Text; using AspNet.Security.OAuth.Validation; using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; @@ -45,41 +44,15 @@ namespace Microsoft.Extensions.DependencyInjection builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); - // Note: TryAddEnumerable() is used here to ensure the initializer is only registered once. + // Register the options initializers used by the OAuth validation handler and OpenIddict. + // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. builder.Services.TryAddEnumerable(new[] { - ServiceDescriptor.Singleton, OpenIddictValidationInitializer>(), + ServiceDescriptor.Singleton, OpenIddictValidationConfiguration>(), + ServiceDescriptor.Singleton, OpenIddictValidationConfiguration>(), ServiceDescriptor.Singleton, OAuthValidationInitializer>() }); - // Register the OpenIddict validation handler in the authentication options, - // so it can be discovered by the default authentication handler provider. - builder.Services.Configure(options => - { - // Note: this method is guaranteed to be idempotent. To prevent multiple schemes from being - // registered (which would result in an exception being thrown), a manual check is made here. - if (options.SchemeMap.TryGetValue(OpenIddictValidationDefaults.AuthenticationScheme, out var handler)) - { - // If the handler type doesn't correspond to the OpenIddict handler, throw an exception. - if (handler.HandlerType != typeof(OpenIddictValidationHandler)) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The OpenIddict validation handler cannot be registered as an authentication scheme.") - .AppendLine("This may indicate that an instance of the OAuth validation or JWT bearer handler was registered.") - .Append("Make sure that neither 'services.AddAuthentication().AddOAuthValidation()' nor ") - .Append("'services.AddAuthentication().AddJwtBearer()' are called from 'ConfigureServices'.") - .ToString()); - } - - return; - } - - options.AddScheme(OpenIddictValidationDefaults.AuthenticationScheme, scheme => - { - scheme.HandlerType = typeof(OpenIddictValidationHandler); - }); - }); - return new OpenIddictValidationBuilder(builder.Services); } diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs similarity index 92% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs index aceae60e..0ab77543 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs @@ -11,18 +11,45 @@ using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Moq; using OpenIddict.Abstractions; using Xunit; namespace OpenIddict.Server.Internal.Tests { - public class OpenIddictServerInitializerTests + public class OpenIddictServerConfigurationTests { + [Fact] + public void Configure_ThrowsAnExceptionWhenSchemeIsAlreadyRegisteredWithDifferentHandlerType() + { + // Arrange + var options = new AuthenticationOptions(); + options.AddScheme(OpenIddictServerDefaults.AuthenticationScheme, builder => + { + builder.HandlerType = typeof(OpenIdConnectServerHandler); + }); + + var initializer = new OpenIddictServerConfiguration( + Mock.Of(), + Mock.Of()); + + // Act and assert + var exception = Assert.Throws(() => initializer.Configure(options)); + + Assert.Equal(new StringBuilder() + .AppendLine("The OpenIddict server handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OpenID Connect server was registered.") + .Append("Make sure that 'services.AddAuthentication().AddOpenIdConnectServer()' is not used.") + .ToString(), exception.Message); + } + [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenRandomNumberGeneratorIsNull() { diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs index 2924583a..e4ee497f 100644 --- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs +++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs @@ -173,7 +173,7 @@ namespace OpenIddict.Server.Tests } [Theory] - [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictServerInitializer))] + [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictServerConfiguration))] [InlineData(typeof(IPostConfigureOptions), typeof(OpenIdConnectServerInitializer))] public void AddServer_RegistersInitializers(Type serviceType, Type implementationType) { diff --git a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs similarity index 74% rename from test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs rename to test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs index f5b865ff..0f90b2ef 100644 --- a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs +++ b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs @@ -10,16 +10,41 @@ using System.Threading.Tasks; using AspNet.Security.OAuth.Validation; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Moq; using Xunit; namespace OpenIddict.Validation.Internal.Tests { - public class OpenIddictValidationInitializerTests + public class OpenIddictValidationConfigurationTests { + [Fact] + public void Configure_ThrowsAnExceptionWhenSchemeIsAlreadyRegisteredWithDifferentHandlerType() + { + // Arrange + var options = new AuthenticationOptions(); + options.AddScheme(OpenIddictValidationDefaults.AuthenticationScheme, builder => + { + builder.HandlerType = typeof(OAuthValidationHandler); + }); + + var initializer = new OpenIddictValidationConfiguration(Mock.Of()); + + // Act and assert + var exception = Assert.Throws(() => initializer.Configure(options)); + + Assert.Equal(new StringBuilder() + .AppendLine("The OpenIddict validation handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OAuth validation or JWT bearer handler was registered.") + .Append("Make sure that neither 'services.AddAuthentication().AddOAuthValidation()' nor ") + .Append("'services.AddAuthentication().AddJwtBearer()' are called from 'ConfigureServices'.") + .ToString(), exception.Message); + } + [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenEventsTypeIsNull() { diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs index d0f92f00..7105b69b 100644 --- a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs +++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs @@ -134,7 +134,7 @@ namespace OpenIddict.Validation.Tests } [Theory] - [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictValidationInitializer))] + [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictValidationConfiguration))] [InlineData(typeof(IPostConfigureOptions), typeof(OAuthValidationInitializer))] public void AddValidation_RegistersInitializers(Type serviceType, Type implementationType) { From a60014dda5e93c818ad706a62630ccdf2b0437ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Fri, 21 Sep 2018 18:20:19 +0200 Subject: [PATCH 2/2] Introduce OpenIddictAuthorizationManager.FindByApplicationIdAsync() and decorate the built-in models with [DebuggerDisplay] --- .../IOpenIddictAuthorizationManager.cs | 11 ++++++ .../Stores/IOpenIddictAuthorizationStore.cs | 11 ++++++ .../OpenIddictAuthorizationManager.cs | 23 +++++++++++ .../OpenIddictApplication.cs | 2 + .../OpenIddictAuthorization.cs | 2 + .../OpenIddictScope.cs | 2 + .../OpenIddictToken.cs | 2 + .../Stores/OpenIddictAuthorizationStore.cs | 25 ++++++++++++ .../OpenIddictApplication.cs | 2 + .../OpenIddictAuthorization.cs | 2 + .../OpenIddictScope.cs | 2 + .../OpenIddictToken.cs | 2 + .../Stores/OpenIddictAuthorizationStore.cs | 38 +++++++++++++++++++ .../OpenIddictApplication.cs | 2 + .../OpenIddictAuthorization.cs | 2 + .../OpenIddictScope.cs | 2 + .../OpenIddictToken.cs | 2 + .../Stores/OpenIddictAuthorizationStore.cs | 24 ++++++++++++ 18 files changed, 156 insertions(+) diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs index 5a64369f..210dc783 100644 --- a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs @@ -141,6 +141,17 @@ namespace OpenIddict.Abstractions /// Task> FindAsync([NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default); + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the authorizations corresponding to the specified application. + /// + Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + /// /// Retrieves an authorization using its unique identifier. /// diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs index 77324e8a..211936a3 100644 --- a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs @@ -125,6 +125,17 @@ namespace OpenIddict.Abstractions [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken); + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the authorizations corresponding to the specified application. + /// + Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// /// Retrieves an authorization using its unique identifier. /// diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index 1a42933d..772f4921 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -446,6 +446,26 @@ namespace OpenIddict.Core builder.ToImmutable(); } + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the authorizations corresponding to the specified application. + /// + public virtual Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + return Store.FindByApplicationIdAsync(identifier, cancellationToken); + } + /// /// Retrieves an authorization using its unique identifier. /// @@ -1082,6 +1102,9 @@ namespace OpenIddict.Core async Task> IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, ImmutableArray scopes, CancellationToken cancellationToken) => (await FindAsync(subject, client, status, type, scopes, cancellationToken)).CastArray(); + async Task> IOpenIddictAuthorizationManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) + => (await FindByApplicationIdAsync(identifier, cancellationToken)).CastArray(); + async Task IOpenIddictAuthorizationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs index e60df750..b9962550 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -24,6 +25,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict application. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")] public class OpenIddictApplication where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs index 7ebfd69d..d7996a7e 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -24,6 +25,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict authorization. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictAuthorization where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs index 30967531..91d487f3 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -23,6 +24,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict scope. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")] public class OpenIddictScope where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs index 378f8f5c..411b9f74 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -23,6 +24,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict token. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictToken where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs index 69dfe095..fc20a7cc 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -378,6 +378,31 @@ namespace OpenIddict.EntityFramework builder.ToImmutable(); } + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the authorizations corresponding to the specified application. + /// + public virtual async Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + var key = ConvertIdentifierFromString(identifier); + + return ImmutableArray.CreateRange( + await (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Application.Id.Equals(key) + select authorization).ToListAsync(cancellationToken)); + } + /// /// Retrieves an authorization using its unique identifier. /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs index e5306535..c452d1a9 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -31,6 +32,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict application. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")] public class OpenIddictApplication where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs index 67cc0272..6f4f210e 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -31,6 +32,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict authorization. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictAuthorization where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs index b28ba8a0..0c739c04 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -23,6 +24,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict scope. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")] public class OpenIddictScope where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs index 92cca5d3..5dc84464 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -31,6 +32,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict token. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictToken where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs index e6521006..e0258e75 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs @@ -449,6 +449,44 @@ namespace OpenIddict.EntityFrameworkCore builder.ToImmutable(); } + /// + /// Exposes a compiled query allowing to retrieve the list of + /// authorizations corresponding to the specified application identifier. + /// + private static readonly Func> FindByApplicationId = + // Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be + // filtered using authorization.Application.Id.Equals(key). To work around this issue, + // this compiled query uses an explicit join before applying the equality check. + // See https://github.com/openiddict/openiddict-core/issues/499 for more information. + EF.CompileAsyncQuery((TContext context, TKey identifier) => + from authorization in context.Set() + .Include(authorization => authorization.Application) + .AsTracking() + join application in context.Set().AsTracking() on authorization.Application.Id equals application.Id + where application.Id.Equals(identifier) + select authorization); + + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the authorizations corresponding to the specified application. + /// + public virtual async Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + return ImmutableArray.CreateRange(await FindByApplicationId(Context, + ConvertIdentifierFromString(identifier)).ToListAsync(cancellationToken)); + } + /// /// Exposes a compiled query allowing to retrieve an authorization using its unique identifier. /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs b/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs index 9b4893f0..750660b5 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict application. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")] public class OpenIddictApplication { /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs b/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs index 57abefb3..6eba16fa 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict authorization. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictAuthorization { /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs b/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs index 8b6909a7..c1cb4939 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict scope. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")] public class OpenIddictScope { /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs b/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs index c212b5d5..65ed4398 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict token. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictToken { /// diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs index 501999c7..92e9117c 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs @@ -314,6 +314,30 @@ namespace OpenIddict.MongoDb Enumerable.All(scopes, scope => authorization.Scopes.Contains(scope))).ToListAsync(cancellationToken)); } + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the authorizations corresponding to the specified application. + /// + public virtual async Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + + return ImmutableArray.CreateRange(await collection.Find(authorization => + authorization.ApplicationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); + } + /// /// Retrieves an authorization using its unique identifier. ///