Untitled

mail@pastecode.io avatar
unknown
plain_text
4 months ago
11 kB
2
Indexable
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
//public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)        
public ActionResult Login(LoginViewModel model, string returnUrl)
{
    //We need to be sure basic session values are there for site operation (such as company logo, Adobe key, KBItems, etc.)
    if (Session["appPath"] == null)
    {
        setSiteBasics();//session variables required for site operation even before user logs in
    }

    //We post to this ActionResult multiple times. The first time, it's for intial authentication and this will be null.
    //Subsequent posts are related to MFA and the user will be in cache. We can use the data in cache to save on the database hit.
    var cachedUser = MotorBase_Biz.Account.GetCurrentUserFromCache(HttpContext.User.Identity.Name);

    if (cachedUser == null && model.portalUser != null) {

        //The user logged in from spsuite.com and we haven't redirected yet, so no HttpContext.User.Identity.Name. We can look in cache this way.
        cachedUser = MotorBase_Biz.Account.GetCurrentUserFromCache(model.portalUser.strUserName);

    }
    //If the user hasn't entered a username and a password, throw a validation error.
    if (!ModelState.IsValid && cachedUser == null)
    {
        return View(model);
    }

    //This will contain everything about the current user, needed to process MFA or send them on their merry way after MFA is satisfied
    spReturntblEmployee portalUser = new spReturntblEmployee();

    //If the user is not logged in yet, we need to try their username and password.
    if (cachedUser == null)
    {
        //returns a non null object if they are authenticated properly
        portalUser = MotorBase_Biz.Account.AuthenticateUser(model.Username, model.Password);

        if (!String.IsNullOrEmpty(portalUser.strEmployeeID))
        {
            //successfully authenticated, set our session variable for Employee ID
            Session["EmpID"] = portalUser.strEmployeeID;

            //Clean up our user table, used for licensing products in the suite and IP security.
            List<SqlParameter> nvcParams = new List<SqlParameter>();
            nvcParams.Add(new SqlParameter("strEmployeeID", portalUser.strEmployeeID));

            var gUtils = new MotorBase_Biz.generalUtils();

            // Old records can go, as can ours from a previous login. This table only ever contains active users.
            bool result = gUtils.ExecuteNonQuery("Delete from tblzWebUser Where dtmLastActivity < DATEADD(HOUR, -1, GETDATE()) Or strEmpID = @strEmployeeID", nvcParams, CommandType.Text);

            //Set our authentication cookie so the site considers the user authenticated.
            FormsAuthentication.SetAuthCookie(portalUser.strUserName, false);

            gUtils.logger("** authenticated username: " + portalUser.strUserName);

            //assign the web roles to the user. These roles determine access to parts of the menu/site.
            var UserRoles = MotorBase_Biz.generalUtils.GetRolesForUser(portalUser.strUserName);
            portalUser.userRoles = UserRoles;
            portalUser.strWebStartPage = "/About/MyProfile";

            //We use the session id to detect concurrent logins from different sessions.
            portalUser.strSessionId = Session.SessionID;

            //add this user to cache. We can retrieve it from there, if we need any of these properties for site operations
            MotorBase_Biz.Account.AddUserToCache(portalUser);
        }

    }
    else if (cachedUser != null)
    {
        portalUser = cachedUser;//grab the user from cache. They are already authenticated. We use this variable for MFA below
    }

    if (!String.IsNullOrEmpty(portalUser.strEmployeeID))
    {
        //If we already triggered MFA, we are waiting on a code and don't need to trigger again. (MFAStates.WaitOnCode)
        //Has this user opted in? If so, is it time to trigger? If they previously indicated to be remembered for 60 days we want to account for that.
        int CheckTriggerMFA = MotorBase_Biz.Account.CheckTriggerMFA(portalUser.strEmployeeID, portalUser.strUserName);

        if (CheckTriggerMFA == 0 || (String.IsNullOrEmpty(portalUser.strMFAMethod1) && String.IsNullOrEmpty(portalUser.strMFAMethod2)))
        {
            //If user opted out, or has no method stored we can forgo MFA
            model.mfaState = MFAStates.NoMFA;
        }
        else if (CheckTriggerMFA == 1 && model.mfaState != MFAStates.WaitOnCode)
        {
            //If the portal user has only one MFA method, no need to stop and ask. Just send the code. 
            if ((!String.IsNullOrEmpty(portalUser.strMFAMethod1) && String.IsNullOrEmpty(portalUser.strMFAMethod2)) || (String.IsNullOrEmpty(portalUser.strMFAMethod1) && !String.IsNullOrEmpty(portalUser.strMFAMethod2)))
            {
                var strCurrentMFAMethod = (String.IsNullOrEmpty(portalUser.strMFAMethod1) ? portalUser.strMFAMethod2 : portalUser.strMFAMethod1);

                if (!String.IsNullOrEmpty(strCurrentMFAMethod))
                {
                    model.strCurrentMFA = strCurrentMFAMethod;

                    //We need to generate our code and send it to the method specified
                    string MFACode = GetMFACode();

                    model.portalUser = portalUser;

                    if (MFACode != "")
                    {
                        model.MFACode = MFACode;
                        portalUser.strCurrentMFA = model.strCurrentMFA;
                        portalUser.strSessionId = Session.SessionID;
                        MotorBase_Biz.Account.AddSecurityAuth(model.portalUser, MFACode);

                        bool codeSendResult = SendMFACode(MFACode, model.strCurrentMFA);

                        if (!codeSendResult)
                        {
                            ViewBag.MFAResult = "We are sorry, there has been an error attempting to send your code. Please try another method, or again later.";
                            model.mfaState = MFAStates.TriggerMFA;
                        }
                        else
                            model.mfaState = MFAStates.WaitOnCode;

                        //The UI lets the user know the code has been sent and presents a place to enter this code.
                        return View("Login",model);
                    }
                }
            }

            //If the user has opted in and we aren't yet awaiting a code, we need to trigger the code send
            model.mfaState = MFAStates.TriggerMFA;

        }       //Waiting on a code but having a value in strCurrentMFA but no value in the MFA Code yet means the user has chosen a method to send the code
        else if (model.mfaState == MFAStates.WaitOnCode && !String.IsNullOrEmpty(model.strCurrentMFA) && String.IsNullOrEmpty(model.MFACode))
        {
            //We need to generate our code and send it to the method specified
            string MFACode = GetMFACode();

            model.portalUser = portalUser;

            if (MFACode != "")
            {
                model.MFACode = MFACode;
                portalUser.strCurrentMFA = model.strCurrentMFA;
                portalUser.strSessionId = Session.SessionID;
                MotorBase_Biz.Account.AddSecurityAuth(model.portalUser, MFACode);

                bool codeSendResult = SendMFACode(MFACode, model.strCurrentMFA);

                if (!codeSendResult)
                {
                    ViewBag.MFAResult = "We are sorry, there has been an error attempting to send your code. Please try again later.";
                    model.mfaState = MFAStates.TriggerMFA;
                }
                else
                    model.mfaState = MFAStates.WaitOnCode;

                //The UI lets the user know the code has been sent and presents a place to enter this code.
                //The UI lets the user know the code has been sent and presents a place to enter this code.
                return View("Login", model);
            }

        }
        //If we are waiting on a code, the code exists, as does the current MFA method, the user had entered a code and we need to validate it
        else if (model.mfaState == MFAStates.WaitOnCode && !String.IsNullOrEmpty(model.strCurrentMFA) && !String.IsNullOrEmpty(model.MFACode))
        {

            //Checks tblSecurityAuth to validate the code. Can't be older than 15 minutes and has to match exactly. If more than one was triggered, only the last one works
            int authID = MotorBase_Biz.Account.CheckMFACode(model.strCurrentMFA, model.MFACode, model.Username);

            if (authID == 0)
            {
                ViewBag.MFAResult = "Incorrect or expired code. Please send and use another code.";
                model.mfaState = MFAStates.TriggerMFA;
                model.portalUser = portalUser;
                return View(model);

            }
            else
            {
                //Update tblSecurityAuth with our login date and time. 
                MotorBase_Biz.Account.UpdateMFACode(authID, portalUser.denyRoleID);

                model.mfaState = MFAStates.MFASatisfied;

                //If the user indicated we to remember for 60 days, we need to set that value.
                if (model.ysnRememberMFA)
                {
                    portalUser.ysnRememberMFA = true;

                    MotorBase_Biz.Account.UpdateMFARemember(portalUser);
                }


            }
        }

        //If this user has MFA turned on, now that they have authenticated let's require the code.
        if (model.mfaState != MFAStates.MFASatisfied && model.mfaState != MFAStates.NoMFA && portalUser.ysnMFAOptIn)
        {
            model.mfaState = MFAStates.TriggerMFA;
            model.portalUser = portalUser;

            return View("Login", model);

        }
        else
        {
            //Authentication and MFA is satisfied!

            //These are items we will handle as part of our cached object when we can carry out clean up
            string redirectNow = SetSessionValuesForUser(portalUser);

            if (String.IsNullOrEmpty(redirectNow))
                redirectNow = "/Report/Dashboard";

            if (Session["ReturnUrl"] == null)
                Session["ReturnUrl"] = "";

            if (String.IsNullOrEmpty(Session["ReturnUrl"].ToString()))
            {
                if (portalUser.strPassword == "123")
                {
                    //The user still has the default password. Need to request that they change it.
                    Session["ChangePasswordNow"] = "1";
                }

                portalUser.strWebStartPage = redirectNow;
                return Redirect(portalUser.strWebStartPage);
            }
            else
            {
                return Redirect(Session["ReturnUrl"].ToString());//They could be using a bookmark or a QR code
            }

        }


    }
    else
    {
        ModelState.AddModelError("", "Invalid username or password. Please try again");
        return View(model);
    }

}
Leave a Comment