Web Connection
Keeping User out of the Website
Gravatar is a globally recognized avatar based on your email address. Keeping User out of the Website
  Roy Miller
  All
  Oct 1, 2014 @ 03:58pm
Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser

Gravatar is a globally recognized avatar based on your email address. Re: Keeping User out of the Website
  Rick Strahl
  Roy Miller
  Oct 2, 2014 @ 04:04pm
Roy,

Not sure - code should run identically whether you run from within VFP or an EXE assuming the rest of the environment is set up the same. Make sure you have your paths set up the same and are using the same data paths etc. If unsure run the EXE out of the same folders as the EXE...

+++ Rick ---



Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser




Rick Strahl
West Wind Technologies

Making waves on the Web
from Maui

Gravatar is a globally recognized avatar based on your email address. Re: Keeping User out of the Website
  Roy Miller
  Rick Strahl
  Oct 3, 2014 @ 11:02am
Rick ... Got it fixed and believe it or not .... it was FoxPro ... the following code cured it

IF RLOCK("mcgInfo")
UNLOCK IN mcgInfo
ENDIF

Note I didn't lock the record ... but somehow FoxPro was not refreshing the table. Also, after trying different mechanisms, including many hours of nonsense .... this simple code fixed it! Can't take credit for getting this in play, Paul Mrozowski suggested this ... viola!



Roy,

Not sure - code should run identically whether you run from within VFP or an EXE assuming the rest of the environment is set up the same. Make sure you have your paths set up the same and are using the same data paths etc. If unsure run the EXE out of the same folders as the EXE...

+++ Rick ---



Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser




Gravatar is a globally recognized avatar based on your email address. Re: Keeping User out of the Website
  Rick Strahl
  Roy Miller
  Oct 3, 2014 @ 02:25pm

Hmmm... is this a busy site then under load?

RLOCK() will force a buffer refresh and ensure you get current data when READING. Without that it's possible to get stale data from VFP as VFP caches data in memory. RLOCK() will force the buffers to be flushed and any subsequent data access to read again from disk.

There are also other ways such as GO RECNO() on the current table to force buffer refreshes from disk. Lots more info here too: http://fox.wikis.com/wc.dll?Wiki~VFPTableConcurrency

Not exactly the most clear topic, but lots of issues and workarounds suggested there.

I ran into this problem years ago on a very busy site and there we had to instrument all sorts of workarounds to make this work reliably, but this should only happen with FoxPro data. SQL data should be no problem.

+++ Rick ---



Rick ... Got it fixed and believe it or not .... it was FoxPro ... the following code cured it

IF RLOCK("mcgInfo")
UNLOCK IN mcgInfo
ENDIF

Note I didn't lock the record ... but somehow FoxPro was not refreshing the table. Also, after trying different mechanisms, including many hours of nonsense .... this simple code fixed it! Can't take credit for getting this in play, Paul Mrozowski suggested this ... viola!



Roy,

Not sure - code should run identically whether you run from within VFP or an EXE assuming the rest of the environment is set up the same. Make sure you have your paths set up the same and are using the same data paths etc. If unsure run the EXE out of the same folders as the EXE...

+++ Rick ---



Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser







Rick Strahl
West Wind Technologies

Making waves on the Web
from Maui

Gravatar is a globally recognized avatar based on your email address. Re: Keeping User out of the Website
  Roy Miller
  Rick Strahl
  Oct 3, 2014 @ 05:01pm
No the site is about to active. I checked out the go recno() and it works just as well. Thanks, this will be more effective than the other.

Thanks

Hmmm... is this a busy site then under load?

RLOCK() will force a buffer refresh and ensure you get current data when READING. Without that it's possible to get stale data from VFP as VFP caches data in memory. RLOCK() will force the buffers to be flushed and any subsequent data access to read again from disk.

There are also other ways such as GO RECNO() on the current table to force buffer refreshes from disk. Lots more info here too: http://fox.wikis.com/wc.dll?Wiki~VFPTableConcurrency

Not exactly the most clear topic, but lots of issues and workarounds suggested there.

I ran into this problem years ago on a very busy site and there we had to instrument all sorts of workarounds to make this work reliably, but this should only happen with FoxPro data. SQL data should be no problem.

+++ Rick ---



Rick ... Got it fixed and believe it or not .... it was FoxPro ... the following code cured it

IF RLOCK("mcgInfo")
UNLOCK IN mcgInfo
ENDIF

Note I didn't lock the record ... but somehow FoxPro was not refreshing the table. Also, after trying different mechanisms, including many hours of nonsense .... this simple code fixed it! Can't take credit for getting this in play, Paul Mrozowski suggested this ... viola!



Roy,

Not sure - code should run identically whether you run from within VFP or an EXE assuming the rest of the environment is set up the same. Make sure you have your paths set up the same and are using the same data paths etc. If unsure run the EXE out of the same folders as the EXE...

+++ Rick ---



Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser







Gravatar is a globally recognized avatar based on your email address. Re: Keeping User out of the Website
  Rick Strahl
  Roy Miller
  Oct 3, 2014 @ 05:43pm

Another thing that's more reliable yet is what I do in the wwSession logic:

THIS.OpenTable()

** Force table buffers to refresh
REPLACE SessionId with SessionId

lcLocateId = PADR(lcSessionId,FSIZE("SessionId"))
LOCATE FOR SessionId = lcLocateId AND ;
LastOn > DATETIME() - THIS.nSessionTimeout

Note the REPLACE SessionId with SessionId which is silly of course - but it actually forces a write to disk which also forces the buffers to refresh. This is what I used in said super high volume sites in the 90's and that was the only thing that worked for every scenario.

The GOTO Recno() also works in most cases... the nice thing about that is that you can make it generic - the REPLACE trick not so much. For the formaer I just created a helper function in wwUtils:

************************************************************************
* ForceTableRefresh
****************************************
*** Function: Forces a table to refresh its read buffers
*** Assume: Works on the current active cursor
*** Pass: lcAlias - Optional alias name. Else Alias() is used
*** Return: nothing
************************************************************************

FUNCTION ForceTableRefresh(lcAlias)
LOCAL lcOldAlias, llAliasSpecified

llAliasSpecified = !EMPTY(lcAlias)

IF llAliasSpecified
lcOldAlias = ALIAS()
SELECT (lcAlias)
ENDIF

IF EMPTY(ALIAS())
RETURN
ENDIF

IF !EOF()
*** THIS FORCES A VFP BUFFER REFRESH ON TABLE
GO RECNO()
ELSE
GO TOP
IF !EOF()
*** THIS FORCES A VFP BUFFER REFRESH ON TABLE
GO RECNO()
ENDIF
ENDIF

IF llAliasSpecified AND !EMPTY(lcOldAlias)
SELECT (lcOldAlias)
ENDIF

ENDFUNC
* ForceTableRefresh


+++ Rick ---



No the site is about to active. I checked out the go recno() and it works just as well. Thanks, this will be more effective than the other.

Thanks

Hmmm... is this a busy site then under load?

RLOCK() will force a buffer refresh and ensure you get current data when READING. Without that it's possible to get stale data from VFP as VFP caches data in memory. RLOCK() will force the buffers to be flushed and any subsequent data access to read again from disk.

There are also other ways such as GO RECNO() on the current table to force buffer refreshes from disk. Lots more info here too: http://fox.wikis.com/wc.dll?Wiki~VFPTableConcurrency

Not exactly the most clear topic, but lots of issues and workarounds suggested there.

I ran into this problem years ago on a very busy site and there we had to instrument all sorts of workarounds to make this work reliably, but this should only happen with FoxPro data. SQL data should be no problem.

+++ Rick ---



Rick ... Got it fixed and believe it or not .... it was FoxPro ... the following code cured it

IF RLOCK("mcgInfo")
UNLOCK IN mcgInfo
ENDIF

Note I didn't lock the record ... but somehow FoxPro was not refreshing the table. Also, after trying different mechanisms, including many hours of nonsense .... this simple code fixed it! Can't take credit for getting this in play, Paul Mrozowski suggested this ... viola!



Roy,

Not sure - code should run identically whether you run from within VFP or an EXE assuming the rest of the environment is set up the same. Make sure you have your paths set up the same and are using the same data paths etc. If unsure run the EXE out of the same folders as the EXE...

+++ Rick ---



Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser










Rick Strahl
West Wind Technologies

Making waves on the Web
from Maui

Gravatar is a globally recognized avatar based on your email address. Re: Keeping User out of the Website
  Roy Miller
  Rick Strahl
  Oct 4, 2014 @ 01:02pm
Thanks Rick ... good discussion.

Another thing that's more reliable yet is what I do in the wwSession logic:

THIS.OpenTable()

** Force table buffers to refresh
REPLACE SessionId with SessionId

lcLocateId = PADR(lcSessionId,FSIZE("SessionId"))
LOCATE FOR SessionId = lcLocateId AND ;
LastOn > DATETIME() - THIS.nSessionTimeout

Note the REPLACE SessionId with SessionId which is silly of course - but it actually forces a write to disk which also forces the buffers to refresh. This is what I used in said super high volume sites in the 90's and that was the only thing that worked for every scenario.

The GOTO Recno() also works in most cases... the nice thing about that is that you can make it generic - the REPLACE trick not so much. For the formaer I just created a helper function in wwUtils:

************************************************************************
* ForceTableRefresh
****************************************
*** Function: Forces a table to refresh its read buffers
*** Assume: Works on the current active cursor
*** Pass: lcAlias - Optional alias name. Else Alias() is used
*** Return: nothing
************************************************************************

FUNCTION ForceTableRefresh(lcAlias)
LOCAL lcOldAlias, llAliasSpecified

llAliasSpecified = !EMPTY(lcAlias)

IF llAliasSpecified
lcOldAlias = ALIAS()
SELECT (lcAlias)
ENDIF

IF EMPTY(ALIAS())
RETURN
ENDIF

IF !EOF()
*** THIS FORCES A VFP BUFFER REFRESH ON TABLE
GO RECNO()
ELSE
GO TOP
IF !EOF()
*** THIS FORCES A VFP BUFFER REFRESH ON TABLE
GO RECNO()
ENDIF
ENDIF

IF llAliasSpecified AND !EMPTY(lcOldAlias)
SELECT (lcOldAlias)
ENDIF

ENDFUNC
* ForceTableRefresh


+++ Rick ---



No the site is about to active. I checked out the go recno() and it works just as well. Thanks, this will be more effective than the other.

Thanks

Hmmm... is this a busy site then under load?

RLOCK() will force a buffer refresh and ensure you get current data when READING. Without that it's possible to get stale data from VFP as VFP caches data in memory. RLOCK() will force the buffers to be flushed and any subsequent data access to read again from disk.

There are also other ways such as GO RECNO() on the current table to force buffer refreshes from disk. Lots more info here too: http://fox.wikis.com/wc.dll?Wiki~VFPTableConcurrency

Not exactly the most clear topic, but lots of issues and workarounds suggested there.

I ran into this problem years ago on a very busy site and there we had to instrument all sorts of workarounds to make this work reliably, but this should only happen with FoxPro data. SQL data should be no problem.

+++ Rick ---



Rick ... Got it fixed and believe it or not .... it was FoxPro ... the following code cured it

IF RLOCK("mcgInfo")
UNLOCK IN mcgInfo
ENDIF

Note I didn't lock the record ... but somehow FoxPro was not refreshing the table. Also, after trying different mechanisms, including many hours of nonsense .... this simple code fixed it! Can't take credit for getting this in play, Paul Mrozowski suggested this ... viola!



Roy,

Not sure - code should run identically whether you run from within VFP or an EXE assuming the rest of the environment is set up the same. Make sure you have your paths set up the same and are using the same data paths etc. If unsure run the EXE out of the same folders as the EXE...

+++ Rick ---



Rick,


I am attempting to do the following

1) Stop a user from logging into the website when a flag is set that indicates maintenance in progress
2) If a user has already logged in, and subsequently the maintenance flag set to true, then the very next time the user hits the website, they are denied access

When I run the code under VFP umbrella, i.e. go into foxpro the process works

When I run it as an EXE outside the umbrella, it fails. I'm a little perplexed as how to trouble shoot this. Any help is appreciated.


I sub classed wwprocess and wwusersercurity with the following


DEFINE CLASS mcgBaseProcess AS wwProcess

cAuthenticationMode = "UserSecurity"
cAuthenticationUserSecurityClass = "mcgUserSecurity"
lEnableSessionState = .T.

* -- works on development computer not on server ... always shows maintenenace in progress
FUNCTION OnProcessInit
LOCAL lcPageFile
lcSessionCookieName = THIS.cSessionKey
lcSessionId = THIS.oRequest.GetCookie(lcSessionCookieName)
lcSessionCookieName = THIS.cSessionKey
Process.InitSession("")
session = this.osession
lcPageFile = lower(JUSTFNAME(Request.GetPhysicalPath()))

IF ! INLIST(UPPER(lcPageFile), 'LOGIN', 'LOGON', 'DEFAULT')
llvalidsession = session.IsValidSession(lcSessionId)
IF ! llValidSession
session.endSession(lcSessionid)
ENDIF
ENDIF

IF !This.Authenticate() && If they're not, a login form should appear
RETURN
ENDIF

ENDFUNC



FUNCTION OnAuthenticateUser(tcUserName, tcPassword, tcErrorMsg)

This.oUserSecurity = CREATEOBJECT(This.cAuthenticationUserSecurityClass)


IF !This.oUserSecurity.Authenticate(m.tcUserName, m.tcPassword)
*** Set lcErrorMsg to pass back via REF parm
tcErrorMsg = this.oUserSecurity.cErrorMsg

IF "expired" $ tcErrorMsg
* TODO: Response.Redirect(Server.oConfig.cRedirectSignupTo)
ENDIF

RETURN .F.
ELSE

session.setSessionVar('username', tcusername)
session.setSessionvar('subky',this.oUserSECURITY.Ouser.subky)
session.setSessionvar('admin',this.oUSERSECURITY.oUser.Admin)
session.setSessionvar('admin2',IIF( this.oUSERSECURITY.oUser.Admin = .t., 'yes', 'no'))
session.setSessionvar('email',this.oUSERSECURITY.oUser.email)
session.setSessionvar('logoncount',this.oUSERSECURITY.oUser.logoncount)
session.setSessionvar('sessionid',session.getsessionid())
This.cAuthenticatedUser = tcUserName
Response.Redirect('mcglogin.mcg')


ENDIF
RETURN .T.
ENDFUNC

************************************************************************
* wwWebPage :: Authenticate
****************************************
*** Function: Handles authenticating of a specific user.
*** Assume:
*** Pass:
*** Return:
************************************************************************

FUNCTION Authenticate(lcValidUserName,lcErrorMessage,llNoForcedLogin)
LOCAL lcUsername, lcPassword, lcErrorMsg, loLogin, lcHtml, lcCurrentUrl, l_lMaintenance


IF EMPTY(lcValidUserName)
lcValidUserName = ""
ENDIF


l_lMaintenance = oDataUtils.IsMaintenanceInProgress()

DO CASE
*** Logout: Note only works with UserSecurity. Basic cannot be undone
CASE UPPER(lcValidUserName) = "LOGOUT"
THIS.lEnableSessionState = .T.
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,"")
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .t.
CASE THIS.cAuthenticationMode == "UserSecurity" OR ;
this.cAuthenticationMode == "Custom"

*** Require Session State
this.lEnableSessionState = .T.

*** Check and see if we already authenticated
lcUserName = Session.GetSessionVar(this.cAuthenticationUserSecurityKey)
IF !EMPTY(lcUserName) AND ! odataUtils.IsMaintenanceInProgress()
*** Yup - already authenticated return .T.
this.cAuthenticatedUser = lcUsername

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .F.
ENDIF

lcCurrentUrl = Request.GetCurrentUrl()

lcErrorMsg = ""
lcUsername = ""
lcPassword = ""

*** Only try to authenticate on Postback
IF Request.IsPostBack()
*** Retrieve the default form field values
lcUsername = Request.Form("WebLogin_txtUsername")
lcPassword = Request.Form("WebLogin_txtPassword")

*** Check and see whether this user is authenticated
IF this.OnAuthenticateUser(lcUserName,lcPassword,@lcErrorMsg)
*** Assign auth user and write into Session
IF EMPTY(this.cAuthenticatedUser)
this.cAuthenticatedUser = lcUsername
ENDIF
Session.SetSessionVar(this.cAuthenticationUserSecurityKey,lcUsername)

*** Override Page setting so it doesn't
*** look like a Postback after validation
IF VARTYPE(__WebPage) = "O"
__WebPage.IsPostBack = .F.
ENDIF

* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
*RETURN .T.
ENDIF

*** If we failed to auth redisplay login
*** with an error message
ENDIF

*** Display a generic login form on a GET operation
IF EMPTY(Response.cStyleSheet)
Response.cStyleSheet = this.ResolveUrl("~/westwind.css")
ENDIF

*** You display a custom page by creating a template and showing that
*** instead. Just make sure the page has WebLogin_txtUsername and WebLogin_txtPassword
*** Form variables
*** Respone.ExpandTemplate( this.oConfig.cHtmlPagePath + "CustomLogin.wc")

*** Generate Page output
LOCAL loLogin as wwWebLogin
loLogin = CREATEOBJECT([WWC_WEBLOGINCONTROL])
loLogin.Id = "WebLogin"
loLogin.width = "350px"
loLogin.Username = lcUsername

* -- RMM
loLogin.UserMessage = IIF( l_lMaintenance, 'Sorry for the inconvenience. Try back later.' , this.cAuthenticationUserMessage)

*loLogin.UserMessage = this.cAuthenticationUserMessage
loLogin.Center = .t.
loLogin.UserSecurityClass = this.cAuthenticationUserSecurityClass

loLogin.ErrorMessage = IIF( EMPTY(lcErrorMsg), IIF( l_lMaintenance, 'Maintenance in Progress',space(0)), lcErrorMsg)

lcHtml = loLogin.Render()



lcHtml = "<br /><br />" + CRLF +;
"<form method='POST' action=''>" + CRLF + ;
lcHtml + CRLF +;
"</form>"

THIS.StandardPage("Case Works Account Login",lcHtml)

CASE THIS.cAuthenticationMode = "Basic"
IF EMPTY(lcValidUsername)
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF

* RETURN .T.

ENDIF

IF UPPER(lcValidUserName) = "WCINI"
lcValidUsername = Request.GetWcIniValue("AdminAccount")
IF EMPTY(lcValidUsername)
* - RMM
IF ! l_lmaintenance
RETURN .T.
ENDIF
* Return .t.
ENDIF
ENDIF

*** Retrieve the actual username
lcUsername = JUSTSTEM( Request.GetAuthenticatedUser() )
this.cAuthenticatedUser = lcUsername

IF !EMPTY(lcUserName)
IF UPPER(lcValidUserName) == "ANY" AND !EMPTY(lcUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF UPPER(lcUsername) == UPPER(lcValidUserName)
* -- RMM
IF ! l_lMaintenance
RETURN .t.
endif
*RETURN .T.
ENDIF

IF AT(",",lcValidUserName) > 0
lcValidUserName = "," + lcValidUsername + ","
IF AT(","+lcUserName+",",lcValidUserName) > 0
* -- RMM
IF ! l_lMaintenance
RETURN .T.
ENDIF
* RETURN .T.
ENDIF
ENDIF
ENDIF

IF llNoForcedLogin
RETURN .f.
ENDIF

this.cAuthenticatedUser = lcUsername

*** Prompt for Authentication from the user
* -- RMM
IF ! l_lMaitenance
Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
endif
*Response.BasicAuthentication(Request.GetServerName(),lcErrorMessage)
ENDCASE
RETURN .F.
ENDFUNC
* wwProcess :: Authenticate

ENDDEFINE

I also sub classed wwUserSercurity

DEFINE CLASS mcgUserSecurity AS wwUserSecurity

cerrormsg = "You Owe Us Money."
*-- Alias of the user file.
calias = "subscriber"

*-- Filename for the user file.
cfilename = "subscriber"

************************************************************************
* wwUserSecurity :: GetUser
****************************************
*** Function: Retrieves a user data record object without affecting the
*** currently active user. Pass in a PK or Username and Password.
*** GetUser always returns a record unless the record cannot be found. Use Logon to check for Active and Expired status.
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUser(lcPK, lcPassword)

*** lcPK could also be the username

THIS.lError = .F.

IF .NOT. THIS.OPEN()
THIS.lError = .T.
THIS.SetError("Couldn't open "+THIS.cfilename)
RETURN .F.
ENDIF

THIS.lNewUser = .F.

*** Allow retrieving a blank user record
IF lcPK="BLANK"
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .T.
ENDIF

* -- RMM 2/21/13
SELECT (THIS.cfilename)

*** If 2 parameters we have username and password
*** Otherwise it's a PK
IF PCOUNT()>1
IF THIS.lCaseSensitive
LOCATE FOR username=PADR(lcPK, LEN(username)) .AND. ;
PASSWORD = PADR(lcPassword, LEN(password))
ELSE
LOCATE FOR LOWER(username)=PADR(LOWER(lcPK), LEN(username)) .AND. ;
LOWER(PASSWORD) = PADR(LOWER(lcPassword), LEN(password))
ENDIF

* -- RMM
l_lFound = FOUND()
* -- is there maintenance going on?
l_lMaintenance = oDataUtils.IsMaintenanceInProgress()


DO case
CASE l_lFound AND ! l_lMaintenance
SCATTER MEMO NAME THIS.ouser
RETURN .T.
CASE l_lFound AND l_lMaintenance
THIS.SetError("Maintenance In Progress")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.
OTHERWISE
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser
RETURN .f.

ENDCASE

* -- RMM 9/25/14
*!* *** Found: Load the record into THIS.oUser
*!* IF FOUND()
*!* SCATTER MEMO NAME THIS.ouser
*!*
*!* * -- testing how to store varaibles with sessions
*!* RETURN .T.
*!* ENDIF
*!*
*!* *** No match - return blank user object and error message
*!* THIS.SetError("Invalid Password or Username")
*!* SCATTER BLANK MEMO NAME THIS.ouser
*!* RETURN .F.

ENDIF

*** A PK was passed in
* LOCATE FOR pk = PADR(lcPK,LEN(pk))

* -- RMM 2/21/13
SELECT (THIS.cfilename)
LOCATE FOR subky = PADR(lcPK,LEN(subky))

*** Found: set THIS.oUser record
IF FOUND()
SCATTER MEMO NAME THIS.ouser
RETURN .T.
ENDIF

*** No match - blank user record and error message
THIS.SetError("Invalid Password or Username")
SCATTER BLANK MEMO NAME THIS.ouser

RETURN .F.
ENDFUNC
* wwUserSecurity :: GetUser










© 1996-2024