/* * 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.Logging; using Microsoft.Extensions.Options; using Xunit; namespace OpenIddict.Core.Tests; public class OpenIddictCoreBuilderTests { [Fact] public void Constructor_ThrowsAnExceptionForNullServices() { // Arrange var services = (IServiceCollection) null!; // Act and assert var exception = Assert.Throws(() => new OpenIddictCoreBuilder(services)); Assert.Equal("services", exception.ParamName); } [Fact] public void ReplaceApplicationManager_ThrowsAnExceptionForClosedSourceManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceApplicationManager(typeof(ClosedGenericApplicationManager))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceApplicationManager_ThrowsAnExceptionForInvalidManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceApplicationManager(typeof(object))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceApplicationManager_OverridesDefaultOpenGenericManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.ReplaceApplicationManager(typeof(OpenGenericApplicationManager<>)); // Assert var descriptor = Assert.Single(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(OpenIddictApplicationManager<>)); Assert.Equal(typeof(OpenGenericApplicationManager<>), descriptor.ImplementationType); } [Fact] public void ReplaceAuthorizationManager_ThrowsAnExceptionForClosedSourceManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceAuthorizationManager(typeof(ClosedGenericAuthorizationManager))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceAuthorizationManager_ThrowsAnExceptionForInvalidManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceAuthorizationManager(typeof(object))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceAuthorizationManager_OverridesDefaultOpenGenericManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.ReplaceAuthorizationManager(typeof(OpenGenericAuthorizationManager<>)); // Assert var descriptor = Assert.Single(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(OpenIddictAuthorizationManager<>)); Assert.Equal(typeof(OpenGenericAuthorizationManager<>), descriptor.ImplementationType); } [Fact] public void ReplaceResourceManager_ThrowsAnExceptionForClosedSourceManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceResourceManager(typeof(ClosedGenericResourceManager))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceResourceManager_ThrowsAnExceptionForInvalidManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceResourceManager(typeof(object))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceResourceManager_OverridesDefaultOpenGenericManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.ReplaceResourceManager(typeof(OpenGenericResourceManager<>)); // Assert var descriptor = Assert.Single(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(OpenIddictResourceManager<>)); Assert.Equal(typeof(OpenGenericResourceManager<>), descriptor.ImplementationType); } [Fact] public void ReplaceScopeManager_ThrowsAnExceptionForClosedSourceManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceScopeManager(typeof(ClosedGenericScopeManager))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceScopeManager_ThrowsAnExceptionForInvalidManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceScopeManager(typeof(object))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceScopeManager_OverridesDefaultOpenGenericManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.ReplaceScopeManager(typeof(OpenGenericScopeManager<>)); // Assert var descriptor = Assert.Single(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(OpenIddictScopeManager<>)); Assert.Equal(typeof(OpenGenericScopeManager<>), descriptor.ImplementationType); } [Fact] public void ReplaceSessionManager_ThrowsAnExceptionForClosedSourceManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceSessionManager(typeof(ClosedGenericSessionManager))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceSessionManager_ThrowsAnExceptionForInvalidManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceSessionManager(typeof(object))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceSessionManager_OverridesDefaultOpenGenericManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.ReplaceSessionManager(typeof(OpenGenericSessionManager<>)); // Assert var descriptor = Assert.Single(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(OpenIddictSessionManager<>)); Assert.Equal(typeof(OpenGenericSessionManager<>), descriptor.ImplementationType); } [Fact] public void ReplaceTokenManager_ThrowsAnExceptionForClosedSourceManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceTokenManager(typeof(ClosedGenericTokenManager))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceTokenManager_ThrowsAnExceptionForInvalidManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.ReplaceTokenManager(typeof(object))); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0232), exception.Message, StringComparison.Ordinal); } [Fact] public void ReplaceTokenManager_OverridesDefaultOpenGenericManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.ReplaceTokenManager(typeof(OpenGenericTokenManager<>)); // Assert var descriptor = Assert.Single(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(OpenIddictTokenManager<>)); Assert.Equal(typeof(OpenGenericTokenManager<>), descriptor.ImplementationType); } [Fact] public void DisableAdditionalFiltering_FilteringIsCorrectlyDisabled() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.DisableAdditionalFiltering(); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.True(options.DisableAdditionalFiltering); } [Fact] public void DisableEntityCaching_CachingIsCorrectlyDisabled() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.DisableEntityCaching(); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.True(options.DisableEntityCaching); } [Fact] public void DisableAutomaticClientSecretRehashing_RehashingIsCorrectlyDisabled() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.DisableAutomaticClientSecretRehashing(); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.True(options.DisableAutomaticClientSecretRehashing); } [Theory] [InlineData("MD5")] [InlineData("SHA384")] [InlineData("Invalid")] public void SetClientSecretKeyDerivationHashAlgorithm_ThrowsAnExceptionForInvalidAlgorithm(string algorithmName) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); var algorithm = new System.Security.Cryptography.HashAlgorithmName(algorithmName); // Act and assert var exception = Assert.Throws(() => builder.SetClientSecretKeyDerivationHashAlgorithm(algorithm)); Assert.Equal("algorithm", exception.ParamName); } [Theory] [InlineData("SHA1")] [InlineData("SHA256")] [InlineData("SHA512")] public void SetClientSecretKeyDerivationHashAlgorithm_AlgorithmIsCorrectlySet(string algorithmName) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); var algorithm = new System.Security.Cryptography.HashAlgorithmName(algorithmName); // Act builder.SetClientSecretKeyDerivationHashAlgorithm(algorithm); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.Equal(algorithm, options.ClientSecretKeyDerivationHashAlgorithm); } [Theory] [InlineData(-1)] [InlineData(0)] [InlineData(9_999)] [InlineData(10_000_001)] public void SetClientSecretKeyDerivationIterations_ThrowsAnExceptionForInvalidIterations(int iterations) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.SetClientSecretKeyDerivationIterations(iterations)); Assert.Equal("iterations", exception.ParamName); } [Theory] [InlineData(10_000)] [InlineData(50_000)] [InlineData(10_000_000)] public void SetClientSecretKeyDerivationIterations_IterationsAreCorrectlySet(int iterations) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetClientSecretKeyDerivationIterations(iterations); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.Equal(iterations, options.ClientSecretKeyDerivationIterations); } [Theory] [InlineData(-1)] [InlineData(0)] [InlineData(255)] [InlineData(2049)] public void SetClientSecretKeyDerivationOutputLength_ThrowsAnExceptionForInvalidLength(int length) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.SetClientSecretKeyDerivationOutputLength(length)); Assert.Equal("length", exception.ParamName); } [Theory] [InlineData(256)] [InlineData(512)] [InlineData(2048)] public void SetClientSecretKeyDerivationOutputLength_LengthIsCorrectlySet(int length) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetClientSecretKeyDerivationOutputLength(length); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.Equal(length, options.ClientSecretKeyDerivationOutputLength); } [Theory] [InlineData(-1)] [InlineData(0)] [InlineData(127)] [InlineData(1025)] public void SetClientSecretKeyDerivationSaltLength_ThrowsAnExceptionForInvalidLength(int length) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.SetClientSecretKeyDerivationSaltLength(length)); Assert.Equal("length", exception.ParamName); } [Theory] [InlineData(128)] [InlineData(256)] [InlineData(1024)] public void SetClientSecretKeyDerivationSaltLength_LengthIsCorrectlySet(int length) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetClientSecretKeyDerivationSaltLength(length); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.Equal(length, options.ClientSecretKeyDerivationSaltLength); } [Theory] [InlineData(-10)] [InlineData(0)] [InlineData(9)] public void SetEntityCacheLimit_ThrowsAnExceptionForInvalidLimit(int limit) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws(() => builder.SetEntityCacheLimit(limit)); Assert.Equal("limit", exception.ParamName); } [Fact] public void SetEntityCacheLimit_LimitIsCorrectlyDisabled() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetEntityCacheLimit(42); // Assert var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().CurrentValue; Assert.Equal(42, options.EntityCacheLimit); } [Fact] public void SetDefaultApplicationEntity_ReplacesUntypedManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetDefaultApplicationEntity(); // Assert Assert.Contains(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(IOpenIddictApplicationManager) && service.ImplementationFactory is not null); } [Fact] public void SetDefaultAuthorizationEntity_ReplacesUntypedManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetDefaultAuthorizationEntity(); // Assert Assert.Contains(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(IOpenIddictAuthorizationManager) && service.ImplementationFactory is not null); } [Fact] public void SetDefaultResourceEntity_ReplacesUntypedManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetDefaultResourceEntity(); // Assert Assert.Contains(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(IOpenIddictResourceManager) && service.ImplementationFactory is not null); } [Fact] public void SetDefaultScopeEntity_ReplacesUntypedManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetDefaultScopeEntity(); // Assert Assert.Contains(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(IOpenIddictScopeManager) && service.ImplementationFactory is not null); } [Fact] public void SetDefaultSessionEntity_ReplacesUntypedManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetDefaultSessionEntity(); // Assert Assert.Contains(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(IOpenIddictSessionManager) && service.ImplementationFactory is not null); } [Fact] public void SetDefaultTokenEntity_ReplacesUntypedManager() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act builder.SetDefaultTokenEntity(); // Assert Assert.Contains(services, service => service.Lifetime is ServiceLifetime.Scoped && service.ServiceType == typeof(IOpenIddictTokenManager) && service.ImplementationFactory is not null); } private static OpenIddictCoreBuilder CreateBuilder(IServiceCollection services) => services.AddOpenIddict().AddCore(); private static ServiceCollection CreateServices() { var services = new ServiceCollection(); services.AddOptions(); return services; } private sealed class CustomApplication; private sealed class CustomAuthorization; private sealed class CustomResource; private sealed class CustomScope; private sealed class CustomSession; private sealed class CustomToken; private sealed class ClosedGenericApplicationManager : OpenIddictApplicationManager { public ClosedGenericApplicationManager( IOpenIddictApplicationCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictApplicationStore store) : base(cache, logger, options, store) { } } private sealed class OpenGenericApplicationManager : OpenIddictApplicationManager where TApplication : class { public OpenGenericApplicationManager( IOpenIddictApplicationCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictApplicationStore store) : base(cache, logger, options, store) { } } private sealed class ClosedGenericAuthorizationManager : OpenIddictAuthorizationManager { public ClosedGenericAuthorizationManager( IOpenIddictAuthorizationCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictAuthorizationStore store) : base(cache, logger, options, store) { } } private sealed class OpenGenericAuthorizationManager : OpenIddictAuthorizationManager where TAuthorization : class { public OpenGenericAuthorizationManager( IOpenIddictAuthorizationCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictAuthorizationStore store) : base(cache, logger, options, store) { } } private sealed class ClosedGenericResourceManager : OpenIddictResourceManager { public ClosedGenericResourceManager( IOpenIddictResourceCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictResourceStore store) : base(cache, logger, options, store) { } } private sealed class OpenGenericResourceManager : OpenIddictResourceManager where TResource : class { public OpenGenericResourceManager( IOpenIddictResourceCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictResourceStore store) : base(cache, logger, options, store) { } } private sealed class ClosedGenericScopeManager : OpenIddictScopeManager { public ClosedGenericScopeManager( IOpenIddictScopeCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictScopeStore store) : base(cache, logger, options, store) { } } private sealed class OpenGenericScopeManager : OpenIddictScopeManager where TScope : class { public OpenGenericScopeManager( IOpenIddictScopeCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictScopeStore store) : base(cache, logger, options, store) { } } private sealed class ClosedGenericSessionManager : OpenIddictSessionManager { public ClosedGenericSessionManager( IOpenIddictSessionCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictSessionStore store) : base(cache, logger, options, store) { } } private sealed class OpenGenericSessionManager : OpenIddictSessionManager where TSession : class { public OpenGenericSessionManager( IOpenIddictSessionCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictSessionStore store) : base(cache, logger, options, store) { } } private sealed class ClosedGenericTokenManager : OpenIddictTokenManager { public ClosedGenericTokenManager( IOpenIddictTokenCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictTokenStore store) : base(cache, logger, options, store) { } } private sealed class OpenGenericTokenManager : OpenIddictTokenManager where TToken : class { public OpenGenericTokenManager( IOpenIddictTokenCache cache, ILogger> logger, IOptionsMonitor options, IOpenIddictTokenStore store) : base(cache, logger, options, store) { } } }