Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LoginForm Shapes #15701

Merged
merged 8 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Threading.Tasks;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.ReCaptcha.Configuration;
using OrchardCore.ReCaptcha.Services;
using OrchardCore.Settings;
using OrchardCore.Users.Models;

namespace OrchardCore.ReCaptcha.Drivers;

public class ReCaptchaLoginFormDisplayDriver : DisplayDriver<LoginForm>
{
private readonly ISiteService _siteService;
private readonly ReCaptchaService _reCaptchaService;

public ReCaptchaLoginFormDisplayDriver(
ISiteService siteService,
ReCaptchaService reCaptchaService)
{
_siteService = siteService;
_reCaptchaService = reCaptchaService;
}

public override async Task<IDisplayResult> EditAsync(LoginForm model, BuildEditorContext context)
{
var _reCaptchaSettings = (await _siteService.GetSiteSettingsAsync()).As<ReCaptchaSettings>();

if (!_reCaptchaSettings.IsValid() || !_reCaptchaService.IsThisARobot())
{
return null;
}

return View("LoginFormReCaptcha_Edit", model).Location("Content:after");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ReCaptcha.Core\OrchardCore.ReCaptcha.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Settings.Core\OrchardCore.Settings.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Users.Abstractions\OrchardCore.Users.Abstractions.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Users.Core\OrchardCore.Users.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Workflows.Abstractions\OrchardCore.Workflows.Abstractions.csproj" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE

var layout = await _layoutAccessor.GetLayoutAsync();

if (_reCaptchaService.IsThisARobot())
{
var afterLoginZone = layout.Zones["AfterLogin"];
await afterLoginZone.AddAsync(await _shapeFactory.CreateAsync("ReCaptcha"));
}

var afterForgotPasswordZone = layout.Zones["AfterForgotPassword"];
await afterForgotPasswordZone.AddAsync(await _shapeFactory.CreateAsync("ReCaptcha"));

Expand Down
2 changes: 2 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using OrchardCore.Settings;
using OrchardCore.Settings.Deployment;
using OrchardCore.Users.Events;
using OrchardCore.Users.Models;

namespace OrchardCore.ReCaptcha
{
Expand Down Expand Up @@ -45,6 +46,7 @@ public override void ConfigureServices(IServiceCollection services)
services.AddScoped<IRegistrationFormEvents, RegistrationFormEventHandler>();
services.AddScoped<ILoginFormEvent, LoginFormEventEventHandler>();
services.AddScoped<IPasswordRecoveryFormEvents, PasswordRecoveryFormEventEventHandler>();
services.AddScoped<IDisplayDriver<LoginForm>, ReCaptchaLoginFormDisplayDriver>();
services.Configure<MvcOptions>((options) =>
{
options.Filters.Add<ReCaptchaLoginFilter>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="mb-3">
<captcha mode="AlwaysShow" language="@Orchard.CultureName()" />
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OrchardCore.DisplayManagement;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.Modules;
using OrchardCore.Mvc.Core.Utilities;
Expand All @@ -37,10 +39,13 @@ public class AccountController : AccountBaseController
private readonly ISiteService _siteService;
private readonly IEnumerable<ILoginFormEvent> _accountEvents;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly IDisplayManager<LoginForm> _loginFormDisplayManager;
private readonly IUpdateModelAccessor _updateModelAccessor;
private readonly INotifier _notifier;
private readonly IClock _clock;
private readonly IDistributedCache _distributedCache;
private readonly IEnumerable<IExternalLoginEventHandler> _externalLoginHandlers;

protected readonly IHtmlLocalizer H;
protected readonly IStringLocalizer S;

Expand All @@ -57,6 +62,8 @@ public AccountController(
IClock clock,
IDistributedCache distributedCache,
IDataProtectionProvider dataProtectionProvider,
IDisplayManager<LoginForm> loginFormDisplayManager,
IUpdateModelAccessor updateModelAccessor,
IEnumerable<IExternalLoginEventHandler> externalLoginHandlers)
{
_signInManager = signInManager;
Expand All @@ -69,6 +76,8 @@ public AccountController(
_clock = clock;
_distributedCache = distributedCache;
_dataProtectionProvider = dataProtectionProvider;
_loginFormDisplayManager = loginFormDisplayManager;
_updateModelAccessor = updateModelAccessor;
_externalLoginHandlers = externalLoginHandlers;

H = htmlLocalizer;
Expand Down Expand Up @@ -105,10 +114,13 @@ public async Task<IActionResult> Login(string returnUrl = null)
}
}

var formShape = await _loginFormDisplayManager.BuildEditorAsync(_updateModelAccessor.ModelUpdater, false);

CopyTempDataErrorsToModelState();

ViewData["ReturnUrl"] = returnUrl;

return View();
return View(formShape);
}

[HttpGet]
Expand Down Expand Up @@ -149,77 +161,79 @@ public async Task<IActionResult> DefaultExternalLogin(string protectedToken, str
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
[ActionName(nameof(Login))]
public async Task<IActionResult> LoginPOST(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;

ArgumentNullException.ThrowIfNull(model);
var model = new LoginForm();

if (TryValidateModel(model) && ModelState.IsValid)
var formShape = await _loginFormDisplayManager.UpdateEditorAsync(model, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty);

var disableLocalLogin = (await _siteService.GetSiteSettingsAsync()).As<LoginSettings>().DisableLocalLogin;

if (disableLocalLogin)
{
var disableLocalLogin = (await _siteService.GetSiteSettingsAsync()).As<LoginSettings>().DisableLocalLogin;
if (disableLocalLogin)
{
ModelState.AddModelError(string.Empty, S["Local login is disabled."]);
}
else
ModelState.AddModelError(string.Empty, S["Local login is disabled."]);
}
else
{
await _accountEvents.InvokeAsync((e, model, modelState) => e.LoggingInAsync(model.UserName, (key, message) => modelState.AddModelError(key, message)), model, ModelState, _logger);

if (ModelState.IsValid)
{
await _accountEvents.InvokeAsync((e, model, modelState) => e.LoggingInAsync(model.UserName, (key, message) => modelState.AddModelError(key, message)), model, ModelState, _logger);
if (ModelState.IsValid)
var user = await _userService.GetUserAsync(model.UserName);
if (user != null)
{
var user = await _userService.GetUserAsync(model.UserName);
if (user != null)
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, lockoutOnFailure: true);
if (result.Succeeded)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, lockoutOnFailure: true);
if (result.Succeeded)
if (!await AddConfirmEmailErrorAsync(user) && !AddUserEnabledError(user))
{
if (!await AddConfirmEmailErrorAsync(user) && !AddUserEnabledError(user))
{
result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: true);
result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: true);

if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
await _accountEvents.InvokeAsync((e, user) => e.LoggedInAsync(user), user, _logger);
if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
await _accountEvents.InvokeAsync((e, user) => e.LoggedInAsync(user), user, _logger);

return await LoggedInActionResultAsync(user, returnUrl);
}
return await LoggedInActionResultAsync(user, returnUrl);
}
}
}

if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(TwoFactorAuthenticationController.LoginWithTwoFactorAuthentication),
typeof(TwoFactorAuthenticationController).ControllerName(),
new
{
returnUrl,
model.RememberMe
});
}

if (result.IsLockedOut)
{
ModelState.AddModelError(string.Empty, S["The account is locked out"]);
await _accountEvents.InvokeAsync((e, user) => e.IsLockedOutAsync(user), user, _logger);
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(TwoFactorAuthenticationController.LoginWithTwoFactorAuthentication),
typeof(TwoFactorAuthenticationController).ControllerName(),
new
{
returnUrl,
model.RememberMe
});
}

return View();
}
if (result.IsLockedOut)
{
ModelState.AddModelError(string.Empty, S["The account is locked out"]);
await _accountEvents.InvokeAsync((e, user) => e.IsLockedOutAsync(user), user, _logger);

// Login failed with a known user.
await _accountEvents.InvokeAsync((e, user) => e.LoggingInFailedAsync(user), user, _logger);
return View();
}

ModelState.AddModelError(string.Empty, S["Invalid login attempt."]);
// Login failed with a known user.
await _accountEvents.InvokeAsync((e, user) => e.LoggingInFailedAsync(user), user, _logger);
}

// Login failed unknown user.
await _accountEvents.InvokeAsync((e, model) => e.LoggingInFailedAsync(model.UserName), model, _logger);
ModelState.AddModelError(string.Empty, S["Invalid login attempt."]);
}

// Login failed unknown user.
await _accountEvents.InvokeAsync((e, model) => e.LoggingInFailedAsync(model.UserName), model, _logger);
}

// If we got this far, something failed, redisplay form.
return View(model);
return View(formShape);
}

[HttpPost]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Threading.Tasks;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Settings;
using OrchardCore.Users.Models;

namespace OrchardCore.Users.Drivers;

public class ForgotPasswordLoginFormDisplayDriver : DisplayDriver<LoginForm>
{
private readonly ISiteService _siteService;

public ForgotPasswordLoginFormDisplayDriver(ISiteService siteService)
{
_siteService = siteService;
}

public override async Task<IDisplayResult> EditAsync(LoginForm model, BuildEditorContext context)
{
var settings = (await _siteService.GetSiteSettingsAsync()).As<ResetPasswordSettings>();

if (!settings.AllowResetPassword)
{
return null;
}

return View("LoginFormForgotPassword_Edit", model).Location("Links:5");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Entities;
using OrchardCore.Users.Models;
using OrchardCore.Users.ViewModels;

namespace OrchardCore.Users.Drivers;

public class LoginFormDisplayDriver : DisplayDriver<LoginForm>
{
public override IDisplayResult Edit(LoginForm model)
{
return Initialize<LoginViewModel>("LoginFormCredentials_Edit", vm =>
{
vm.UserName = model.UserName;
vm.RememberMe = model.RememberMe;
}).Location("Content");
}

public override async Task<IDisplayResult> UpdateAsync(LoginForm model, IUpdateModel updater)
{
var viewModel = new LoginViewModel();

await updater.TryUpdateModelAsync(viewModel, Prefix);

model.UserName = viewModel.UserName;
model.Password = viewModel.Password;
model.RememberMe = viewModel.RememberMe;

model.Put(viewModel);

return Edit(model);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Threading.Tasks;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Settings;
using OrchardCore.Users.Models;

namespace OrchardCore.Users.Drivers;

public class RegisterUserLoginFormDisplayDriver : DisplayDriver<LoginForm>
{
private readonly ISiteService _siteService;

public RegisterUserLoginFormDisplayDriver(ISiteService siteService)
{
_siteService = siteService;
}

public override async Task<IDisplayResult> EditAsync(LoginForm model, BuildEditorContext context)
{
var settings = (await _siteService.GetSiteSettingsAsync()).As<RegistrationSettings>();

if (settings.UsersCanRegister != UserRegistrationType.AllowRegistration)
{
return null;
}

return View("LoginFormRegisterUser_Edit", model).Location("Links:10");
}
}
4 changes: 3 additions & 1 deletion src/OrchardCore.Modules/OrchardCore.Users/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ public override void ConfigureServices(IServiceCollection services)

services.AddScoped<CustomUserSettingsService>();
services.AddRecipeExecutionStep<CustomUserSettingsStep>();
services.AddScoped<IDisplayDriver<LoginForm>, LoginFormDisplayDriver>();
}
}

Expand Down Expand Up @@ -379,6 +380,7 @@ public override void ConfigureServices(IServiceCollection services)

services.AddScoped<INavigationProvider, RegistrationAdminMenu>();
services.AddScoped<IDisplayDriver<ISite>, RegistrationSettingsDisplayDriver>();
services.AddScoped<IDisplayDriver<LoginForm>, RegisterUserLoginFormDisplayDriver>();
}
}

Expand All @@ -401,7 +403,6 @@ public class ResetPasswordStartup : StartupBase
private const string ResetPasswordConfirmationPath = "ResetPasswordConfirmation";
private const string ResetPasswordControllerName = "ResetPassword";


public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
{
routes.MapAreaControllerRoute(
Expand Down Expand Up @@ -439,6 +440,7 @@ public override void ConfigureServices(IServiceCollection services)

services.AddScoped<INavigationProvider, ResetPasswordAdminMenu>();
services.AddScoped<IDisplayDriver<ISite>, ResetPasswordSettingsDisplayDriver>();
services.AddScoped<IDisplayDriver<LoginForm>, ForgotPasswordLoginFormDisplayDriver>();
}
}

Expand Down
Loading