Question
· Aug 23, 2023

Override standard login process

Hi Guys,

For Login in CSP application, I am displaying custom Login page which is rendered from subclass CSS.CSP.Login that extends %CSP.Login, and also got IBA.CSP.Page that extends %CSP.Page with overridden method OnPreHTTP(). This setup is working perfectly for normal login.  

When I define Invalid login limit and enable Disable account if login limit reached in System > Security Management > System-wide Security Parameters, the users get disabled after certain invalid login attempts.

Since, cache do not have time period field to disable the account for certain time eg: enable after 15 mins (or may be I missed it, please let me know if there is any), I have implemented it in a way when the disabled users try to login after 15 mins with correct credentials, I would let them into the application.

But, as soon as I hit Login button in the OnPage() method in CSS.CSP.Login classthe cache standard authentication validation runs and checks the Security.Users Enabled property value as disabled(No) and modifies the Users properties and throws the error in screenshot below. 

From the stacktrace in the screenshot, it seems the server side authentication validation was done in the Cache standard login methods eg: UsersCSPLogin() from routine %sySecurity. 

Is there anyway I can override these methods or access the request data before it hit these methods and change the Users properties as per my need? So, after 15 mins if user logins with correct details I would change the Users Enabled properties as Yes and set Login Attempts to and let the user login successfully.

Note: OnPreHTTP() method never gets called during this process.
           I have included the CSS.CSP.Login.cls in custom pages Login page field.

Thank you

Kind Regards,

Sandeep

Product version: Caché 2018.1
$ZV: Cache for Windows (x86-64) 2018.1.5 (Build 659) Mon Mar 22 2021 07:15:21 EDT
Discussion (4)2
Log in or sign up to continue

Hi @Sylvain Guilbaud,

The class CSS.CSP.Login inherits almost the same methods from %CSP.Login class and it's only the Styles that has been modified.
 

Class CSS.CSP.Login Extends %CSP.Login
{

Parameters....

// This method represents both the methods (OnPage() and OnLoginPage()) from %CSP.Login class
ClassMethod OnPage() As %Status
{
    // text strings
    Set ConfigName = $P($zu(86),"*",2)
    // get key, lookup in localization global
    Set tLang = $$$SessionLanguage
    Set tTitle = $$FormatText^%occMessages($$$GetSysMessage(tLang,..#DOMAIN,"logintitle","Login %1"),ConfigName)
    Set tPrompt = $$$GetSysMessage(tLang,..#DOMAIN,"loginenter","Please login")
    Set tUserName = $$$GetSysMessage(tLang,..#DOMAIN,"loginusername","User Name")
    Set tPassword = $$$GetSysMessage(tLang,..#DOMAIN,"loginpassword","Password")
    Set tLogin = $$$GetSysMessage(tLang,..#DOMAIN,"login","LOGIN")
    &html<<html><head>
          <title></title>>

    Do ..DrawHEAD()

&html<<script language="javascript" type="text/javascript">
function Validate()
{
    /* Login validation */
    var ok = true;
    if ( IsTextNull('CompanyCode') )
    {
        document.getElementById('CompanyCodeErrorMsg').innerHTML="<br>Company Code is required";
        ok = false;
    }
    else
        document.getElementById('CompanyCodeErrorMsg').innerHTML="";

    if ( IsTextNull('UserName') )
    {
        document.getElementById('UserNameErrorMsg').innerHTML="<br>User name is required";
        ok = false;
    }
    else
        document.getElementById('UserNameErrorMsg').innerHTML="";

    if ( IsTextNull('CachePassword') )
    {
        document.getElementById('CachePasswordErrorMsg').innerHTML="<br>Password is required";
        ok = false;
    }
    else
        document.getElementById('CachePasswordErrorMsg').innerHTML="";

    return ok;
}
    </script>>

    &html< </head>
        <body  style="margin-left:0; margin-top:0; margin-width:0 margin-height:0;" >
    >

    Do ..DrawTitle(tTitle)

    // Show standard login form
    &html<<div style="position: absolute; top:50%; left: 50%; transform:translate(-50%,-50%);">
            <form name="Login" method="post" action="#($ZConvert($G(%request.Data("Error:FullURL",1)),"O","HTML"))#" onsubmit="return Validate();">
        >

    &html<<table class="entryTable" align="center">
        <tr>
            <td colspan="3">
                <div class="loginCaption" style="padding-bottom: 5px;" align="center">#(tPrompt)#:</div>
            </td>
        </tr>
        <tr>
            <td class="loginCaption" nowrap>Company:</td>
            <td colspan="2">
                <input type="text" size="40" name="CompanyCode" id="CompanyCode">&nbsp;<span class="errorText" id="CompanyCodeErrorMsg"></span>
            </td>
        </tr>
        <tr>
            <td class="loginCaption" nowrap>#(tUserName)#:</td>
            <td colspan="2">
                <input type="text" size="40" name="UserName" id="UserName">&nbsp;<span class="errorText" id="UserNameErrorMsg"></span>
            </td>
        </tr>
        <tr>
            <td class="loginCaption" nowrap>#(tPassword)#:</td>
            <td colspan="2">
                <input type="password" size="25" name="CachePassword" id="CachePassword">&nbsp;<span class="errorText" id="CachePasswordErrorMsg"></span>
            </td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td><input type="submit" name="CacheLogin" value="#(tLogin)#" class="button"></td>
        </tr>
    >

    // test for error
    Set tMsg = $Get(%request.Data("Error:ErrorCode",1))

    If ((tMsg'="")&&($SYSTEM.Status.GetErrorCodes(tMsg)'[$$$ERRORCODE($$$RequireAuthentication))) {
        &html<<tr><td><center>>
        Do ShowError^%apiCSP(tMsg)
        &html<</center></td></tr>>
    }

    &html<</td></tr><tr><td style="height:180px;"><br/></td></tr></table></body></html>>

    Quit $$$OK
}

And IBA.CSP.Page uses only the OnPreHTTP from %CSP.Page and gets called only if the login is successful. Whenever the login is unsuccessful the CSS.CSP.Login page is loaded to render the Login page with error message. This happens as soon as I hit the login button and the input validation is done in server side by the Cache standard methods itself, so I needed to access those methods to modify the data as per my need before the Cache does.

Class IBA.CSP.Page Extends %CSP.Page
{

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
	/* If unknown user redirect to Login page */
	if ($username = "UnknownUser" ) {
		if ( %request.PageName '= "PasswordChange.csp" ) && ( %request.PageName '= "CSS.CSP.Login.cls" ) {
			set %response.Redirect = "login page"
			quit 0
		}
	} else {
		if (%session.Get("User") '= $username)  {
			//check if user active and redirect to appropriate page

		} else {
			set %session.EndSession=1
			set %response.Redirect = "logout"
			quit 0
		}

	}

}

I have implemented other solution but it would be clean to use the available features, so any suggestion would be highly appreciated.

Many Thanks!

Actually, I have been locking the users in different table when Invalid login limits exceeds and release the lock after 15 minutes with correct credentials.

I was just trying to make use of the existing features easily but it seems bit difficult.

If we had some property in security.users to enable the disabled account after certain time period it would have solved my problem.

Your suggestion sounds good and less messy. I will give it a try if I could not find proper solution.

Thanks @Vitaliy Serdtsev