diff --git a/identity-server/hosts/Configuration/Host.Configuration.csproj b/identity-server/hosts/Configuration/Host.Configuration.csproj index 3228ed659..bf309ccf7 100644 --- a/identity-server/hosts/Configuration/Host.Configuration.csproj +++ b/identity-server/hosts/Configuration/Host.Configuration.csproj @@ -19,10 +19,7 @@ - - - @@ -30,7 +27,6 @@ - diff --git a/identity-server/hosts/EntityFramework-dotnet9/IdentityServerExtensions.cs b/identity-server/hosts/EntityFramework-dotnet9/IdentityServerExtensions.cs index d4f82b3e2..648d450c8 100644 --- a/identity-server/hosts/EntityFramework-dotnet9/IdentityServerExtensions.cs +++ b/identity-server/hosts/EntityFramework-dotnet9/IdentityServerExtensions.cs @@ -68,7 +68,6 @@ internal static WebApplicationBuilder ConfigureIdentityServer(this WebApplicatio }) .AddAppAuthRedirectUriValidator() .AddServerSideSessions() - .AddScopeParser() // this is something you will want in production to reduce load on and requests to the DB //.AddConfigurationStoreCache() @@ -82,21 +81,6 @@ internal static WebApplicationBuilder ConfigureIdentityServer(this WebApplicatio .AddCustomTokenRequestValidator() .AddScopeParser() .AddMutualTlsSecretValidators() - - // Comment out `AddInMemoryOidcProviders` if you want to use the seeded identity providers - .AddInMemoryOidcProviders( - [ - new Duende.IdentityServer.Models.OidcProvider - { - Scheme = "dynamicprovider-idsvr", - DisplayName = "IdentityServer (via Dynamic Providers)", - Authority = "https://demo.duendesoftware.com", - ClientId = "login", - ResponseType = "id_token", - Scope = "openid profile" - } - ]) - .AddLicenseSummary(); builder.Services.AddDistributedMemoryCache(); diff --git a/identity-server/hosts/EntityFramework/IdentityServerExtensions.cs b/identity-server/hosts/EntityFramework/IdentityServerExtensions.cs index f5d75e388..9fed14c7c 100644 --- a/identity-server/hosts/EntityFramework/IdentityServerExtensions.cs +++ b/identity-server/hosts/EntityFramework/IdentityServerExtensions.cs @@ -67,7 +67,6 @@ internal static WebApplicationBuilder ConfigureIdentityServer(this WebApplicatio }) .AddAppAuthRedirectUriValidator() .AddServerSideSessions() - .AddScopeParser() // this is something you will want in production to reduce load on and requests to the DB //.AddConfigurationStoreCache() @@ -81,18 +80,6 @@ internal static WebApplicationBuilder ConfigureIdentityServer(this WebApplicatio .AddCustomTokenRequestValidator() .AddScopeParser() .AddMutualTlsSecretValidators() - .AddInMemoryOidcProviders( - [ - new Duende.IdentityServer.Models.OidcProvider - { - Scheme = "dynamicprovider-idsvr", - DisplayName = "IdentityServer (via Dynamic Providers)", - Authority = "https://demo.duendesoftware.com", - ClientId = "login", - ResponseType = "id_token", - Scope = "openid profile" - } - ]) .AddLicenseSummary(); builder.Services.AddDistributedMemoryCache(); diff --git a/identity-server/hosts/Shared/Customization/ParameterizedScopeParser.cs b/identity-server/hosts/Shared/Customization/ParameterizedScopeParser.cs index 5993fd615..f076a584d 100644 --- a/identity-server/hosts/Shared/Customization/ParameterizedScopeParser.cs +++ b/identity-server/hosts/Shared/Customization/ParameterizedScopeParser.cs @@ -36,7 +36,7 @@ public override void ParseScopeValue(ParseScopeContext scopeContext) } else { - // we get in here with a scope exactly "transaction", which is to say we're ignoring it + // we get in here with a scope exactly "transaction", which is to say we're ignoring it // and not including it in the results scopeContext.SetIgnore(); } diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationDetails.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationDetails.cs new file mode 100644 index 000000000..137bb36c4 --- /dev/null +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationDetails.cs @@ -0,0 +1,6 @@ +// Copyright (c) Duende Software. All rights reserved. +// See LICENSE in the project root for license information. + +namespace Duende.IdentityServer.Licensing.V2.Diagnostics.DiagnosticEntries; + +internal record RegisteredImplementationDetails(Type TInterface, List TDefaultImplementations); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationsDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationsDiagnosticEntry.cs index 911b4b04d..97ddf74f7 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationsDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/RegisteredImplementationsDiagnosticEntry.cs @@ -2,12 +2,16 @@ // See LICENSE in the project root for license information. using System.Text.Json; +using Duende.IdentityServer.Events; using Duende.IdentityServer.Hosting; +using Duende.IdentityServer.Hosting.DynamicProviders; using Duende.IdentityServer.Internal; using Duende.IdentityServer.ResponseHandling; using Duende.IdentityServer.Services; +using Duende.IdentityServer.Services.Default; using Duende.IdentityServer.Services.KeyManagement; using Duende.IdentityServer.Stores; +using Duende.IdentityServer.Stores.Empty; using Duende.IdentityServer.Stores.Serialization; using Duende.IdentityServer.Validation; @@ -16,139 +20,142 @@ namespace Duende.IdentityServer.Licensing.V2.Diagnostics.DiagnosticEntries; internal class RegisteredImplementationsDiagnosticEntry(ServiceCollectionAccessor serviceCollectionAccessor) : IDiagnosticEntry { - private readonly Dictionary> _typesToInspect = new() + private readonly Dictionary> _typesToInspect = new() { { - "Root", [ typeof(IIdentityServerTools) ] + "Root", [new (typeof(IIdentityServerTools), [typeof(IdentityServerTools)])] }, { "Hosting", [ - typeof(IEndpointHandler), - typeof(IEndpointResult), - typeof(IEndpointRouter), - typeof(IHttpResponseWriter<>) + new (typeof(IEndpointHandler), []), + new (typeof(IEndpointResult), []), + new (typeof(IEndpointRouter), [typeof(EndpointRouter)]), + new (typeof(IHttpResponseWriter<>), []), ] }, { - "Infrastructure", [typeof(IClock), typeof(IConcurrencyLock<>)] + "Infrastructure", [ + new(typeof(IClock), [typeof(DefaultClock)]), + new(typeof(IConcurrencyLock<>), [typeof(DefaultConcurrencyLock<>)]), + ] }, { "ResponseHandling", [ - typeof(IAuthorizeInteractionResponseGenerator), - typeof(IAuthorizeResponseGenerator), - typeof(IBackchannelAuthenticationResponseGenerator), - typeof(IDeviceAuthorizationResponseGenerator), - typeof(IDiscoveryResponseGenerator), - typeof(IIntrospectionResponseGenerator), - typeof(IPushedAuthorizationResponseGenerator), - typeof(ITokenResponseGenerator), - typeof(ITokenRevocationResponseGenerator), - typeof(IUserInfoResponseGenerator) + new(typeof(IAuthorizeInteractionResponseGenerator), [typeof(AuthorizeInteractionResponseGenerator)]), + new(typeof(IAuthorizeResponseGenerator), [typeof(AuthorizeResponseGenerator)]), + new(typeof(IBackchannelAuthenticationResponseGenerator), [typeof(BackchannelAuthenticationResponseGenerator)]), + new(typeof(IDeviceAuthorizationResponseGenerator), [typeof(DeviceAuthorizationResponseGenerator)]), + new(typeof(IDiscoveryResponseGenerator), [typeof(DiscoveryResponseGenerator)]), + new(typeof(IIntrospectionResponseGenerator), [typeof(IntrospectionResponseGenerator)]), + new(typeof(IPushedAuthorizationResponseGenerator), [typeof(PushedAuthorizationResponseGenerator)]), + new(typeof(ITokenResponseGenerator), [typeof(TokenResponseGenerator)]), + new(typeof(ITokenRevocationResponseGenerator), [typeof(TokenRevocationResponseGenerator)]), + new(typeof(IUserInfoResponseGenerator), [typeof(UserInfoResponseGenerator)]), ] }, { "Services", [ - typeof(IAutomaticKeyManagerKeyStore), - typeof(IBackchannelAuthenticationInteractionService), - typeof(IBackchannelAuthenticationThrottlingService), - typeof(IBackchannelAuthenticationUserNotificationService), - typeof(IBackChannelLogoutHttpClient), - typeof(IBackChannelLogoutService), - typeof(ICache<>), - typeof(ICancellationTokenProvider), - typeof(IClaimsService), - typeof(IConsentService), - typeof(ICorsPolicyService), - typeof(IDeviceFlowCodeService), - typeof(IDeviceFlowInteractionService), - typeof(IDeviceFlowThrottlingService), - typeof(IEventService), - typeof(IEventSink), - typeof(IHandleGenerationService), - typeof(IIdentityServerInteractionService), - typeof(IIssuerNameService), - typeof(IJwtRequestUriHttpClient), - typeof(IKeyManager), - typeof(IKeyMaterialService), - typeof(ILogoutNotificationService), - typeof(IPersistedGrantService), - typeof(IProfileService), - typeof(IPushedAuthorizationSerializer), - typeof(IPushedAuthorizationService), - typeof(IRefreshTokenService), - typeof(IReplayCache), - typeof(IReturnUrlParser), - typeof(IServerUrls), - typeof(ISessionCoordinationService), - typeof(ISessionManagementService), - typeof(ISigningKeyProtector), - typeof(ISigningKeyStoreCache), - typeof(ITokenCreationService), - typeof(ITokenService), - typeof(IUserCodeGenerator), - typeof(IUserCodeService), - typeof(IUserSession) + new(typeof(IAutomaticKeyManagerKeyStore), [typeof(AutomaticKeyManagerKeyStore)]), + new(typeof(IBackchannelAuthenticationInteractionService), [typeof(DefaultBackchannelAuthenticationInteractionService)]), + new(typeof(IBackchannelAuthenticationThrottlingService), [typeof(DistributedBackchannelAuthenticationThrottlingService)]), + new(typeof(IBackchannelAuthenticationUserNotificationService), [typeof(NopBackchannelAuthenticationUserNotificationService)]), + new(typeof(IBackChannelLogoutHttpClient), [typeof(DefaultBackChannelLogoutHttpClient)]), + new(typeof(IBackChannelLogoutService), [typeof(DefaultBackChannelLogoutService)]), + new(typeof(ICache<>), [typeof(DefaultCache<>)]), + new(typeof(ICancellationTokenProvider), [typeof(DefaultCancellationTokenProvider)]), + new(typeof(IClaimsService), [typeof(DefaultClaimsService)]), + new(typeof(IConsentService), [typeof(DefaultConsentService)]), + new(typeof(ICorsPolicyService), [typeof(DefaultCorsPolicyService)]), + new(typeof(IDeviceFlowCodeService), [typeof(DefaultDeviceFlowCodeService)]), + new(typeof(IDeviceFlowInteractionService), [typeof(DefaultDeviceFlowInteractionService)]), + new(typeof(IDeviceFlowThrottlingService), [typeof(DistributedDeviceFlowThrottlingService)]), + new(typeof(IEventService), [typeof(DefaultEventService)]), + new(typeof(IEventSink), [typeof(DefaultEventSink)]), + new(typeof(IHandleGenerationService), [typeof(DefaultHandleGenerationService)]), + new(typeof(IIdentityServerInteractionService), [typeof(DefaultIdentityServerInteractionService)]), + new(typeof(IIssuerNameService), [typeof(DefaultIssuerNameService)]), + new(typeof(IJwtRequestUriHttpClient), [typeof(DefaultJwtRequestUriHttpClient)]), + new(typeof(IKeyManager), [typeof(KeyManager)]), + new(typeof(IKeyMaterialService), [typeof(DefaultKeyMaterialService)]), + new(typeof(ILogoutNotificationService), [typeof(LogoutNotificationService)]), + new(typeof(IPersistedGrantService), [typeof(DefaultPersistedGrantService)]), + new(typeof(IProfileService), [typeof(DefaultProfileService)]), + new(typeof(IPushedAuthorizationSerializer), [typeof(PushedAuthorizationSerializer)]), + new(typeof(IPushedAuthorizationService), [typeof(PushedAuthorizationService)]), + new(typeof(IRefreshTokenService), [typeof(DefaultRefreshTokenService)]), + new(typeof(IReplayCache), [typeof(DefaultReplayCache)]), + new(typeof(IReturnUrlParser), [typeof(OidcReturnUrlParser)]), + new(typeof(IServerUrls), [typeof(DefaultServerUrls)]), + new(typeof(ISessionCoordinationService), [typeof(DefaultSessionCoordinationService)]), + new(typeof(ISessionManagementService), []), + new(typeof(ISigningKeyProtector), [typeof(DataProtectionKeyProtector)]), + new(typeof(ISigningKeyStoreCache), [typeof(InMemoryKeyStoreCache)]), + new(typeof(ITokenCreationService), [typeof(DefaultTokenCreationService)]), + new(typeof(ITokenService), [typeof(DefaultTokenService)]), + new(typeof(IUserCodeGenerator), [typeof(NumericUserCodeGenerator)]), + new(typeof(IUserCodeService), [typeof(DefaultUserCodeService)]), + new(typeof(IUserSession), [typeof(DefaultUserSession)]), ] }, { "Stores", [ - typeof(IAuthorizationCodeStore), - typeof(IAuthorizationParametersMessageStore), - typeof(IBackChannelAuthenticationRequestStore), - typeof(IClientStore), - typeof(IConsentMessageStore), - typeof(IDeviceFlowStore), - typeof(IIdentityProviderStore), - typeof(IMessageStore<>), - typeof(IPersistentGrantSerializer), - typeof(IPersistedGrantStore), - typeof(IPushedAuthorizationRequestStore), - typeof(IReferenceTokenStore), - typeof(IRefreshTokenStore), - typeof(IResourceStore), - typeof(IServerSideSessionsMarker), - typeof(IServerSideSessionStore), - typeof(IServerSideTicketStore), - typeof(ISigningCredentialStore), - typeof(ISigningKeyStore), - typeof(IUserConsentStore), - typeof(IValidationKeysStore) + new(typeof(IAuthorizationCodeStore), [typeof(DefaultAuthorizationCodeStore)]), + new(typeof(IAuthorizationParametersMessageStore), []), + new(typeof(IBackChannelAuthenticationRequestStore), [typeof(DefaultBackChannelAuthenticationRequestStore)]), + new(typeof(IClientStore), [typeof(EmptyClientStore)]), + new(typeof(IConsentMessageStore), [typeof(ConsentMessageStore)]), + new(typeof(IDeviceFlowStore), []), + new(typeof(IIdentityProviderStore), [typeof(NopIdentityProviderStore)]), + new(typeof(IMessageStore<>), [typeof(ProtectedDataMessageStore<>)]), + new(typeof(IPersistentGrantSerializer), [typeof(PersistentGrantSerializer)]), + new(typeof(IPersistedGrantStore), [typeof(InMemoryPersistedGrantStore)]), + new(typeof(IPushedAuthorizationRequestStore), [typeof(InMemoryPushedAuthorizationRequestStore)]), + new(typeof(IReferenceTokenStore), [typeof(DefaultReferenceTokenStore)]), + new(typeof(IRefreshTokenStore), [typeof(DefaultRefreshTokenStore)]), + new(typeof(IResourceStore), [typeof(EmptyResourceStore)]), + new(typeof(IServerSideSessionsMarker), []), + new(typeof(IServerSideSessionStore),[]), + new(typeof(IServerSideTicketStore),[]), + new(typeof(ISigningCredentialStore), []), + new(typeof(ISigningKeyStore), [typeof(FileSystemKeyStore)]), + new(typeof(IUserConsentStore), [typeof(DefaultUserConsentStore)]), + new(typeof(IValidationKeysStore), []), ] }, { "Validation", [ - typeof(IApiSecretValidator), - typeof(IAuthorizeRequestValidator), - typeof(IBackchannelAuthenticationRequestIdValidator), - typeof(IBackchannelAuthenticationRequestValidator), - typeof(IBackchannelAuthenticationUserValidator), - typeof(IClientConfigurationValidator), - typeof(IClientSecretValidator), - typeof(ICustomAuthorizeRequestValidator), - typeof(ICustomBackchannelAuthenticationValidator), - typeof(ICustomTokenRequestValidator), - typeof(ICustomTokenValidator), - typeof(IDeviceAuthorizationRequestValidator), - typeof(IDeviceCodeValidator), - typeof(IDPoPProofValidator), - typeof(IEndSessionRequestValidator), - typeof(IExtensionGrantValidator), - typeof(IIdentityProviderConfigurationValidator), - typeof(IIntrospectionRequestValidator), - typeof(IJwtRequestValidator), - typeof(IPushedAuthorizationRequestValidator), - typeof(IRedirectUriValidator), - typeof(IResourceOwnerPasswordValidator), - typeof(IResourceValidator), - typeof(IScopeParser), - typeof(ISecretParser), - typeof(ISecretsListParser), - typeof(ISecretsListValidator), - typeof(ISecretValidator), - typeof(ITokenRequestValidator), - typeof(ITokenRevocationRequestValidator), - typeof(ITokenValidator), - typeof(IUserInfoRequestValidator) + new(typeof(IApiSecretValidator),[typeof(ApiSecretValidator)]), + new(typeof(IAuthorizeRequestValidator), [typeof(AuthorizeRequestValidator)]), + new(typeof(IBackchannelAuthenticationRequestIdValidator), [typeof(BackchannelAuthenticationRequestIdValidator)]), + new(typeof(IBackchannelAuthenticationRequestValidator), [typeof(BackchannelAuthenticationRequestValidator)]), + new(typeof(IBackchannelAuthenticationUserValidator), [typeof(NopBackchannelAuthenticationUserValidator)]), + new(typeof(IClientConfigurationValidator), [typeof(DefaultClientConfigurationValidator)]), + new(typeof(IClientSecretValidator), [typeof(ClientSecretValidator)]), + new(typeof(ICustomAuthorizeRequestValidator), [typeof(DefaultCustomAuthorizeRequestValidator)]), + new(typeof(ICustomBackchannelAuthenticationValidator), [typeof(DefaultCustomBackchannelAuthenticationValidator)]), + new(typeof(ICustomTokenRequestValidator), [typeof(DefaultCustomTokenRequestValidator)]), + new(typeof(ICustomTokenValidator), [typeof(DefaultCustomTokenValidator)]), + new(typeof(IDeviceAuthorizationRequestValidator), [typeof(DeviceAuthorizationRequestValidator)]), + new(typeof(IDeviceCodeValidator), [typeof(DeviceCodeValidator)]), + new(typeof(IDPoPProofValidator), [typeof(DefaultDPoPProofValidator)]), + new(typeof(IEndSessionRequestValidator), [typeof(EndSessionRequestValidator)]), + new(typeof(IExtensionGrantValidator), []), + new(typeof(IIdentityProviderConfigurationValidator), [typeof(DefaultIdentityProviderConfigurationValidator)]), + new(typeof(IIntrospectionRequestValidator), [typeof(IntrospectionRequestValidator)]), + new(typeof(IJwtRequestValidator), [typeof(JwtRequestValidator)]), + new(typeof(IPushedAuthorizationRequestValidator), [typeof(PushedAuthorizationRequestValidator)]), + new(typeof(IRedirectUriValidator), [typeof(StrictRedirectUriValidator)]), + new(typeof(IResourceOwnerPasswordValidator), [typeof(NotSupportedResourceOwnerPasswordValidator)]), + new(typeof(IResourceValidator), [typeof(DefaultResourceValidator)]), + new(typeof(IScopeParser), [typeof(DefaultScopeParser)]), + new(typeof(ISecretParser), [typeof(BasicAuthenticationSecretParser), typeof(PostBodySecretParser)]), + new(typeof(ISecretsListParser), [typeof(SecretParser)]), + new(typeof(ISecretsListValidator), [typeof(SecretValidator)]), + new(typeof(ISecretValidator), [typeof(HashedSharedSecretValidator)]), + new(typeof(ITokenRequestValidator),[typeof(TokenRequestValidator)]), + new(typeof(ITokenRevocationRequestValidator), [typeof(TokenRevocationRequestValidator)]), + new(typeof(ITokenValidator), [typeof(TokenValidator)]), + new(typeof(IUserInfoRequestValidator), [typeof(UserInfoRequestValidator)]), ] } }; @@ -161,9 +168,9 @@ public Task WriteAsync(Utf8JsonWriter writer) { writer.WriteStartArray(group.Key); - foreach (var type in group.Value) + foreach (var implementationInfo in group.Value) { - WriteImplementationDetails(type, type.Name, writer); + WriteImplementationDetails(implementationInfo, writer); } writer.WriteEndArray(); @@ -174,36 +181,31 @@ public Task WriteAsync(Utf8JsonWriter writer) return Task.CompletedTask; } - private void WriteImplementationDetails(Type targetType, string serviceName, Utf8JsonWriter writer) + private void WriteImplementationDetails(RegisteredImplementationDetails registeredImplementationDetails, Utf8JsonWriter writer) { - writer.WriteStartObject(); - writer.WriteStartArray(serviceName); - var services = serviceCollectionAccessor.ServiceCollection.Where(descriptor => - descriptor.ServiceType == targetType && - descriptor.ImplementationType != null); - if (services.Any()) + descriptor.ServiceType == registeredImplementationDetails.TInterface && + descriptor.ImplementationType != null && + !registeredImplementationDetails.TDefaultImplementations.Contains(descriptor.ImplementationType)); + + if (!services.Any()) { - foreach (var service in services) - { - var type = service.ImplementationType!; - writer.WriteStartObject(); - writer.WriteString("TypeName", type.FullName); - writer.WriteString("Assembly", type.Assembly.GetName().Name); - writer.WriteString("AssemblyVersion", type.Assembly.GetName().Version?.ToString()); - writer.WriteEndObject(); - } + return; } - else + + writer.WriteStartObject(); + writer.WriteStartArray(registeredImplementationDetails.TInterface.Name); + + foreach (var service in services) { + var type = service.ImplementationType!; writer.WriteStartObject(); - writer.WriteString("TypeName", "Not Registered"); - writer.WriteString("Assembly", "Not Registered"); - writer.WriteString("AssemblyVersion", "Not Registered"); + writer.WriteString("TypeName", type.FullName); + writer.WriteString("Assembly", type.Assembly.GetName().Name); + writer.WriteString("AssemblyVersion", type.Assembly.GetName().Version?.ToString()); writer.WriteEndObject(); } - writer.WriteEndArray(); writer.WriteEndObject(); } diff --git a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/RegisteredImplementationsDiagnosticEntryTests.cs b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/RegisteredImplementationsDiagnosticEntryTests.cs index 3150513a9..918cf1734 100644 --- a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/RegisteredImplementationsDiagnosticEntryTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/RegisteredImplementationsDiagnosticEntryTests.cs @@ -1,20 +1,40 @@ // Copyright (c) Duende Software. All rights reserved. // See LICENSE in the project root for license information. +using System.Reflection; using Duende.IdentityServer.Licensing.V2; using Duende.IdentityServer.Licensing.V2.Diagnostics.DiagnosticEntries; using Duende.IdentityServer.Services; +using Duende.IdentityServer.Validation; using Microsoft.Extensions.DependencyInjection; using UnitTests.Common; +using UnitTests.Validation.Setup; namespace IdentityServer.UnitTests.Licensing.V2.DiagnosticEntries; public class RegisteredImplementationsDiagnosticEntryTests { [Fact] - public async Task WriteAsync_ShouldWriteRegisteredImplementationInfo() + public async Task Should_Not_Write_NonDefault_Implementations() { var serviceCollection = new ServiceCollection() + .AddSingleton() // Default + .AddSingleton(); // Default + var subject = new RegisteredImplementationsDiagnosticEntry(new ServiceCollectionAccessor(serviceCollection)); + + var result = await DiagnosticEntryTestHelper.WriteEntryToJson(subject); + + var registeredImplementations = result.RootElement.GetProperty("RegisteredImplementations"); + registeredImplementations.TryGetProperty("Services", out _).ShouldBeTrue(); + var services = registeredImplementations.GetProperty("Services"); + services.EnumerateArray().Any(entry => entry.TryGetProperty(nameof(ISecretParser), out _)).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Write_Type_Information_For_Non_Default_Implementation() + { + var serviceCollection = new ServiceCollection() + .AddSingleton() .AddSingleton(); var subject = new RegisteredImplementationsDiagnosticEntry(new ServiceCollectionAccessor(serviceCollection)); @@ -22,7 +42,7 @@ public async Task WriteAsync_ShouldWriteRegisteredImplementationInfo() var registeredImplementations = result.RootElement.GetProperty("RegisteredImplementations"); var services = registeredImplementations.GetProperty("Services"); - var profileServiceEntry = services.EnumerateArray().ToList().SingleOrDefault(entry => entry.TryGetProperty(nameof(IProfileService), out _)); + var profileServiceEntry = services.EnumerateArray().SingleOrDefault(entry => entry.TryGetProperty(nameof(IProfileService), out _)); var assemblyInfo = profileServiceEntry.GetProperty(nameof(IProfileService)).EnumerateArray().First(); var expectedTypeInfo = typeof(MockProfileService); assemblyInfo.GetProperty("TypeName").GetString().ShouldBe(expectedTypeInfo.FullName); @@ -31,7 +51,7 @@ public async Task WriteAsync_ShouldWriteRegisteredImplementationInfo() } [Fact] - public async Task WriteAsync_GroupsImplementationsByCategory() + public async Task Should_Group_Registered_Implementations_By_Category() { var subject = new RegisteredImplementationsDiagnosticEntry(new ServiceCollectionAccessor(new ServiceCollection())); @@ -48,10 +68,10 @@ public async Task WriteAsync_GroupsImplementationsByCategory() } [Fact] - public async Task WriteAsync_HandlesMultipleRegistrationsForAService() + public async Task Should_Handle_Multiple_Registered_Non_Default_Implementations() { var serviceCollection = new ServiceCollection() - .AddSingleton() + .AddSingleton() .AddSingleton(); var subject = new RegisteredImplementationsDiagnosticEntry(new ServiceCollectionAccessor(serviceCollection)); @@ -59,9 +79,9 @@ public async Task WriteAsync_HandlesMultipleRegistrationsForAService() var registeredImplementations = result.RootElement.GetProperty("RegisteredImplementations"); var services = registeredImplementations.GetProperty("Services"); - var profileServiceEntry = services.EnumerateArray().ToList().SingleOrDefault(entry => entry.TryGetProperty(nameof(IProfileService), out _)); + var profileServiceEntry = services.EnumerateArray().SingleOrDefault(entry => entry.TryGetProperty(nameof(IProfileService), out _)); var firstAssemblyInfo = profileServiceEntry.GetProperty(nameof(IProfileService)).EnumerateArray().First(); - var firstExpectedTypeInfo = typeof(DefaultProfileService); + var firstExpectedTypeInfo = typeof(TestProfileService); firstAssemblyInfo.GetProperty("TypeName").GetString().ShouldBe(firstExpectedTypeInfo.FullName); firstAssemblyInfo.GetProperty("Assembly").GetString().ShouldBe(firstExpectedTypeInfo.Assembly.GetName().Name); firstAssemblyInfo.GetProperty("AssemblyVersion").GetString().ShouldBe(firstExpectedTypeInfo.Assembly.GetName().Version?.ToString()); @@ -73,7 +93,7 @@ public async Task WriteAsync_HandlesMultipleRegistrationsForAService() } [Fact] - public async Task WriteAsync_HandlesNoServiceRegisteredForInterface() + public async Task Should_Handle_No_Non_Default_Implementations_Registered() { var subject = new RegisteredImplementationsDiagnosticEntry(new ServiceCollectionAccessor(new ServiceCollection())); @@ -81,29 +101,24 @@ public async Task WriteAsync_HandlesNoServiceRegisteredForInterface() var registeredImplementations = result.RootElement.GetProperty("RegisteredImplementations"); var services = registeredImplementations.GetProperty("Services"); - var profileServiceEntry = services.EnumerateArray().ToList().SingleOrDefault(entry => entry.TryGetProperty(nameof(IProfileService), out _)); - var assemblyInfo = profileServiceEntry.GetProperty(nameof(IProfileService)).EnumerateArray().First(); - assemblyInfo.GetProperty("TypeName").GetString().ShouldBe("Not Registered"); - assemblyInfo.GetProperty("Assembly").GetString().ShouldBe("Not Registered"); - assemblyInfo.GetProperty("AssemblyVersion").GetString().ShouldBe("Not Registered"); + services.EnumerateArray().Any(entry => entry.TryGetProperty(nameof(IProfileService), out _)).ShouldBeFalse(); } [Fact] - public async Task WriteAsync_ShouldIncludeAllPublicInterfaces() + public void Should_Track_All_Public_Interfaces() { var interfaces = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetExportedTypes()) .Where(type => type.IsInterface && type.IsPublic && type.Namespace != null && type.Namespace.StartsWith( "Duende.IdentityServer")) - .Select(type => type.Name); + .Select(type => type); var subject = new RegisteredImplementationsDiagnosticEntry(new ServiceCollectionAccessor(new ServiceCollection())); + var typesTrackedField = typeof(RegisteredImplementationsDiagnosticEntry) + .GetField("_typesToInspect", BindingFlags.NonPublic | BindingFlags.Instance) + ?.GetValue(subject) as Dictionary>; + var typesTracked = typesTrackedField?.SelectMany(kvp => kvp.Value).Select(details => details.TInterface); - var result = await DiagnosticEntryTestHelper.WriteEntryToJson(subject); - - var registeredImplementations = result.RootElement.GetProperty("RegisteredImplementations"); - var entries = registeredImplementations.EnumerateObject() - .SelectMany(property => property.Value.EnumerateArray()).Select(element => element.EnumerateObject().First().Name); - entries.ShouldBe(interfaces, ignoreOrder: true); + typesTracked.ShouldBe(interfaces, ignoreOrder: true); } }