Subject |
Re: PasswordMaskEF.cc bug? |
From |
Peter Hegelbach <peter@hegelbach.com> |
Date |
Wed, 21 Feb 2024 08:23:55 +0100 |
Newsgroups |
dbase.getting-started |
Attachment(s) |
Pic.png, Testform.wfm, Password.cc, PasswordHidden.ico, PasswordVisible.ico |
I can confirm that.
I have therefore slightly modified Password.cc in this respect. I also
added the possibility to hash the password and a reveal function for the
input.
Where I got stuck: Support for Ctrl-C, pasting the password from the
clipboard.
Peter
| ** END HEADER -- Diese Zeile nicht entfernen
//
// Erstellt am 20.02.24
//
parameter bModal
local f
f = new TestformForm()
if (bModal)
f.mdi = false // Nicht-MDI festlegen
f.ReadModal()
else
f.Open()
endif
class TestformForm of FORM
set procedure to :hitTime2:Password.cc
with (this)
metric = 6 // Pixel
height = 423.0
left = 800.0
top = 0.0
width = 336.0
text = ""
endwith
this.E_Passwort = new ph_PasswordEntry(this)
with (this.E_Passwort)
left = 94
top = 133
width = 180
value = ""
maxLength = 20
fontBold = true
endwith
endclass
| // Metric: Should work with both Character and Pixels
/*
------------------------------------------------------------------
Password.cc
Author: Ken Mayer
Date..: Spring, 1998
August 21, 2001
April 2, 2003
January 27, 2004
June 15, 2004 -- minor little fixes ...
July 15, 2004 -- added captureEnter property, see below.
A variation on the Password CC that shipped with Visual dBASE 5.x
The original code is 1994 (c) Borland.
August, 2001: Added caseSensitive and minLength properties,
and code in isEnteredPasswordOK and LostFocus events
to check for these values.
April, 2003: Modified so that the IsEnteredPasswordOk event
increments the numtries and then compares against
numTriesAllowed, and if so, it sets the stopTrying property
to true. This allows a form to close if using canClose
event, and so on. See TestPass.wfm
Check details in code, including LostFocus ...
January, 2004: Added the CheckSelect API function prototype to
detect if the user has selected any text and use this as a
basis to determine the behavior of the Backspace and Delete
keys.
Modifications to this class done by Marc Hamelin.
Original code from Marc VdB.
April, 2005: Added captureEnter property and code in Procedure 'Key'
to ignor or capture 'ENTER' key activity -- when captureEnter is True
and PasswordEntry length is 0; an Alert message is generated the
same as XML Command LOGOUT's password behavior. -- Wes Rue
June, 2015: In IsValidChar method, added a new case to allow
for some symbols currently used in many passwords, such as:
!@#$%^&* ... Also updated the password Mask and font for
this control to use Wingdings font, and better character
than the asterisk.
July, 2018: minor change to code to deal with activeControl issues
(if a users presses enter, focus is not where it should be ...).
Change to code at top of LostFocus event handler, suggestion
by Mervyn Bick.
Usage:
set procedure to password.cc additive
// in the form designer, place an instance
// of the password entryfield onto the form
// set the "correctPassword" to a value
// in some fashion, you probably do not want
// to hardcode it, but ... when you know the
// userid, get the password from the same
// place, and assign it:
form.passwordEntry1.setCorrectPassword( form.rowset.fields["password"].value )
// set the number of retries allowed:
form.passwordEntry1.numTriesAllowed = 3 // (default)
==========================================================
NOTE: See detailed examples in TESTPASS.WFM and
CHNGPASS.WFM in the dUFLP library ...
==========================================================
-- Custom Properties:
maskChar -- character that appears instead of what's typed in
Defaults to "*", but with some creativity, you
could change it to just about anything. You may
want to consider using a dingbat font, for example,
(setting the passwordEntryfield's fontName) and
using a character from that ...
numTriesAllowed -- defaults to 3, but can be changed
programmatically -- if you set it to 0,
there won't be a check -- the user can keep
trying ...
numTries -- do not try to change this -- it's the counter
used to determine if the number of tries allowed
has been reached.
stopTrying -- logical value used to exit ... [defaults to false]
validCheckRequired -- logical, defaults to true --
used for such situations as a change password form
caseSensitive -- defaults to false -- if it's false, then
all tests compare against the uppercase version
of the values, if it is true, then we do not,
and a value of "TeSt" will not be equal to "Test",
which will not be equal to "test", or "TEST".
minLength -- minimum length of password -- defaults to zero,
which means no error checking for length will
occur.
captureEnter -- defaults to false -- if it is false, then 'ENTER'
key is ignored; if it is true, pressing 'ENTER' key
when PasswordEntry length is 0 generates Alert message
the same as XML Command LOGOUT's password behavior.
maskChar -- with the font set to WingDings, this uses
the chr() function to put specific characters
into the mask:
chr( 108 ) Solid Dot
chr( 116 ) diamond shape
chr( 117 ) slightly fatter diamond shape
chr( 118 ) diamond made of smaller diamonds
("star bullet" in Word)
chr( 173 ) eight pointed star
And of course you could try a wide variety of other symbols ...
// See Example: TestPass.wfm (which uses ChngPass.wfm)
------------------------------------------------------------------
*/
#define BACKSPACE_KEY 8 // -- ASCII value of Backspace key
#define DELETE_KEY 127 // -- ASCII value of Delete key
#define ENTER_KEY 13 // -- ASCII value of Enter key
class ph_PasswordEntry( oParent ) of Entryfield( oParent ) custom
with( this )
Border := true
PageNo := 1
alignVertical := 1 // Mitte
systemtheme := false
borderStyle := 4
fontName := "Wingdings"
fontBold := true
fontSize := 10
Value := ""
Top := 3
onOpen := class::onOpen
key := class::key
onLostFocus := CLASS::LostFocus
endwith
// protect correctPassword, NumTries // geschützte eigenschaften, nur in dieser und davon abgeleiteten Klassen lesbar
// Add custom properties
this.correctPassword = ""
this.maskChar = chr(164) // mask character
this.CamouflageOn = true // added PH 05.01.24
this.numTriesAllowed = 3 // number of tries allowed
this.numTries = 0 // counter ...
this.stopTrying = false
this.validCheckRequired = true
this.minLength = 0
this.captureEnter = false
// Add ph für Hash-Code statt unverschlüsseltes Passwort
this.correctHash = null
*-------------------------------------------------------------------------------
function formatButton
privat cBtnName, c, cn
cBtnName = "Btn_" + trim(this.Name)
c = "this.parent." + cBtnName // parent is normally the form
cn = c + " = new Image(this.parent)"
&cn
cn = c + ".owner = this"
&cn
with (&c)
group = true
top = this.top + 2
left = this.left + this.width + 4
height = 18
width = 23
onLeftMouseUp = class::Change_Camouflage
pageNo = this.PageNo
borderStyle = 3
datasource = "filename PasswordHidden.ico"
tabStop = false
transparent = true
endwith
return
*-------------------------------------------------------------------------------
function Change_Camouflage
if this.owner.CamouflageOn = true
this.owner.CamouflageOn = false
this.datasource = "filename :hitTime2:PasswordVisible.ico"
this.owner.fontName = CONTROLFONTNAME
this.owner.value = this.owner.enteredPassword
else
this.owner.CamouflageOn = true
this.datasource = "filename :hitTime2:PasswordHidden.ico"
this.owner.fontName = "Wingdings"
this.owner.value = repl(this.owner.maskChar, len(this.owner.enteredPassword))
endif
return
*-------------------------------------------------------------------------------
function onDesignOpen( lFromPalette )
if lFromPalette and form.metric == 6 // Pixels
this.height := 22
this.width := 105
endif
return
*-------------------------------------------------------------------------------
function OnOpen(nChar, nPosition)
// Declare the CheckSelect API function prototype
if Type("CheckSelect") # "FP"
extern CLONG CheckSelect(CHANDLE, CUINT, ;
CPTR CLONG, CPTR CLONG) user32 from "SendMessageA"
endif
// Custom properties
if type("this.enteredPassword") = "U"
this.enteredPassword = ""
this.correctPassword = "" // This property must be manually assigned
// whereever this control is instantiated
endif
this.formatButton()
this.PasswordValid = false
return
*-------------------------------------------------------------------------------
function SetCorrectPassword(password)
this.correctPassword = password
this.enteredPassword = ""
return
*-------------------------------------------------------------------------------
function SetCorrectHashCode(HashCode)
this.correctHash = HashCode
this.enteredPassword = ""
return
*-------------------------------------------------------------------------------
function IsEnteredPasswordOk
private cPass
cPass = this.enteredPassword
if this.correctPassword = null // alte Version, mit unverschlüsseltem Passwort
this.PasswordValid = iif(cPass == cCorrect, true, false)
else // Version mit HashCode
local h
h = new hash()
this.PasswordValid = iif(h.cmpr(cPass, this.correctHash), true, false)
endif
this.numTries++ // increment counter
if this.numTries => this.numTriesAllowed
this.stopTrying := true
endif
return ( this.PasswordValid )
*-------------------------------------------------------------------------------
procedure Key(nChar, nPosition)
// Handles keys entered in the password entryfield
private enteredChar, returnValue
// Get the character positions of the selected text (if any)
local SelectStartPos, SelectEndPos
SelectStartPos = 0
SelectEndPos = 0
CheckSelect(this.hwnd,0xB0,SelectStartPos,SelectEndPos)
enteredChar = chr(nChar)
returnValue = true // By default output whatever key was typed
do case // Check for keys that modify the value
case nChar = BACKSPACE_KEY
// if the text is not selected, delete the previous character,
// if the text is selected, delete the characters corresponding
// to the selection range
if SelectStartPos == 0 AND SelectEndPos == 0
this.enteredPassword = ;
stuff(this.enteredPassword, nPosition - 1, 1, "")
else
this.enteredPassword = ;
stuff(this.enteredPassword, SelectStartPos + 1, ;
SelectEndPos - SelectStartPos, "")
endif
case nChar = DELETE_KEY
// if the text is not selected, delete the current character,
// if the text is selected, delete the characters corresponding
// to the selection range
if SelectStartPos == 0 AND SelectEndPos == 0
this.enteredPassword = ;
stuff(this.enteredPassword, nPosition, 1, "")
else
this.enteredPassword = ;
stuff(this.enteredPassword, SelectStartPos + 1, ;
SelectEndPos - SelectStartPos, "")
endif
case nChar = ENTER_KEY
if this.captureEnter AND this.enteredPassword.length = 0
msgbox( "Kein Passwort eingegeben!","Warnung!", 48 )
this.setFocus()
endif
otherwise
if class::IsValidChar(enteredChar) // Check if alphanumeric
// if the text is not selected, insert the character entered,
// if the text is selected, overwrite the characters
// corresponding to the selection range with the character
// entered
if SelectStartPos == 0 AND SelectEndPos == 0
this.enteredPassword = ;
stuff(this.enteredPassword, nPosition, 1, enteredChar)
else
this.enteredPassword = ;
stuff(this.enteredPassword, SelectStartPos + 1, ;
SelectEndPos - SelectStartPos, enteredChar)
endif
if this.camouflageOn
returnValue = asc( this.maskChar ) // Output camouflage character
else
returnValue = nChar
endif
else
returnValue = false
? chr(7) // Beep
endif
endcase
return returnValue
*-------------------------------------------------------------------------------
procedure IsValidChar(char)
// Make sure entered key is alphanumeric
private returnValue
do case
case isalpha(char) // Letter a-z, A-Z
returnValue = true
case char >= "0" and char <= "9" // Digit?
returnValue = true
// if you need to allow others, add them here:
case char $ "!#$%&()*+-./:<=>?@[]_" // Special characters
returnValue = true
otherwise // Invalid?
returnValue = false
endcase
return returnValue
endclass
|
|