Solved

Visual Basic Code to Get AD Details & Validate Input

Posted on 2014-11-13
13
214 Views
Last Modified: 2014-11-20
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

0
Comment
Question by:bjblackmore
  • 7
  • 6
13 Comments
 

Author Comment

by:bjblackmore
Comment Utility
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
 

Author Comment

by:bjblackmore
Comment Utility
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
 
LVL 70

Expert Comment

by:Chris Dent
Comment Utility
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
 

Author Comment

by:bjblackmore
Comment Utility
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
 
LVL 70

Expert Comment

by:Chris Dent
Comment Utility
Have you imported System.DirectoryServices? And have you added a reference to System.DirectoryServices to your project?

Chris
0
 
LVL 70

Expert Comment

by:Chris Dent
Comment Utility
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
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 

Author Comment

by:bjblackmore
Comment Utility
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
 
LVL 70

Expert Comment

by:Chris Dent
Comment Utility
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
 

Author Comment

by:bjblackmore
Comment Utility
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
 

Author Comment

by:bjblackmore
Comment Utility
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
 
LVL 70

Accepted Solution

by:
Chris Dent earned 500 total points
Comment Utility
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
 

Author Comment

by:bjblackmore
Comment Utility
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
 
LVL 70

Expert Comment

by:Chris Dent
Comment Utility
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

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles to another domain controller. Log onto the new domain controller with a user account t…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now