579 lines
22 KiB
C#
579 lines
22 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.Threading.Tasks;
|
|
using gehGassi.Core.Interfaces;
|
|
using gehGassi.Domain.Users;
|
|
using gehGassi.Web.Auth;
|
|
using gehGassi.Web.Helper;
|
|
using gehGassi.Web.Models;
|
|
using gehGassi.Web.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Localization;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace gehGassi.Web.Controllers
|
|
{
|
|
/// <summary>
|
|
/// Controller für die Verwaltung von Logins, Logouts usw.
|
|
/// </summary>
|
|
[Authorize]
|
|
public class AccountController : BaseController
|
|
{
|
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
|
private readonly ILogger<AccountController> _logger;
|
|
private readonly IStringLocalizer<AccountController> _localizer;
|
|
private readonly IEmailSender _emailSender;
|
|
private readonly IUserService _userService;
|
|
private readonly IOptions<LicenseOptions> _licenseOptions;
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
|
private readonly IOptions<SessionSettings> _sessionSettings;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Istanz
|
|
/// </summary>
|
|
/// <param name="signInManager">Instanz eines SignInManager</param>
|
|
/// <param name="logger">Instanz eines ILogger</param>
|
|
/// <param name="localizer">Instanz eines IStringLocalizer</param>
|
|
/// <param name="emailSender">Instanz eines IEmailSender</param>
|
|
/// <param name="userService">Isntanz eines IUserService</param>
|
|
/// <param name="licenseOptions">Instanz eines IOptions LicenseOptions</param>
|
|
/// <param name="userManager">Instanz eines UserManager</param>
|
|
/// <param name="sessionSettings">Instanz eines IOptions SessionSettings</param>
|
|
public AccountController(SignInManager<ApplicationUser> signInManager, ILogger<AccountController> logger, IStringLocalizer<AccountController> localizer,
|
|
IEmailSender emailSender, IUserService userService, IOptions<LicenseOptions> licenseOptions, UserManager<ApplicationUser> userManager,
|
|
IOptions<SessionSettings> sessionSettings)
|
|
{
|
|
_signInManager = signInManager;
|
|
_logger = logger;
|
|
_localizer = localizer;
|
|
_emailSender = emailSender;
|
|
_userService = userService;
|
|
_licenseOptions = licenseOptions;
|
|
_userManager = userManager;
|
|
_sessionSettings = sessionSettings;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt eine View zur Anmeldung an
|
|
/// </summary>
|
|
/// <param name="returnUrl">Optional: Return URL</param>
|
|
/// <returns>View</returns>
|
|
[AllowAnonymous]
|
|
public IActionResult Login(string returnUrl)
|
|
{
|
|
var model = new LoginVm();
|
|
#if DEBUG
|
|
//model.UserName = "office@creativebits.com";
|
|
//model.Password = "!eLearningFox#321";
|
|
#endif
|
|
ViewBag.ReturnUrl = returnUrl;
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Login
|
|
/// </summary>
|
|
/// <param name="model">Benutzerdaten</param>
|
|
/// <param name="returnUrl">Optional: ReturnUrl</param>
|
|
/// <returns>Redirect oder View</returns>
|
|
[AllowAnonymous]
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Login(LoginVm model, string returnUrl)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
//Hundebesitzer und hundeausführer düfen nicht in das Backend.
|
|
//Ausser es sind Admins oder Poweruser mit Test-Rollen
|
|
var userCheck = await _signInManager.UserManager.FindByNameAsync(model.UserName);
|
|
if (userCheck != null)
|
|
{
|
|
var roles = await _userManager.GetRolesAsync(userCheck);
|
|
if (!roles.Contains("Administrator"))
|
|
{
|
|
if (!roles.Contains("PowerUser"))
|
|
{
|
|
if (roles.Contains("DogOwner") || roles.Contains("DogWalker"))
|
|
{
|
|
return Redirect("https://gehgassi.com");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, _sessionSettings.Value.PersistentCookie, lockoutOnFailure: true);
|
|
if (result.Succeeded)
|
|
{
|
|
|
|
_logger.LogInformation("User logged in.");
|
|
var user = await _userService.GetByUsernameAsync(model.UserName);
|
|
user.LastLoginDate = DateTimeOffset.UtcNow;
|
|
await _userService.CommitAsync(user.UserName);
|
|
|
|
var roles = await _userManager.GetRolesAsync(user);
|
|
|
|
CultureInfo.CurrentCulture = new CultureInfo(user.PreferredLanguage);
|
|
if (roles.Contains("Administrator"))
|
|
return RedirectToAction("Index", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
if (roles.Contains("PowerUser"))
|
|
return RedirectToAction("Index", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
else if (roles.Contains("Customer"))
|
|
return RedirectToAction("IndexCustomer", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
else if (roles.Contains("AppUser"))
|
|
{
|
|
await _signInManager.SignOutAsync();
|
|
return RedirectToAction("Login", "Account", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
//return RedirectToAction("IndexAppUser", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
}
|
|
else if (roles.Contains("ApiUser"))
|
|
{
|
|
await _signInManager.SignOutAsync();
|
|
return RedirectToAction("Login", "Account", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
}
|
|
else
|
|
return RedirectToAction("Status401", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
}
|
|
if (result.RequiresTwoFactor)
|
|
{
|
|
return RedirectToAction("VerifyAuthenticatorCode");
|
|
}
|
|
if (result.IsLockedOut)
|
|
{
|
|
_logger.LogWarning("User account locked out.");
|
|
return RedirectToAction("Lockout");
|
|
}
|
|
else
|
|
{
|
|
//Prüfen ob die Email-Adresse noch nicht bestätigt wurde....
|
|
bool errorAdded = false;
|
|
var user = await _userManager.FindByEmailAsync(model.UserName);
|
|
if (user != null)
|
|
{
|
|
if (await _userManager.IsEmailConfirmedAsync(user) == false)
|
|
{
|
|
ModelState.AddModelError(string.Empty, _localizer["Err_Login_EmailNotConfirmed"]);
|
|
ViewBag.ShowEmailConfirmation = true;
|
|
errorAdded = true;
|
|
}
|
|
}
|
|
if (!errorAdded)
|
|
ModelState.AddModelError(string.Empty, _localizer["Err_Login_Invalid"]);
|
|
}
|
|
}
|
|
ViewBag.ReturnUrl = returnUrl;
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abmelden eines Benutzers
|
|
/// </summary>
|
|
/// <returns>Redirect</returns>
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Logout()
|
|
{
|
|
await _signInManager.SignOutAsync();
|
|
_logger.LogInformation("User logged out.");
|
|
return RedirectToAction("Login", "Account", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt eine View an die über eine Sperre informiert
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[AllowAnonymous]
|
|
public IActionResult Lockout()
|
|
{
|
|
var timeLockedInMinutes = (int)_signInManager.Options.Lockout.DefaultLockoutTimeSpan.TotalMinutes;
|
|
return View(timeLockedInMinutes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt eine View für Passwort-Vergessen an
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult ForgotPassword()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handling für Passwort Vergessen
|
|
/// </summary>
|
|
/// <param name="model">Benutzereingaben</param>
|
|
/// <returns>Redirect oder View</returns>
|
|
[HttpPost]
|
|
[AllowAnonymous]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> ForgotPassword(ForgotPasswordVm model)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var user = await _signInManager.UserManager.FindByNameAsync(model.Email);
|
|
if (user != null && (await _signInManager.UserManager.IsEmailConfirmedAsync(user)))
|
|
{
|
|
var token = await _signInManager.UserManager.GeneratePasswordResetTokenAsync(user);
|
|
var callbackUrl = Url.ResetPasswordCallbackLink(user.Id, token, Request.Scheme);
|
|
await _emailSender.SendPasswordResetAsync(model.Email, callbackUrl, _localizer, _licenseOptions);
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return RedirectToAction(nameof(ForgotPasswordConfirmation));
|
|
}
|
|
|
|
// If we got this far, something failed, redisplay form
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt die Bestätigung für Passwort vergessen an
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult ForgotPasswordConfirmation()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt eine View zum Zurücksetzen des Passwortes an
|
|
/// </summary>
|
|
/// <param name="code">Rücksetzcode</param>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult ResetPassword(string code = null)
|
|
{
|
|
if (code == null)
|
|
{
|
|
throw new ApplicationException("A code must be supplied for password reset.");
|
|
}
|
|
var model = new ResetPasswordVm() { Code = code };
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zurücksetzen des Passwortes eines Benutzers
|
|
/// </summary>
|
|
/// <param name="model">Benutzereingaben</param>
|
|
/// <returns></returns>
|
|
[HttpPost]
|
|
[AllowAnonymous]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> ResetPassword(ResetPasswordVm model)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return View(model);
|
|
}
|
|
|
|
if (await _userService.IsUsernameAvailableAsync(model.Email))
|
|
{
|
|
// Don't reveal that the user does not exist
|
|
return RedirectToAction(nameof(ResetPasswordConfirmation));
|
|
}
|
|
try
|
|
{
|
|
var user = await _signInManager.UserManager.FindByNameAsync(model.Email);
|
|
if (user != null)
|
|
{
|
|
var result = await _signInManager.UserManager.ResetPasswordAsync(user, model.Code, model.Password);
|
|
if (result.Succeeded)
|
|
{
|
|
return RedirectToAction(nameof(ResetPasswordConfirmation));
|
|
}
|
|
foreach (var error in result.Errors)
|
|
{
|
|
ModelState.AddModelError(string.Empty, error.Description);
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Anzeigen eines Erfolgs-Views für das Zurücksetzen eines Passwortes
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult ResetPasswordConfirmation()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
#region Reset Password für API
|
|
|
|
/// <summary>
|
|
/// Zeigt eine View zum Zurücksetzen des Passwortes an
|
|
/// </summary>
|
|
/// <param name="code">Rücksetzcode</param>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult ResetPasswordApp(string code = null)
|
|
{
|
|
if (code == null)
|
|
{
|
|
throw new ApplicationException("A code must be supplied for password reset.");
|
|
}
|
|
var model = new ResetPasswordVm() { Code = code };
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zurücksetzen des Passwortes eines Benutzers
|
|
/// </summary>
|
|
/// <param name="model">Benutzereingaben</param>
|
|
/// <returns></returns>
|
|
[HttpPost]
|
|
[AllowAnonymous]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> ResetPasswordApp(ResetPasswordVm model)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return View(model);
|
|
}
|
|
|
|
if (await _userService.IsUsernameAvailableAsync(model.Email))
|
|
{
|
|
// Don't reveal that the user does not exist
|
|
return RedirectToAction(nameof(ResetPasswordConfirmationApp));
|
|
}
|
|
try
|
|
{
|
|
var user = await _signInManager.UserManager.FindByNameAsync(model.Email);
|
|
if (user != null)
|
|
{
|
|
var result = await _signInManager.UserManager.ResetPasswordAsync(user, model.Code, model.Password);
|
|
if (result.Succeeded)
|
|
{
|
|
return RedirectToAction(nameof(ResetPasswordConfirmationApp));
|
|
}
|
|
foreach (var error in result.Errors)
|
|
{
|
|
ModelState.AddModelError(string.Empty, error.Description);
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Anzeigen eines Erfolgs-Views für das Zurücksetzen eines Passwortes
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult ResetPasswordConfirmationApp()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
#endregion
|
|
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult AccessDenied(string returnUrl)
|
|
{
|
|
return View();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Anzeige der email-Bestätigung der Email-Adresse
|
|
/// </summary>
|
|
/// <param name="userId"></param>
|
|
/// <param name="code"></param>
|
|
/// <returns></returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> ConfirmEmail(string userId, string code)
|
|
{
|
|
var model = new LoginVm();
|
|
if (userId == null || code == null)
|
|
{
|
|
ViewBag.ShowEmailConfirmation = true;
|
|
ModelState.AddModelError(string.Empty, _localizer["Common_ConfirmEmailFailed_Desc"]);
|
|
return View("Login", model);
|
|
}
|
|
var user = await _userManager.FindByIdAsync(userId);
|
|
if (user == null)
|
|
{
|
|
ViewBag.ShowEmailConfirmation = true;
|
|
ModelState.AddModelError(string.Empty, _localizer["Common_ConfirmEmailFailed_Desc"]);
|
|
return View("Login", model);
|
|
}
|
|
var result = await _userManager.ConfirmEmailAsync(user, code);
|
|
if (!result.Succeeded)
|
|
{
|
|
ViewBag.ShowEmailConfirmation = true;
|
|
ModelState.AddModelError(string.Empty, _localizer["Common_ConfirmEmailFailed_Desc"]);
|
|
return View("Login", model);
|
|
}
|
|
|
|
ViewBag.ShowEmailConfirmationSuccess = true;
|
|
return View("Login", model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Anzeige der email-Bestätigung der Email-Adresse für die App
|
|
/// </summary>
|
|
/// <param name="userId"></param>
|
|
/// <param name="code"></param>
|
|
/// <returns></returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> ConfirmEmailApp(string userId, string code)
|
|
{
|
|
var model = _localizer["Common_ConfirmEmail_Success"].Value;
|
|
if (userId == null || code == null)
|
|
{
|
|
model = _localizer["Common_ConfirmEmailFailed_Desc"].Value;
|
|
}
|
|
var user = await _userManager.FindByIdAsync(userId);
|
|
if (user == null)
|
|
{
|
|
model = _localizer["Common_ConfirmEmailFailed_Desc"].Value;
|
|
}
|
|
var result = await _userManager.ConfirmEmailAsync(user, code);
|
|
if (!result.Succeeded)
|
|
{
|
|
model = _localizer["Common_ConfirmEmailFailed_Desc"].Value;
|
|
}
|
|
|
|
return View("ConfirmEmailApp", model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt eine View für das erneute Senden des Email-Bestätigungscodes an
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult SendEmailConfirmation()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handling für Email-Bestätigung neu senden
|
|
/// </summary>
|
|
/// <param name="model">Benutzereingaben</param>
|
|
/// <returns>Redirect oder View</returns>
|
|
[HttpPost]
|
|
[AllowAnonymous]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> SendEmailConfirmation(ForgotPasswordVm model)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var user = await _signInManager.UserManager.FindByNameAsync(model.Email);
|
|
if (user != null && (await _signInManager.UserManager.IsEmailConfirmedAsync(user)) == false)
|
|
{
|
|
var code = await _signInManager.UserManager.GenerateEmailConfirmationTokenAsync(user);
|
|
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
|
|
await _emailSender.SendEmailConfirmationAsync(user.UserName, callbackUrl, _localizer, LicenseOptions);
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return RedirectToAction(nameof(SendEmailConfirmationDone));
|
|
}
|
|
|
|
// If we got this far, something failed, redisplay form
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zeigt die Bestätigung für das erneute Senden des Email-Bestätigungscodes an
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public IActionResult SendEmailConfirmationDone()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
/// <summary>
|
|
/// View für den 2 Faktor Auth-Code
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> VerifyAuthenticatorCode()
|
|
{
|
|
// Require that the user has already logged in via username/password or external login
|
|
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
|
if (user == null)
|
|
{
|
|
return View("Error");
|
|
}
|
|
return View(new VerifyAuthenticatorCodeViewModel());
|
|
}
|
|
|
|
/// <summary>
|
|
/// View für den 2 Faktor Auth-Code
|
|
/// </summary>
|
|
/// <returns>View</returns>
|
|
[HttpPost]
|
|
[AllowAnonymous]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> VerifyAuthenticatorCode(VerifyAuthenticatorCodeViewModel model)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return View(model);
|
|
}
|
|
|
|
// The following code protects for brute force attacks against the two factor codes.
|
|
// If a user enters incorrect codes for a specified amount of time then the user account
|
|
// will be locked out for a specified amount of time.
|
|
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(model.Code, false, false);
|
|
if (result.Succeeded)
|
|
{
|
|
_logger.LogInformation("User logged in.");
|
|
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
|
user.LastLoginDate = DateTimeOffset.UtcNow;
|
|
await _userService.CommitAsync(user.UserName);
|
|
|
|
var roles = await _userManager.GetRolesAsync(user);
|
|
|
|
CultureInfo.CurrentCulture = new CultureInfo(user.PreferredLanguage);
|
|
if (roles.Contains("Administrator"))
|
|
return RedirectToAction("Index", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
if (roles.Contains("PowerUser"))
|
|
return RedirectToAction("Index", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
else if (roles.Contains("Customer"))
|
|
return RedirectToAction("IndexCustomer", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
else if (roles.Contains("AppUser"))
|
|
return RedirectToAction("IndexAppUser", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
else
|
|
return RedirectToAction("Status401", "Home", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName });
|
|
}
|
|
if (result.IsLockedOut)
|
|
{
|
|
return View("Lockout");
|
|
}
|
|
else
|
|
{
|
|
ModelState.AddModelError("Code", _localizer["Err_Invalid_Code"]);
|
|
return View(model);
|
|
}
|
|
}
|
|
}
|
|
} |