diff --git a/identity-server/hosts/main/Program.cs b/identity-server/hosts/main/Program.cs index cee27845f..4645ee5ae 100644 --- a/identity-server/hosts/main/Program.cs +++ b/identity-server/hosts/main/Program.cs @@ -38,12 +38,17 @@ .ConfigureServices() .ConfigurePipeline(); - var usage = app.Services.GetRequiredService(); + if (app.Environment.IsDevelopment()) + { + app.Lifetime.ApplicationStopping.Register(() => + { + var usage = app.Services.GetRequiredService(); + Console.Write(Summary(usage)); + Console.ReadKey(); + }); + } app.Run(); - - Console.Write(Summary(usage)); - Console.ReadKey(); } catch (Exception ex) { @@ -55,7 +60,7 @@ Log.CloseAndFlush(); } -string Summary(LicenseUsageSummary usage) +static string Summary(LicenseUsageSummary usage) { var sb = new StringBuilder(); sb.AppendLine("IdentityServer Usage Summary:"); @@ -67,4 +72,3 @@ string Summary(LicenseUsageSummary usage) return sb.ToString(); } - \ No newline at end of file diff --git a/identity-server/src/IdentityServer/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStore.cs b/identity-server/src/IdentityServer/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStore.cs index bfa04987e..b8c4c3aff 100644 --- a/identity-server/src/IdentityServer/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStore.cs +++ b/identity-server/src/IdentityServer/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStore.cs @@ -21,7 +21,6 @@ public class PostConfigureApplicationCookieTicketStore : IPostConfigureOptions _logger; /// @@ -38,7 +37,6 @@ public PostConfigureApplicationCookieTicketStore( ILogger logger) { _httpContextAccessor = httpContextAccessor; - _licenseUsage = httpContextAccessor.HttpContext?.RequestServices.GetRequiredService(); _logger = logger; _scheme = identityServerOptions.Authentication.CookieAuthenticationScheme ?? @@ -69,7 +67,8 @@ public void PostConfigure(string name, CookieAuthenticationOptions options) } IdentityServerLicenseValidator.Instance.ValidateServerSideSessions(); - _licenseUsage.FeatureUsed(LicenseFeature.ServerSideSessions); + var licenseUsage = _httpContextAccessor.HttpContext?.RequestServices.GetRequiredService(); + licenseUsage.FeatureUsed(LicenseFeature.ServerSideSessions); var sessionStore = _httpContextAccessor.HttpContext!.RequestServices.GetService(); if (sessionStore is InMemoryServerSideSessionStore) diff --git a/identity-server/src/IdentityServer/Licensing/LicenseUsageSummary.cs b/identity-server/src/IdentityServer/Licensing/LicenseUsageSummary.cs index 38451b33a..51dbcf66c 100644 --- a/identity-server/src/IdentityServer/Licensing/LicenseUsageSummary.cs +++ b/identity-server/src/IdentityServer/Licensing/LicenseUsageSummary.cs @@ -8,12 +8,12 @@ namespace Duende.IdentityServer.Licensing; /// -/// Usage summary for the current license. +/// Usage summary for the current IdentityServer instance intended for auditing purposes. /// -/// -/// -/// -/// +/// License edition retrieved from license key. +/// Clients used in the current IdentityServer instance. +/// Issuers used in the current IdentityServer instance. +/// Features used in the current IdentityServer instance. public record LicenseUsageSummary( string LicenseEdition, IReadOnlyCollection ClientsUsed, diff --git a/identity-server/test/IdentityServer.UnitTests/Common/MockHttpContextAccessor.cs b/identity-server/test/IdentityServer.UnitTests/Common/MockHttpContextAccessor.cs index 6ce802a4f..b9d7f2525 100644 --- a/identity-server/test/IdentityServer.UnitTests/Common/MockHttpContextAccessor.cs +++ b/identity-server/test/IdentityServer.UnitTests/Common/MockHttpContextAccessor.cs @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. +using System; using Duende.IdentityServer; using Duende.IdentityServer.Configuration; using Duende.IdentityServer.Models; @@ -24,7 +25,8 @@ public MockHttpContextAccessor( IdentityServerOptions options = null, IUserSession userSession = null, IMessageStore endSessionStore = null, - IServerUrls urls = null) + IServerUrls urls = null, + Action configureServices = null) { options = options ?? TestIdentityServerOptions.Create(); @@ -63,6 +65,11 @@ public MockHttpContextAccessor( services.AddSingleton(urls); } + if (configureServices != null) + { + configureServices(services); + } + _context.RequestServices = services.BuildServiceProvider(); } diff --git a/test/IdentityServer.UnitTests/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStoreTests.cs b/test/IdentityServer.UnitTests/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStoreTests.cs new file mode 100644 index 000000000..59dd07471 --- /dev/null +++ b/test/IdentityServer.UnitTests/Configuration/DependencyInjection/PostConfigureApplicationCookieTicketStoreTests.cs @@ -0,0 +1,53 @@ +using Duende.IdentityServer.Configuration; +using Duende.IdentityServer.Licensing.V2; +using FluentAssertions; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using UnitTests.Common; +using Xunit; + +namespace UnitTests.Configuration.DependencyInjection; + +public class PostConfigureApplicationCookieTicketStoreTests +{ + + [Fact] + public void can_be_constructed_without_httpcontext_and_used_later_with_httpcontext() + { + // Register the dependencies of the usage tracker so that we can resolve it in PostConfigure + var httpContextAccessor = new MockHttpContextAccessor(configureServices: sp => + { + sp.AddSingleton(TestLogger.Create()); + sp.AddSingleton(); + sp.AddSingleton(); + }); + + // The mock http context accessor has a convenient HttpContext, but + // initially we simulate not having it by stashing it away and setting + // the accessor's context to null. + var savedContext = httpContextAccessor.HttpContext; + httpContextAccessor.HttpContext = null; + + var sut = new PostConfigureApplicationCookieTicketStore( + httpContextAccessor, + new IdentityServerOptions + { + Authentication = new AuthenticationOptions + { + // This is needed so that we operate on the correct scheme + CookieAuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme + } + }, + Options.Create(new()), + TestLogger.Create() + ); + + // Now that we've constructed, we can bring back the http context and run PostConfigure + httpContextAccessor.HttpContext = savedContext; + var cookieOpts = new CookieAuthenticationOptions(); + sut.PostConfigure(CookieAuthenticationDefaults.AuthenticationScheme, cookieOpts); + + cookieOpts.SessionStore.Should().BeOfType(); + } +} \ No newline at end of file