Password entry and validation
November 21, 1999
by Pat McClellan
Dear Multimedia Handyman,
The program that I am desiging has the user put in a password, then on another screen they are asked to verify that password by typing it in again. If they get it wrong they are sent to another screen if they get it right they are sent in another direction. Please help. One more thing, when the user types in the password the first and second time the password field is protected (meaning * appear on the screen instead of letters).
sheri kord
Dear sheri,
Let's take care of the basic concepts first, then we'll get around to writing the actual code. When you have an editable field or text member on the screen, the data which is entered into it is in a format called "string". That just means that it's a string of characters, including spaces and punctuation marks if there are any. Try this experiment in Director's message window:
password = "SHERI" put password -- "SHERI" confirmWord = "sheri" put confirmWord -- "sheri" put password = confirmWord -- 1
This bit of code indicates that Director is not case sensitive when comparing strings. That could be particularly important in this situation because passwords are sometimes case sensitive. Lingo does provide a way to distinguish the case of the letters: charToNum. Take a look:
put charToNum("A") -- 65 put charToNum("a") -- 97
As you can see, the number (the ASCII value) is differs for upper case and lower case. Although it'll be a little more tedious, we can create a test for case sensitive matches as well.
Either way, we'll need to create a way of storing the entered password for later comparison. We have several choices about how we do this. Perhaps the easiest method would be to use a global variable -- which for this example might logically be named gPassword. When the user enters an initial password, that string would be saved in the global variable. Then, later in the program when they need to confirm the password, gPassword would be easily accessible for comparison.
Object-oriented Lingo purists would probably prefer to not have the value stored in a global variable. Why? Because it's a value which doesn't need to be widely accessed, so why not encapsulate the data. The object-oriented approach would be to create a password object with methods to store the password in a property of that object and then later compare another string to it. The password would not be "exposed", but rather would be encapsulated within the object. While there's merit to this way of thinking, if you're a beginner, there's little harm in using the global variable approach instead.
Yet another alternative occurs to me. Although you don't say so in your question, in many applications, you'll want to be able to save the user's password so that they can quit the program and then use the same password later. This is very easy to do, using setPref and getPref. I think this might be the most versatile approach to the objective. So here's the overview:
- As the user types a password into an editable field member, a behavior on that sprite changes the entered characters to asterisks while the actual characters are stored as a property of that behavior.
- When a button is pressed, a message is sent to the field sprite to save the password.
- The behavior on the field sprite receives the message and saves the password on the user's computer using segPref.
- Later, when the user enters characters into a field member to confirm the password, the same behavior converts the letters to asterisks.
- When a button is pressed, a message is sent to the field sprite's behavior to confirm the password.
- The behavior on the field sprite receives the message, retrieves the original password from the user's computer using getPref, then compares the two values (either case sensitive or not.)
- If the two values match, the behavior will go to a particular frame, and if they don't match, it will go to another frame.
A sample movie is available for download in Mac or PC format. This is a D7 movie.
Let's start with the behavior on the field sprite. It will need handlers to convert characters to asterisks while storing the original characters, save the entered text as a password, retreive the saved password, and compare the two strings. We'll need the author to specify the frames to go to for a match or no match. We'll also add the options to make it a case sensitive validation, give the author the choice of replacement characters (asterisk by default), and allow the author to choose the prefs filename. Here's what the dialog box will look like when it's dropped on a field or text sprite:
You'll see that I've also included a field where you can specify the valid characters. In this demo, the available characters are the letters A-Z. If you wanted, you could also include numerals, but you'd probably not want to include punctuation or special characters. Here's the code for this behavior:
-- passwordEntry behavior -- copyright © 1999, ZZP Online, LLC -- free use to Director Online readers property pText property pValidChars property pPrefs property pSlug property pCaseSensitive property pMatchFrame, pNoMatchFrame property pMem on getPropertyDescriptionList me set pdlist to [:] addprop pdlist, #pValidChars, [#comment:"Valid chars", ¬ #format:#string, #default:"abcdefghijklmnopqrstuvwxyz"] addprop pdlist, #pPrefs, [#comment:"Prefs filename - 8 char ¬ max:", #format:#string, #default:"password"] addprop pdlist, #pSlug, [#comment:"Substitute character:", ¬ #format:#string, #default:"*"] addprop pdlist, #pCaseSensitive, [#comment:"Case sensitive?", ¬ #format:#boolean, #default:0] addprop pdlist, #pMatchFrame, [#comment:"Match frame:", ¬ #format:#marker, #default:0] addprop pdlist, #pNoMatchFrame, [#comment:"No match frame:", ¬ #format:#marker, #default:0] return pdlist end getPropertyDescriptionList on beginSprite me pMem = sprite(me.spriteNum).member pText = "" pMem.text = "" end on keyDown me if the key = BACKSPACE then delete the last char of pText else if the key = TAB then pass else if pValidChars contains the key then put the key after pText end if displayAsterisks me end on displayAsterisks me charCount = pText.char.count displayString = "" repeat with i = 1 to charCount put pSlug after displayString end repeat pMem.text = displayString the selStart = charCount the selEnd = charCount end displayAsterisks on savePassword me setPref(pPrefs, pText) end on validatePassword me password = getPref(pPrefs) if pText = password then status = #match else status = #noMatch end if if pCaseSensitive and status = #match then charCount = pText.char.count repeat with whichChar = 1 to charCount x = charToNum(pText.char[whichChar]) y = charToNum(password.char[whichChar]) if x <> y then status = #noMatch exit repeat end if end repeat end if if status = #match then go to frame pMatchFrame else go to frame pNoMatchFrame end if end
You'll see that I have a keyDown handler. That may be unfamiliar to some of you. That's the handler which intercepts the user's key stroke and processes it before the letter appears in the editable field or text sprite. This handler allows BACKSPACE and TAB to pass without processing, but all other keys are checked to see if they're included in the string of valid characters. If so, then the key is appended to our pText property, and an asterisk (or other selected character) is displayed in its place.
The savePassword handler is fairly simple so I won't explain it..
The validatePassword handler starts by retrieving the password from the prefs file. Then it proceeds to do a simple string comparison (not case sensitive). If the strings do match and the author has specified case sensitive validation, then the ASCII value of each character is compared to the stored password. If any letter is not a match, then we skip the rest of the comparison. After that, it we still have a match, then we go to the pMatchFrame, else we go to the pNoMatchFrame.
All that's left is commanding this behavior to do its stuff. For that, I made this little password commands behavior which is attached to the buttons:
-- password commands behavior -- copyright © 1999, ZZP Online, LLC -- free use to Director Online readers property pWhichSprite, pCommand on getPropertyDescriptionList me commandList = [#savePassword, #validatePassword] pdlist = [:] addprop pdlist, #pWhichSprite, [#comment:"Which sprite?", ¬ #format:#integer, #default:1] addprop pdlist, #pCommand, [#comment:"Which command?", ¬ #format:#symbol, #default:#savePassword, #range:commandList] return pdlist end getPropertyDescriptionList on mouseUp me sendSprite pWhichSprite, pCommand end
That's all there is to passwords. If you don't need the ability for users to choose their own password, I recommend the password behavior that comes with D7. Darrel Plant wrote this great behavior which allows the author to specify the password when you're creating the movie.
Good luck with your program.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.