// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Identity.Core;
namespace Microsoft.AspNetCore.Identity
{
///
/// Helper functions for configuring identity services.
///
public class IdentityBuilder
{
///
/// Creates a new instance of .
///
/// The to use for the users.
/// The to attach to.
public IdentityBuilder(Type user, IServiceCollection services)
{
UserType = user;
Services = services;
}
///
/// Creates a new instance of .
///
/// The to use for the users.
/// The to use for the roles.
/// The to attach to.
public IdentityBuilder(Type user, Type role, IServiceCollection services) : this(user, services)
=> RoleType = role;
///
/// Gets the used for users.
///
///
/// The used for users.
///
public Type UserType { get; private set; }
///
/// Gets the used for roles.
///
///
/// The used for roles.
///
public Type RoleType { get; private set; }
///
/// Gets the services are attached to.
///
///
/// The services are attached to.
///
public IServiceCollection Services { get; private set; }
private IdentityBuilder AddScoped(Type serviceType, Type concreteType)
{
Services.AddScoped(serviceType, concreteType);
return this;
}
///
/// Adds an for the .
///
/// The user validator type.
/// The current instance.
public virtual IdentityBuilder AddUserValidator() where TValidator : class
=> AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TValidator));
///
/// Adds an for the .
///
/// The type of the claims principal factory.
/// The current instance.
public virtual IdentityBuilder AddClaimsPrincipalFactory() where TFactory : class
=> AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory));
///
/// Adds an .
///
/// The type of the error describer.
/// The current instance.
public virtual IdentityBuilder AddErrorDescriber() where TDescriber : IdentityErrorDescriber
{
Services.AddScoped();
return this;
}
///
/// Adds an for the .
///
/// The validator type used to validate passwords.
/// The current instance.
public virtual IdentityBuilder AddPasswordValidator() where TValidator : class
=> AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator));
///
/// Adds an for the .
///
/// The user store type.
/// The current instance.
public virtual IdentityBuilder AddUserStore() where TStore : class
=> AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore));
///
/// Adds a token provider.
///
/// The type of the token provider to add.
/// The name of the provider to add.
/// The current instance.
public virtual IdentityBuilder AddTokenProvider(string providerName) where TProvider : class
=> AddTokenProvider(providerName, typeof(TProvider));
///
/// Adds a token provider for the .
///
/// The name of the provider to add.
/// The type of the to add.
/// The current instance.
public virtual IdentityBuilder AddTokenProvider(string providerName, Type provider)
{
if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo()))
{
throw new InvalidOperationException(Resources.FormatInvalidManagerType(provider.Name, "IUserTwoFactorTokenProvider", UserType.Name));
}
Services.Configure(options =>
{
options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider);
});
Services.AddTransient(provider);
return this;
}
///
/// Adds a for the .
///
/// The type of the user manager to add.
/// The current instance.
public virtual IdentityBuilder AddUserManager() where TUserManager : class
{
var userManagerType = typeof(UserManager<>).MakeGenericType(UserType);
var customType = typeof(TUserManager);
if (!userManagerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
{
throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "UserManager", UserType.Name));
}
if (userManagerType != customType)
{
Services.AddScoped(customType, services => services.GetRequiredService(userManagerType));
}
return AddScoped(userManagerType, customType);
}
///
/// Adds Role related services for TRole, including IRoleStore, IRoleValidator, and RoleManager.
///
/// The role type.
/// The current instance.
public virtual IdentityBuilder AddRoles() where TRole : class
{
RoleType = typeof(TRole);
AddRoleValidator>();
Services.TryAddScoped>();
Services.AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(UserClaimsPrincipalFactory<,>).MakeGenericType(UserType, RoleType));
return this;
}
///
/// Adds an for the .
///
/// The role validator type.
/// The current instance.
public virtual IdentityBuilder AddRoleValidator() where TRole : class
{
if (RoleType == null)
{
throw new InvalidOperationException(Resources.NoRoleType);
}
return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(TRole));
}
///
/// Adds an and .
///
/// The personal data protector type.
/// The personal data protector key ring type.
/// The current instance.
public virtual IdentityBuilder AddPersonalDataProtection()
where TProtector : class,ILookupProtector
where TKeyRing : class, ILookupProtectorKeyRing
{
Services.AddSingleton();
Services.AddSingleton();
Services.AddSingleton();
return this;
}
///
/// Adds a for the .
///
/// The role store.
/// The current instance.
public virtual IdentityBuilder AddRoleStore() where TStore : class
{
if (RoleType == null)
{
throw new InvalidOperationException(Resources.NoRoleType);
}
return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(TStore));
}
///
/// Adds a for the .
///
/// The type of the role manager to add.
/// The current instance.
public virtual IdentityBuilder AddRoleManager() where TRoleManager : class
{
if (RoleType == null)
{
throw new InvalidOperationException(Resources.NoRoleType);
}
var managerType = typeof(RoleManager<>).MakeGenericType(RoleType);
var customType = typeof(TRoleManager);
if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
{
throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "RoleManager", RoleType.Name));
}
if (managerType != customType)
{
Services.AddScoped(typeof(TRoleManager), services => services.GetRequiredService(managerType));
}
return AddScoped(managerType, typeof(TRoleManager));
}
}
}