/* * 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 System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; namespace OpenIddict.Server; /// /// Represents an immutable descriptor of an OpenIddict server event handler. /// [DebuggerDisplay("{ServiceDescriptor?.ServiceType}")] public class OpenIddictServerHandlerDescriptor { /// /// Creates a new instance of the class. /// private OpenIddictServerHandlerDescriptor() { } /// /// Gets the context type associated with the event. /// public Type ContextType { get; private set; } = default!; /// /// Gets the list of filters responsible for excluding the handler /// from the activated handlers if it doesn't meet the criteria. /// public ImmutableArray FilterTypes { get; private set; } = ImmutableArray.Create(); /// /// Gets the order assigned to the handler. /// public int Order { get; private set; } /// /// Gets the service descriptor associated with the handler. /// public ServiceDescriptor ServiceDescriptor { get; private set; } = default!; /// /// Gets the type associated with the handler. /// public OpenIddictServerHandlerType Type { get; private set; } /// /// Creates a builder allowing to initialize an immutable descriptor. /// /// The event context type. /// A new descriptor builder. public static Builder CreateBuilder() where TContext : BaseContext => new Builder(); /// /// Contains methods allowing to build a descriptor instance. /// /// The event context type. public class Builder where TContext : BaseContext { private ServiceDescriptor? _descriptor; private readonly List _filters = new(); private int _order; private OpenIddictServerHandlerType _type; /// /// Adds the type of a handler filter to the filters list. /// /// The event handler filter type. /// The builder instance, so that calls can be easily chained. public Builder AddFilter(Type type) { if (type is null) { throw new ArgumentNullException(nameof(type)); } if (!typeof(IOpenIddictServerHandlerFilter<>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0104)); } _filters.Add(type); return this; } /// /// Adds the type of a handler filter to the filters list. /// /// The event handler filter type. /// The builder instance, so that calls can be easily chained. public Builder AddFilter() where TFilter : IOpenIddictServerHandlerFilter => AddFilter(typeof(TFilter)); /// /// Imports the properties set on the specified descriptor. /// /// The existing descriptor properties are copied from. /// All the properties previously set on this instance are automatically replaced. /// The builder instance, so that calls can be easily chained. public Builder Import(OpenIddictServerHandlerDescriptor descriptor) { if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } if (descriptor.ContextType != typeof(TContext)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0284)); } _descriptor = descriptor.ServiceDescriptor; _filters.Clear(); _filters.AddRange(descriptor.FilterTypes); _order = descriptor.Order; _type = descriptor.Type; return this; } /// /// Sets the service descriptor. /// /// The service descriptor. /// The builder instance, so that calls can be easily chained. public Builder SetServiceDescriptor(ServiceDescriptor descriptor) { if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } var type = descriptor.ServiceType; if (!typeof(IOpenIddictServerHandler<>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0104)); } _descriptor = descriptor; return this; } /// /// Sets the order in which the event handler will be invoked. /// /// The handler order. /// The builder instance, so that calls can be easily chained. public Builder SetOrder(int order) { _order = order; return this; } /// /// Sets the type associated to the handler. /// /// The handler type. /// The builder instance, so that calls can be easily chained. public Builder SetType(OpenIddictServerHandlerType type) { if (!Enum.IsDefined(typeof(OpenIddictServerHandlerType), type)) { throw new InvalidEnumArgumentException(nameof(type), (int) type, typeof(OpenIddictServerHandlerType)); } _type = type; return this; } /// /// Configures the descriptor to use the specified inline handler. /// /// The handler instance. /// The builder instance, so that calls can be easily chained. public Builder UseInlineHandler(Func handler) { if (handler is null) { throw new ArgumentNullException(nameof(handler)); } return UseSingletonHandler(new OpenIddictServerHandler(handler)); } /// /// Configures the descriptor to use the specified scoped handler. /// /// The handler type. /// The builder instance, so that calls can be easily chained. public Builder UseScopedHandler() where THandler : IOpenIddictServerHandler => SetServiceDescriptor(new ServiceDescriptor( typeof(THandler), typeof(THandler), ServiceLifetime.Scoped)); /// /// Configures the descriptor to use the specified scoped handler. /// /// The handler type. /// The factory used to create the handler. /// The builder instance, so that calls can be easily chained. public Builder UseScopedHandler(Func factory) where THandler : IOpenIddictServerHandler { if (factory is null) { throw new ArgumentNullException(nameof(factory)); } return SetServiceDescriptor(new ServiceDescriptor( typeof(THandler), factory, ServiceLifetime.Scoped)); } /// /// Configures the descriptor to use the specified singleton handler. /// /// The handler type. /// The builder instance, so that calls can be easily chained. public Builder UseSingletonHandler() where THandler : IOpenIddictServerHandler => SetServiceDescriptor(new ServiceDescriptor( typeof(THandler), typeof(THandler), ServiceLifetime.Singleton)); /// /// Configures the descriptor to use the specified singleton handler. /// /// The handler type. /// The factory used to create the handler. /// The builder instance, so that calls can be easily chained. public Builder UseSingletonHandler(Func factory) where THandler : IOpenIddictServerHandler { if (factory is null) { throw new ArgumentNullException(nameof(factory)); } return SetServiceDescriptor(new ServiceDescriptor( typeof(THandler), factory, ServiceLifetime.Singleton)); } /// /// Configures the descriptor to use the specified singleton handler. /// /// The handler type. /// The handler instance. /// The builder instance, so that calls can be easily chained. public Builder UseSingletonHandler(THandler handler) where THandler : IOpenIddictServerHandler { if (handler is null) { throw new ArgumentNullException(nameof(handler)); } return SetServiceDescriptor(new ServiceDescriptor(typeof(THandler), handler)); } /// /// Build a new descriptor instance, based on the parameters that were previously set. /// /// The builder instance, so that calls can be easily chained. public OpenIddictServerHandlerDescriptor Build() => new OpenIddictServerHandlerDescriptor { ContextType = typeof(TContext), FilterTypes = _filters.ToImmutableArray(), Order = _order, ServiceDescriptor = _descriptor ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0105)), Type = _type }; } }