1 // Copyright (c) .NET Foundation. All rights reserved. 2 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 4 using System; 5 using System.Reflection; 6 using Microsoft.Extensions.DependencyInjection; 7 using Microsoft.Extensions.DependencyInjection.Extensions; 8 using Microsoft.Extensions.Identity.Core; 9 10 namespace Microsoft.AspNetCore.Identity 11 { 12 /// <summary> 13 /// Helper functions for configuring identity services. 14 /// </summary> 15 public class IdentityBuilder 16 { 17 /// <summary> 18 /// Creates a new instance of <see cref="IdentityBuilder"/>. 19 /// </summary> 20 /// <param name="user">The <see cref="Type"/> to use for the users.</param> 21 /// <param name="services">The <see cref="IServiceCollection"/> to attach to.</param> IdentityBuilder(Type user, IServiceCollection services)22 public IdentityBuilder(Type user, IServiceCollection services) 23 { 24 UserType = user; 25 Services = services; 26 } 27 28 /// <summary> 29 /// Creates a new instance of <see cref="IdentityBuilder"/>. 30 /// </summary> 31 /// <param name="user">The <see cref="Type"/> to use for the users.</param> 32 /// <param name="role">The <see cref="Type"/> to use for the roles.</param> 33 /// <param name="services">The <see cref="IServiceCollection"/> to attach to.</param> IdentityBuilder(Type user, Type role, IServiceCollection services)34 public IdentityBuilder(Type user, Type role, IServiceCollection services) : this(user, services) 35 => RoleType = role; 36 37 /// <summary> 38 /// Gets the <see cref="Type"/> used for users. 39 /// </summary> 40 /// <value> 41 /// The <see cref="Type"/> used for users. 42 /// </value> 43 public Type UserType { get; private set; } 44 45 46 /// <summary> 47 /// Gets the <see cref="Type"/> used for roles. 48 /// </summary> 49 /// <value> 50 /// The <see cref="Type"/> used for roles. 51 /// </value> 52 public Type RoleType { get; private set; } 53 54 /// <summary> 55 /// Gets the <see cref="IServiceCollection"/> services are attached to. 56 /// </summary> 57 /// <value> 58 /// The <see cref="IServiceCollection"/> services are attached to. 59 /// </value> 60 public IServiceCollection Services { get; private set; } 61 AddScoped(Type serviceType, Type concreteType)62 private IdentityBuilder AddScoped(Type serviceType, Type concreteType) 63 { 64 Services.AddScoped(serviceType, concreteType); 65 return this; 66 } 67 68 /// <summary> 69 /// Adds an <see cref="IUserValidator{TUser}"/> for the <seealso cref="UserType"/>. 70 /// </summary> 71 /// <typeparam name="TValidator">The user validator type.</typeparam> 72 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 73 public virtual IdentityBuilder AddUserValidator<TValidator>() where TValidator : class 74 => AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TValidator)); 75 76 /// <summary> 77 /// Adds an <see cref="IUserClaimsPrincipalFactory{TUser}"/> for the <seealso cref="UserType"/>. 78 /// </summary> 79 /// <typeparam name="TFactory">The type of the claims principal factory.</typeparam> 80 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 81 public virtual IdentityBuilder AddClaimsPrincipalFactory<TFactory>() where TFactory : class 82 => AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory)); 83 84 /// <summary> 85 /// Adds an <see cref="IdentityErrorDescriber"/>. 86 /// </summary> 87 /// <typeparam name="TDescriber">The type of the error describer.</typeparam> 88 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 89 public virtual IdentityBuilder AddErrorDescriber<TDescriber>() where TDescriber : IdentityErrorDescriber 90 { 91 Services.AddScoped<IdentityErrorDescriber, TDescriber>(); 92 return this; 93 } 94 95 /// <summary> 96 /// Adds an <see cref="IPasswordValidator{TUser}"/> for the <seealso cref="UserType"/>. 97 /// </summary> 98 /// <typeparam name="TValidator">The validator type used to validate passwords.</typeparam> 99 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 100 public virtual IdentityBuilder AddPasswordValidator<TValidator>() where TValidator : class 101 => AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator)); 102 103 /// <summary> 104 /// Adds an <see cref="IUserStore{TUser}"/> for the <seealso cref="UserType"/>. 105 /// </summary> 106 /// <typeparam name="TStore">The user store type.</typeparam> 107 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 108 public virtual IdentityBuilder AddUserStore<TStore>() where TStore : class 109 => AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore)); 110 111 /// <summary> 112 /// Adds a token provider. 113 /// </summary> 114 /// <typeparam name="TProvider">The type of the token provider to add.</typeparam> 115 /// <param name="providerName">The name of the provider to add.</param> 116 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 117 public virtual IdentityBuilder AddTokenProvider<TProvider>(string providerName) where TProvider : class 118 => AddTokenProvider(providerName, typeof(TProvider)); 119 120 /// <summary> 121 /// Adds a token provider for the <seealso cref="UserType"/>. 122 /// </summary> 123 /// <param name="providerName">The name of the provider to add.</param> 124 /// <param name="provider">The type of the <see cref="IUserTwoFactorTokenProvider{TUser}"/> to add.</param> 125 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> AddTokenProvider(string providerName, Type provider)126 public virtual IdentityBuilder AddTokenProvider(string providerName, Type provider) 127 { 128 if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo())) 129 { 130 throw new InvalidOperationException(Resources.FormatInvalidManagerType(provider.Name, "IUserTwoFactorTokenProvider", UserType.Name)); 131 } 132 Services.Configure<IdentityOptions>(options => 133 { 134 options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider); 135 }); 136 Services.AddTransient(provider); 137 return this; 138 } 139 140 /// <summary> 141 /// Adds a <see cref="UserManager{TUser}"/> for the <seealso cref="UserType"/>. 142 /// </summary> 143 /// <typeparam name="TUserManager">The type of the user manager to add.</typeparam> 144 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 145 public virtual IdentityBuilder AddUserManager<TUserManager>() where TUserManager : class 146 { 147 var userManagerType = typeof(UserManager<>).MakeGenericType(UserType); 148 var customType = typeof(TUserManager); 149 if (!userManagerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo())) 150 { 151 throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "UserManager", UserType.Name)); 152 } 153 if (userManagerType != customType) 154 { 155 Services.AddScoped(customType, services => services.GetRequiredService(userManagerType)); 156 } 157 return AddScoped(userManagerType, customType); 158 } 159 160 /// <summary> 161 /// Adds Role related services for TRole, including IRoleStore, IRoleValidator, and RoleManager. 162 /// </summary> 163 /// <typeparam name="TRole">The role type.</typeparam> 164 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 165 public virtual IdentityBuilder AddRoles<TRole>() where TRole : class 166 { 167 RoleType = typeof(TRole); AddRoleValidator()168 AddRoleValidator<RoleValidator<TRole>>(); Services.TryAddScoped()169 Services.TryAddScoped<RoleManager<TRole>>(); MakeGenericType(UserType)170 Services.AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(UserClaimsPrincipalFactory<,>).MakeGenericType(UserType, RoleType)); 171 return this; 172 } 173 174 /// <summary> 175 /// Adds an <see cref="IRoleValidator{TRole}"/> for the <seealso cref="RoleType"/>. 176 /// </summary> 177 /// <typeparam name="TRole">The role validator type.</typeparam> 178 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 179 public virtual IdentityBuilder AddRoleValidator<TRole>() where TRole : class 180 { 181 if (RoleType == null) 182 { 183 throw new InvalidOperationException(Resources.NoRoleType); 184 } 185 return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(TRole)); 186 } 187 188 /// <summary> 189 /// Adds an <see cref="ILookupProtector"/> and <see cref="ILookupProtectorKeyRing"/>. 190 /// </summary> 191 /// <typeparam name="TProtector">The personal data protector type.</typeparam> 192 /// <typeparam name="TKeyRing">The personal data protector key ring type.</typeparam> 193 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 194 public virtual IdentityBuilder AddPersonalDataProtection<TProtector, TKeyRing>() 195 where TProtector : class,ILookupProtector 196 where TKeyRing : class, ILookupProtectorKeyRing 197 { Services.AddSingleton()198 Services.AddSingleton<IPersonalDataProtector, DefaultPersonalDataProtector>(); Services.AddSingleton()199 Services.AddSingleton<ILookupProtector, TProtector>(); Services.AddSingleton()200 Services.AddSingleton<ILookupProtectorKeyRing, TKeyRing>(); 201 return this; 202 } 203 204 /// <summary> 205 /// Adds a <see cref="IRoleStore{TRole}"/> for the <seealso cref="RoleType"/>. 206 /// </summary> 207 /// <typeparam name="TStore">The role store.</typeparam> 208 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 209 public virtual IdentityBuilder AddRoleStore<TStore>() where TStore : class 210 { 211 if (RoleType == null) 212 { 213 throw new InvalidOperationException(Resources.NoRoleType); 214 } 215 return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(TStore)); 216 } 217 218 /// <summary> 219 /// Adds a <see cref="RoleManager{TRole}"/> for the <seealso cref="RoleType"/>. 220 /// </summary> 221 /// <typeparam name="TRoleManager">The type of the role manager to add.</typeparam> 222 /// <returns>The current <see cref="IdentityBuilder"/> instance.</returns> 223 public virtual IdentityBuilder AddRoleManager<TRoleManager>() where TRoleManager : class 224 { 225 if (RoleType == null) 226 { 227 throw new InvalidOperationException(Resources.NoRoleType); 228 } 229 var managerType = typeof(RoleManager<>).MakeGenericType(RoleType); 230 var customType = typeof(TRoleManager); 231 if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo())) 232 { 233 throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "RoleManager", RoleType.Name)); 234 } 235 if (managerType != customType) 236 { 237 Services.AddScoped(typeof(TRoleManager), services => services.GetRequiredService(managerType)); 238 } 239 return AddScoped(managerType, typeof(TRoleManager)); 240 } 241 } 242 } 243