| Author | Topic: WAA session manager |
---|
 | Allen Lee | WAA session manager
on Wed, 13 Apr 2016 23:00:55 -0700Given that:
1. the WAA 1.90.331 session manager will assign a WAA session to the
wrong user (PDR 6483)
2. under certain circumstances, information that is stored in a WAA
session carries information that stems from a wrong connection
3. the problem occurs when the same WAA package is requested from more
then one client within a short time frame
Has anyone developed their own session manager that replaces the default
WAA session manager?
Can anyone, who experienced this problem in WAA, say that the problem no
longer exists in CXP? |
 | Thomas Braun | Re: WAA session manager
on Thu, 14 Apr 2016 08:38:15 +0200Allen Lee wrote:
> Has anyone developed their own session manager that replaces the default
> WAA session manager?
Yes. I did.
But AFAIK, the WAA session manage uses cookies to identify the client,
while I don't, I use session keys that are posted with each web page, so it
is no drop-in replacement.
Thomas |
 | Allen Lee | Re: WAA session manager
on Thu, 14 Apr 2016 01:01:59 -0700On 4/13/2016 11:38 PM, Thomas Braun wrote:
> Allen Lee wrote:
>
>> Has anyone developed their own session manager that replaces the default
>> WAA session manager?
>
> Yes. I did.
>
> But AFAIK, the WAA session manage uses cookies to identify the client,
> while I don't, I use session keys that are posted with each web page, so it
> is no drop-in replacement.
>
> Thomas
>
By session keys, do you mean that you store parameters in an array via
oContext:setCargo and then pass the parameters to the next page via
oContext:getCargo?
If so, what method do you use to create a unique identifier for each user? |
 | Thomas Braun | Re: WAA session manager
on Thu, 14 Apr 2016 17:41:13 +0200Allen Lee wrote:
> By session keys, do you mean that you store parameters in an array via
No, the session key is a unique identifier which is part of each page
submitted:
<input type="hidden" value="6VkLK5Hs4txCrMxc2Y3fKanDZQCLMKe9Z6A" name="SID">
If not part of the page, a new one is created and then stored in a
"session.dbf" file at the first contact/login of the user:
{"KEY", "C",64,0},;
{"EXPIRES","C",12,0},;
{"USERID","C",3,0},;
{"DATA","M",4,0} }
"DATA" holds the cargo contents.
This is the code I use to replace setCargo/getCargo etc., maybe you can use
parts of it:
"base" is the base class for my own HTML3 class... at the bottom are some
helper function you will need as well...hope this helps
Thomas
METHOD Base:openSession(nExpireMinutes, cUserId)
local cCookie := ::GetVar("SID")
local nArea := Select()
local lFound := FALSE
local cAlias := "SESSION"
LOCAL cIpAddr := ""
DEFAULT nExpireMinutes TO 60
DEFAULT cUserID TO ""
IF ::context != NIL
cIpAddr := ::context:getRemoteAddr()
ENDIF
if !Empty(cCookie)
lFound := (cAlias)->(dbSeek(cCookie,.f.,"KEY"))
ELSE
cCookie := CreateNewSession( cIpAddr, nExpireMinutes, cUserId)
endif
if lFound .and. !Empty((cAlias)->DATA)
::cargoData := Bin2Var((cAlias)->DATA)
else
::cargoData := {}
endif
::SessionID := cCookie
dbSelectArea(nArea)
return lFound
METHOD Base:CloseSession()
if !Empty(::SessionID)
DeleteSession(::SessionID)
endif
::SessionID := ""
return NIL
METHOD Base:setCargo(x1,x2)
local cCookie := ::GetVar("SID")
local lRet := FALSE
local nArea := Select()
local cAlias := "SESSION"
local i := 0
IF EMPTY(cCookie)
cCookie := ::SessionID
ENDIF
if !(x1 == NIL)
if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
if (cAlias)->(RecLock())
if x2 == NIL
::cargoData := x1
else
x1 := ALLTRIM(x1)
if !ValType(::cargoData) == 'A'
::cargoData := {}
else
i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) }
)
endif
if i == 0
aadd(::cargoData, {x1,x2})
else
::cargoData[i][2] := x2
endif
endif
(cAlias)->DATA := Var2Bin(::cargoData)
(cAlias)->(dbrUnlock())
lRet := TRUE
endif
endif
endif
dbSelectArea(nArea)
return lRet
METHOD Base:delCargo(x1)
local cCookie := ::GetVar("SID")
local lRet := FALSE
local nArea := Select()
local cAlias := "SESSION"
local i := 0
IF EMPTY(cCookie)
cCookie := ::SessionID
ENDIF
if !(x1 == NIL)
if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
if (cAlias)->(RecLock())
x1 := ALLTRIM(x1)
if !ValType(::cargoData) == 'A'
::cargoData := {}
else
i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } )
endif
if i != 0
AREMOVE(::cargoData, i, 1)
endif
(cAlias)->DATA := Var2Bin(::cargoData)
(cAlias)->(dbrUnlock())
lRet := TRUE
endif
endif
endif
dbSelectArea(nArea)
return lRet
METHOD Base:ClearCargo()
local cCookie := ::GetVar("SID")
local lRet := FALSE
local nArea := Select()
local cAlias := "SESSION"
IF EMPTY(cCookie)
cCookie := ::SessionID
ENDIF
if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
::cargoData := {}
if (cAlias)->(RecLock())
(cAlias)->DATA := Var2Bin(::cargoData)
(cAlias)->(dbrUnlock())
lRet := TRUE
endif
ENDIF
dbSelectArea(nArea)
return lRet
METHOD Base:setAllCargo()
local cCookie := ::GetVar("SID")
local lRet := FALSE
local nArea := Select()
local cAlias := "SESSION"
local j:= 0, i := 0
LOCAL aVars := ::GetAllVars(), x1, x2
IF EMPTY(cCookie)
cCookie := ::SessionID
ENDIF
if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
FOR j := 1 TO LEN(aVars)
x1 := aVars[j,1]
x2 := aVars[j,2,1]
if !(x1 == NIL)
x1 := ALLTRIM(x1)
if !ValType(::cargoData) == 'A'
::cargoData := {}
else
i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } )
endif
if i == 0
aadd(::cargoData, {x1,x2})
else
::cargoData[i][2] := x2
endif
endif
NEXT
if (cAlias)->(RecLock())
(cAlias)->DATA := Var2Bin(::cargoData)
(cAlias)->(dbrUnlock())
lRet := TRUE
endif
ENDIF
dbSelectArea(nArea)
return lRet
METHOD Base:getCargo(cName)
local xRet
local i
if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A'
cName := ALLTRIM(cName)
if (i := AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } )) > 0
xRet := ::cargoData[i][2]
endif
elseif cName == NIL
xRet := ::cargoData
endif
return xRet
METHOD Base:getAllCargo()
return ::cargoData
METHOD Base:IsCargo(cName)
local lRet := FALSE
if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A'
lRet := (AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } ) > 0)
endif
return lRet
-------------- HELPERS -------------------
FUNCTION CreateNewSession(cIpAddr, nExpiry, cUserId)
LOCAL nSel := SELECT(), cSID := ""
LOCAL lNewRec := .T., nNewRec := 0
LOCAL cNow := DTOS(DATE()) + STRTRAN(TIME(), ":", "" )
DEFAULT cIpAddr TO ""
DEFAULT nExpiry TO EXPIRE_MINUTES
DEFAULT cUserID TO ""
SELECT SESSION
IF FilLock(10)
cSID := RandomPass(64)
Falls Session-ID bereits vorhanden, solange versuchen, bis
eine neue herauskommt.
DO WHILE dbSeek( cSID, .F. , "KEY" )
cSID := RandomPass(64)
ENDDO
/* Session-Datensatz recyclen */
IF LASTREC() != 0
OrdSetFocus("EXPIRES")
GO TOP
IF ! EOF() .AND. SESSION->expires < cNow
IF RecLock(5)
lNewRec := .F.
nNewRec := RECNO()
ENDIF
ENDIF
OrdSetFocus("KEY")
ENDIF
IF lNewRec
APPEND BLANK
ELSE
dbGoto(nNewRec)
ENDIF
SESSION->KEY := cSID
SESSION->EXPIRES := CalcExpiration( nExpiry )
SESSION->ipaddr := cIpAddr
SESSION->userid := cUserID
SESSION->data := ""
SESSION->loggedin := TRUE
dbUnlock()
ENDIF
SELECT (nSel)
RETURN cSID
FUNCTION CalcExpiration(nMinutes)
LOCAL cExpiration, nExpire
LOCAL dExpire := DATE()
nExpire := SECONDS() + nMinutes * 60 Ablaufzeit
Expiration-Zeit bei jedem Zugriff neu berechnen, Tageswechsel
berücksichtigen
IF nExpire >= 86400 24 Stunden
dExpire := dExpire + 1
nExpire := nExpire - 86400
ENDIF
nExpire := ROUND(nExpire / 60,0)
cExpiration := DTOS(dExpire) + STRZERO(INT(nExpire/60),2,0) +
STRZERO(nExpire%60,2,0)
RETURN cExpiration
FUNCTION DeleteSession(cSid)
LOCAL nSel := SELECT(), lRet := TRUE
LOCAL cNow := DTOS(DATE()) + "0000"
SELECT SESSION
Session vorhanden?
IF dbSeek( cSID, .F. , "KEY" )
Logout
IF RecLock(5)
SESSION->key := ""
SESSION->userid := ""
SESSION->loggedin := FALSE
SESSION->ipaddr := ""
SESSION->data := ""
SESSION->expires := cNow
dbrUnlock()
ENDIF
ENDIF
SELECT (nSel)
RETURN lRet
FUNCTION SessionExpired(cSid, oCon)
LOCAL nSel := SELECT(), lRet := TRUE
LOCAL cNow := DTOS(DATE()) + SUBSTR(STRTRAN(TIME(), ":", "" ),1,4)
SELECT SESSION
Session vorhanden?
IF dbSeek( cSID, .F. , "KEY" )
lRet := SESSION->expires < cNow .OR. !SESSION->loggedin .OR.
!(TRIM(oCon:getRemoteAddr()) = TRIM(SESSION->ipaddr))
IF !lRet
EVTUSER->(dbSeek(SESSION->userid,.F.,"ID"))
Wenn OK, Session verlängern
IF RecLock(5)
SESSION->EXPIRES := CalcExpiration( EXPIRE_MINUTES )
dbrUnlock()
ENDIF
ELSE
Logout
DeleteSession(cSid, oCon)
ENDIF
ENDIF
SELECT (nSel)
RETURN lRet
FUNCTION RandomPass(nLen, cFormat)
LOCAL nI, cChar, nZahl := 64
LOCAL cRet := ""
DEFAULT cFormat TO ""
FOR nI := 1 TO nLen
DO WHILE nZahl >= 58 .AND. nZahl <= 64
nZahl := Random(42)+48
/*
Wegen Verwechslungsgefahr 1iI 0Oo entfernen
*/
IF nZahl == 48 .OR. nZahl == 49 .OR. nZahl == 73 .OR.;
nZahl == 79 .OR. nZahl == 105 .OR. nZahl == 111
nZahl := 64
ENDIF
ENDDO
cChar := CHR(nZahl)
IF Random(1)=0
cChar := LOWER(cChar)
ENDIF
cRet += cChar
nZahl := 64
NEXT
IF ! EMPTY(cFormat)
cRet := Transform( cRet , cFormat )
ENDIF
RETURN cRet |
 | Allen Lee | Re: WAA session manager
on Thu, 14 Apr 2016 11:06:47 -0700Thank you very much, Thomas.
I know this is going help a great deal.
I'm going to begin testing an implementation based on your model.
Your sharing is greatly appreciated.
If you vacation in Vancouver, Canada then I invite you stay as a guest
in my home.
On 4/14/2016 8:41 AM, Thomas Braun wrote:
> Allen Lee wrote:
>
>> By session keys, do you mean that you store parameters in an array via
>
> No, the session key is a unique identifier which is part of each page
> submitted:
>
> <input type="hidden" value="6VkLK5Hs4txCrMxc2Y3fKanDZQCLMKe9Z6A" name="SID">
>
> If not part of the page, a new one is created and then stored in a
> "session.dbf" file at the first contact/login of the user:
>
> {"KEY", "C",64,0},;
> {"EXPIRES","C",12,0},;
> {"USERID","C",3,0},;
> {"DATA","M",4,0} }
>
> "DATA" holds the cargo contents.
>
> This is the code I use to replace setCargo/getCargo etc., maybe you can use
> parts of it:
>
> "base" is the base class for my own HTML3 class... at the bottom are some
> helper function you will need as well...hope this helps
>
> Thomas
>
> METHOD Base:openSession(nExpireMinutes, cUserId)
> local cCookie := ::GetVar("SID")
> local nArea := Select()
> local lFound := FALSE
> local cAlias := "SESSION"
> LOCAL cIpAddr := ""
>
> DEFAULT nExpireMinutes TO 60
> DEFAULT cUserID TO ""
>
> IF ::context != NIL
> cIpAddr := ::context:getRemoteAddr()
> ENDIF
>
> if !Empty(cCookie)
> lFound := (cAlias)->(dbSeek(cCookie,.f.,"KEY"))
> ELSE
> cCookie := CreateNewSession( cIpAddr, nExpireMinutes, cUserId)
> endif
>
> if lFound .and. !Empty((cAlias)->DATA)
> ::cargoData := Bin2Var((cAlias)->DATA)
> else
> ::cargoData := {}
> endif
>
> ::SessionID := cCookie
>
> dbSelectArea(nArea)
> return lFound
>
> METHOD Base:CloseSession()
>
> if !Empty(::SessionID)
> DeleteSession(::SessionID)
> endif
>
> ::SessionID := ""
>
> return NIL
>
>
> METHOD Base:setCargo(x1,x2)
> local cCookie := ::GetVar("SID")
> local lRet := FALSE
> local nArea := Select()
> local cAlias := "SESSION"
> local i := 0
>
> IF EMPTY(cCookie)
> cCookie := ::SessionID
> ENDIF
>
> if !(x1 == NIL)
> if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
> if (cAlias)->(RecLock())
> if x2 == NIL
> ::cargoData := x1
> else
> x1 := ALLTRIM(x1)
> if !ValType(::cargoData) == 'A'
> ::cargoData := {}
> else
> i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) }
> )
> endif
> if i == 0
> aadd(::cargoData, {x1,x2})
> else
> ::cargoData[i][2] := x2
> endif
> endif
> (cAlias)->DATA := Var2Bin(::cargoData)
> (cAlias)->(dbrUnlock())
> lRet := TRUE
> endif
> endif
> endif
> dbSelectArea(nArea)
> return lRet
>
>
> METHOD Base:delCargo(x1)
> local cCookie := ::GetVar("SID")
> local lRet := FALSE
> local nArea := Select()
> local cAlias := "SESSION"
> local i := 0
>
> IF EMPTY(cCookie)
> cCookie := ::SessionID
> ENDIF
>
> if !(x1 == NIL)
> if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
> if (cAlias)->(RecLock())
> x1 := ALLTRIM(x1)
> if !ValType(::cargoData) == 'A'
> ::cargoData := {}
> else
> i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } )
> endif
> if i != 0
> AREMOVE(::cargoData, i, 1)
> endif
> (cAlias)->DATA := Var2Bin(::cargoData)
> (cAlias)->(dbrUnlock())
> lRet := TRUE
> endif
> endif
> endif
> dbSelectArea(nArea)
> return lRet
>
> METHOD Base:ClearCargo()
> local cCookie := ::GetVar("SID")
> local lRet := FALSE
> local nArea := Select()
> local cAlias := "SESSION"
>
> IF EMPTY(cCookie)
> cCookie := ::SessionID
> ENDIF
>
> if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
> ::cargoData := {}
> if (cAlias)->(RecLock())
> (cAlias)->DATA := Var2Bin(::cargoData)
> (cAlias)->(dbrUnlock())
> lRet := TRUE
> endif
> ENDIF
>
> dbSelectArea(nArea)
> return lRet
>
>
> METHOD Base:setAllCargo()
> local cCookie := ::GetVar("SID")
> local lRet := FALSE
> local nArea := Select()
> local cAlias := "SESSION"
> local j:= 0, i := 0
> LOCAL aVars := ::GetAllVars(), x1, x2
>
> IF EMPTY(cCookie)
> cCookie := ::SessionID
> ENDIF
>
> if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie))
> FOR j := 1 TO LEN(aVars)
> x1 := aVars[j,1]
> x2 := aVars[j,2,1]
> if !(x1 == NIL)
> x1 := ALLTRIM(x1)
> if !ValType(::cargoData) == 'A'
> ::cargoData := {}
> else
> i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } )
> endif
> if i == 0
> aadd(::cargoData, {x1,x2})
> else
> ::cargoData[i][2] := x2
> endif
> endif
> NEXT
>
> if (cAlias)->(RecLock())
> (cAlias)->DATA := Var2Bin(::cargoData)
> (cAlias)->(dbrUnlock())
> lRet := TRUE
> endif
> ENDIF
>
> dbSelectArea(nArea)
> return lRet
>
>
> METHOD Base:getCargo(cName)
> local xRet
> local i
>
> if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A'
> cName := ALLTRIM(cName)
> if (i := AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } )) > 0
> xRet := ::cargoData[i][2]
> endif
> elseif cName == NIL
> xRet := ::cargoData
> endif
>
> return xRet
>
> METHOD Base:getAllCargo()
> return ::cargoData
>
> METHOD Base:IsCargo(cName)
> local lRet := FALSE
>
> if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A'
> lRet := (AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } ) > 0)
> endif
> return lRet
>
> -------------- HELPERS -------------------
>
>
> FUNCTION CreateNewSession(cIpAddr, nExpiry, cUserId)
> LOCAL nSel := SELECT(), cSID := ""
> LOCAL lNewRec := .T., nNewRec := 0
> LOCAL cNow := DTOS(DATE()) + STRTRAN(TIME(), ":", "" )
>
> DEFAULT cIpAddr TO ""
> DEFAULT nExpiry TO EXPIRE_MINUTES
> DEFAULT cUserID TO ""
>
> SELECT SESSION
> IF FilLock(10)
> cSID := RandomPass(64)
>
> Falls Session-ID bereits vorhanden, solange versuchen, bis
> eine neue herauskommt.
> DO WHILE dbSeek( cSID, .F. , "KEY" )
> cSID := RandomPass(64)
> ENDDO
>
> /* Session-Datensatz recyclen */
> IF LASTREC() != 0
> OrdSetFocus("EXPIRES")
> GO TOP
> IF ! EOF() .AND. SESSION->expires < cNow
> IF RecLock(5)
> lNewRec := .F.
> nNewRec := RECNO()
> ENDIF
> ENDIF
> OrdSetFocus("KEY")
> ENDIF
>
> IF lNewRec
> APPEND BLANK
> ELSE
> dbGoto(nNewRec)
> ENDIF
>
> SESSION->KEY := cSID
> SESSION->EXPIRES := CalcExpiration( nExpiry )
> SESSION->ipaddr := cIpAddr
> SESSION->userid := cUserID
> SESSION->data := ""
> SESSION->loggedin := TRUE
> dbUnlock()
>
> ENDIF
>
> SELECT (nSel)
>
> RETURN cSID
>
> FUNCTION CalcExpiration(nMinutes)
> LOCAL cExpiration, nExpire
> LOCAL dExpire := DATE()
>
> nExpire := SECONDS() + nMinutes * 60 Ablaufzeit
>
> Expiration-Zeit bei jedem Zugriff neu berechnen, Tageswechsel
> berücksichtigen
> IF nExpire >= 86400 24 Stunden
> dExpire := dExpire + 1
> nExpire := nExpire - 86400
> ENDIF
>
> nExpire := ROUND(nExpire / 60,0)
>
> cExpiration := DTOS(dExpire) + STRZERO(INT(nExpire/60),2,0) +
> STRZERO(nExpire%60,2,0)
>
> RETURN cExpiration
>
> FUNCTION DeleteSession(cSid)
> LOCAL nSel := SELECT(), lRet := TRUE
> LOCAL cNow := DTOS(DATE()) + "0000"
>
> SELECT SESSION
>
> Session vorhanden?
> IF dbSeek( cSID, .F. , "KEY" )
>
> Logout
> IF RecLock(5)
> SESSION->key := ""
> SESSION->userid := ""
> SESSION->loggedin := FALSE
> SESSION->ipaddr := ""
> SESSION->data := ""
> SESSION->expires := cNow
> dbrUnlock()
> ENDIF
>
> ENDIF
>
> SELECT (nSel)
>
> RETURN lRet
>
> FUNCTION SessionExpired(cSid, oCon)
> LOCAL nSel := SELECT(), lRet := TRUE
> LOCAL cNow := DTOS(DATE()) + SUBSTR(STRTRAN(TIME(), ":", "" ),1,4)
>
> SELECT SESSION
>
> Session vorhanden?
> IF dbSeek( cSID, .F. , "KEY" )
>
> lRet := SESSION->expires < cNow .OR. !SESSION->loggedin .OR.
> !(TRIM(oCon:getRemoteAddr()) = TRIM(SESSION->ipaddr))
>
> IF !lRet
> EVTUSER->(dbSeek(SESSION->userid,.F.,"ID"))
>
> Wenn OK, Session verlängern
> IF RecLock(5)
> SESSION->EXPIRES := CalcExpiration( EXPIRE_MINUTES )
> dbrUnlock()
> ENDIF
> ELSE
> Logout
> DeleteSession(cSid, oCon)
> ENDIF
>
> ENDIF
>
> SELECT (nSel)
>
> RETURN lRet
>
> FUNCTION RandomPass(nLen, cFormat)
> LOCAL nI, cChar, nZahl := 64
> LOCAL cRet := ""
>
> DEFAULT cFormat TO ""
>
> FOR nI := 1 TO nLen
>
> DO WHILE nZahl >= 58 .AND. nZahl <= 64
> nZahl := Random(42)+48
> /*
> Wegen Verwechslungsgefahr 1iI 0Oo entfernen
> */
> IF nZahl == 48 .OR. nZahl == 49 .OR. nZahl == 73 .OR.;
> nZahl == 79 .OR. nZahl == 105 .OR. nZahl == 111
>
> nZahl := 64
>
> ENDIF
> ENDDO
> cChar := CHR(nZahl)
> IF Random(1)=0
> cChar := LOWER(cChar)
> ENDIF
> cRet += cChar
> nZahl := 64
>
> NEXT
>
> IF ! EMPTY(cFormat)
> cRet := Transform( cRet , cFormat )
> ENDIF
>
> RETURN cRet
> |