Visual Basic Code to Get AD Details & Validate Input

I need our users to supply a 4 -6 digit PIN number, which will be stored in their account in AD (as customAttribute2), so if/when they phone support, the helpdesk/support tech can ask them for the PIN as a method of authentication.

I'd like to program a small visual basic application that we can roll out to the user's desktop, or get to launch once when the user logons, that will ask them to set the PIN number.

I've just downloaded Visual Studio 2012, and have started programing a small application. I've done some VB Script programming and some very basic visual basic programming a few years ago, however I enjoy the challenge & learning. So far I've build the basic form, with the button's and text/input boxes.

Dialog Box
I've got the cancel/exit button, and have set the input box to number only. But I'm having trouble getting it to query Active Directory. I wondered if anyone had some example snippets of code that I could use/modify.

I need code that will get the current logged on user's Windows ID, which will be show in the text box by the 'Hello'.
I also need code that will query CustomAttribute2 for an existing PIN, which will be shown by Current PIN:
Finally code that will update CustomAttribute2 with a new PIN when 'OK' is pressent.

Although I've set the Max length to 6, I'd also like to set a Mix length to 4, but there doesn't seem to be a Min length property.

Code I have so far:
Allow number only:
    Private Sub TextBox1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TextBox1.KeyPress
        If Not Char.IsDigit(e.KeyChar) And Not Char.IsControl(e.KeyChar) Then
            e.Handled = True
        End If
    End Sub

Open in new window


Get Logged on Windows Username (doesn't work/needs modifying):
   
 Function GetUserName() As String
        If TypeOf My.User.CurrentPrincipal Is 
          Security.Principal.WindowsPrincipal Then
            Dim parts() As String = Split(My.User.Name, "\")
            Dim username As String = parts(1)
            Return username
        Else
            ' The application is using custom authentication.
            Return My.User.Name
        End If
    End Function

Open in new window

bjblackmoreAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

bjblackmoreAuthor Commented:
Think I have the username bit:
        Dim currentADUser As System.DirectoryServices.AccountManagement.UserPrincipal
        currentADUser = System.DirectoryServices.AccountManagement.UserPrincipal.Current
        Dim DisplayName As String = currentADUser.GivenName & " " & currentADUser.Surname
        Label2.Text = "Hello " & DisplayName

Open in new window

0
bjblackmoreAuthor Commented:
Have managed to find code to validate minimum 4 numbers:
 Private Sub EnterPIN_Leave(sender As Object, e As EventArgs) Handles EnterPIN.Leave
        If EnterPIN.Text.Length < 4 Then
            MsgBox("Please enter 4 - 6 numbers")
        End If
    End Sub

Open in new window

0
Chris DentPowerShell DeveloperCommented:
Have you thought about validating the input using a regular expression? A simple regular expression like \d{4,6} would let you determine whether or not the user had entered 4 to 6 ({4,6} digits (\d). It's more expensive than a length check, but would stop someone entering "  123" or "1a2b3c" and so on.

Chris
0
Problems using Powershell and Active Directory?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

bjblackmoreAuthor Commented:
Thanks for the reply.
The code I have above works near perfectly for the input validation. It will only allow you to enter 0-9, alpha characters just won't work, which is fine.

My main stumbling block currently is the AD data push/pull.

I've found the below, but I keep getting errors on DirectoryEntry & DirectorySearcher not being defined.

        Dim ADEntry As New DirectoryEntry(OUDistinguishedName)
        Dim ADSearch As New DirectorySearcher(ADEntry)
        ADSearch.Filter = ("(&(objectClass=user)(sAMAccountName=ADName))")
        ADSearch.SearchScope = System.DirectoryServices.SearchScope.OneLevel
        ADSearch.PropertiesToLoad.Add("sAMAccountName")
        ADSearch.PropertiesToLoad.Add("extensionAttribute1")
        Dim ADSearchResult As SearchResultCollection = ADSearch.FindAll()
        If ADSearchResult Is Nothing Then
            Return Nothing
        Else
            Return ADSearchResult
        End If

Open in new window

0
Chris DentPowerShell DeveloperCommented:
Have you imported System.DirectoryServices? And have you added a reference to System.DirectoryServices to your project?

Chris
0
Chris DentPowerShell DeveloperCommented:
Oh and since you're using FindAll(), if your directory is larger than 1000 users you will need to enable Paging on the query:

ADSearch.PageSize = 1000

That will allow it to exceed the 1000 limit (returns multiple pages, 1000 per page where 1000 is the maximum supported by Active Directory).

Chris
0
bjblackmoreAuthor Commented:
yes, I've added reference 'System.Directoryservices.AccountManagement' & 'System.DirectoryServices'. I wasn't sure if I needed both. The first worked on it's own for getting the logged in username. But when I got the errors in the new AD code, I also added 'System.Directoryservices'.

I don't really need to use FindAll() thinking about it. As I'm searching for the samAccountName of the user, of which there should/can only be one in AD.
0
Chris DentPowerShell DeveloperCommented:
Then FindOne() would be good for you for sure. That'll make:
Dim ADSearchResult As SearchResult = ADSearch.FindOne()

Open in new window

Once you have that you'll be able to address the properties of the search result without resorting to a loop.

If you're still getting errors about instantiating DirectoryEntry it implies "Imports System.DirectoryServices" has been missed.

Chris
0
bjblackmoreAuthor Commented:
The references were added, but not imported into the name space. Seems to get less erorrs now.

However, 1 error that still exists on the below is line 8 (Dim dirEntryResults As New DirectoryEntry(results.Path)). I get
'results' is not declared. It may be inaccessible due to its protection level.

    Public Sub UpdateUserADAccount(ByVal ADName As String, ByVal EnterPIN As String)
        Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
        Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
        dirSearcher.Filter = "(&(objectCategory=Person)(objectClass=user) (SAMAccountName=ben.blackmore)"
        dirSearcher.SearchScope = SearchScope.Subtree
        Dim searchResults As SearchResult = dirSearcher.FindOne()
        If Not searchResults Is Nothing Then
            Dim dirEntryResults As New DirectoryEntry(results.Path)
            SetADProperty(dirEntryResults, "extensionAttribute1", EnterPIN)
            dirEntryResults.CommitChanges()
            dirEntryResults.Close()
        End If
        dirEntry.Close()
    End Sub

Open in new window

0
bjblackmoreAuthor Commented:
Think I fixed that. Should be

Dim dirEntryResults As New DirectoryEntry(searchResults.Path)
not
Dim dirEntryResults As New DirectoryEntry(results.Path)

        Dim EnterPIN As String
        Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
        Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
        dirSearcher.Filter = "(&(objectClass=user)(objectCategory=person)(sAMAccountName=" & ADName & "))"
        dirSearcher.SearchScope = SearchScope.Subtree
        Dim searchResults As SearchResult = dirSearcher.FindOne()
        If Not searchResults Is Nothing Then
            Dim dirEntryResults As New DirectoryEntry(searchResults.Path)
            SetADProperty(dirEntryResults, "extensionAttribute3", EnterPIN)
            dirEntryResults.CommitChanges()
            dirEntryResults.Close()
        End If
        dirEntry.Close()

Open in new window

0
Chris DentPowerShell DeveloperCommented:
Looks much better :)

You know though, you can do this:
If Not searchResults Is Nothing Then
  Dim dirEntryResults as DirectoryEntry = searchResults.GetDirectoryEntry()
  ' ...
End If

Open in new window

This is especially useful if you went to lengths to authenticate your DirectorySearcher, you won't have to re-authenticate when it gets to dirEntryResults.

It's perhaps worth noting at this time that if that PIN is sensitive you should review how you're storing it. By default Active Directory allows all authenticated users read access, so anyone in your directory could (at least in theory) grab the PIN numbers for every user.

Chris
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
bjblackmoreAuthor Commented:
Apologies for my delay in replying. Hectic week. All seems to be working now. I can pull the username & attribute from AD, display it, and update it.

The PIN isn't really sensative. Just a way of being able to quickly authenticate users when they call the service desk. They'd have to have an AD account to be able to read the attribute. Plus know where to look, and what that attribute was for. And none of our users actually know how to query AD anyway, so it should be OK.
0
Chris DentPowerShell DeveloperCommented:
Should it become a problem later I did think of one way around the security problem. Obviously you have no need to use it, but it's a point of interest (at least for me :)).

If you were to generate a SHA1 (or even MD5) hash from the PIN, perhaps with a bit of salt, you could easily obscure the value in AD.

That is, if the user enters "1234" and you add some salt, let's say the string "PIN obscured by bjblackmore" to make "1234 PIN obscured by bjblackmore" you get this SHA1 hash:

97635531f0d33f81d69281b9f2939f2d77e49293

Hashing is one way, so short of trying every combination (and figuring out the salt) it wouldn't be easy to get back to the PIN (not impossible, just not easy).

Whatever app the service desk uses would need to compare the hash based on the user PIN (and the salt only the code knows about, or knows how to generate / acquire) and tell them good or bad. If the hash generated by the service desk matches the hash in AD the PIN must match and it's all good.

Chris
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.