-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathl7auth.PRG
More file actions
363 lines (319 loc) · 11.9 KB
/
l7auth.PRG
File metadata and controls
363 lines (319 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
* L7Auth.PRG
#INCLUDE L7.H
*** ========================================================= ***
define class L7AuthLog AS SESSION
cErrorMsg = ""
cLogFile = ""
cLogAlias = "" && see ACCESS method
cPreviousLogAlias = ""
cLogFilePath = ".\" && see ACCESS method, also often assigned by Application
cLogFilePrefix = "L7AuthLog"
lDailyLogs = .T.
lQuarterlyFolders = .T. && separates logs into different subfolders by quarter, eg: ..\LogFiles\2006-Q1\
lCloseLog = .F.
* 05/15/2010: inserted UserToken C(32) to allow link to Session table.
cStructureBase = [Time T, App C(10), ClassName C(32), Lockout L, IpAddress C(15), User_ID C(20), Success L, Reason C(32), Removed L]
cStructureAugment = [UserToken C(32)] && easier override
* --------------------------------------------------------- *
function cLogAlias_ACCESS
return this.GetLogAlias(date())
endfunc
* --------------------------------------------------------- *
function GetLogAlias(tdDate)
local ldDate
ldDate = EVL(m.tdDate, DATE()) && default to today
return this.cLogFilePrefix + ;
iif(THIS.lDailyLogs, [_] + dtos(m.ldDate), [])
* e.g., "L7AuthLog_20020301" on 03/01/2002.
endfunc
* --------------------------------------------------------- *
function cPreviousLogAlias_ACCESS
return this.cLogFilePrefix + ;
iif(this.lDailyLogs, [_] + dtos(date() - 1), [])
endfunc
* --------------------------------------------------------- *
*[[ DRY w/ L7LogRequest--inherit from a new base?
function GetLogFilePath(tdDate)
local lcPath, ldDate
ldDate = evl(m.tdDate, date()) && default to today
lcPath = iif( empty( this.cLogFilePath), [.\], addbs(this.cLogFilePath))
if this.lQuarterlyFolders
* "Yes", this seems rather specific for a framework, but as quarterly
* is a good minimum forensic retention period, a default that facilitates
* quarterly log maintenance is worthwhile.
lcPath = addbs(m.lcPath + ;
str(year(m.ldDate), 4, 0) + "-Q" + str(ceiling(month(m.ldDate) / 3), 1, 0))
* e.g., path.../2006-Q1/
endif
return m.lcPath
endfunc && GetLogFilePath
* --------------------------------------------------------- *
function DESTROY
this.CloseLog()
endfunc
* --------------------------------------------------------- *
function INIT(lcBaseName)
do StandardVfpSettings && 9/1/09, required for new private datasession
if !empty(m.lcBaseName)
this.cLogFilePrefix = m.lcBaseName
endif
endfunc
* --------------------------------------------------------- *
function Log(toAuth) && Template Method
local lnSelect, llRet
lnSelect = select()
if this.OpenLog()
this.AddEntry(m.toAuth)
this.AfterAddEntry(m.toAuth) && hook
this.OptionalCloseLog()
llRet = .T.
endif
select (m.lnSelect)
return m.llRet
endfunc && Log
* --------------------------------------------------------- *
function AddEntry(toAuth)
LOCAL lnSelect, llIsRequest, llIsPage, loNewRec
llIsPage = VARTYPE( m.Page) = "O"
llIsRequest = VARTYPE( m.Request) = "O"
lnSelect = SELECT()
select (this.cLogAlias)
scatter memo blank name loNewRec
with m.loNewRec
.className = upper(toAuth.class)
.lockout = toAuth.lLockoutTrigger
.success = toAuth.lSuccess
.app = goL7App.cApplication
if !isnull(toAuth.cUser_Id)
.User_id = upper(toAuth.cUser_Id)
endif
if m.llIsPage
.Time = Page.tNow
else
.Time = DATETIME()
endif
if m.llIsRequest
.IpAddress = Request.cIpAddress
endif
this.AddAugmentedData(m.loNewRec, m.toAuth)
endwith
insert into (this.cLogAlias) from name m.loNewRec
select (m.lnSelect)
return
endfunc && AddEntry
* --------------------------------------------------------- *
function AddAugmentedData(toRec, toAuth)
endfunc
* --------------------------------------------------------- *
function AfterAddEntry(toAuth)
this.checkLockout(m.toAuth)
return
endfunc
* --------------------------------------------------------- *
function checkLockout(toAuth)
local lnSelect, lnCount
if toAuth.lLockoutTrigger and !isnull(toAuth.cUser_Id) and !empty(toAuth.cUser_Id)
lnSelect = select()
select (this.cLogAlias)
count for Removed = .f. and Lockout = .t. and User_ID = toAuth.cUser_Id and Time > datetime() - (60 * L7_AUTH_LOCKOUT_WINDOW) ;
to lnCount
if m.lnCount >= L7_AUTH_LOCKOUT_ATTEMPTS
toAuth.lLockout = .t. && passes suggestion back to App (nothing can be done here now, as Account gets altered)
endif
*!* #DEFINE L7_AUTH_LOCKOUT_ATTEMPTS 5
*!* #DEFINE L7_AUTH_LOCKOUT_WINDOW 15
*!* #DEFINE L7_AUTH_LOCKOUT_CLEAR 60
endif
return
endfunc
* --------------------------------------------------------- *
function OpenLog
local lcAlias, llRet, lcPath
llRet = .T.
lcAlias = THIS.GetLogAlias()
if !used(m.lcAlias)
* cLogFileAlias is created from an ACCESS method, so that
* daily log files are easily handled.
this.ClosePreviousLog()
lcPath = this.GetLogFilePath()
if !file( addbs(m.lcPath) + m.lcAlias + ".dbf")
llRet = this.CreateLog()
ENDIF
if m.llRet
use (addbs(m.lcPath) + m.lcAlias + ".dbf") again shared in 0
endif
endif
return m.llRet
endfunc && OpenLog
* --------------------------------------------------------- *
function CreateLog
local lnSelect, lcFile, lcStru, llRet, loExc
lnSelect = select()
try
lcStru = THIS.GetStructure()
lcFile = THIS.GetLogFileName()
select 0
create table (m.lcFile) free (&lcStru)
use
llRet = .T.
catch TO loExc
llRet = .F.
this.cErrorMsg = "CreateLog: " + loExc.Message
finally
select (m.lnSelect)
endtry
RETURN m.llRet
endfunc && CreateLog
* --------------------------------------------------------- *
function GetStructure
local lcStru
lcStru = this.cStructureBase
if !empty(this.cStructureAugment)
lcStru = this.cStructureAugment + [,] + m.lcStru
endif
return m.lcStru
endfunc
* --------------------------------------------------------- *
function GetLogFileName
local lcRet
lcRet = this.GetLogFilePath()
if !directory(m.lcRet)
mkdir (m.lcRet)
endif
lcRet = addbs(m.lcRet) + forceext(this.cLogAlias, ".dbf")
return m.lcRet
endfunc
* --------------------------------------------------------- *
function CloseLog
use in select( this.cLogAlias)
endfunc
* --------------------------------------------------------- *
function OptionalCloseLog
if this.lCloseLog
this.CloseLog()
endif
return
endfunc
* --------------------------------------------------------- *
function ClosePreviousLog
if this.lDailyLogs
use in select( this.cPreviousLogAlias)
endif
return
endfunc
* --------------------------------------------------------- *
enddefine && L7AuthLog
*** ================================================ ***
define class L7AuthBehavior as Custom
lAttempt = .t. && yes unless otherwise selected
lSuccess = .f. && fails unless otherwise selected
lLogOutcome = .f.
cReason = "No reason"
cMessageToUser = null && if null, show them cReason
lLockoutTrigger = .f. && if .t., this event can lead to lockouts
lLockout = .f. && if .t., lockout suggested (if supported)
nSeverity = L7_SEVERITY_DEBUG
cUser_Id = null && app can populate with user's attempt (or actual)
* ------------------------------------------------ *
function cMessageToUser_access
return nvl(this.cMessageToUser, this.cReason)
endfunc
* ------------------------------------------------ *
function INIT(tcUser_Id)
if vartype(m.tcUser_Id) = 'C' && not all behaviors know
this.cUser_Id = m.tcUser_Id
endif
return
endfunc && INIT
enddefine
* L7Page.cLoginFailureMessage had:
* "User ID not found and/or incorrect password! (Note: Passwords are case-sensitive.)"
*** ================================================ ***
define class L7AuthNoAttempt as L7AuthBehavior
lAttempt = .f. && typical for public access
lSuccess = .f.
cReason = "No login attempted"
enddefine
*** ================================================ ***
define class L7AuthSuccess as L7AuthBehavior
lSuccess = .t.
lLogOutcome = .t.
cReason = "Successful login"
enddefine
*** ================================================ ***
define class L7AuthBadForm as L7AuthBehavior
cReason = "Invalid login form"
enddefine
*** ================================================ ***
define class L7AuthNoUser as L7AuthBehavior
cReason = "User ID not entered"
enddefine
*** ================================================ ***
define class L7AuthNoPassword as L7AuthBehavior
cReason = "Password not entered"
enddefine
*** ================================================ ***
define class L7AuthBadAttempt as L7AuthBehavior && abstract subclass
cMessageToUser = "User ID not found and/or incorrect password! (Note: Passwords are case-sensitive.)"
lLogOutcome = .t.
nSeverity = L7_SEVERITY_INFO && can be escalated if lockout occurs
enddefine
*** ================================================ ***
define class L7AuthBadUser as L7AuthBadAttempt && inherits generic message to user
cReason = "User not found"
enddefine
*** ================================================ ***
define class L7AuthAmbiguousUser as L7AuthBadAttempt && inherits generic message to user
cReason = "User token not unique, cannot identify user"
nSeverity = L7_SEVERITY_NOTICE
enddefine
*** ================================================ ***
define class L7AuthBadPassword as L7AuthBadAttempt && inherits generic message to user
lLockoutTrigger = .T.
cReason = "Incorrect password"
enddefine
*** ================================================ ***
define class L7AuthIPUnallowed as L7AuthBehavior
cReason = "User not connecting from approved network!"
lLogOutcome = .t.
enddefine
*** ================================================ ***
define class L7AuthAccountRevoked as L7AuthBehavior
cReason = "Account has been revoked!"
nSeverity = L7_SEVERITY_NOTICE
lLogOutcome = .t.
enddefine
*** ================================================ ***
define class L7AuthAccountDisabled as L7AuthBehavior
cReason = "Account is disabled!"
lLogOutcome = .t.
nSeverity = L7_SEVERITY_NOTICE
enddefine
*** ================================================ ***
define class L7AuthAccountInactive as L7AuthBehavior
cReason = "Inactive account!"
lLogOutcome = .t.
nSeverity = L7_SEVERITY_NOTICE
enddefine
*** ================================================ ***
define class L7AuthPasswordExpired as L7AuthBehavior
enddefine
*** ================================================ ***
define class L7AuthAccountLockout as L7AuthBehavior
cReason = "Account is locked out!"
* note no severity bump--we notify *when* the lockout occurs, not on each hit thereafter
enddefine
*** ================================================ ***
define class L7AuthAccountExpired as L7AuthBehavior
cReason = "Account has expired!"
nSeverity = L7_SEVERITY_NOTICE
lLogOutcome = .t.
enddefine
*** ================================================ ***
define class L7AuthOther as L7AuthBehavior
cReason = "Other login failure" && set to better string in app
nSeverity = L7_SEVERITY_WARNING && mainly because it's unexpected (I think)
lLogOutcome = .t.
enddefine
*** ================================================ ***
*end: L7Auth.PRG