using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using System.Web.Security; using Fab2ApprovalSystem.DMO; using Fab2ApprovalSystem.Misc; using Fab2ApprovalSystem.Models; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security; using Newtonsoft.Json; namespace Fab2ApprovalSystem.Controllers; [Authorize] public class AccountController : Controller { private string _apiBaseUrl; public AccountController() : this(new UserManager(new UserStore(new ApplicationDbContext()))) { _apiBaseUrl = Environment.GetEnvironmentVariable("FabApprovalApiBaseUrl") ?? throw new ArgumentNullException("FabApprovalApiBaseUrl environment variable not found"); } public AccountController(UserManager userManager) { UserManager = userManager; } public UserManager UserManager { get; private set; } // GET: /Account/Login [AllowAnonymous] // try to make the browser refresh the login page every time, to prevent issues with changing usernames and the anti-forgery token validation [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } private void SetSessionParameters(LoginResult loginResult, LoginModel user) { Session["JWT"] = loginResult.AuthTokens.JwtToken; Session["RefreshToken"] = loginResult.AuthTokens.RefreshToken; Session[GlobalVars.SESSION_USERID] = user.UserID; Session[GlobalVars.SESSION_USERNAME] = user.FullName; Session[GlobalVars.IS_ADMIN] = user.IsAdmin; Session[GlobalVars.IS_MANAGER] = user.IsManager; Session[GlobalVars.OOO] = user.OOO; Session[GlobalVars.CAN_CREATE_PARTS_REQUEST] = user.IsAdmin || PartsRequestController.CanCreatePartsRequest(user.UserID); FormsAuthentication.SetAuthCookie(user.LoginID, true); } [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task Login(LoginModel model, string returnUrl) { try { bool isLoginValid; HttpClient httpClient = HttpClientFactory.Create(); httpClient.BaseAddress = new Uri(_apiBaseUrl); LoginResult loginResult = await AccountDMO.LoginAsync(httpClient, model); #if (DEBUG) isLoginValid = true; #endif #if (!DEBUG) bool isIFX = false; if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") { isLoginValid = true; } else { isLoginValid = loginResult.IsAuthenticated; if (isLoginValid) isIFX = true; } #endif if (isLoginValid) { UserAccountDMO userDMO = new UserAccountDMO(); LoginModel user = userDMO.GetUser(model.LoginID); if (user != null) { SetSessionParameters(loginResult, user); return RedirectToLocal(returnUrl); } else { ModelState.AddModelError("", "The user name does not exist in the DB. Please contact the System Admin"); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } } catch (Exception ex) { Functions.WriteEvent(GlobalVars.AppSettings, @User.Identity.Name + " " + ex.InnerException, System.Diagnostics.EventLogEntryType.Error); EventLogDMO.Add(new WinEventLog() { IssueID = 99999, UserID = @User.Identity.Name, DocumentType = "Login", OperationType = "Error", Comments = "Reject - " + ex.Message }); ModelState.AddModelError("", ex.Message); } return View(model); // If we got this far, something failed, redisplay form } [HttpPost] [AllowAnonymous] public async Task ExternalAuthSetup(AuthAttempt authAttempt) { try { bool isLoginValid; HttpClient httpClient = HttpClientFactory.Create(); httpClient.BaseAddress = new Uri(_apiBaseUrl); LoginResult loginResult = await AccountDMO.ExternalAuthSetupAsync(httpClient, authAttempt); #if (DEBUG) isLoginValid = true; #endif #if (!DEBUG) bool isIFX = false; if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") { isLoginValid = true; } else { isLoginValid = loginResult.IsAuthenticated; if (isLoginValid) isIFX = true; } #endif if (isLoginValid) { UserAccountDMO userDMO = new UserAccountDMO(); LoginModel user = userDMO.GetUser(authAttempt.LoginID); if (user != null) { SetSessionParameters(loginResult, user); return new HttpResponseMessage(HttpStatusCode.OK); } else { ModelState.AddModelError("", "The user name does not exist in the DB. Please contact the System Admin"); return new HttpResponseMessage(HttpStatusCode.NotFound); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); return new HttpResponseMessage(HttpStatusCode.Unauthorized); } } catch (Exception ex) { Functions.WriteEvent(GlobalVars.AppSettings, @User.Identity.Name + " " + ex.InnerException, System.Diagnostics.EventLogEntryType.Error); EventLogDMO.Add(new WinEventLog() { IssueID = 99999, UserID = @User.Identity.Name, DocumentType = "Login", OperationType = "Error", Comments = "Reject - " + ex.Message }); ModelState.AddModelError("", ex.Message); return new HttpResponseMessage(HttpStatusCode.InternalServerError); } } // GET: /Account/Register [AllowAnonymous] public ActionResult Register() { return View(); } // POST: /Account/Disassociate [HttpPost] [ValidateAntiForgeryToken] public async Task Disassociate(string loginProvider, string providerKey) { ManageMessageId? message = null; IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey)); if (result.Succeeded) { message = ManageMessageId.RemoveLoginSuccess; } else { message = ManageMessageId.Error; } return RedirectToAction("Manage", new { Message = message }); } // GET: /Account/Manage #pragma warning disable IDE0060 // Remove unused parameter public ActionResult Manage(ManageMessageId? message) { return View(); } #pragma warning restore IDE0060 // Remove unused parameter // POST: /Account/ExternalLogin [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult ExternalLogin(string provider, string returnUrl) { // Request a redirect to the external login provider return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); } // POST: /Account/LinkLogin [HttpPost] [ValidateAntiForgeryToken] public ActionResult LinkLogin(string provider) { // Request a redirect to the external login provider to link a login for the current user return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId()); } // GET: /Account/LinkLoginCallback public async Task LinkLoginCallback() { ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); if (loginInfo == null) { return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login); if (result.Succeeded) { return RedirectToAction("Manage"); } return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } // POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { FormsAuthentication.SignOut(); return RedirectToAction("Login", "Account"); } // GET: /Account/ExternalLoginFailure [AllowAnonymous] public ActionResult ExternalLoginFailure() { return View(); } [ChildActionOnly] public ActionResult RemoveAccountList() { IList linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId()); ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1; return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts); } protected override void Dispose(bool disposing) { if (disposing && UserManager != null) { UserManager.Dispose(); UserManager = null; } base.Dispose(disposing); } #region Helpers // Used for XSRF protection when adding external logins private const string XsrfKey = "XsrfId"; private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); } private void AddErrors(IdentityResult result) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } } private bool HasPassword() { ApplicationUser user = UserManager.FindById(User.Identity.GetUserId()); if (user != null) { return user.PasswordHash != null; } return false; } public enum ManageMessageId { ChangePasswordSuccess, SetPasswordSuccess, RemoveLoginSuccess, Error } private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction("MyTasks", "Home"); } } private class ChallengeResult : HttpUnauthorizedResult { public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null) { } public ChallengeResult(string provider, string redirectUri, string userId) { LoginProvider = provider; RedirectUri = redirectUri; UserId = userId; } public string LoginProvider { get; set; } public string RedirectUri { get; set; } public string UserId { get; set; } public override void ExecuteResult(ControllerContext context) { AuthenticationProperties properties = new AuthenticationProperties() { RedirectUri = RedirectUri }; if (UserId != null) { properties.Dictionary[XsrfKey] = UserId; } context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); } } #endregion }