Courtesy Review

G

Greg Maxey

I have been tinkering with some code for requiring a more robust password
for Word documents. I am not a computer security expert so my use of "more
robust" may be laughable. The goal is to require a password from 5 to 10
characters long that includes at least one upper case letter, lower case
leter, number and special character. The code shown below seems to be
working as desired. It was put together piecemeal without a lot of thought
given to the clearest path from A to Z. It could be fraught with poor
practices, inefficiencies, or even clear no-nos.

Appreciate any constuctive critical comments that would point any of those
out. Thanks.

Sub SetRobustPassword()
Dim pStr As String
Dim pStrConfirm As String
Dim bValidated As Boolean
Dim i As Long
Dim pInvalidStr As String
Dim bValidLen As Boolean, bUC As Boolean, bLC As Boolean, bNum As Boolean,
bSC As Boolean
Do
'Set all boolean variables to false
bValidated = False: bValidLen = False: bUC = False: bLC = False: bNum =
False: bSC = False
'Define the password
pStr = InputBox("Enter your password." & vbCr + vbCr & "The password " _
& "must contain 5 to 10 characters." & vbCr & vbCr & "It must
include one upper case " _
& "character, one lower case character, one numerical character, "
_
& "and one of the following special characters: @#$%*.", _
"Create Password")
If StrPtr(pStr) = 0 Then Exit Do 'User clicked cancel
'Validate password meets parameters
For i = 1 To Len(pStr)
'Verify proper length
If i = 1 Then
Select Case Len(pStr)
Case Is < 5
MsgBox "Your password is too short. The password must contain
between five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password Invalid"
Exit For
Case Is > 10
MsgBox "Your password is too long. The password must contain
between five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password Invalid"
Exit For
Case Else
bValidLen = True
End Select
End If
'When condition met set boolean variable to true
If Mid(pStr, i, 1) Like "[A-Z]" Then bUC = True
If Mid(pStr, i, 1) Like "[a-z]" Then bLC = True
If Mid(pStr, i, 1) Like "[0-9]" Then bNum = True
If InStr("@#$%*", Mid(pStr, i, 1)) > 0 Then bSC = True
'If all conditions met set boolean variable to true
If bUC And bLC And bNum And bSC Then
bValidated = True
Exit For
End If
Next i
'Notify user of invalid conditions
If Not bValidated And bValidLen Then
pInvalidStr = "The password must contain"
If Not bUC Then pInvalidStr = pInvalidStr & " at least one UCase
character,"
If Not bLC Then pInvalidStr = pInvalidStr & " at least one LCase
character,"
If Not bNum Then pInvalidStr = pInvalidStr & " at least one numerical
character,"
If Not bSC Then pInvalidStr = pInvalidStr & " at least one special
character (@#$%*),"
'Clean up the string
pInvalidStr = Left(pInvalidStr, Len(pInvalidStr) - 1) & "."
On Error Resume Next
pInvalidStr = Left(pInvalidStr, InStrRev(pInvalidStr, ",") - 1) & " and"
& Mid(pInvalidStr, InStrRev(pInvalidStr, ",") + 1)
MsgBox pInvalidStr
pInvalidStr = ""
'Confirm valid passwords
ElseIf bValidated Then
pStrConfirm = InputBox("Re-enter your password.", "Confirm Password")
If pStrConfirm <> pStr Then
bValidated = False
MsgBox "Your password was not confirmed. Please try again.",
vbInformation + vbOKOnly, "Confirmation Failed"
End If
End If
Loop Until bValidated
'Set password
If bValidated Then
'ActiveDocument.Password = pStr 'STETTED for testing purposes.
'ActiveDocument.Save
MsgBox "Your password has been set."
End If
End Sub

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the strong
man stumbles, or where the doer of deeds could have done them better. The
credit belongs to the man in the arena, whose face is marred by dust and
sweat and blood, who strives valiantly...who knows the great enthusiasms,
the great devotions, who spends himself in a worthy cause, who at the best
knows in the end the triumph of high achievement, and who at the worst, if
he fails, at least fails while daring greatly, so that his place shall never
be with those cold and timid souls who have never known neither victory nor
defeat." - TR
 
T

Tony Jollans

Hi Greg,

I think you are making it over-complicated with the loop through each
character. You have:

For i = 1 To Len(pStr)
If i = 1 then
' check length
endif
If Mid(pStr, i, 1) Like "[A-Z]" Then bUC = True
' etc.
Next

when all you need is something like:

' Check length
If pStr Like "*[A-Z]*" Then bUC = True
If pStr Like "*[a-z]*" Then bLC = True
If pStr Like "*[0-9]*" Then bNum = True
If pStr Like "*[@#$%*]*" Then bSC = True

and I suspect the rest could be slightly simplified as well

--
Enjoy,
Tony

www.WordArticles.com

Greg Maxey said:
I have been tinkering with some code for requiring a more robust password
for Word documents. I am not a computer security expert so my use of "more
robust" may be laughable. The goal is to require a password from 5 to 10
characters long that includes at least one upper case letter, lower case
leter, number and special character. The code shown below seems to be
working as desired. It was put together piecemeal without a lot of thought
given to the clearest path from A to Z. It could be fraught with poor
practices, inefficiencies, or even clear no-nos.

Appreciate any constuctive critical comments that would point any of those
out. Thanks.

Sub SetRobustPassword()
Dim pStr As String
Dim pStrConfirm As String
Dim bValidated As Boolean
Dim i As Long
Dim pInvalidStr As String
Dim bValidLen As Boolean, bUC As Boolean, bLC As Boolean, bNum As Boolean,
bSC As Boolean
Do
'Set all boolean variables to false
bValidated = False: bValidLen = False: bUC = False: bLC = False: bNum =
False: bSC = False
'Define the password
pStr = InputBox("Enter your password." & vbCr + vbCr & "The password " _
& "must contain 5 to 10 characters." & vbCr & vbCr & "It must
include one upper case " _
& "character, one lower case character, one numerical character, "
_
& "and one of the following special characters: @#$%*.", _
"Create Password")
If StrPtr(pStr) = 0 Then Exit Do 'User clicked cancel
'Validate password meets parameters
For i = 1 To Len(pStr)
'Verify proper length
If i = 1 Then
Select Case Len(pStr)
Case Is < 5
MsgBox "Your password is too short. The password must contain
between five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password
Invalid"
Exit For
Case Is > 10
MsgBox "Your password is too long. The password must contain
between five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password
Invalid"
Exit For
Case Else
bValidLen = True
End Select
End If
'When condition met set boolean variable to true
If Mid(pStr, i, 1) Like "[A-Z]" Then bUC = True
If Mid(pStr, i, 1) Like "[a-z]" Then bLC = True
If Mid(pStr, i, 1) Like "[0-9]" Then bNum = True
If InStr("@#$%*", Mid(pStr, i, 1)) > 0 Then bSC = True
'If all conditions met set boolean variable to true
If bUC And bLC And bNum And bSC Then
bValidated = True
Exit For
End If
Next i
'Notify user of invalid conditions
If Not bValidated And bValidLen Then
pInvalidStr = "The password must contain"
If Not bUC Then pInvalidStr = pInvalidStr & " at least one UCase
character,"
If Not bLC Then pInvalidStr = pInvalidStr & " at least one LCase
character,"
If Not bNum Then pInvalidStr = pInvalidStr & " at least one numerical
character,"
If Not bSC Then pInvalidStr = pInvalidStr & " at least one special
character (@#$%*),"
'Clean up the string
pInvalidStr = Left(pInvalidStr, Len(pInvalidStr) - 1) & "."
On Error Resume Next
pInvalidStr = Left(pInvalidStr, InStrRev(pInvalidStr, ",") - 1) & "
and" & Mid(pInvalidStr, InStrRev(pInvalidStr, ",") + 1)
MsgBox pInvalidStr
pInvalidStr = ""
'Confirm valid passwords
ElseIf bValidated Then
pStrConfirm = InputBox("Re-enter your password.", "Confirm Password")
If pStrConfirm <> pStr Then
bValidated = False
MsgBox "Your password was not confirmed. Please try again.",
vbInformation + vbOKOnly, "Confirmation Failed"
End If
End If
Loop Until bValidated
'Set password
If bValidated Then
'ActiveDocument.Password = pStr 'STETTED for testing purposes.
'ActiveDocument.Save
MsgBox "Your password has been set."
End If
End Sub

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is marred
by dust and sweat and blood, who strives valiantly...who knows the great
enthusiasms, the great devotions, who spends himself in a worthy cause,
who at the best knows in the end the triumph of high achievement, and who
at the worst, if he fails, at least fails while daring greatly, so that
his place shall never be with those cold and timid souls who have never
known neither victory nor defeat." - TR
 
G

Greg Maxey

Tony,

Thanks. That is the exactly the type of thing I was looking for. Revised
code:

Sub SetRobustPassword()
Dim pStr As String
Dim pStrConfirm As String
Dim bValidated As Boolean
Dim i As Long
Dim pInvalidStr As String
Dim bValidLen As Boolean, bUC As Boolean, bLC As Boolean, bNum As Boolean,
bSC As Boolean
Do
'Set all boolean variables to false
bValidated = False: bValidLen = False: bUC = False: bLC = False: bNum =
False: bSC = False
'Define the password
pStr = InputBox("Enter your password." & vbCr + vbCr & "The password " _
& "must contain 5 to 10 characters." & vbCr & vbCr & "It must
include one upper case " _
& "character, one lower case character, one numerical character, "
_
& "and one of the following special characters: @#$%*.", _
"Create Password")
If StrPtr(pStr) = 0 Then Exit Do 'User clicked cancel
'Validate password meets parameters
'Check length
Select Case Len(pStr)
Case Is < 5
MsgBox "Your password is too short. The password must contain between
five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password Invalid"
Case Is > 10
MsgBox "Your password is too long. The password must contain between
five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password Invalid"
Case Else
bValidLen = True
End Select
If bValidLen Then
'When condition met set boolean variable to true
If pStr Like "*[A-Z]*" Then bUC = True
If pStr Like "*[a-z]*" Then bLC = True
If pStr Like "*[0-9]*" Then bNum = True
If pStr Like "*[@#$%*]*" Then bSC = True
'When all conditions met set boolean variable to true
If bUC And bLC And bNum And bSC Then
bValidated = True
End If
'Notify user of invalid conditions
If Not bValidated Then
pInvalidStr = "The password must contain"
If Not bUC Then pInvalidStr = pInvalidStr & " at least one UCase
character,"
If Not bLC Then pInvalidStr = pInvalidStr & " at least one LCase
character,"
If Not bNum Then pInvalidStr = pInvalidStr & " at least one numerical
character,"
If Not bSC Then pInvalidStr = pInvalidStr & " at least one special
character (@#$%*),"
'Clean up the string
pInvalidStr = Left(pInvalidStr, Len(pInvalidStr) - 1) & "."
On Error Resume Next
pInvalidStr = Left(pInvalidStr, InStrRev(pInvalidStr, ",") - 1) & "
and" & Mid(pInvalidStr, InStrRev(pInvalidStr, ",") + 1)
MsgBox pInvalidStr
pInvalidStr = ""
'Confirm valid passwords
ElseIf bValidated Then
pStrConfirm = InputBox("Re-enter your password.", "Confirm Password")
If StrComp(pStr, pStrConfirm, vbTextCompare) <> 0 Then
bValidated = False
MsgBox "Your password was not confirmed. Please try again.",
vbInformation + vbOKOnly, "Confirmation Failed"
End If
End If
End If
Loop Until bValidated
'Set password
If bValidated Then
'ActiveDocument.Password = pStr
ActiveDocument.Save
MsgBox "Your password has been set."
End If
End Sub

I look it over again in a day or so to see if I can find more places to
simplify. Thanks.

Tony said:
Hi Greg,

I think you are making it over-complicated with the loop through each
character. You have:

For i = 1 To Len(pStr)
If i = 1 then
' check length
endif
If Mid(pStr, i, 1) Like "[A-Z]" Then bUC = True
' etc.
Next

when all you need is something like:

' Check length
If pStr Like "*[A-Z]*" Then bUC = True
If pStr Like "*[a-z]*" Then bLC = True
If pStr Like "*[0-9]*" Then bNum = True
If pStr Like "*[@#$%*]*" Then bSC = True

and I suspect the rest could be slightly simplified as well


Greg Maxey said:
I have been tinkering with some code for requiring a more robust
password for Word documents. I am not a computer security expert so
my use of "more robust" may be laughable. The goal is to require a
password from 5 to 10 characters long that includes at least one
upper case letter, lower case leter, number and special character. The
code shown below seems to be working as desired. It was put
together piecemeal without a lot of thought given to the clearest
path from A to Z. It could be fraught with poor practices,
inefficiencies, or even clear no-nos. Appreciate any constuctive critical
comments that would point any of
those out. Thanks.

Sub SetRobustPassword()
Dim pStr As String
Dim pStrConfirm As String
Dim bValidated As Boolean
Dim i As Long
Dim pInvalidStr As String
Dim bValidLen As Boolean, bUC As Boolean, bLC As Boolean, bNum As
Boolean, bSC As Boolean
Do
'Set all boolean variables to false
bValidated = False: bValidLen = False: bUC = False: bLC = False:
bNum = False: bSC = False
'Define the password
pStr = InputBox("Enter your password." & vbCr + vbCr & "The
password " _ & "must contain 5 to 10 characters." & vbCr &
vbCr & "It must include one upper case " _
& "character, one lower case character, one numerical
character, " _
& "and one of the following special characters: @#$%*.", _
"Create Password")
If StrPtr(pStr) = 0 Then Exit Do 'User clicked cancel
'Validate password meets parameters
For i = 1 To Len(pStr)
'Verify proper length
If i = 1 Then
Select Case Len(pStr)
Case Is < 5
MsgBox "Your password is too short. The password must contain
between five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password
Invalid"
Exit For
Case Is > 10
MsgBox "Your password is too long. The password must contain
between five and ten characters. " _
& "Please try again.", vbInformation + vbOKOnly, "Password
Invalid"
Exit For
Case Else
bValidLen = True
End Select
End If
'When condition met set boolean variable to true
If Mid(pStr, i, 1) Like "[A-Z]" Then bUC = True
If Mid(pStr, i, 1) Like "[a-z]" Then bLC = True
If Mid(pStr, i, 1) Like "[0-9]" Then bNum = True
If InStr("@#$%*", Mid(pStr, i, 1)) > 0 Then bSC = True
'If all conditions met set boolean variable to true
If bUC And bLC And bNum And bSC Then
bValidated = True
Exit For
End If
Next i
'Notify user of invalid conditions
If Not bValidated And bValidLen Then
pInvalidStr = "The password must contain"
If Not bUC Then pInvalidStr = pInvalidStr & " at least one UCase
character,"
If Not bLC Then pInvalidStr = pInvalidStr & " at least one LCase
character,"
If Not bNum Then pInvalidStr = pInvalidStr & " at least one
numerical character,"
If Not bSC Then pInvalidStr = pInvalidStr & " at least one special
character (@#$%*),"
'Clean up the string
pInvalidStr = Left(pInvalidStr, Len(pInvalidStr) - 1) & "."
On Error Resume Next
pInvalidStr = Left(pInvalidStr, InStrRev(pInvalidStr, ",") - 1) &
" and" & Mid(pInvalidStr, InStrRev(pInvalidStr, ",") + 1)
MsgBox pInvalidStr
pInvalidStr = ""
'Confirm valid passwords
ElseIf bValidated Then
pStrConfirm = InputBox("Re-enter your password.", "Confirm
Password") If pStrConfirm <> pStr Then
bValidated = False
MsgBox "Your password was not confirmed. Please try again.",
vbInformation + vbOKOnly, "Confirmation Failed"
End If
End If
Loop Until bValidated
'Set password
If bValidated Then
'ActiveDocument.Password = pStr 'STETTED for testing purposes.
'ActiveDocument.Save
MsgBox "Your password has been set."
End If
End Sub

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is
marred by dust and sweat and blood, who strives valiantly...who
knows the great enthusiasms, the great devotions, who spends himself
in a worthy cause, who at the best knows in the end the triumph of
high achievement, and who at the worst, if he fails, at least fails
while daring greatly, so that his place shall never be with those
cold and timid souls who have never known neither victory nor
defeat." - TR

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is
marred by dust and sweat and blood, who strives valiantly...who knows
the great enthusiasms, the great devotions, who spends himself in a
worthy cause, who at the best knows in the end the triumph of high
achievement, and who at the worst, if he fails, at least fails while
daring greatly, so that his place shall never be with those cold and
timid souls who have never known neither victory nor defeat." - TR
 
K

Karl E. Peterson

Greg said:
Tony,

Thanks. That is the exactly the type of thing I was looking for. Revised
code:

I'd suggest separating the UI from the logic. Your code would be far more readable,
thus reviewable, if it were simply:

Public Enum PasswordProblems
ppTooShort = 1
ppTooLong = 2
ppMissingUCase = 4
ppMissingLCase = 8
ppMissingNumeric = 16
ppMissingSpecial = 32
End Enum

Public Function TestPassword(ByVal pStr As String) As Long
Dim nRet As Long

'Check length
If Len(pStr) < 5 Then
nRet = nRet Or ppTooShort
ElseIf Len(pStr) > 10 Then
nRet = nRet Or ppTooLong
End If

' Check special character conditions
If pStr Like "*[A-Z]*" Then nRet = nRet Or ppMissingUCase
If pStr Like "*[a-z]*" Then nRet = nRet Or ppMissingLCase
If pStr Like "*[0-9]*" Then nRet = nRet Or ppMissingNumeric
If pStr Like "*[@#$%*]*" Then nRet = nRet Or ppMissingSpecial

' Return findings
TestPassword = nRet
End Function

You're not really looking for advice on how you interact with the user, right?
 
G

Greg Maxey

Karl,

Right.

I am going to have to study what you have done for a bit. I have never used
Enum before so I need to get my head around the idea and your use of "OR"
e.g.,

nRet = nRet Or ppMissingUCase

I can step through and see that nRet is going to = 60 if all conditions are
met, but I don't yet understand why. Taking baby steps here ;-)

Why do you use "Or" instead of using "+" ??

Thanks.


Greg said:
Tony,

Thanks. That is the exactly the type of thing I was looking for. Revised
code:

I'd suggest separating the UI from the logic. Your code would be far
more readable, thus reviewable, if it were simply:

Public Enum PasswordProblems
ppTooShort = 1
ppTooLong = 2
ppMissingUCase = 4
ppMissingLCase = 8
ppMissingNumeric = 16
ppMissingSpecial = 32
End Enum

Public Function TestPassword(ByVal pStr As String) As Long
Dim nRet As Long

'Check length
If Len(pStr) < 5 Then
nRet = nRet Or ppTooShort
ElseIf Len(pStr) > 10 Then
nRet = nRet Or ppTooLong
End If

' Check special character conditions
If pStr Like "*[A-Z]*" Then nRet = nRet Or ppMissingUCase
If pStr Like "*[a-z]*" Then nRet = nRet Or ppMissingLCase
If pStr Like "*[0-9]*" Then nRet = nRet Or ppMissingNumeric
If pStr Like "*[@#$%*]*" Then nRet = nRet Or ppMissingSpecial

' Return findings
TestPassword = nRet
End Function

You're not really looking for advice on how you interact with the
user, right?

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is
marred by dust and sweat and blood, who strives valiantly...who knows
the great enthusiasms, the great devotions, who spends himself in a
worthy cause, who at the best knows in the end the triumph of high
achievement, and who at the worst, if he fails, at least fails while
daring greatly, so that his place shall never be with those cold and
timid souls who have never known neither victory nor defeat." - TR
 
T

Tony Jollans

Karl,
LOL!

The length check is a bit clunky - rather more simply

'Check length
If Len(pStr) < 5 Then nRet = nRet Or ppTooShort
If Len(pStr) > 10 Then nRet = nRet Or ppTooLong

--
Enjoy,
Tony

www.WordArticles.com

Greg Maxey said:
Karl,

Right.

I am going to have to study what you have done for a bit. I have never
used Enum before so I need to get my head around the idea and your use of
"OR" e.g.,

nRet = nRet Or ppMissingUCase

I can step through and see that nRet is going to = 60 if all conditions
are met, but I don't yet understand why. Taking baby steps here ;-)

Why do you use "Or" instead of using "+" ??

Thanks.


Greg said:
Tony,

Thanks. That is the exactly the type of thing I was looking for.
Revised code:

I'd suggest separating the UI from the logic. Your code would be far
more readable, thus reviewable, if it were simply:

Public Enum PasswordProblems
ppTooShort = 1
ppTooLong = 2
ppMissingUCase = 4
ppMissingLCase = 8
ppMissingNumeric = 16
ppMissingSpecial = 32
End Enum

Public Function TestPassword(ByVal pStr As String) As Long
Dim nRet As Long

'Check length
If Len(pStr) < 5 Then
nRet = nRet Or ppTooShort
ElseIf Len(pStr) > 10 Then
nRet = nRet Or ppTooLong
End If

' Check special character conditions
If pStr Like "*[A-Z]*" Then nRet = nRet Or ppMissingUCase
If pStr Like "*[a-z]*" Then nRet = nRet Or ppMissingLCase
If pStr Like "*[0-9]*" Then nRet = nRet Or ppMissingNumeric
If pStr Like "*[@#$%*]*" Then nRet = nRet Or ppMissingSpecial

' Return findings
TestPassword = nRet
End Function

You're not really looking for advice on how you interact with the
user, right?

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is
marred by dust and sweat and blood, who strives valiantly...who knows
the great enthusiasms, the great devotions, who spends himself in a
worthy cause, who at the best knows in the end the triumph of high
achievement, and who at the worst, if he fails, at least fails while
daring greatly, so that his place shall never be with those cold and
timid souls who have never known neither victory nor defeat." - TR
 
G

Greg Maxey

Karl,

Ok. I now understand why:

Sub Test
Dim i as Long
i = 5 Or 5
Msgbox i 'Returns 5
End Sub

So I now understand why if all the conditions are met then your nRet
returned 60. I am still not sure why you prefer Or over simply "+." Please
enlighten.

Since this is purely a learning exerience, I wanted to see if I could modify
your function so that it both tested the password and built any invalid
password message. To do this I added a Type statement PasswordTestResults
with two variables. Any bombs hidden in that approach?

Thanks for your interest and help.

Option Explicit
Type PasswordTestResults
lngResult As Long
pInvalidMsg As String
End Type

Public Enum PasswordParameters
ppTooShort = -1
ppTooLong = -2
ppUCase = 4
ppLCase = 8
ppNumeric = 16
ppSpecial = 32
End Enum

Sub SetRobustPassword()
Dim bValidated As Boolean
Dim pStr As String
Dim TestResults As PasswordTestResults
Dim pStrConfirm As String
Do
'Set boolean variable to false
bValidated = False
'Define the password
pStr = InputBox("Enter your password." & vbCr + vbCr & "The password " _
& "must contain 5 to 10 characters." & vbCr & vbCr & "It must
include one upper case " _
& "character, one lower case character, one numerical character, "
_
& "and one of the following special characters: @#$%*.", _
"Create Password")
If StrPtr(pStr) = 0 Then Exit Sub 'User clicked cancel
'Test if password meets parameters
TestResults = PasswordTest(pStr)
If TestResults.lngResult = 60 Then 'Test passed
'Confirm password
pStrConfirm = InputBox("Re-enter your password.", "Confirm Password")
If StrComp(pStr, pStrConfirm, vbTextCompare) = 0 Then
bValidated = True
Else
MsgBox "Your password was not confirmed. Please try again.",
vbExclamation + vbOKOnly, "Confirmation Failed"
End If
Else
'Notify user of invalid conditions
MsgBox TestResults.pInvalidMsg, vbExclamation + vbOKOnly, "Invalid
Password"
End If
Loop Until bValidated
'Set password
ActiveDocument.Password = pStr
ActiveDocument.Save
MsgBox "Your password has been set."
End Sub

Public Function PasswordTest(ByVal pStr As String) As PasswordTestResults
'Notes:
'1. The "Or" operator beforms a bitwise comparison which has the same
effect of adding "+" the PasswordParameter
' enumeration constants. A final lngParameter of 60 is required to pass
the test.
Dim lngParameter As Long
Dim pStrInvalidMsg As String
pStrInvalidMsg = "The password must contain"
'Check length
If Len(pStr) < 5 Then
lngParameter = lngParameter Or ppTooShort
pStrInvalidMsg = "The password is too short. " & pStrInvalidMsg & " at
least 5 characters,"
ElseIf Len(pStr) > 10 Then
lngParameter = lngParameter Or ppTooLong
pStrInvalidMsg = "The password is too long. " & pStrInvalidMsg & " less
than 10 characters,"
End If
'Check character conditions
If pStr Like "*[A-Z]*" Then
lngParameter = lngParameter Or ppUCase
Else
pStrInvalidMsg = pStrInvalidMsg & " at least one UCase character,"
End If
If pStr Like "*[a-z]*" Then
lngParameter = lngParameter Or ppLCase
Else
pStrInvalidMsg = pStrInvalidMsg & " at least one LCase character,"
End If
If pStr Like "*[0-9]*" Then
lngParameter = lngParameter Or ppNumeric
Else
pStrInvalidMsg = pStrInvalidMsg & " at least one numerical character,"
End If
If pStr Like "*[@#$%*]*" Then
lngParameter = lngParameter Or ppSpecial
Else
pStrInvalidMsg = pStrInvalidMsg & " at least one special character
(@#$%*),"
End If
'Clean up the string
pStrInvalidMsg = Left(pStrInvalidMsg, Len(pStrInvalidMsg) - 1) & "."
On Error Resume Next
pStrInvalidMsg = Left(pStrInvalidMsg, InStrRev(pStrInvalidMsg, ",") - 1) & "
and" _
& Mid(pStrInvalidMsg, InStrRev(pStrInvalidMsg, ",") + 1)
' Return findings
PasswordTest.lngResult = lngParameter '60 required to pass test.
PasswordTest.pInvalidMsg = pStrInvalidMsg
pStrInvalidMsg = ""
End Function






Greg said:
Tony,

Thanks. That is the exactly the type of thing I was looking for. Revised
code:

I'd suggest separating the UI from the logic. Your code would be far
more readable, thus reviewable, if it were simply:

Public Enum PasswordProblems
ppTooShort = 1
ppTooLong = 2
ppMissingUCase = 4
ppMissingLCase = 8
ppMissingNumeric = 16
ppMissingSpecial = 32
End Enum

Public Function TestPassword(ByVal pStr As String) As Long
Dim nRet As Long

'Check length
If Len(pStr) < 5 Then
nRet = nRet Or ppTooShort
ElseIf Len(pStr) > 10 Then
nRet = nRet Or ppTooLong
End If

' Check special character conditions
If pStr Like "*[A-Z]*" Then nRet = nRet Or ppMissingUCase
If pStr Like "*[a-z]*" Then nRet = nRet Or ppMissingLCase
If pStr Like "*[0-9]*" Then nRet = nRet Or ppMissingNumeric
If pStr Like "*[@#$%*]*" Then nRet = nRet Or ppMissingSpecial

' Return findings
TestPassword = nRet
End Function

You're not really looking for advice on how you interact with the
user, right?

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is
marred by dust and sweat and blood, who strives valiantly...who knows
the great enthusiasms, the great devotions, who spends himself in a
worthy cause, who at the best knows in the end the triumph of high
achievement, and who at the worst, if he fails, at least fails while
daring greatly, so that his place shall never be with those cold and
timid souls who have never known neither victory nor defeat." - TR
 
G

Greg Maxey

Karl,

While I certainly found the method and your suggestion to use Enum and the
bit with "Or" interesting, it seems that all I really need to know in this
case is if the password is valid "True" or "False."

I have revised the process accordingly with the goal of using the Function
to both validate the password and form the necessary message text if
invalid. Like when I started, this thing as revised could still be a
minefield full of poor practices. I really appreciate your interest and the
comments you have made so far. Thanks.


Option Explicit
Type PasswordTestResults
bValid As Boolean
pInvalidMsg As String
End Type
Sub SetRobustPassword()
Dim bConfirmed As Boolean
Dim pStr As String
Dim TestData As PasswordTestResults
Dim pStrConfirm As String
Do
'Set boolean variable to false
bConfirmed = False
'Define the password
pStr = InputBox("Enter your password." & vbCr + vbCr & "The password " _
& "must contain 5 to 10 characters." & vbCr & vbCr & "It must
include one upper case " _
& "character, one lower case character, one numerical character, "
_
& "and one of the following special characters: @#$%*.", _
"Create Password")
If StrPtr(pStr) = 0 Then Exit Sub 'User clicked cancel
'Test if password meets parameters
TestData = PasswordTest(pStr)
If TestData.bValid Then 'Test passed
'Confirm password
pStrConfirm = InputBox("Re-enter the password.", "Confirm Password")
If StrComp(pStr, pStrConfirm, vbTextCompare) = 0 Then
bConfirmed = True
'Set password
'ActiveDocument.Password = pStr '(STETTED out for testing)
ActiveDocument.Save
MsgBox "Your password has been set.", vbInformation + vbOKOnly,
"Password Set"
Else
MsgBox "Your password was not confirmed. Please try again.",
vbExclamation + vbOKOnly, "Confirmation Failed"
End If
Else
'Notify user of invalid conditions
MsgBox TestData.pInvalidMsg, vbExclamation + vbOKOnly, "Invalid
Password"
End If
Loop Until bConfirmed
End Sub


Public Function PasswordTest(ByVal pStr As String) As PasswordTestResults
Dim bValidated As Boolean
Dim pStrInvalidMsg As String
'Start with a true condtion. If any parameter test fails then set condition
to false.
bValidated = True
'Set boilerplate message text.
pStrInvalidMsg = "The password must contain"
'Verify parameters.
Select Case True
'If password is too short or too long there is no point checking further.
Case Len(pStr) < 5
bValidated = False
pStrInvalidMsg = "The password is too short. " & pStrInvalidMsg & " at
least 5 characters,"
Case Len(pStr) > 10
bValidated = False
pStrInvalidMsg = "The password is too long. " & pStrInvalidMsg & " less
than 10 characters,"
'If length is OK then verify remaining parameters.
Case Else
If Not pStr Like "*[A-Z]*" Then
bValidated = False
pStrInvalidMsg = pStrInvalidMsg & " at least one UCase character,"
End If
If Not pStr Like "*[a-z]*" Then
bValidated = False
pStrInvalidMsg = pStrInvalidMsg & " at least one LCase character,"
End If
If Not pStr Like "*[0-9]*" Then
bValidated = False
pStrInvalidMsg = pStrInvalidMsg & " at least one numerical character,"
End If
If Not pStr Like "*[@#$%*]*" Then
bValidated = False
pStrInvalidMsg = pStrInvalidMsg & " at least one special character
(@#$%*),"
End If
End Select
'Clean up the string
pStrInvalidMsg = Left(pStrInvalidMsg, Len(pStrInvalidMsg) - 1) & "."
On Error Resume Next
pStrInvalidMsg = Left(pStrInvalidMsg, InStrRev(pStrInvalidMsg, ",") - 1) & "
and" _
& Mid(pStrInvalidMsg, InStrRev(pStrInvalidMsg, ",") + 1)
'Return data
PasswordTest.bValid = bValidated 'True required to pass test.
PasswordTest.pInvalidMsg = pStrInvalidMsg
pStrInvalidMsg = ""
End Function






Greg said:
Tony,

Thanks. That is the exactly the type of thing I was looking for. Revised
code:

I'd suggest separating the UI from the logic. Your code would be far
more readable, thus reviewable, if it were simply:

Public Enum PasswordProblems
ppTooShort = 1
ppTooLong = 2
ppMissingUCase = 4
ppMissingLCase = 8
ppMissingNumeric = 16
ppMissingSpecial = 32
End Enum

Public Function TestPassword(ByVal pStr As String) As Long
Dim nRet As Long

'Check length
If Len(pStr) < 5 Then
nRet = nRet Or ppTooShort
ElseIf Len(pStr) > 10 Then
nRet = nRet Or ppTooLong
End If

' Check special character conditions
If pStr Like "*[A-Z]*" Then nRet = nRet Or ppMissingUCase
If pStr Like "*[a-z]*" Then nRet = nRet Or ppMissingLCase
If pStr Like "*[0-9]*" Then nRet = nRet Or ppMissingNumeric
If pStr Like "*[@#$%*]*" Then nRet = nRet Or ppMissingSpecial

' Return findings
TestPassword = nRet
End Function

You're not really looking for advice on how you interact with the
user, right?

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the
strong man stumbles, or where the doer of deeds could have done them
better. The credit belongs to the man in the arena, whose face is
marred by dust and sweat and blood, who strives valiantly...who knows
the great enthusiasms, the great devotions, who spends himself in a
worthy cause, who at the best knows in the end the triumph of high
achievement, and who at the worst, if he fails, at least fails while
daring greatly, so that his place shall never be with those cold and
timid souls who have never known neither victory nor defeat." - TR
 
K

Karl E. Peterson

Greg said:
Ok. I now understand why:

Sub Test
Dim i as Long
i = 5 Or 5
Msgbox i 'Returns 5
End Sub

So I now understand why if all the conditions are met then your nRet
returned 60. I am still not sure why you prefer Or over simply "+." Please
enlighten.

It's an old habit one acquires when dealing with bitflags. Using Or, you
absolutely/positively turn on a single bit. Nothing more, nothing less. If there's
a possibility of flags being combined in multiple ways, using + would pose a
potential problem because it's not working on individual bits. The more you work
with APIs, the more familiar bitflags will be. If they're totally unfamiliar, it's
the concept that each bit in a number represents a single true/false value. So with
a Long you get 32 flags! Very economical.

So, if we could assign values in binary to an Enum, instead of:

Public Enum PasswordProblems
ppTooShort = 1
ppTooLong = 2
ppMissingUCase = 4
ppMissingLCase = 8
ppMissingNumeric = 16
ppMissingSpecial = 32
End Enum

We would have had:

Public Enum PasswordProblems
ppTooShort = 00000000 00000001
ppTooLong = 00000000 00000010
ppMissingUCase = 00000000 00000100
ppMissingLCase = 00000000 00001000
ppMissingNumeric = 00000000 00010000
ppMissingSpecial = 00000000 00100000
End Enum

See the pattern? You can then test for any of the flags using And. For example, if
you want to know whether there weren't any numerics, you'd test like this:

If nRet And ppMissingNumeric Then 'no numbers!

You can also use it as a True/False value, where (perversely) success=0 and
failure<>0.

Make sense?
 
K

Karl E. Peterson

Greg said:
While I certainly found the method and your suggestion to use Enum and the
bit with "Or" interesting, it seems that all I really need to know in this
case is if the password is valid "True" or "False."

You have that as well.

If PasswordTest(ThePassword) > 0 Then 'bad password.
 
G

Greg Maxey

Karl,

Copy all.

Thanks again for your interest and taking the time to explain this.

--
Greg Maxey

See my web site http://gregmaxey.mvps.org
for an eclectic collection of Word Tips.

"It is not the critic who counts, not the man who points out how the strong
man stumbles, or where the doer of deeds could have done them better. The
credit belongs to the man in the arena, whose face is marred by dust and
sweat and blood, who strives valiantly...who knows the great enthusiasms,
the great devotions, who spends himself in a worthy cause, who at the best
knows in the end the triumph of high achievement, and who at the worst, if
he fails, at least fails while daring greatly, so that his place shall never
be with those cold and timid souls who have never known neither victory nor
defeat." - TR
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top