From 4d53c3fd97b86ae6013bf2a211246e8a2330e39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 10 Aug 2020 23:39:45 +0300 Subject: [PATCH] Resolved #4985: Change AbpResourceOwnerPasswordValidator to support the external login system. --- .../AbpIdentityAspNetCoreOptions.cs | 7 -- .../Identity/AspNetCore/AbpSignInManager.cs | 4 +- .../Volo/Abp/Identity/AbpIdentityOptions.cs | 12 +++ .../Identity}/ExternalLoginProviderBase.cs | 2 +- .../ExternalLoginProviderDictionary.cs | 5 +- .../Identity}/ExternalLoginProviderInfo.cs | 2 +- .../Abp/Identity}/ExternalLoginUserInfo.cs | 4 +- .../Abp/Identity}/IExternalLoginProvider.cs | 2 +- .../AbpIdentityAspNetCoreTestModule.cs | 3 +- ...p.IdentityServer.Domain.csproj.DotSettings | 2 - .../AbpResourceOwnerPasswordValidator.cs | 91 ++++++++++++++----- 11 files changed, 91 insertions(+), 43 deletions(-) create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs rename modules/identity/src/{Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore => Volo.Abp.Identity.Domain/Volo/Abp/Identity}/ExternalLoginProviderBase.cs (99%) rename modules/identity/src/{Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore => Volo.Abp.Identity.Domain/Volo/Abp/Identity}/ExternalLoginProviderDictionary.cs (83%) rename modules/identity/src/{Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore => Volo.Abp.Identity.Domain/Volo/Abp/Identity}/ExternalLoginProviderInfo.cs (93%) rename modules/identity/src/{Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore => Volo.Abp.Identity.Domain/Volo/Abp/Identity}/ExternalLoginUserInfo.cs (84%) rename modules/identity/src/{Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore => Volo.Abp.Identity.Domain/Volo/Abp/Identity}/IExternalLoginProvider.cs (97%) delete mode 100644 modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj.DotSettings diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreOptions.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreOptions.cs index d7c5f57265..590d691eaa 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreOptions.cs +++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreOptions.cs @@ -6,12 +6,5 @@ /// Default: true. /// public bool ConfigureAuthentication { get; set; } = true; - - public ExternalLoginProviderDictionary ExternalLoginProviders { get; } - - public AbpIdentityAspNetCoreOptions() - { - ExternalLoginProviders = new ExternalLoginProviderDictionary(); - } } } diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs index f9072be6e8..8e4b435b48 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.Identity.AspNetCore { public class AbpSignInManager : SignInManager { - protected AbpIdentityAspNetCoreOptions AbpOptions { get; } + protected AbpIdentityOptions AbpOptions { get; } public AbpSignInManager( IdentityUserManager userManager, @@ -20,7 +20,7 @@ namespace Volo.Abp.Identity.AspNetCore ILogger> logger, IAuthenticationSchemeProvider schemes, IUserConfirmation confirmation, - IOptions options + IOptions options ) : base( userManager, contextAccessor, diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs new file mode 100644 index 0000000000..6cfaf75c0c --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs @@ -0,0 +1,12 @@ +namespace Volo.Abp.Identity +{ + public class AbpIdentityOptions + { + public ExternalLoginProviderDictionary ExternalLoginProviders { get; } + + public AbpIdentityOptions() + { + ExternalLoginProviders = new ExternalLoginProviderDictionary(); + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderBase.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderBase.cs similarity index 99% rename from modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderBase.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderBase.cs index d1db82e254..0898577e3d 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderBase.cs @@ -6,7 +6,7 @@ using Volo.Abp.Domain.Repositories; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; -namespace Volo.Abp.Identity.AspNetCore +namespace Volo.Abp.Identity { public abstract class ExternalLoginProviderBase : IExternalLoginProvider { diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderDictionary.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderDictionary.cs similarity index 83% rename from modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderDictionary.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderDictionary.cs index bca983cb86..822971efb0 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderDictionary.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderDictionary.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using JetBrains.Annotations; -namespace Volo.Abp.Identity.AspNetCore +namespace Volo.Abp.Identity { public class ExternalLoginProviderDictionary : Dictionary { diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderInfo.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderInfo.cs similarity index 93% rename from modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderInfo.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderInfo.cs index 86ef94d58f..4d1989d5dc 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderInfo.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginProviderInfo.cs @@ -1,7 +1,7 @@ using System; using JetBrains.Annotations; -namespace Volo.Abp.Identity.AspNetCore +namespace Volo.Abp.Identity { public class ExternalLoginProviderInfo { diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginUserInfo.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginUserInfo.cs similarity index 84% rename from modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginUserInfo.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginUserInfo.cs index 530a9bcfe0..65e4a2c776 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginUserInfo.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/ExternalLoginUserInfo.cs @@ -1,6 +1,6 @@ using JetBrains.Annotations; -namespace Volo.Abp.Identity.AspNetCore +namespace Volo.Abp.Identity { public class ExternalLoginUserInfo { @@ -28,7 +28,7 @@ namespace Volo.Abp.Identity.AspNetCore [CanBeNull] public string ProviderKey { get; set; } - public ExternalLoginUserInfo([System.Diagnostics.CodeAnalysis.NotNull] string email) + public ExternalLoginUserInfo([NotNull] string email) { Email = Check.NotNullOrWhiteSpace(email, nameof(email)); } diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IExternalLoginProvider.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IExternalLoginProvider.cs similarity index 97% rename from modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IExternalLoginProvider.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IExternalLoginProvider.cs index 29d0f0df3c..79e730d901 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IExternalLoginProvider.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IExternalLoginProvider.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace Volo.Abp.Identity.AspNetCore +namespace Volo.Abp.Identity { public interface IExternalLoginProvider { diff --git a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestModule.cs b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestModule.cs index 7b39096a0f..c3c035a208 100644 --- a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestModule.cs +++ b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestModule.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.Modularity; @@ -26,7 +25,7 @@ namespace Volo.Abp.Identity.AspNetCore public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => + Configure(options => { options.ExternalLoginProviders.Add(FakeExternalLoginProvider.Name); }); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj.DotSettings b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj.DotSettings deleted file mode 100644 index 58ad6c8854..0000000000 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp71 \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs index 90910e1894..5aeddd8b86 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using IdentityModel; @@ -8,8 +10,11 @@ using IdentityServer4.Models; using IdentityServer4.Services; using IdentityServer4.Validation; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.IdentityServer.Localization; using Volo.Abp.Security.Claims; @@ -27,6 +32,8 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } protected ILogger> Logger { get; } protected IStringLocalizer Localizer { get; } + protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + protected AbpIdentityOptions AbpIdentityOptions { get; } public AbpResourceOwnerPasswordValidator( UserManager userManager, @@ -34,7 +41,9 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity IdentitySecurityLogManager identitySecurityLogManager, IEventService events, ILogger> logger, - IStringLocalizer localizer) + IStringLocalizer localizer, + IOptions abpIdentityOptions, + IHybridServiceScopeFactory serviceScopeFactory) { UserManager = userManager; SignInManager = signInManager; @@ -42,6 +51,8 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity Events = events; Logger = logger; Localizer = localizer; + ServiceScopeFactory = serviceScopeFactory; + AbpIdentityOptions = abpIdentityOptions.Value; } /// @@ -52,35 +63,71 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity [UnitOfWork] public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { + using var scope = ServiceScopeFactory.CreateScope(); + await ReplaceEmailToUsernameOfInputIfNeeds(context); - var user = await UserManager.FindByNameAsync(context.UserName); - string errorDescription; - if (user != null) + + IdentityUser user = null; + + async Task SetSuccessResultAsync() { - var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true); - if (result.Succeeded) - { - var sub = await UserManager.GetUserIdAsync(user); + var sub = await UserManager.GetUserIdAsync(user); - Logger.LogInformation("Credentials validated for username: {username}", context.UserName); - await Events.RaiseAsync(new UserLoginSuccessEvent(context.UserName, sub, context.UserName, interactive: false)); + Logger.LogInformation("Credentials validated for username: {username}", context.UserName); + await Events.RaiseAsync(new UserLoginSuccessEvent(context.UserName, sub, context.UserName, interactive: false)); - var additionalClaims = new List(); + var additionalClaims = new List(); - await AddCustomClaimsAsync(additionalClaims, user, context); + await AddCustomClaimsAsync(additionalClaims, user, context); - context.Result = new GrantValidationResult( - sub, - OidcConstants.AuthenticationMethods.Password, - additionalClaims.ToArray() - ); + context.Result = new GrantValidationResult( + sub, + OidcConstants.AuthenticationMethods.Password, + additionalClaims.ToArray() + ); - await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + await IdentitySecurityLogManager.SaveAsync( + new IdentitySecurityLogContext { Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, - Action = result.ToIdentitySecurityLogAction(), - }); + Action = IdentityServerSecurityLogActionConsts.LoginSucceeded + } + ); + } + + if (AbpIdentityOptions.ExternalLoginProviders.Any()) + { + foreach (var externalLoginProviderInfo in AbpIdentityOptions.ExternalLoginProviders.Values) + { + var externalLoginProvider = (IExternalLoginProvider) scope.ServiceProvider + .GetRequiredService(externalLoginProviderInfo.Type); + if (await externalLoginProvider.TryAuthenticateAsync(context.UserName, context.Password)) + { + user = await UserManager.FindByNameAsync(context.UserName); + if (user == null) + { + user = await externalLoginProvider.CreateUserAsync(context.UserName, externalLoginProviderInfo.Name); + } + else + { + await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name); + } + + await SetSuccessResultAsync(); + return; + } + } + } + + user = await UserManager.FindByNameAsync(context.UserName); + string errorDescription; + if (user != null) + { + var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true); + if (result.Succeeded) + { + await SetSuccessResultAsync(); return; } else if (result.IsLockedOut) @@ -102,7 +149,7 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity errorDescription = Localizer["InvalidUserNameOrPassword"]; } - await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, Action = result.ToIdentitySecurityLogAction(),