Skip to content

Commit

Permalink
Added new license management services (#1637)
Browse files Browse the repository at this point in the history
  • Loading branch information
josephdecock authored Dec 11, 2024
1 parent b5712bd commit 978f37c
Show file tree
Hide file tree
Showing 37 changed files with 1,168 additions and 55 deletions.
3 changes: 2 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
<PackageReference Update="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(FrameworkVersion)" />
<PackageReference Update="AngleSharp" Version="1.1.2" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.8.0" />

<PackageReference Update="Microsoft.Extensions.Diagnostics.Testing" Version="9.0.0" />
<PackageReference Update="Microsoft.Extensions.TimeProvider.Testing" Version="9.0.0" />

<!--microsoft extensions -->
<PackageReference Update="Microsoft.Extensions.Caching.Memory" Version="$(ExtensionsVersion)"/>
Expand Down
1 change: 0 additions & 1 deletion hosts/main/Host.Main.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

<ItemGroup>
<ProjectReference Include="..\..\src\IdentityServer\Duende.IdentityServer.csproj" />
<ProjectReference Include="..\..\src\Configuration\Duende.IdentityServer.Configuration.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 0 additions & 4 deletions hosts/main/HostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Security.Claims;
using Duende.IdentityServer;
using Duende.IdentityServer.Configuration;
using IdentityServerHost.Extensions;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.IdentityModel.Tokens;
Expand Down Expand Up @@ -159,9 +158,6 @@ internal static WebApplication ConfigurePipeline(this WebApplication app)
app.MapRazorPages()
.RequireAuthorization();

app.MapDynamicClientRegistration()
.AllowAnonymous();

// Map /metrics that displays Otel data in human readable form.
app.UseOpenTelemetryPrometheusScrapingEndpoint();

Expand Down
13 changes: 7 additions & 6 deletions hosts/main/IdentityServerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal static WebApplicationBuilder ConfigureIdentityServer(this WebApplicatio
UseX509Certificate = true
});
})
//.AddServerSideSessions()
.AddServerSideSessions()
.AddInMemoryClients(Clients.Get().ToList())
.AddInMemoryIdentityResources(Resources.IdentityResources)
.AddInMemoryApiScopes(Resources.ApiScopes)
Expand All @@ -64,15 +64,16 @@ internal static WebApplicationBuilder ConfigureIdentityServer(this WebApplicatio
ResponseType = "id_token",
Scope = "openid profile"
}
]);
])
.AddLicenseSummary();


builder.Services.AddDistributedMemoryCache();

builder.Services.AddIdentityServerConfiguration(opt =>
{
// opt.DynamicClientRegistration.SecretLifetime = TimeSpan.FromHours(1);
}).AddInMemoryClientConfigurationStore();
// builder.Services.AddIdentityServerConfiguration(opt =>
// {
// // opt.DynamicClientRegistration.SecretLifetime = TimeSpan.FromHours(1);
// }).AddInMemoryClientConfigurationStore();

return builder;
}
Expand Down
23 changes: 22 additions & 1 deletion hosts/main/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

using Duende.IdentityServer.Licensing;
using IdentityServerHost;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;
using System.Globalization;
using System.Text;

Console.Title = "IdentityServer (Main)";

Expand Down Expand Up @@ -36,7 +38,12 @@
.ConfigureServices()
.ConfigurePipeline();

var usage = app.Services.GetRequiredService<LicenseUsageSummary>();

app.Run();

Console.Write(Summary(usage));
Console.ReadKey();
}
catch (Exception ex)
{
Expand All @@ -46,4 +53,18 @@
{
Log.Information("Shut down complete");
Log.CloseAndFlush();
}
}

string Summary(LicenseUsageSummary usage)
{
var sb = new StringBuilder();
sb.AppendLine("IdentityServer Usage Summary:");
sb.AppendLine(CultureInfo.InvariantCulture, $" License: {usage.LicenseEdition}");
var features = usage.FeaturesUsed.Count > 0 ? string.Join(", ", usage.FeaturesUsed) : "None";
sb.AppendLine(CultureInfo.InvariantCulture, $" Business and Enterprise Edition Features Used: {features}");
sb.AppendLine(CultureInfo.InvariantCulture, $" {usage.ClientsUsed.Count} Client Id(s) Used");
sb.AppendLine(CultureInfo.InvariantCulture, $" {usage.IssuersUsed.Count} Issuer(s) Used");

return sb.ToString();
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
using Duende.IdentityServer.Internal;
using Duende.IdentityServer.Stores.Empty;
using Duende.IdentityServer.Endpoints.Results;
using Duende.IdentityServer.Licensing;
using Duende.IdentityServer.Licensing.V2;

namespace Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -206,6 +208,10 @@ public static IIdentityServerBuilder AddCoreServices(this IIdentityServerBuilder

builder.Services.AddTransient(services => IdentityServerLicenseValidator.Instance.GetLicense());

builder.Services.AddSingleton<LicenseAccessor>();
builder.Services.AddSingleton<ProtocolRequestCounter>();
builder.Services.AddSingleton<LicenseUsageTracker>();

return builder;
}

Expand Down Expand Up @@ -392,6 +398,15 @@ public static IIdentityServerBuilder AddDefaultSecretValidators(this IIdentitySe
return builder;
}

/// <summary>
/// Adds the license summary, which provides information about the current license usage.
/// </summary>
public static IIdentityServerBuilder AddLicenseSummary(this IIdentityServerBuilder builder)
{
builder.Services.AddTransient<LicenseUsageSummary>(services => services.GetRequiredService<LicenseUsageTracker>().GetSummary());
return builder;
}

internal static void AddTransientDecorator<TService, TImplementation>(this IServiceCollection services)
where TService : class
where TImplementation : class, TService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See LICENSE in the project root for license information.


using Duende.IdentityServer.Licensing.V2;
using Duende.IdentityServer.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
Expand All @@ -20,6 +21,7 @@ public class PostConfigureApplicationCookieTicketStore : IPostConfigureOptions<C
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly string _scheme;
private readonly LicenseUsageTracker _licenseUsage;
private readonly ILogger<PostConfigureApplicationCookieTicketStore> _logger;

/// <summary>
Expand All @@ -36,6 +38,7 @@ public PostConfigureApplicationCookieTicketStore(
ILogger<PostConfigureApplicationCookieTicketStore> logger)
{
_httpContextAccessor = httpContextAccessor;
_licenseUsage = httpContextAccessor.HttpContext?.RequestServices.GetRequiredService<LicenseUsageTracker>();
_logger = logger;

_scheme = identityServerOptions.Authentication.CookieAuthenticationScheme ??
Expand Down Expand Up @@ -66,6 +69,7 @@ public void PostConfigure(string name, CookieAuthenticationOptions options)
}

IdentityServerLicenseValidator.Instance.ValidateServerSideSessions();
_licenseUsage.FeatureUsed(LicenseFeature.ServerSideSessions);

var sessionStore = _httpContextAccessor.HttpContext!.RequestServices.GetService<IServerSideSessionStore>();
if (sessionStore is InMemoryServerSideSessionStore)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Duende.IdentityServer.Licensing.V2;

namespace Microsoft.AspNetCore.Builder;

Expand Down Expand Up @@ -78,6 +79,12 @@ internal static void Validate(this IApplicationBuilder app)
var env = serviceProvider.GetRequiredService<IHostEnvironment>();
IdentityServerLicenseValidator.Instance.Initialize(loggerFactory, options, env.IsDevelopment());

if (options.KeyManagement.Enabled)
{
var licenseUsage = serviceProvider.GetRequiredService<LicenseUsageTracker>();
licenseUsage.FeatureUsed(LicenseFeature.KeyManagement);
}

TestService(serviceProvider, typeof(IPersistedGrantStore), logger, "No storage mechanism for grants specified. Use the 'AddInMemoryPersistedGrants' extension method to register a development version.");
TestService(serviceProvider, typeof(IClientStore), logger, "No storage mechanism for clients specified. Use the 'AddInMemoryClients' extension method to register a development version.");
TestService(serviceProvider, typeof(IResourceStore), logger, "No storage mechanism for resources specified. Use the 'AddInMemoryIdentityResources' or 'AddInMemoryApiResources' extension method to register a development version.");
Expand Down
7 changes: 6 additions & 1 deletion src/IdentityServer/Endpoints/PushedAuthorizationEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,31 @@
using System.Collections.Specialized;
using System.Net;
using System.Threading.Tasks;
using Duende.IdentityServer.Licensing.V2;

namespace Duende.IdentityServer.Endpoints;
internal class PushedAuthorizationEndpoint : IEndpointHandler
{
private readonly IClientSecretValidator _clientValidator;
private readonly IPushedAuthorizationRequestValidator _parValidator;
private readonly IPushedAuthorizationResponseGenerator _responseGenerator;
private readonly LicenseUsageTracker _features;
private readonly IdentityServerOptions _options;
private readonly ILogger<PushedAuthorizationEndpoint> _logger;

public PushedAuthorizationEndpoint(
IClientSecretValidator clientValidator,
IPushedAuthorizationRequestValidator parValidator,
IAuthorizeRequestValidator authorizeRequestValidator,
IPushedAuthorizationResponseGenerator responseGenerator,
LicenseUsageTracker features,
IdentityServerOptions options,
ILogger<PushedAuthorizationEndpoint> logger
)
{
_clientValidator = clientValidator;
_parValidator = parValidator;
_responseGenerator = responseGenerator;
_features = features;
_options = options;
_logger = logger;
}
Expand All @@ -48,6 +51,8 @@ public async Task<IEndpointResult> ProcessAsync(HttpContext context)

_logger.LogDebug("Start pushed authorization request");

_features.FeatureUsed(LicenseFeature.PAR);

NameValueCollection values;
IFormCollection form;
if(HttpMethods.IsPost(context.Request.Method))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,30 @@
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
using Duende.IdentityServer.Licensing.V2;

namespace Duende.IdentityServer.Hosting.DynamicProviders;

class DynamicAuthenticationSchemeProvider : IAuthenticationSchemeProvider
{
private readonly IAuthenticationSchemeProvider _inner;
private readonly DynamicProviderOptions _options;
private readonly LicenseUsageTracker _licenseUsageTracker;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<DynamicAuthenticationSchemeProvider> _logger;

public DynamicAuthenticationSchemeProvider(
Decorator<IAuthenticationSchemeProvider> inner,
DynamicProviderOptions options,
LicenseUsageTracker licenseUsageTracker,
IHttpContextAccessor httpContextAccessor,
ILogger<DynamicAuthenticationSchemeProvider> logger)
{
_inner = inner.Instance;
_options = options;
_httpContextAccessor = httpContextAccessor;
_logger = logger;
_licenseUsageTracker = licenseUsageTracker;
}

public void AddScheme(AuthenticationScheme scheme)
Expand Down Expand Up @@ -115,6 +119,7 @@ private async Task<AuthenticationScheme> GetDynamicSchemeAsync(string name)
if (providerType != null)
{
IdentityServerLicenseValidator.Instance.ValidateDynamicProviders();
_licenseUsageTracker.FeatureUsed(LicenseFeature.DynamicProviders);
dynamicScheme = new DynamicAuthenticationScheme(idp, providerType.HandlerType);
cache.Add(name, dynamicScheme);
}
Expand Down
27 changes: 13 additions & 14 deletions src/IdentityServer/Hosting/EndpointRouter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Duende Software. All rights reserved.
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.


Expand All @@ -8,34 +8,33 @@
using System;
using System.Collections.Generic;
using Duende.IdentityServer.Configuration;
using Duende.IdentityServer.Licensing.V2;

namespace Duende.IdentityServer.Hosting;

internal class EndpointRouter : IEndpointRouter
internal class EndpointRouter(
IEnumerable<Endpoint> endpoints,
ProtocolRequestCounter requestCounter,
IdentityServerOptions options,
ILogger<EndpointRouter> logger)
: IEndpointRouter
{
private readonly IEnumerable<Endpoint> _endpoints;
private readonly IdentityServerOptions _options;
private readonly ILogger _logger;

public EndpointRouter(IEnumerable<Endpoint> endpoints, IdentityServerOptions options, ILogger<EndpointRouter> logger)
{
_endpoints = endpoints;
_options = options;
_logger = logger;
}
private readonly ILogger _logger = logger;

public IEndpointHandler Find(HttpContext context)
{
ArgumentNullException.ThrowIfNull(context);

foreach(var endpoint in _endpoints)
foreach(var endpoint in endpoints)
{
var path = endpoint.Path;
if (context.Request.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
{
var endpointName = endpoint.Name;
_logger.LogDebug("Request path {path} matched to endpoint type {endpoint}", context.Request.Path, endpointName);

requestCounter.Increment();

return GetEndpointHandler(endpoint, context);
}
}
Expand All @@ -47,7 +46,7 @@ public IEndpointHandler Find(HttpContext context)

private IEndpointHandler GetEndpointHandler(Endpoint endpoint, HttpContext context)
{
if (_options.Endpoints.IsEndpointEnabled(endpoint))
if (options.Endpoints.IsEndpointEnabled(endpoint))
{
if (context.RequestServices.GetService(endpoint.Handler) is IEndpointHandler handler)
{
Expand Down
9 changes: 6 additions & 3 deletions src/IdentityServer/Hosting/IdentityServerMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
using Duende.IdentityServer.Models;
using System.Linq;
using Duende.IdentityServer.Configuration;
using Microsoft.AspNetCore.WebUtilities;
using System.Collections.Generic;
using Duende.IdentityServer.Licensing.V2;
using Microsoft.Extensions.DependencyInjection;

namespace Duende.IdentityServer.Hosting;

Expand Down Expand Up @@ -99,7 +99,10 @@ public async Task Invoke(
using var activity = Tracing.BasicActivitySource.StartActivity("IdentityServerProtocolRequest");
activity?.SetTag(Tracing.Properties.EndpointType, endpointType);

IdentityServerLicenseValidator.Instance.ValidateIssuer(await issuerNameService.GetCurrentAsync());
var issuer = await issuerNameService.GetCurrentAsync();
var licenseUsage = context.RequestServices.GetRequiredService<LicenseUsageTracker>();
licenseUsage.IssuerUsed(issuer);
IdentityServerLicenseValidator.Instance.ValidateIssuer(issuer);

_logger.LogInformation("Invoking IdentityServer endpoint: {endpointType} for {url}", endpointType, requestPath);

Expand Down
21 changes: 21 additions & 0 deletions src/IdentityServer/Licensing/LicenseUsageSummary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

#nullable enable

using System.Collections.Generic;

namespace Duende.IdentityServer.Licensing;

/// <summary>
/// Usage summary for the current license.
/// </summary>
/// <param name="LicenseEdition"></param>
/// <param name="ClientsUsed"></param>
/// <param name="IssuersUsed"></param>
/// <param name="FeaturesUsed"></param>
public record LicenseUsageSummary(
string LicenseEdition,
IReadOnlyCollection<string> ClientsUsed,
IReadOnlyCollection<string> IssuersUsed,
IReadOnlyCollection<string> FeaturesUsed);
Loading

0 comments on commit 978f37c

Please sign in to comment.