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 { /// /// Controller für die Verwaltung von Logins, Logouts usw. /// [Authorize] public class AccountController : BaseController { private readonly SignInManager _signInManager; private readonly ILogger _logger; private readonly IStringLocalizer _localizer; private readonly IEmailSender _emailSender; private readonly IUserService _userService; private readonly IOptions _licenseOptions; private readonly UserManager _userManager; private readonly IOptions _sessionSettings; /// /// Erstellt eine Istanz /// /// Instanz eines SignInManager /// Instanz eines ILogger /// Instanz eines IStringLocalizer /// Instanz eines IEmailSender /// Isntanz eines IUserService /// Instanz eines IOptions LicenseOptions /// Instanz eines UserManager /// Instanz eines IOptions SessionSettings public AccountController(SignInManager signInManager, ILogger logger, IStringLocalizer localizer, IEmailSender emailSender, IUserService userService, IOptions licenseOptions, UserManager userManager, IOptions sessionSettings) { _signInManager = signInManager; _logger = logger; _localizer = localizer; _emailSender = emailSender; _userService = userService; _licenseOptions = licenseOptions; _userManager = userManager; _sessionSettings = sessionSettings; } /// /// Zeigt eine View zur Anmeldung an /// /// Optional: Return URL /// View [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); } /// /// Login /// /// Benutzerdaten /// Optional: ReturnUrl /// Redirect oder View [AllowAnonymous] [HttpPost] [ValidateAntiForgeryToken] public async Task 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); } /// /// Abmelden eines Benutzers /// /// Redirect [HttpPost] [ValidateAntiForgeryToken] public async Task Logout() { await _signInManager.SignOutAsync(); _logger.LogInformation("User logged out."); return RedirectToAction("Login", "Account", new { culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName }); } /// /// Zeigt eine View an die über eine Sperre informiert /// /// View [AllowAnonymous] public IActionResult Lockout() { var timeLockedInMinutes = (int)_signInManager.Options.Lockout.DefaultLockoutTimeSpan.TotalMinutes; return View(timeLockedInMinutes); } /// /// Zeigt eine View für Passwort-Vergessen an /// /// View [HttpGet] [AllowAnonymous] public IActionResult ForgotPassword() { return View(); } /// /// Handling für Passwort Vergessen /// /// Benutzereingaben /// Redirect oder View [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task 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); } /// /// Zeigt die Bestätigung für Passwort vergessen an /// /// View [HttpGet] [AllowAnonymous] public IActionResult ForgotPasswordConfirmation() { return View(); } /// /// Zeigt eine View zum Zurücksetzen des Passwortes an /// /// Rücksetzcode /// View [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); } /// /// Zurücksetzen des Passwortes eines Benutzers /// /// Benutzereingaben /// [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task 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); } /// /// Anzeigen eines Erfolgs-Views für das Zurücksetzen eines Passwortes /// /// [HttpGet] [AllowAnonymous] public IActionResult ResetPasswordConfirmation() { return View(); } #region Reset Password für API /// /// Zeigt eine View zum Zurücksetzen des Passwortes an /// /// Rücksetzcode /// View [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); } /// /// Zurücksetzen des Passwortes eines Benutzers /// /// Benutzereingaben /// [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task 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); } /// /// Anzeigen eines Erfolgs-Views für das Zurücksetzen eines Passwortes /// /// [HttpGet] [AllowAnonymous] public IActionResult ResetPasswordConfirmationApp() { return View(); } #endregion [HttpGet] [AllowAnonymous] public IActionResult AccessDenied(string returnUrl) { return View(); } /// /// Anzeige der email-Bestätigung der Email-Adresse /// /// /// /// [HttpGet] [AllowAnonymous] public async Task 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); } /// /// Anzeige der email-Bestätigung der Email-Adresse für die App /// /// /// /// [HttpGet] [AllowAnonymous] public async Task 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); } /// /// Zeigt eine View für das erneute Senden des Email-Bestätigungscodes an /// /// View [HttpGet] [AllowAnonymous] public IActionResult SendEmailConfirmation() { return View(); } /// /// Handling für Email-Bestätigung neu senden /// /// Benutzereingaben /// Redirect oder View [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task 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); } /// /// Zeigt die Bestätigung für das erneute Senden des Email-Bestätigungscodes an /// /// View [HttpGet] [AllowAnonymous] public IActionResult SendEmailConfirmationDone() { return View(); } /// /// View für den 2 Faktor Auth-Code /// /// View [HttpGet] [AllowAnonymous] public async Task 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()); } /// /// View für den 2 Faktor Auth-Code /// /// View [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task 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); } } } }